mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-09-01 06:45:38 +00:00
Compare commits
195 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
c52cf4d537 | ||
|
1e8dc4f375 | ||
|
7988124de5 | ||
|
f0aa65c832 | ||
|
1c96c09ec9 | ||
|
9685aae967 | ||
|
c7a6608777 | ||
|
bca67d3d27 | ||
|
9c39909a9c | ||
|
f278505db2 | ||
|
e91cc118df | ||
|
e1763ba13c | ||
|
cad0c3c8a9 | ||
|
49103b30f2 | ||
|
df47a59ecb | ||
|
091ec763f9 | ||
|
33b7c5316f | ||
|
986093cf8d | ||
|
33c62acc5c | ||
|
8b671f013a | ||
|
5789ab84a6 | ||
|
6d6e04dc12 | ||
|
7a7c99f3a1 | ||
|
9896f5edbd | ||
|
12a98135bf | ||
|
a30dfb6b19 | ||
|
a4d4eddd92 | ||
|
b393893c7a | ||
|
572fb7e943 | ||
|
7173d7a6a5 | ||
|
7b577d72b5 | ||
|
5a140c2e5c | ||
|
e922cdb113 | ||
|
189da8236c | ||
|
2c62d802b6 | ||
|
23df761b70 | ||
|
6a80641ee2 | ||
|
02e1e94ab9 | ||
|
f28b91b8cf | ||
|
6849615de6 | ||
|
743f84099d | ||
|
a041b1738c | ||
|
1056ef8418 | ||
|
ac77e10a0f | ||
|
eae6f0525c | ||
|
341b7e61da | ||
|
eb6d2224bd | ||
|
bb1b5f986b | ||
|
1c1cc65cbd | ||
|
5dedd16bf5 | ||
|
66d51b575d | ||
|
f26df713c0 | ||
|
b8f486dee9 | ||
|
ac7e66684c | ||
|
be3d2bc1a4 | ||
|
8f28eebe5a | ||
|
d17a87bd28 | ||
|
59eb0af705 | ||
|
7d5840d449 | ||
|
fb7f0ddaaa | ||
|
4bdc7ea51a | ||
|
3ac9f7e676 | ||
|
843ef93202 | ||
|
00726ed51a | ||
|
0cbad5b99b | ||
|
d180252c52 | ||
|
ca5cd28d48 | ||
|
12e0d064cd | ||
|
386a5abc7d | ||
|
084233a207 | ||
|
94e665b3fa | ||
|
0a5c4fa159 | ||
|
9fddd7a716 | ||
|
92473dfc46 | ||
|
8ee0292d8e | ||
|
174105b9fa | ||
|
7f45708c86 | ||
|
015229d551 | ||
|
267e9610c7 | ||
|
32797f5780 | ||
|
ce85512673 | ||
|
5f630f2c5d | ||
|
4fc9106d37 | ||
|
614c051a71 | ||
|
94f06d23c0 | ||
|
a8e0b0d0a3 | ||
|
0f3263f710 | ||
|
2a81cadd27 | ||
|
f69822107f | ||
|
4dec6cab65 | ||
|
0287ce0c9d | ||
|
aae597bfde | ||
|
8347fb69c2 | ||
|
685632db0c | ||
|
5169b4556a | ||
|
3f099da257 | ||
|
d4c4cbe62b | ||
|
c66975ffc5 | ||
|
c35ac067b2 | ||
|
851a8c8b3a | ||
|
d081f7cb85 | ||
|
2c5534ca18 | ||
|
08fb58e10d | ||
|
13c3e40044 | ||
|
b5e525b251 | ||
|
663698c7a6 | ||
|
957f1ae00f | ||
|
763855fe9e | ||
|
e6880f9584 | ||
|
786f592d7e | ||
|
38d83c3c76 | ||
|
7897ba2293 | ||
|
a43931e987 | ||
|
2fec4eb2b4 | ||
|
3f59259dec | ||
|
2c110ed3d5 | ||
|
0d9a39bd26 | ||
|
5e273b0892 | ||
|
e91c83964a | ||
|
35acee98d5 | ||
|
8434463621 | ||
|
20f117500f | ||
|
850bce2754 | ||
|
7c05b9ed91 | ||
|
5abb79d5be | ||
|
70a06835b8 | ||
|
2e73225586 | ||
|
65c7473407 | ||
|
901e67a83f | ||
|
33c615e5f9 | ||
|
fdae9784f4 | ||
|
25f7aa6621 | ||
|
156a980c30 | ||
|
e9d5d491b8 | ||
|
b9bfc10d0f | ||
|
12876afa07 | ||
|
e66e56b020 | ||
|
6a7a20da88 | ||
|
35cdeebe19 | ||
|
c2fdcf7d39 | ||
|
627638a6cf | ||
|
414e5bf560 | ||
|
343749dc26 | ||
|
6a68aa2ecb | ||
|
70bb296a70 | ||
|
a25a2ec0d9 | ||
|
1644ce31e7 | ||
|
abcf66292d | ||
|
42a5483ba9 | ||
|
83282f8700 | ||
|
8485c8e417 | ||
|
4670afee7c | ||
|
e946b88d82 | ||
|
780ae4663d | ||
|
34676334b6 | ||
|
d799edbcc6 | ||
|
00f32d555d | ||
|
b7a8a01ac7 | ||
|
3a8546732a | ||
|
d656afa1d5 | ||
|
5b3190d39c | ||
|
c5dca95504 | ||
|
dce1d5d5e6 | ||
|
bb31faff1b | ||
|
162b49b417 | ||
|
f8b43d5ba9 | ||
|
e8f297db5a | ||
|
4d332ff241 | ||
|
83007d7600 | ||
|
5f72ae98eb | ||
|
940ead1fb8 | ||
|
bb28ca5371 | ||
|
0edae73a9a | ||
|
19a2d6d169 | ||
|
f3b847c4b0 | ||
|
1a2484e5bc | ||
|
6ed55cb1d5 | ||
|
099f19f99c | ||
|
7d2a6b53d4 | ||
|
6f0c68a4d4 | ||
|
9a377bb9da | ||
|
3cfe47d3f0 | ||
|
84c0bba1ef | ||
|
6aad970d1c | ||
|
298a36bffb | ||
|
846cee5066 | ||
|
50760ef05b | ||
|
0b383ad769 | ||
|
55bad42088 | ||
|
258c39d4a5 | ||
|
9819bf5df0 | ||
|
174c89f772 | ||
|
52e14b5c93 | ||
|
83ef426514 | ||
|
e14c6c39f1 |
@@ -1,3 +1,4 @@
|
||||
apparmor-*
|
||||
parser/po/*.mo
|
||||
parser/af_names.h
|
||||
parser/cap_names.h
|
||||
@@ -5,6 +6,7 @@ parser/tst_misc
|
||||
parser/tst_regex
|
||||
parser/tst_symtab
|
||||
parser/tst_variable
|
||||
parser/tst/simple_tests/generated_*/*
|
||||
parser/parser_lex.c
|
||||
parser/parser_version.h
|
||||
parser/parser_yacc.c
|
||||
|
19
Makefile
19
Makefile
@@ -10,10 +10,6 @@ DIRS=parser \
|
||||
changehat/libapparmor \
|
||||
changehat/mod_apparmor \
|
||||
changehat/pam_apparmor \
|
||||
management/apparmor-dbus \
|
||||
management/applets/apparmorapplet-gnome \
|
||||
management/yastui \
|
||||
common \
|
||||
tests
|
||||
|
||||
REPO_URL?=lp:apparmor
|
||||
@@ -24,12 +20,17 @@ REPO_URL?=lp:apparmor
|
||||
RELEASE_DIR=apparmor-${VERSION}
|
||||
__SETUP_DIR?=.
|
||||
|
||||
# We create a separate version for tags because git can't handle tags
|
||||
# with embedded ~s in them. No spaces around '-' or they'll get
|
||||
# embedded in ${VERSION}
|
||||
TAG_VERSION=$(subst ~,-,${VERSION})
|
||||
|
||||
.PHONY: tarball
|
||||
tarball: clean
|
||||
REPO_VERSION=`$(value REPO_VERSION_CMD)` ; \
|
||||
make export_dir __EXPORT_DIR=${RELEASE_DIR} __REPO_VERSION=$${REPO_VERSION} ; \
|
||||
make setup __SETUP_DIR=${RELEASE_DIR} ; \
|
||||
tar cvzf ${RELEASE_DIR}.tar.gz ${RELEASE_DIR}
|
||||
tar --exclude deprecated -cvzf ${RELEASE_DIR}.tar.gz ${RELEASE_DIR}
|
||||
|
||||
.PHONY: snapshot
|
||||
snapshot: clean
|
||||
@@ -37,7 +38,7 @@ snapshot: clean
|
||||
SNAPSHOT_DIR=apparmor-${VERSION}~$${REPO_VERSION} ;\
|
||||
make export_dir __EXPORT_DIR=$${SNAPSHOT_DIR} __REPO_VERSION=$${REPO_VERSION} ; \
|
||||
make setup __SETUP_DIR=$${SNAPSHOT_DIR} ; \
|
||||
tar cvzf $${SNAPSHOT_DIR}.tar.gz $${SNAPSHOT_DIR} ;
|
||||
tar --exclude deprecated -cvzf $${SNAPSHOT_DIR}.tar.gz $${SNAPSHOT_DIR} ;
|
||||
|
||||
|
||||
.PHONY: export_dir
|
||||
@@ -49,6 +50,9 @@ export_dir:
|
||||
.PHONY: clean
|
||||
clean:
|
||||
-rm -rf ${RELEASE_DIR} ./apparmor-${VERSION}~*
|
||||
for dir in $(DIRS); do \
|
||||
make -C $$dir clean; \
|
||||
done
|
||||
|
||||
.PHONY: setup
|
||||
setup:
|
||||
@@ -56,4 +60,5 @@ setup:
|
||||
|
||||
.PHONY: tag
|
||||
tag:
|
||||
bzr tag apparmor_${VERSION}
|
||||
bzr tag apparmor_${TAG_VERSION}
|
||||
|
||||
|
45
README
45
README
@@ -17,7 +17,8 @@ library, available under the LGPL license, which allows change_hat(2)
|
||||
and change_profile(2) to be used by non-GPL binaries).
|
||||
|
||||
For more information, you can read the techdoc.pdf (available after
|
||||
building the parser) and http://apparmor.wiki.kernel.org.
|
||||
building the parser) and by visiting the http://apparmor.net/ web
|
||||
site.
|
||||
|
||||
|
||||
-------------
|
||||
@@ -29,13 +30,26 @@ AppArmor consists of several different parts:
|
||||
changehat/ source for using changehat with Apache, PAM and Tomcat
|
||||
common/ common makefile rules
|
||||
desktop/ empty
|
||||
kernel-patches/ patches for various kernel versions
|
||||
kernel-patches/ compatibility patches for various kernel versions
|
||||
libraries/ libapparmor source and language bindings
|
||||
parser/ source for parser/loader and corresponding documentation
|
||||
profiles/ configuration files, reference profiles and abstractions
|
||||
tests/ regression and stress testsuites
|
||||
utils/ high-level utilities for working with AppArmor
|
||||
|
||||
--------------------------------------
|
||||
Important note on AppArmor kernel code
|
||||
--------------------------------------
|
||||
|
||||
While most of the kernel AppArmor code has been accepted in the
|
||||
upstream Linux kernel, a few important pieces were not included. These
|
||||
missing pieces unfortunately are important bits for AppArmor userspace
|
||||
and kernel interaction; therefore we have included compatibility
|
||||
patches in the kernel-patches/ subdirectory, versioned by upstream
|
||||
kernel (2.6.37 patches should apply cleanly to 2.6.38 source).
|
||||
|
||||
Without these patches applied to the kernel, the AppArmor userspace
|
||||
will not function correctly.
|
||||
|
||||
------------------------------------------
|
||||
Building and Installing AppArmor Userspace
|
||||
@@ -48,43 +62,52 @@ the following order.
|
||||
libapparmor:
|
||||
$ cd ./libraries/libapparmor
|
||||
$ sh ./autogen.sh
|
||||
$ sh ./configure --prefix=/usr --with-perl
|
||||
$ sh ./configure --prefix=/usr --with-perl # see below
|
||||
$ make
|
||||
$ make check
|
||||
$ make install
|
||||
|
||||
[optional arguments to libapparmor's configure include --with-python
|
||||
and --with-ruby, to generate python and ruby bindings to libapparmor,
|
||||
respectively.]
|
||||
|
||||
|
||||
Utilities:
|
||||
$ cd utils
|
||||
$ make
|
||||
$ make check
|
||||
$ make install
|
||||
|
||||
|
||||
parser:
|
||||
$ cd parser
|
||||
$ make
|
||||
$ make tests # not strictly necessary as they are run during the
|
||||
# build by default
|
||||
$ make check
|
||||
$ make install
|
||||
|
||||
|
||||
Apache mod_apparmor:
|
||||
$ cd changehat/mod_apparmor
|
||||
$ LIBS="-lapparmor" make
|
||||
$ make # depends on libapparmor having been built first
|
||||
$ make install
|
||||
|
||||
|
||||
PAM AppArmor:
|
||||
$ cd changehat/pam_apparmor
|
||||
$ LIBS="-lapparmor -lpam" make
|
||||
$ make # depends on libapparmor having been built first
|
||||
$ make install
|
||||
|
||||
|
||||
Profiles:
|
||||
$ cd profiles
|
||||
$ make
|
||||
$ make check # depends on the parser having been built first
|
||||
$ make install
|
||||
|
||||
|
||||
[Note that for the parser and the utils, if you only with to build/use
|
||||
some of the locale languages, you can override the default by passing
|
||||
the LANGS arguments to make; e.g. make all install "LANGS=en_US fr".]
|
||||
|
||||
-------------------
|
||||
AppArmor Testsuites
|
||||
@@ -123,6 +146,14 @@ For details on structure and adding tests, see libraries/libapparmor/README.
|
||||
$ cd libraries/libapparmor
|
||||
$ make check
|
||||
|
||||
Profile checks
|
||||
--------------
|
||||
A basic consistency check to ensure that the parser and aa-logprof parse
|
||||
successfully the current set of shipped profiles. The system or other
|
||||
parser and logprof can be passed in by overriding the PARSER and LOGPROF
|
||||
variables.
|
||||
$ cd profiles
|
||||
$ make && make check
|
||||
|
||||
Stress Tests
|
||||
------------
|
||||
|
@@ -41,12 +41,15 @@ APXS:=$(shell if [ -x "/usr/sbin/apxs2" ] ; then \
|
||||
fi )
|
||||
APXS_INSTALL_DIR=$(shell ${APXS} -q LIBEXECDIR)
|
||||
DESTDIR=
|
||||
LIBAPPARMOR_FLAGS="-I../../libraries/libapparmor/src -L../../libraries/libapparmor/src/.libs -lapparmor"
|
||||
# Need to pass -Wl twice here to get past both apxs2 and libtool, as
|
||||
# libtool will add the path to the RPATH of the library if passed -L/some/path
|
||||
LIBAPPARMOR_FLAGS=-I../../libraries/libapparmor/src -Wl,-Wl,-L../../libraries/libapparmor/src/.libs
|
||||
LDLIBS=-lapparmor
|
||||
|
||||
all: $(TARGET) ${MANPAGES} ${HTMLMANPAGES}
|
||||
|
||||
%.so: %.c
|
||||
${APXS} ${LIBAPPARMOR_FLAGS} -c $<
|
||||
${APXS} ${LIBAPPARMOR_FLAGS} -c $< ${LDLIBS}
|
||||
mv .libs/$@ .
|
||||
|
||||
.PHONY: install
|
||||
|
@@ -37,4 +37,4 @@ clean:
|
||||
rm -f tomcat_apparmor.spec ${NAME}-*.tar.gz Make.rules
|
||||
|
||||
install: $(SPECFILE)
|
||||
ant -Dversion=$(VERSION) -Drelease=$(RELEASE) -Dcatalina_home=${CATALINA_HOME} -Dinstall_lib=${LIB} install_jar install_jni
|
||||
ant -Dversion=$(VERSION) -Drelease=$(MAN_RELEASE) -Dcatalina_home=${CATALINA_HOME} -Dinstall_lib=${LIB} install_jar install_jni
|
||||
|
@@ -37,4 +37,4 @@ clean:
|
||||
rm -f tomcat_apparmor.spec ${NAME}-*.tar.gz Make.rules
|
||||
|
||||
install: $(SPECFILE)
|
||||
ant -Dversion=$(VERSION) -Drelease=$(RELEASE) -Dcatalina_home=${CATALINA_HOME} -Dinstall_lib=${LIB} install_jar install_jni
|
||||
ant -Dversion=$(VERSION) -Drelease=$(MAN_RELEASE) -Dcatalina_home=${CATALINA_HOME} -Dinstall_lib=${LIB} install_jar install_jni
|
||||
|
@@ -27,7 +27,8 @@ XGETTEXT_ARGS=--copyright-holder="NOVELL, Inc." --msgid-bugs-address=apparmor@li
|
||||
# pass in the list of sources in the SOURCES variable
|
||||
PARENT_SOURCES=$(foreach source, ${SOURCES}, ../${source})
|
||||
|
||||
LANGS=$(patsubst %.po, %, $(wildcard *.po))
|
||||
# Can override by passing LANGS=whatever here
|
||||
LANGS?=$(patsubst %.po, %, $(wildcard *.po))
|
||||
TARGET_MOS=$(foreach lang, $(filter-out $(DISABLED_LANGS),$(LANGS)), ${lang}.mo)
|
||||
|
||||
.PHONY: all
|
||||
|
@@ -1 +1 @@
|
||||
2.6.0
|
||||
2.7.0
|
||||
|
218
deprecated/utils/aa-status
Normal file
218
deprecated/utils/aa-status
Normal file
@@ -0,0 +1,218 @@
|
||||
#!/usr/bin/perl -w
|
||||
# ------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (C) 2005-2006 Novell/SUSE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2 of the GNU General Public
|
||||
# License published by the Free Software Foundation.
|
||||
#
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
|
||||
use strict;
|
||||
use Getopt::Long;
|
||||
use Cwd 'abs_path';
|
||||
|
||||
my $confdir = "/etc/apparmor";
|
||||
my $sd_mountpoint;
|
||||
my $check_enabled = 0;
|
||||
my $count_enforced = 0;
|
||||
my $count_profiled = 0;
|
||||
my $count_complain = 0;
|
||||
my $verbose = 0;
|
||||
my $help;
|
||||
|
||||
GetOptions(
|
||||
'complaining' => \$count_complain,
|
||||
'enabled' => \$check_enabled,
|
||||
'enforced' => \$count_enforced,
|
||||
'profiled' => \$count_profiled,
|
||||
'verbose|v' => \$verbose,
|
||||
'help|h' => \$help,
|
||||
) or usage();
|
||||
|
||||
sub usage {
|
||||
print "Usage: $0 [OPTIONS]\n";
|
||||
print "Displays various information about the currently loaded AppArmor policy.\n";
|
||||
print "OPTIONS (one only):\n";
|
||||
print " --enabled returns error code if subdomain not enabled\n";
|
||||
print " --profiled prints the number of loaded policies\n";
|
||||
print " --enforced prints the number of loaded enforcing policies\n";
|
||||
print " --complaining prints the number of loaded non-enforcing policies\n";
|
||||
print " --verbose (default) displays multiple data points about loaded policy set\n";
|
||||
print " --help this message\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
$verbose = 1 if ($count_complain + $check_enabled + $count_enforced + $count_profiled == 0);
|
||||
usage() if $help or ($count_complain + $check_enabled + $count_enforced + $count_profiled + $verbose > 1);
|
||||
|
||||
sub is_subdomain_loaded() {
|
||||
return 1 if (-d "/sys/module/apparmor");
|
||||
if(open(MODULES, "/proc/modules")) {
|
||||
while(<MODULES>) {
|
||||
return 1 if m/^(subdomain|apparmor)\s+/;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub find_subdomainfs() {
|
||||
|
||||
my $sd_mountpoint;
|
||||
if(open(MOUNTS, "/proc/mounts")) {
|
||||
while(<MOUNTS>) {
|
||||
$sd_mountpoint = "$1/apparmor" if m/^\S+\s+(\S+)\s+securityfs\s/ && -e "$1/apparmor";
|
||||
$sd_mountpoint = "$1/subdomain" if m/^\S+\s+(\S+)\s+securityfs\s/ && -e "$1/subdomain";
|
||||
$sd_mountpoint = $1 if m/^\S+\s+(\S+)\s+subdomainfs\s/ && -e "$1";
|
||||
}
|
||||
close(MOUNTS);
|
||||
}
|
||||
|
||||
return $sd_mountpoint;
|
||||
}
|
||||
|
||||
sub get_profiles {
|
||||
my $mountpoint = shift;
|
||||
my %profiles = ();
|
||||
|
||||
if (open(PROFILES, "$mountpoint/profiles")) {
|
||||
while(<PROFILES>) {
|
||||
$profiles{$1} = $2 if m/^([^\(]+)\s+\((\w+)\)$/;
|
||||
}
|
||||
close(PROFILES);
|
||||
}
|
||||
return (%profiles);
|
||||
}
|
||||
|
||||
sub get_processes {
|
||||
my %profiles = @_;
|
||||
my %processes = ();
|
||||
if (opendir(PROC, "/proc")) {
|
||||
my $file;
|
||||
while (defined($file = readdir(PROC))) {
|
||||
if ($file =~ m/^\d+/) {
|
||||
if (open(CURRENT, "/proc/$file/attr/current")) {
|
||||
while (<CURRENT>) {
|
||||
if (m/^([^\(]+)\s+\((\w+)\)$/) {
|
||||
$processes{$file}{'profile'} = $1;
|
||||
$processes{$file}{'mode'} = $2;
|
||||
} elsif (grep(abs_path("/proc/$file/exe") eq $_ , keys(%profiles))) {
|
||||
# keep only unconfined processes that have a profile defined
|
||||
$processes{$file}{'profile'} = abs_path("/proc/$file/exe");
|
||||
$processes{$file}{'mode'} = 'unconfined';
|
||||
}
|
||||
}
|
||||
close(CURRENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(PROC);
|
||||
}
|
||||
return (%processes);
|
||||
}
|
||||
|
||||
my $is_loaded = is_subdomain_loaded();
|
||||
|
||||
if (!$is_loaded) {
|
||||
print STDERR "apparmor module is not loaded.\n" if $verbose;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
print "apparmor module is loaded.\n" if $verbose;
|
||||
|
||||
$sd_mountpoint = find_subdomainfs();
|
||||
if (!$sd_mountpoint) {
|
||||
print STDERR "apparmor filesystem is not mounted.\n" if $verbose;
|
||||
exit 3;
|
||||
}
|
||||
|
||||
if (! -r "$sd_mountpoint/profiles") {
|
||||
print STDERR "You do not have enough privilege to read the profile set.\n" if $verbose;
|
||||
exit 4;
|
||||
}
|
||||
|
||||
#print "subdomainfs is at $sd_mountpoint.\n" if $verbose;
|
||||
|
||||
# processes is a hash table :
|
||||
# * keys : processes pid
|
||||
# * values : hash containing information about the running process:
|
||||
# * 'profile' : name of the profile applied to the running process
|
||||
# * 'mode' : mode of the profile applied to the running process
|
||||
my %processes = ();
|
||||
my %enforced_processes = ();
|
||||
my %complain_processes = ();
|
||||
my %unconfined_processes = ();
|
||||
|
||||
# profiles is a hash table :
|
||||
# * keys : profile name
|
||||
# * value : profile mode
|
||||
my %profiles;
|
||||
my @enforced_profiles = ();
|
||||
my @complain_profiles = ();
|
||||
|
||||
%profiles = get_profiles($sd_mountpoint);
|
||||
@enforced_profiles = grep { $profiles{$_} eq 'enforce' } keys %profiles;
|
||||
@complain_profiles = grep { $profiles{$_} eq 'complain' } keys %profiles;
|
||||
|
||||
# we consider the case where no profiles are loaded to be "disabled" as well
|
||||
my $rc = (keys(%profiles) == 0) ? 2 : 0;
|
||||
|
||||
if ($check_enabled) {
|
||||
exit $rc;
|
||||
}
|
||||
|
||||
if ($count_profiled) {
|
||||
print scalar(keys(%profiles)). "\n";
|
||||
exit $rc;
|
||||
}
|
||||
|
||||
if ($count_enforced) {
|
||||
print $#enforced_profiles + 1 . "\n";
|
||||
exit $rc;
|
||||
}
|
||||
|
||||
if ($count_complain) {
|
||||
print $#complain_profiles + 1 . "\n";
|
||||
exit $rc;
|
||||
}
|
||||
|
||||
|
||||
if ($verbose) {
|
||||
print keys(%profiles) . " profiles are loaded.\n";
|
||||
print $#enforced_profiles + 1 . " profiles are in enforce mode.\n";
|
||||
for (sort(@enforced_profiles)) {
|
||||
print " " . $_ . "\n";
|
||||
}
|
||||
print $#complain_profiles + 1 . " profiles are in complain mode.\n";
|
||||
for (sort(@complain_profiles)) {
|
||||
print " " . $_ . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
%processes = get_processes(%profiles);
|
||||
if ($verbose) {
|
||||
for (keys(%processes)) {
|
||||
$enforced_processes{$_} = $processes{$_} if $processes{$_}{'mode'} eq 'enforce';
|
||||
$complain_processes{$_} = $processes{$_} if $processes{$_}{'mode'} eq 'complain';
|
||||
# some early code uses unconfined instead of unconfined.
|
||||
$unconfined_processes{$_} = $processes{$_} if $processes{$_}{'mode'} =~ /uncon(fi|strai)ned/;
|
||||
}
|
||||
print keys(%processes) . " processes have profiles defined.\n";
|
||||
print keys(%enforced_processes) . " processes are in enforce mode :\n";
|
||||
for (sort { $enforced_processes{$a}{'profile'} cmp $enforced_processes{$b}{'profile'} } keys(%enforced_processes)) {
|
||||
print " " . $enforced_processes{$_}{'profile'} . " ($_) \n";
|
||||
}
|
||||
print keys(%complain_processes) . " processes are in complain mode.\n";
|
||||
for (sort { $complain_processes{$a}{'profile'} cmp $complain_processes{$b}{'profile'} } keys(%complain_processes)) {
|
||||
print " " . $complain_processes{$_}{'profile'} . " ($_) \n";
|
||||
}
|
||||
print keys(%unconfined_processes) . " processes are unconfined but have a profile defined.\n";
|
||||
for (sort { $unconfined_processes{$a}{'profile'} cmp $unconfined_processes{$b}{'profile'} } keys(%unconfined_processes)) {
|
||||
print " " . $unconfined_processes{$_}{'profile'} . " ($_) \n";
|
||||
}
|
||||
}
|
||||
|
||||
exit $rc;
|
BIN
documentation/AppArmor-dfa-variables.odt
Normal file
BIN
documentation/AppArmor-dfa-variables.odt
Normal file
Binary file not shown.
@@ -0,0 +1,553 @@
|
||||
From 0ae314bc92d8b22250f04f85e4bd36ee9ed30890 Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Mon, 4 Oct 2010 15:03:36 -0700
|
||||
Subject: [PATCH 1/3] AppArmor: compatibility patch for v5 network controll
|
||||
|
||||
Add compatibility for v5 network rules.
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
---
|
||||
include/linux/lsm_audit.h | 4 +
|
||||
security/apparmor/Makefile | 19 ++++-
|
||||
security/apparmor/include/net.h | 40 +++++++++
|
||||
security/apparmor/include/policy.h | 3 +
|
||||
security/apparmor/lsm.c | 112 +++++++++++++++++++++++
|
||||
security/apparmor/net.c | 170 ++++++++++++++++++++++++++++++++++++
|
||||
security/apparmor/policy.c | 1 +
|
||||
security/apparmor/policy_unpack.c | 48 ++++++++++-
|
||||
8 files changed, 394 insertions(+), 3 deletions(-)
|
||||
create mode 100644 security/apparmor/include/net.h
|
||||
create mode 100644 security/apparmor/net.c
|
||||
|
||||
diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h
|
||||
index 112a550..d5f3dd7 100644
|
||||
--- a/include/linux/lsm_audit.h
|
||||
+++ b/include/linux/lsm_audit.h
|
||||
@@ -123,6 +123,10 @@ struct common_audit_data {
|
||||
u32 denied;
|
||||
uid_t ouid;
|
||||
} fs;
|
||||
+ struct {
|
||||
+ int type, protocol;
|
||||
+ struct sock *sk;
|
||||
+ } net;
|
||||
};
|
||||
} apparmor_audit_data;
|
||||
#endif
|
||||
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
|
||||
index 2dafe50..7cefef9 100644
|
||||
--- a/security/apparmor/Makefile
|
||||
+++ b/security/apparmor/Makefile
|
||||
@@ -4,9 +4,9 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
|
||||
|
||||
apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
|
||||
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
|
||||
- resource.o sid.o file.o
|
||||
+ resource.o sid.o file.o net.o
|
||||
|
||||
-clean-files := capability_names.h rlim_names.h
|
||||
+clean-files := capability_names.h rlim_names.h af_names.h
|
||||
|
||||
|
||||
# Build a lower case string table of capability names
|
||||
@@ -44,9 +44,24 @@ cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ;\
|
||||
sed -r -n "s/^\# ?define[ \t]+(RLIMIT_[A-Z0-9_]+).*/\1,/p" $< >> $@ ;\
|
||||
echo "};" >> $@
|
||||
|
||||
+# Build a lower case string table of address family names.
|
||||
+# Transform lines from
|
||||
+# #define AF_INET 2 /* Internet IP Protocol */
|
||||
+# to
|
||||
+# [2] = "inet",
|
||||
+quiet_cmd_make-af = GEN $@
|
||||
+cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\
|
||||
+ sed $< >> $@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \
|
||||
+ 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+).*/[\2] = "\L\1",/p';\
|
||||
+ echo "};" >> $@
|
||||
+
|
||||
+
|
||||
$(obj)/capability.o : $(obj)/capability_names.h
|
||||
$(obj)/resource.o : $(obj)/rlim_names.h
|
||||
+$(obj)/net.o : $(obj)/af_names.h
|
||||
$(obj)/capability_names.h : $(srctree)/include/linux/capability.h
|
||||
$(call cmd,make-caps)
|
||||
$(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h
|
||||
$(call cmd,make-rlim)
|
||||
+$(obj)/af_names.h : $(srctree)/include/linux/socket.h
|
||||
+ $(call cmd,make-af)
|
||||
\ No newline at end of file
|
||||
diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h
|
||||
new file mode 100644
|
||||
index 0000000..3c7d599
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/include/net.h
|
||||
@@ -0,0 +1,40 @@
|
||||
+/*
|
||||
+ * AppArmor security module
|
||||
+ *
|
||||
+ * This file contains AppArmor network mediation definitions.
|
||||
+ *
|
||||
+ * Copyright (C) 1998-2008 Novell/SUSE
|
||||
+ * Copyright 2009-2010 Canonical Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License as
|
||||
+ * published by the Free Software Foundation, version 2 of the
|
||||
+ * License.
|
||||
+ */
|
||||
+
|
||||
+#ifndef __AA_NET_H
|
||||
+#define __AA_NET_H
|
||||
+
|
||||
+#include <net/sock.h>
|
||||
+
|
||||
+/* struct aa_net - network confinement data
|
||||
+ * @allowed: basic network families permissions
|
||||
+ * @audit_network: which network permissions to force audit
|
||||
+ * @quiet_network: which network permissions to quiet rejects
|
||||
+ */
|
||||
+struct aa_net {
|
||||
+ u16 allow[AF_MAX];
|
||||
+ u16 audit[AF_MAX];
|
||||
+ u16 quiet[AF_MAX];
|
||||
+};
|
||||
+
|
||||
+extern int aa_net_perm(int op, struct aa_profile *profile, u16 family,
|
||||
+ int type, int protocol, struct sock *sk);
|
||||
+extern int aa_revalidate_sk(int op, struct sock *sk);
|
||||
+
|
||||
+static inline void aa_free_net_rules(struct aa_net *new)
|
||||
+{
|
||||
+ /* NOP */
|
||||
+}
|
||||
+
|
||||
+#endif /* __AA_NET_H */
|
||||
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
|
||||
index aeda5cf..6776929 100644
|
||||
--- a/security/apparmor/include/policy.h
|
||||
+++ b/security/apparmor/include/policy.h
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "capability.h"
|
||||
#include "domain.h"
|
||||
#include "file.h"
|
||||
+#include "net.h"
|
||||
#include "resource.h"
|
||||
|
||||
extern const char *profile_mode_names[];
|
||||
@@ -145,6 +146,7 @@ struct aa_namespace {
|
||||
* @size: the memory consumed by this profiles rules
|
||||
* @file: The set of rules governing basic file access and domain transitions
|
||||
* @caps: capabilities for the profile
|
||||
+ * @net: network controls for the profile
|
||||
* @rlimits: rlimits for the profile
|
||||
*
|
||||
* The AppArmor profile contains the basic confinement data. Each profile
|
||||
@@ -181,6 +183,7 @@ struct aa_profile {
|
||||
|
||||
struct aa_file_rules file;
|
||||
struct aa_caps caps;
|
||||
+ struct aa_net net;
|
||||
struct aa_rlimit rlimits;
|
||||
};
|
||||
|
||||
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
|
||||
index ae3a698..05c018b 100644
|
||||
--- a/security/apparmor/lsm.c
|
||||
+++ b/security/apparmor/lsm.c
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "include/context.h"
|
||||
#include "include/file.h"
|
||||
#include "include/ipc.h"
|
||||
+#include "include/net.h"
|
||||
#include "include/path.h"
|
||||
#include "include/policy.h"
|
||||
#include "include/procattr.h"
|
||||
@@ -620,6 +621,104 @@ static int apparmor_task_setrlimit(struct task_struct *task,
|
||||
return error;
|
||||
}
|
||||
|
||||
+static int apparmor_socket_create(int family, int type, int protocol, int kern)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ int error = 0;
|
||||
+
|
||||
+ if (kern)
|
||||
+ return 0;
|
||||
+
|
||||
+ profile = __aa_current_profile();
|
||||
+ if (!unconfined(profile))
|
||||
+ error = aa_net_perm(OP_CREATE, profile, family, type, protocol,
|
||||
+ NULL);
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_bind(struct socket *sock,
|
||||
+ struct sockaddr *address, int addrlen)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_BIND, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_connect(struct socket *sock,
|
||||
+ struct sockaddr *address, int addrlen)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_CONNECT, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_listen(struct socket *sock, int backlog)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_LISTEN, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_accept(struct socket *sock, struct socket *newsock)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_ACCEPT, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_sendmsg(struct socket *sock,
|
||||
+ struct msghdr *msg, int size)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_SENDMSG, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_recvmsg(struct socket *sock,
|
||||
+ struct msghdr *msg, int size, int flags)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_RECVMSG, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_getsockname(struct socket *sock)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_GETSOCKNAME, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_getpeername(struct socket *sock)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_GETPEERNAME, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_getsockopt(struct socket *sock, int level,
|
||||
+ int optname)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_GETSOCKOPT, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_setsockopt(struct socket *sock, int level,
|
||||
+ int optname)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_SETSOCKOPT, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_shutdown(struct socket *sock, int how)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk);
|
||||
+}
|
||||
+
|
||||
static struct security_operations apparmor_ops = {
|
||||
.name = "apparmor",
|
||||
|
||||
@@ -651,6 +750,19 @@ static struct security_operations apparmor_ops = {
|
||||
.getprocattr = apparmor_getprocattr,
|
||||
.setprocattr = apparmor_setprocattr,
|
||||
|
||||
+ .socket_create = apparmor_socket_create,
|
||||
+ .socket_bind = apparmor_socket_bind,
|
||||
+ .socket_connect = apparmor_socket_connect,
|
||||
+ .socket_listen = apparmor_socket_listen,
|
||||
+ .socket_accept = apparmor_socket_accept,
|
||||
+ .socket_sendmsg = apparmor_socket_sendmsg,
|
||||
+ .socket_recvmsg = apparmor_socket_recvmsg,
|
||||
+ .socket_getsockname = apparmor_socket_getsockname,
|
||||
+ .socket_getpeername = apparmor_socket_getpeername,
|
||||
+ .socket_getsockopt = apparmor_socket_getsockopt,
|
||||
+ .socket_setsockopt = apparmor_socket_setsockopt,
|
||||
+ .socket_shutdown = apparmor_socket_shutdown,
|
||||
+
|
||||
.cred_alloc_blank = apparmor_cred_alloc_blank,
|
||||
.cred_free = apparmor_cred_free,
|
||||
.cred_prepare = apparmor_cred_prepare,
|
||||
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
|
||||
new file mode 100644
|
||||
index 0000000..1765901
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/net.c
|
||||
@@ -0,0 +1,170 @@
|
||||
+/*
|
||||
+ * AppArmor security module
|
||||
+ *
|
||||
+ * This file contains AppArmor network mediation
|
||||
+ *
|
||||
+ * Copyright (C) 1998-2008 Novell/SUSE
|
||||
+ * Copyright 2009-2010 Canonical Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License as
|
||||
+ * published by the Free Software Foundation, version 2 of the
|
||||
+ * License.
|
||||
+ */
|
||||
+
|
||||
+#include "include/apparmor.h"
|
||||
+#include "include/audit.h"
|
||||
+#include "include/context.h"
|
||||
+#include "include/net.h"
|
||||
+#include "include/policy.h"
|
||||
+
|
||||
+#include "af_names.h"
|
||||
+
|
||||
+static const char *sock_type_names[] = {
|
||||
+ "unknown(0)",
|
||||
+ "stream",
|
||||
+ "dgram",
|
||||
+ "raw",
|
||||
+ "rdm",
|
||||
+ "seqpacket",
|
||||
+ "dccp",
|
||||
+ "unknown(7)",
|
||||
+ "unknown(8)",
|
||||
+ "unknown(9)",
|
||||
+ "packet",
|
||||
+};
|
||||
+
|
||||
+/* audit callback for net specific fields */
|
||||
+static void audit_cb(struct audit_buffer *ab, void *va)
|
||||
+{
|
||||
+ struct common_audit_data *sa = va;
|
||||
+
|
||||
+ audit_log_format(ab, " family=");
|
||||
+ if (address_family_names[sa->u.net.family]) {
|
||||
+ audit_log_string(ab, address_family_names[sa->u.net.family]);
|
||||
+ } else {
|
||||
+ audit_log_format(ab, " \"unknown(%d)\"", sa->u.net.family);
|
||||
+ }
|
||||
+
|
||||
+ audit_log_format(ab, " sock_type=");
|
||||
+ if (sock_type_names[sa->aad.net.type]) {
|
||||
+ audit_log_string(ab, sock_type_names[sa->aad.net.type]);
|
||||
+ } else {
|
||||
+ audit_log_format(ab, "\"unknown(%d)\"", sa->aad.net.type);
|
||||
+ }
|
||||
+
|
||||
+ audit_log_format(ab, " protocol=%d", sa->aad.net.protocol);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * audit_net - audit network access
|
||||
+ * @profile: profile being enforced (NOT NULL)
|
||||
+ * @op: operation being checked
|
||||
+ * @family: network family
|
||||
+ * @type: network type
|
||||
+ * @protocol: network protocol
|
||||
+ * @sk: socket auditing is being applied to
|
||||
+ * @error: error code for failure else 0
|
||||
+ *
|
||||
+ * Returns: %0 or sa->error else other errorcode on failure
|
||||
+ */
|
||||
+static int audit_net(struct aa_profile *profile, int op, u16 family, int type,
|
||||
+ int protocol, struct sock *sk, int error)
|
||||
+{
|
||||
+ int audit_type = AUDIT_APPARMOR_AUTO;
|
||||
+ struct common_audit_data sa;
|
||||
+ if (sk) {
|
||||
+ COMMON_AUDIT_DATA_INIT(&sa, NET);
|
||||
+ } else {
|
||||
+ COMMON_AUDIT_DATA_INIT(&sa, NONE);
|
||||
+ }
|
||||
+ /* todo fill in socket addr info */
|
||||
+
|
||||
+ sa.aad.op = op,
|
||||
+ sa.u.net.family = family;
|
||||
+ sa.u.net.sk = sk;
|
||||
+ sa.aad.net.type = type;
|
||||
+ sa.aad.net.protocol = protocol;
|
||||
+ sa.aad.error = error;
|
||||
+
|
||||
+ if (likely(!sa.aad.error)) {
|
||||
+ u16 audit_mask = profile->net.audit[sa.u.net.family];
|
||||
+ if (likely((AUDIT_MODE(profile) != AUDIT_ALL) &&
|
||||
+ !(1 << sa.aad.net.type & audit_mask)))
|
||||
+ return 0;
|
||||
+ audit_type = AUDIT_APPARMOR_AUDIT;
|
||||
+ } else {
|
||||
+ u16 quiet_mask = profile->net.quiet[sa.u.net.family];
|
||||
+ u16 kill_mask = 0;
|
||||
+ u16 denied = (1 << sa.aad.net.type) & ~quiet_mask;
|
||||
+
|
||||
+ if (denied & kill_mask)
|
||||
+ audit_type = AUDIT_APPARMOR_KILL;
|
||||
+
|
||||
+ if ((denied & quiet_mask) &&
|
||||
+ AUDIT_MODE(profile) != AUDIT_NOQUIET &&
|
||||
+ AUDIT_MODE(profile) != AUDIT_ALL)
|
||||
+ return COMPLAIN_MODE(profile) ? 0 : sa.aad.error;
|
||||
+ }
|
||||
+
|
||||
+ return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * aa_net_perm - very course network access check
|
||||
+ * @op: operation being checked
|
||||
+ * @profile: profile being enforced (NOT NULL)
|
||||
+ * @family: network family
|
||||
+ * @type: network type
|
||||
+ * @protocol: network protocol
|
||||
+ *
|
||||
+ * Returns: %0 else error if permission denied
|
||||
+ */
|
||||
+int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type,
|
||||
+ int protocol, struct sock *sk)
|
||||
+{
|
||||
+ u16 family_mask;
|
||||
+ int error;
|
||||
+
|
||||
+ if ((family < 0) || (family >= AF_MAX))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if ((type < 0) || (type >= SOCK_MAX))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /* unix domain and netlink sockets are handled by ipc */
|
||||
+ if (family == AF_UNIX || family == AF_NETLINK)
|
||||
+ return 0;
|
||||
+
|
||||
+ family_mask = profile->net.allow[family];
|
||||
+
|
||||
+ error = (family_mask & (1 << type)) ? 0 : -EACCES;
|
||||
+
|
||||
+ return audit_net(profile, op, family, type, protocol, sk, error);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * aa_revalidate_sk - Revalidate access to a sock
|
||||
+ * @op: operation being checked
|
||||
+ * @sk: sock being revalidated (NOT NULL)
|
||||
+ *
|
||||
+ * Returns: %0 else error if permission denied
|
||||
+ */
|
||||
+int aa_revalidate_sk(int op, struct sock *sk)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ int error = 0;
|
||||
+
|
||||
+ /* aa_revalidate_sk should not be called from interrupt context
|
||||
+ * don't mediate these calls as they are not task related
|
||||
+ */
|
||||
+ if (in_interrupt())
|
||||
+ return 0;
|
||||
+
|
||||
+ profile = __aa_current_profile();
|
||||
+ if (!unconfined(profile))
|
||||
+ error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type,
|
||||
+ sk->sk_protocol, sk);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
|
||||
index 4f0eade..4d5ce13 100644
|
||||
--- a/security/apparmor/policy.c
|
||||
+++ b/security/apparmor/policy.c
|
||||
@@ -745,6 +745,7 @@ static void free_profile(struct aa_profile *profile)
|
||||
|
||||
aa_free_file_rules(&profile->file);
|
||||
aa_free_cap_rules(&profile->caps);
|
||||
+ aa_free_net_rules(&profile->net);
|
||||
aa_free_rlimit_rules(&profile->rlimits);
|
||||
|
||||
aa_free_sid(profile->sid);
|
||||
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
|
||||
index e33aaf7..fa3f1b4 100644
|
||||
--- a/security/apparmor/policy_unpack.c
|
||||
+++ b/security/apparmor/policy_unpack.c
|
||||
@@ -190,6 +190,19 @@ fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name)
|
||||
+{
|
||||
+ if (unpack_nameX(e, AA_U16, name)) {
|
||||
+ if (!inbounds(e, sizeof(u16)))
|
||||
+ return 0;
|
||||
+ if (data)
|
||||
+ *data = le16_to_cpu(get_unaligned((u16 *) e->pos));
|
||||
+ e->pos += sizeof(u16);
|
||||
+ return 1;
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name)
|
||||
{
|
||||
if (unpack_nameX(e, AA_U32, name)) {
|
||||
@@ -468,7 +481,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
|
||||
{
|
||||
struct aa_profile *profile = NULL;
|
||||
const char *name = NULL;
|
||||
- int error = -EPROTO;
|
||||
+ size_t size = 0;
|
||||
+ int i, error = -EPROTO;
|
||||
kernel_cap_t tmpcap;
|
||||
u32 tmp;
|
||||
|
||||
@@ -559,6 +573,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
|
||||
if (!unpack_rlimits(e, profile))
|
||||
goto fail;
|
||||
|
||||
+ size = unpack_array(e, "net_allowed_af");
|
||||
+ if (size) {
|
||||
+
|
||||
+ for (i = 0; i < size; i++) {
|
||||
+ /* discard extraneous rules that this kernel will
|
||||
+ * never request
|
||||
+ */
|
||||
+ if (i > AF_MAX) {
|
||||
+ u16 tmp;
|
||||
+ if (!unpack_u16(e, &tmp, NULL) ||
|
||||
+ !unpack_u16(e, &tmp, NULL) ||
|
||||
+ !unpack_u16(e, &tmp, NULL))
|
||||
+ goto fail;
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (!unpack_u16(e, &profile->net.allow[i], NULL))
|
||||
+ goto fail;
|
||||
+ if (!unpack_u16(e, &profile->net.audit[i], NULL))
|
||||
+ goto fail;
|
||||
+ if (!unpack_u16(e, &profile->net.quiet[i], NULL))
|
||||
+ goto fail;
|
||||
+ }
|
||||
+ if (!unpack_nameX(e, AA_ARRAYEND, NULL))
|
||||
+ goto fail;
|
||||
+ /*
|
||||
+ * allow unix domain and netlink sockets they are handled
|
||||
+ * by IPC
|
||||
+ */
|
||||
+ }
|
||||
+ profile->net.allow[AF_UNIX] = 0xffff;
|
||||
+ profile->net.allow[AF_NETLINK] = 0xffff;
|
||||
+
|
||||
/* get file rules */
|
||||
profile->file.dfa = unpack_dfa(e);
|
||||
if (IS_ERR(profile->file.dfa)) {
|
||||
--
|
||||
1.7.0.4
|
||||
|
@@ -0,0 +1,391 @@
|
||||
From cdc6b35345e5bcfe92bb2b52ef003f94ceedd40d Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Thu, 22 Jul 2010 02:32:02 -0700
|
||||
Subject: [PATCH 2/3] AppArmor: compatibility patch for v5 interface
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
---
|
||||
security/apparmor/Kconfig | 9 +
|
||||
security/apparmor/Makefile | 1 +
|
||||
security/apparmor/apparmorfs-24.c | 287 ++++++++++++++++++++++++++++++++
|
||||
security/apparmor/apparmorfs.c | 18 ++-
|
||||
security/apparmor/include/apparmorfs.h | 6 +
|
||||
5 files changed, 319 insertions(+), 2 deletions(-)
|
||||
create mode 100644 security/apparmor/apparmorfs-24.c
|
||||
|
||||
diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig
|
||||
index 9b9013b..51ebf96 100644
|
||||
--- a/security/apparmor/Kconfig
|
||||
+++ b/security/apparmor/Kconfig
|
||||
@@ -29,3 +29,12 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE
|
||||
boot.
|
||||
|
||||
If you are unsure how to answer this question, answer 1.
|
||||
+
|
||||
+config SECURITY_APPARMOR_COMPAT_24
|
||||
+ bool "Enable AppArmor 2.4 compatability"
|
||||
+ depends on SECURITY_APPARMOR
|
||||
+ default y
|
||||
+ help
|
||||
+ This option enables compatability with AppArmor 2.4. It is
|
||||
+ recommended if compatability with older versions of AppArmor
|
||||
+ is desired.
|
||||
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
|
||||
index 7cefef9..0bb604b 100644
|
||||
--- a/security/apparmor/Makefile
|
||||
+++ b/security/apparmor/Makefile
|
||||
@@ -5,6 +5,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
|
||||
apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
|
||||
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
|
||||
resource.o sid.o file.o net.o
|
||||
+apparmor-$(CONFIG_SECURITY_APPARMOR_COMPAT_24) += apparmorfs-24.o
|
||||
|
||||
clean-files := capability_names.h rlim_names.h af_names.h
|
||||
|
||||
diff --git a/security/apparmor/apparmorfs-24.c b/security/apparmor/apparmorfs-24.c
|
||||
new file mode 100644
|
||||
index 0000000..dc8c744
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/apparmorfs-24.c
|
||||
@@ -0,0 +1,287 @@
|
||||
+/*
|
||||
+ * AppArmor security module
|
||||
+ *
|
||||
+ * This file contains AppArmor /sys/kernel/secrutiy/apparmor interface functions
|
||||
+ *
|
||||
+ * Copyright (C) 1998-2008 Novell/SUSE
|
||||
+ * Copyright 2009-2010 Canonical Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License as
|
||||
+ * published by the Free Software Foundation, version 2 of the
|
||||
+ * License.
|
||||
+ *
|
||||
+ *
|
||||
+ * This file contain functions providing an interface for <= AppArmor 2.4
|
||||
+ * compatibility. It is dependent on CONFIG_SECURITY_APPARMOR_COMPAT_24
|
||||
+ * being set (see Makefile).
|
||||
+ */
|
||||
+
|
||||
+#include <linux/security.h>
|
||||
+#include <linux/vmalloc.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/seq_file.h>
|
||||
+#include <linux/uaccess.h>
|
||||
+#include <linux/namei.h>
|
||||
+
|
||||
+#include "include/apparmor.h"
|
||||
+#include "include/audit.h"
|
||||
+#include "include/context.h"
|
||||
+#include "include/policy.h"
|
||||
+
|
||||
+
|
||||
+/* apparmor/matching */
|
||||
+static ssize_t aa_matching_read(struct file *file, char __user *buf,
|
||||
+ size_t size, loff_t *ppos)
|
||||
+{
|
||||
+ const char matching[] = "pattern=aadfa audit perms=crwxamlk/ "
|
||||
+ "user::other";
|
||||
+
|
||||
+ return simple_read_from_buffer(buf, size, ppos, matching,
|
||||
+ sizeof(matching) - 1);
|
||||
+}
|
||||
+
|
||||
+const struct file_operations aa_fs_matching_fops = {
|
||||
+ .read = aa_matching_read,
|
||||
+};
|
||||
+
|
||||
+/* apparmor/features */
|
||||
+static ssize_t aa_features_read(struct file *file, char __user *buf,
|
||||
+ size_t size, loff_t *ppos)
|
||||
+{
|
||||
+ const char features[] = "file=3.1 capability=2.0 network=1.0 "
|
||||
+ "change_hat=1.5 change_profile=1.1 " "aanamespaces=1.1 rlimit=1.1";
|
||||
+
|
||||
+ return simple_read_from_buffer(buf, size, ppos, features,
|
||||
+ sizeof(features) - 1);
|
||||
+}
|
||||
+
|
||||
+const struct file_operations aa_fs_features_fops = {
|
||||
+ .read = aa_features_read,
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * __next_namespace - find the next namespace to list
|
||||
+ * @root: root namespace to stop search at (NOT NULL)
|
||||
+ * @ns: current ns position (NOT NULL)
|
||||
+ *
|
||||
+ * Find the next namespace from @ns under @root and handle all locking needed
|
||||
+ * while switching current namespace.
|
||||
+ *
|
||||
+ * Returns: next namespace or NULL if at last namespace under @root
|
||||
+ * NOTE: will not unlock root->lock
|
||||
+ */
|
||||
+static struct aa_namespace *__next_namespace(struct aa_namespace *root,
|
||||
+ struct aa_namespace *ns)
|
||||
+{
|
||||
+ struct aa_namespace *parent;
|
||||
+
|
||||
+ /* is next namespace a child */
|
||||
+ if (!list_empty(&ns->sub_ns)) {
|
||||
+ struct aa_namespace *next;
|
||||
+ next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list);
|
||||
+ read_lock(&next->lock);
|
||||
+ return next;
|
||||
+ }
|
||||
+
|
||||
+ /* check if the next ns is a sibling, parent, gp, .. */
|
||||
+ parent = ns->parent;
|
||||
+ while (parent) {
|
||||
+ read_unlock(&ns->lock);
|
||||
+ list_for_each_entry_continue(ns, &parent->sub_ns, base.list) {
|
||||
+ read_lock(&ns->lock);
|
||||
+ return ns;
|
||||
+ }
|
||||
+ if (parent == root)
|
||||
+ return NULL;
|
||||
+ ns = parent;
|
||||
+ parent = parent->parent;
|
||||
+ }
|
||||
+
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * __first_profile - find the first profile in a namespace
|
||||
+ * @root: namespace that is root of profiles being displayed (NOT NULL)
|
||||
+ * @ns: namespace to start in (NOT NULL)
|
||||
+ *
|
||||
+ * Returns: unrefcounted profile or NULL if no profile
|
||||
+ */
|
||||
+static struct aa_profile *__first_profile(struct aa_namespace *root,
|
||||
+ struct aa_namespace *ns)
|
||||
+{
|
||||
+ for ( ; ns; ns = __next_namespace(root, ns)) {
|
||||
+ if (!list_empty(&ns->base.profiles))
|
||||
+ return list_first_entry(&ns->base.profiles,
|
||||
+ struct aa_profile, base.list);
|
||||
+ }
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * __next_profile - step to the next profile in a profile tree
|
||||
+ * @profile: current profile in tree (NOT NULL)
|
||||
+ *
|
||||
+ * Perform a depth first taversal on the profile tree in a namespace
|
||||
+ *
|
||||
+ * Returns: next profile or NULL if done
|
||||
+ * Requires: profile->ns.lock to be held
|
||||
+ */
|
||||
+static struct aa_profile *__next_profile(struct aa_profile *p)
|
||||
+{
|
||||
+ struct aa_profile *parent;
|
||||
+ struct aa_namespace *ns = p->ns;
|
||||
+
|
||||
+ /* is next profile a child */
|
||||
+ if (!list_empty(&p->base.profiles))
|
||||
+ return list_first_entry(&p->base.profiles, typeof(*p),
|
||||
+ base.list);
|
||||
+
|
||||
+ /* is next profile a sibling, parent sibling, gp, subling, .. */
|
||||
+ parent = p->parent;
|
||||
+ while (parent) {
|
||||
+ list_for_each_entry_continue(p, &parent->base.profiles,
|
||||
+ base.list)
|
||||
+ return p;
|
||||
+ p = parent;
|
||||
+ parent = parent->parent;
|
||||
+ }
|
||||
+
|
||||
+ /* is next another profile in the namespace */
|
||||
+ list_for_each_entry_continue(p, &ns->base.profiles, base.list)
|
||||
+ return p;
|
||||
+
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * next_profile - step to the next profile in where ever it may be
|
||||
+ * @root: root namespace (NOT NULL)
|
||||
+ * @profile: current profile (NOT NULL)
|
||||
+ *
|
||||
+ * Returns: next profile or NULL if there isn't one
|
||||
+ */
|
||||
+static struct aa_profile *next_profile(struct aa_namespace *root,
|
||||
+ struct aa_profile *profile)
|
||||
+{
|
||||
+ struct aa_profile *next = __next_profile(profile);
|
||||
+ if (next)
|
||||
+ return next;
|
||||
+
|
||||
+ /* finished all profiles in namespace move to next namespace */
|
||||
+ return __first_profile(root, __next_namespace(root, profile->ns));
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * p_start - start a depth first traversal of profile tree
|
||||
+ * @f: seq_file to fill
|
||||
+ * @pos: current position
|
||||
+ *
|
||||
+ * Returns: first profile under current namespace or NULL if none found
|
||||
+ *
|
||||
+ * acquires first ns->lock
|
||||
+ */
|
||||
+static void *p_start(struct seq_file *f, loff_t *pos)
|
||||
+ __acquires(root->lock)
|
||||
+{
|
||||
+ struct aa_profile *profile = NULL;
|
||||
+ struct aa_namespace *root = aa_current_profile()->ns;
|
||||
+ loff_t l = *pos;
|
||||
+ f->private = aa_get_namespace(root);
|
||||
+
|
||||
+
|
||||
+ /* find the first profile */
|
||||
+ read_lock(&root->lock);
|
||||
+ profile = __first_profile(root, root);
|
||||
+
|
||||
+ /* skip to position */
|
||||
+ for (; profile && l > 0; l--)
|
||||
+ profile = next_profile(root, profile);
|
||||
+
|
||||
+ return profile;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * p_next - read the next profile entry
|
||||
+ * @f: seq_file to fill
|
||||
+ * @p: profile previously returned
|
||||
+ * @pos: current position
|
||||
+ *
|
||||
+ * Returns: next profile after @p or NULL if none
|
||||
+ *
|
||||
+ * may acquire/release locks in namespace tree as necessary
|
||||
+ */
|
||||
+static void *p_next(struct seq_file *f, void *p, loff_t *pos)
|
||||
+{
|
||||
+ struct aa_profile *profile = p;
|
||||
+ struct aa_namespace *root = f->private;
|
||||
+ (*pos)++;
|
||||
+
|
||||
+ return next_profile(root, profile);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * p_stop - stop depth first traversal
|
||||
+ * @f: seq_file we are filling
|
||||
+ * @p: the last profile writen
|
||||
+ *
|
||||
+ * Release all locking done by p_start/p_next on namespace tree
|
||||
+ */
|
||||
+static void p_stop(struct seq_file *f, void *p)
|
||||
+ __releases(root->lock)
|
||||
+{
|
||||
+ struct aa_profile *profile = p;
|
||||
+ struct aa_namespace *root = f->private, *ns;
|
||||
+
|
||||
+ if (profile) {
|
||||
+ for (ns = profile->ns; ns && ns != root; ns = ns->parent)
|
||||
+ read_unlock(&ns->lock);
|
||||
+ }
|
||||
+ read_unlock(&root->lock);
|
||||
+ aa_put_namespace(root);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * seq_show_profile - show a profile entry
|
||||
+ * @f: seq_file to file
|
||||
+ * @p: current position (profile) (NOT NULL)
|
||||
+ *
|
||||
+ * Returns: error on failure
|
||||
+ */
|
||||
+static int seq_show_profile(struct seq_file *f, void *p)
|
||||
+{
|
||||
+ struct aa_profile *profile = (struct aa_profile *)p;
|
||||
+ struct aa_namespace *root = f->private;
|
||||
+
|
||||
+ if (profile->ns != root)
|
||||
+ seq_printf(f, ":%s://", aa_ns_name(root, profile->ns));
|
||||
+ seq_printf(f, "%s (%s)\n", profile->base.hname,
|
||||
+ COMPLAIN_MODE(profile) ? "complain" : "enforce");
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct seq_operations aa_fs_profiles_op = {
|
||||
+ .start = p_start,
|
||||
+ .next = p_next,
|
||||
+ .stop = p_stop,
|
||||
+ .show = seq_show_profile,
|
||||
+};
|
||||
+
|
||||
+static int profiles_open(struct inode *inode, struct file *file)
|
||||
+{
|
||||
+ return seq_open(file, &aa_fs_profiles_op);
|
||||
+}
|
||||
+
|
||||
+static int profiles_release(struct inode *inode, struct file *file)
|
||||
+{
|
||||
+ return seq_release(inode, file);
|
||||
+}
|
||||
+
|
||||
+const struct file_operations aa_fs_profiles_fops = {
|
||||
+ .open = profiles_open,
|
||||
+ .read = seq_read,
|
||||
+ .llseek = seq_lseek,
|
||||
+ .release = profiles_release,
|
||||
+};
|
||||
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
|
||||
index 0848292..28c52ac 100644
|
||||
--- a/security/apparmor/apparmorfs.c
|
||||
+++ b/security/apparmor/apparmorfs.c
|
||||
@@ -187,7 +187,11 @@ void __init aa_destroy_aafs(void)
|
||||
aafs_remove(".remove");
|
||||
aafs_remove(".replace");
|
||||
aafs_remove(".load");
|
||||
-
|
||||
+#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24
|
||||
+ aafs_remove("profiles");
|
||||
+ aafs_remove("matching");
|
||||
+ aafs_remove("features");
|
||||
+#endif
|
||||
securityfs_remove(aa_fs_dentry);
|
||||
aa_fs_dentry = NULL;
|
||||
}
|
||||
@@ -218,7 +222,17 @@ int __init aa_create_aafs(void)
|
||||
aa_fs_dentry = NULL;
|
||||
goto error;
|
||||
}
|
||||
-
|
||||
+#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24
|
||||
+ error = aafs_create("matching", 0444, &aa_fs_matching_fops);
|
||||
+ if (error)
|
||||
+ goto error;
|
||||
+ error = aafs_create("features", 0444, &aa_fs_features_fops);
|
||||
+ if (error)
|
||||
+ goto error;
|
||||
+#endif
|
||||
+ error = aafs_create("profiles", 0440, &aa_fs_profiles_fops);
|
||||
+ if (error)
|
||||
+ goto error;
|
||||
error = aafs_create(".load", 0640, &aa_fs_profile_load);
|
||||
if (error)
|
||||
goto error;
|
||||
diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h
|
||||
index cb1e93a..14f955c 100644
|
||||
--- a/security/apparmor/include/apparmorfs.h
|
||||
+++ b/security/apparmor/include/apparmorfs.h
|
||||
@@ -17,4 +17,10 @@
|
||||
|
||||
extern void __init aa_destroy_aafs(void);
|
||||
|
||||
+#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24
|
||||
+extern const struct file_operations aa_fs_matching_fops;
|
||||
+extern const struct file_operations aa_fs_features_fops;
|
||||
+extern const struct file_operations aa_fs_profiles_fops;
|
||||
+#endif
|
||||
+
|
||||
#endif /* __AA_APPARMORFS_H */
|
||||
--
|
||||
1.7.0.4
|
||||
|
@@ -0,0 +1,68 @@
|
||||
From f17b28f64b963c47e76737f7bb7f58ce3a7c5249 Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Tue, 20 Jul 2010 06:57:08 -0700
|
||||
Subject: [PATCH 3/3] AppArmor: Allow dfa backward compatibility with broken userspace
|
||||
|
||||
The apparmor_parser when compiling policy could generate invalid dfas
|
||||
that did not have sufficient padding to avoid invalid references, when
|
||||
used by the kernel. The kernels check to verify the next/check table
|
||||
size was broken meaning invalid dfas were being created by userspace
|
||||
and not caught.
|
||||
|
||||
To remain compatible with old tools that are not fixed, pad the loaded
|
||||
dfas next/check table. The dfa's themselves are valid except for the
|
||||
high padding for potentially invalid transitions (high bounds error),
|
||||
which have a maximimum is 256 entries. So just allocate an extra null filled
|
||||
256 entries for the next/check tables. This will guarentee all bounds
|
||||
are good and invalid transitions go to the null (0) state.
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
---
|
||||
security/apparmor/match.c | 17 +++++++++++++++++
|
||||
1 files changed, 17 insertions(+), 0 deletions(-)
|
||||
|
||||
diff --git a/security/apparmor/match.c b/security/apparmor/match.c
|
||||
index 06d764c..cf92856 100644
|
||||
--- a/security/apparmor/match.c
|
||||
+++ b/security/apparmor/match.c
|
||||
@@ -57,8 +57,17 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
|
||||
if (bsize < tsize)
|
||||
goto out;
|
||||
|
||||
+ /* Pad table allocation for next/check by 256 entries to remain
|
||||
+ * backwards compatible with old (buggy) tools and remain safe without
|
||||
+ * run time checks
|
||||
+ */
|
||||
+ if (th.td_id == YYTD_ID_NXT || th.td_id == YYTD_ID_CHK)
|
||||
+ tsize += 256 * th.td_flags;
|
||||
+
|
||||
table = kvmalloc(tsize);
|
||||
if (table) {
|
||||
+ /* ensure the pad is clear, else there will be errors */
|
||||
+ memset(table, 0, tsize);
|
||||
*table = th;
|
||||
if (th.td_flags == YYTD_DATA8)
|
||||
UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
|
||||
@@ -134,11 +143,19 @@ static int verify_dfa(struct aa_dfa *dfa, int flags)
|
||||
goto out;
|
||||
|
||||
if (flags & DFA_FLAG_VERIFY_STATES) {
|
||||
+ int warning = 0;
|
||||
for (i = 0; i < state_count; i++) {
|
||||
if (DEFAULT_TABLE(dfa)[i] >= state_count)
|
||||
goto out;
|
||||
/* TODO: do check that DEF state recursion terminates */
|
||||
if (BASE_TABLE(dfa)[i] + 255 >= trans_count) {
|
||||
+ if (warning)
|
||||
+ continue;
|
||||
+ printk(KERN_WARNING "AppArmor DFA next/check "
|
||||
+ "upper bounds error fixed, upgrade "
|
||||
+ "user space tools \n");
|
||||
+ warning = 1;
|
||||
+ } else if (BASE_TABLE(dfa)[i] >= trans_count) {
|
||||
printk(KERN_ERR "AppArmor DFA next/check upper "
|
||||
"bounds error\n");
|
||||
goto out;
|
||||
--
|
||||
1.7.0.4
|
||||
|
@@ -0,0 +1,553 @@
|
||||
From dc13dec93dbd04bfa7a9ba67df1b8ed3431d8d48 Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Wed, 10 Aug 2011 22:02:39 -0700
|
||||
Subject: [PATCH 1/3] AppArmor: compatibility patch for v5 network controll
|
||||
|
||||
Add compatibility for v5 network rules.
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
---
|
||||
include/linux/lsm_audit.h | 4 +
|
||||
security/apparmor/Makefile | 19 ++++-
|
||||
security/apparmor/include/net.h | 40 +++++++++
|
||||
security/apparmor/include/policy.h | 3 +
|
||||
security/apparmor/lsm.c | 112 +++++++++++++++++++++++
|
||||
security/apparmor/net.c | 170 ++++++++++++++++++++++++++++++++++++
|
||||
security/apparmor/policy.c | 1 +
|
||||
security/apparmor/policy_unpack.c | 48 ++++++++++-
|
||||
8 files changed, 394 insertions(+), 3 deletions(-)
|
||||
create mode 100644 security/apparmor/include/net.h
|
||||
create mode 100644 security/apparmor/net.c
|
||||
|
||||
diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h
|
||||
index 88e78de..c63979a 100644
|
||||
--- a/include/linux/lsm_audit.h
|
||||
+++ b/include/linux/lsm_audit.h
|
||||
@@ -124,6 +124,10 @@ struct common_audit_data {
|
||||
u32 denied;
|
||||
uid_t ouid;
|
||||
} fs;
|
||||
+ struct {
|
||||
+ int type, protocol;
|
||||
+ struct sock *sk;
|
||||
+ } net;
|
||||
};
|
||||
} apparmor_audit_data;
|
||||
#endif
|
||||
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
|
||||
index 2dafe50..7cefef9 100644
|
||||
--- a/security/apparmor/Makefile
|
||||
+++ b/security/apparmor/Makefile
|
||||
@@ -4,9 +4,9 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
|
||||
|
||||
apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
|
||||
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
|
||||
- resource.o sid.o file.o
|
||||
+ resource.o sid.o file.o net.o
|
||||
|
||||
-clean-files := capability_names.h rlim_names.h
|
||||
+clean-files := capability_names.h rlim_names.h af_names.h
|
||||
|
||||
|
||||
# Build a lower case string table of capability names
|
||||
@@ -44,9 +44,24 @@ cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ;\
|
||||
sed -r -n "s/^\# ?define[ \t]+(RLIMIT_[A-Z0-9_]+).*/\1,/p" $< >> $@ ;\
|
||||
echo "};" >> $@
|
||||
|
||||
+# Build a lower case string table of address family names.
|
||||
+# Transform lines from
|
||||
+# #define AF_INET 2 /* Internet IP Protocol */
|
||||
+# to
|
||||
+# [2] = "inet",
|
||||
+quiet_cmd_make-af = GEN $@
|
||||
+cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\
|
||||
+ sed $< >> $@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \
|
||||
+ 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+).*/[\2] = "\L\1",/p';\
|
||||
+ echo "};" >> $@
|
||||
+
|
||||
+
|
||||
$(obj)/capability.o : $(obj)/capability_names.h
|
||||
$(obj)/resource.o : $(obj)/rlim_names.h
|
||||
+$(obj)/net.o : $(obj)/af_names.h
|
||||
$(obj)/capability_names.h : $(srctree)/include/linux/capability.h
|
||||
$(call cmd,make-caps)
|
||||
$(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h
|
||||
$(call cmd,make-rlim)
|
||||
+$(obj)/af_names.h : $(srctree)/include/linux/socket.h
|
||||
+ $(call cmd,make-af)
|
||||
\ No newline at end of file
|
||||
diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h
|
||||
new file mode 100644
|
||||
index 0000000..3c7d599
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/include/net.h
|
||||
@@ -0,0 +1,40 @@
|
||||
+/*
|
||||
+ * AppArmor security module
|
||||
+ *
|
||||
+ * This file contains AppArmor network mediation definitions.
|
||||
+ *
|
||||
+ * Copyright (C) 1998-2008 Novell/SUSE
|
||||
+ * Copyright 2009-2010 Canonical Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License as
|
||||
+ * published by the Free Software Foundation, version 2 of the
|
||||
+ * License.
|
||||
+ */
|
||||
+
|
||||
+#ifndef __AA_NET_H
|
||||
+#define __AA_NET_H
|
||||
+
|
||||
+#include <net/sock.h>
|
||||
+
|
||||
+/* struct aa_net - network confinement data
|
||||
+ * @allowed: basic network families permissions
|
||||
+ * @audit_network: which network permissions to force audit
|
||||
+ * @quiet_network: which network permissions to quiet rejects
|
||||
+ */
|
||||
+struct aa_net {
|
||||
+ u16 allow[AF_MAX];
|
||||
+ u16 audit[AF_MAX];
|
||||
+ u16 quiet[AF_MAX];
|
||||
+};
|
||||
+
|
||||
+extern int aa_net_perm(int op, struct aa_profile *profile, u16 family,
|
||||
+ int type, int protocol, struct sock *sk);
|
||||
+extern int aa_revalidate_sk(int op, struct sock *sk);
|
||||
+
|
||||
+static inline void aa_free_net_rules(struct aa_net *new)
|
||||
+{
|
||||
+ /* NOP */
|
||||
+}
|
||||
+
|
||||
+#endif /* __AA_NET_H */
|
||||
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
|
||||
index aeda5cf..6776929 100644
|
||||
--- a/security/apparmor/include/policy.h
|
||||
+++ b/security/apparmor/include/policy.h
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "capability.h"
|
||||
#include "domain.h"
|
||||
#include "file.h"
|
||||
+#include "net.h"
|
||||
#include "resource.h"
|
||||
|
||||
extern const char *profile_mode_names[];
|
||||
@@ -145,6 +146,7 @@ struct aa_namespace {
|
||||
* @size: the memory consumed by this profiles rules
|
||||
* @file: The set of rules governing basic file access and domain transitions
|
||||
* @caps: capabilities for the profile
|
||||
+ * @net: network controls for the profile
|
||||
* @rlimits: rlimits for the profile
|
||||
*
|
||||
* The AppArmor profile contains the basic confinement data. Each profile
|
||||
@@ -181,6 +183,7 @@ struct aa_profile {
|
||||
|
||||
struct aa_file_rules file;
|
||||
struct aa_caps caps;
|
||||
+ struct aa_net net;
|
||||
struct aa_rlimit rlimits;
|
||||
};
|
||||
|
||||
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
|
||||
index 3d2fd14..aa293ae 100644
|
||||
--- a/security/apparmor/lsm.c
|
||||
+++ b/security/apparmor/lsm.c
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "include/context.h"
|
||||
#include "include/file.h"
|
||||
#include "include/ipc.h"
|
||||
+#include "include/net.h"
|
||||
#include "include/path.h"
|
||||
#include "include/policy.h"
|
||||
#include "include/procattr.h"
|
||||
@@ -621,6 +622,104 @@ static int apparmor_task_setrlimit(struct task_struct *task,
|
||||
return error;
|
||||
}
|
||||
|
||||
+static int apparmor_socket_create(int family, int type, int protocol, int kern)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ int error = 0;
|
||||
+
|
||||
+ if (kern)
|
||||
+ return 0;
|
||||
+
|
||||
+ profile = __aa_current_profile();
|
||||
+ if (!unconfined(profile))
|
||||
+ error = aa_net_perm(OP_CREATE, profile, family, type, protocol,
|
||||
+ NULL);
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_bind(struct socket *sock,
|
||||
+ struct sockaddr *address, int addrlen)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_BIND, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_connect(struct socket *sock,
|
||||
+ struct sockaddr *address, int addrlen)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_CONNECT, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_listen(struct socket *sock, int backlog)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_LISTEN, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_accept(struct socket *sock, struct socket *newsock)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_ACCEPT, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_sendmsg(struct socket *sock,
|
||||
+ struct msghdr *msg, int size)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_SENDMSG, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_recvmsg(struct socket *sock,
|
||||
+ struct msghdr *msg, int size, int flags)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_RECVMSG, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_getsockname(struct socket *sock)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_GETSOCKNAME, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_getpeername(struct socket *sock)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_GETPEERNAME, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_getsockopt(struct socket *sock, int level,
|
||||
+ int optname)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_GETSOCKOPT, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_setsockopt(struct socket *sock, int level,
|
||||
+ int optname)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_SETSOCKOPT, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_shutdown(struct socket *sock, int how)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk);
|
||||
+}
|
||||
+
|
||||
static struct security_operations apparmor_ops = {
|
||||
.name = "apparmor",
|
||||
|
||||
@@ -652,6 +751,19 @@ static struct security_operations apparmor_ops = {
|
||||
.getprocattr = apparmor_getprocattr,
|
||||
.setprocattr = apparmor_setprocattr,
|
||||
|
||||
+ .socket_create = apparmor_socket_create,
|
||||
+ .socket_bind = apparmor_socket_bind,
|
||||
+ .socket_connect = apparmor_socket_connect,
|
||||
+ .socket_listen = apparmor_socket_listen,
|
||||
+ .socket_accept = apparmor_socket_accept,
|
||||
+ .socket_sendmsg = apparmor_socket_sendmsg,
|
||||
+ .socket_recvmsg = apparmor_socket_recvmsg,
|
||||
+ .socket_getsockname = apparmor_socket_getsockname,
|
||||
+ .socket_getpeername = apparmor_socket_getpeername,
|
||||
+ .socket_getsockopt = apparmor_socket_getsockopt,
|
||||
+ .socket_setsockopt = apparmor_socket_setsockopt,
|
||||
+ .socket_shutdown = apparmor_socket_shutdown,
|
||||
+
|
||||
.cred_alloc_blank = apparmor_cred_alloc_blank,
|
||||
.cred_free = apparmor_cred_free,
|
||||
.cred_prepare = apparmor_cred_prepare,
|
||||
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
|
||||
new file mode 100644
|
||||
index 0000000..1765901
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/net.c
|
||||
@@ -0,0 +1,170 @@
|
||||
+/*
|
||||
+ * AppArmor security module
|
||||
+ *
|
||||
+ * This file contains AppArmor network mediation
|
||||
+ *
|
||||
+ * Copyright (C) 1998-2008 Novell/SUSE
|
||||
+ * Copyright 2009-2010 Canonical Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License as
|
||||
+ * published by the Free Software Foundation, version 2 of the
|
||||
+ * License.
|
||||
+ */
|
||||
+
|
||||
+#include "include/apparmor.h"
|
||||
+#include "include/audit.h"
|
||||
+#include "include/context.h"
|
||||
+#include "include/net.h"
|
||||
+#include "include/policy.h"
|
||||
+
|
||||
+#include "af_names.h"
|
||||
+
|
||||
+static const char *sock_type_names[] = {
|
||||
+ "unknown(0)",
|
||||
+ "stream",
|
||||
+ "dgram",
|
||||
+ "raw",
|
||||
+ "rdm",
|
||||
+ "seqpacket",
|
||||
+ "dccp",
|
||||
+ "unknown(7)",
|
||||
+ "unknown(8)",
|
||||
+ "unknown(9)",
|
||||
+ "packet",
|
||||
+};
|
||||
+
|
||||
+/* audit callback for net specific fields */
|
||||
+static void audit_cb(struct audit_buffer *ab, void *va)
|
||||
+{
|
||||
+ struct common_audit_data *sa = va;
|
||||
+
|
||||
+ audit_log_format(ab, " family=");
|
||||
+ if (address_family_names[sa->u.net.family]) {
|
||||
+ audit_log_string(ab, address_family_names[sa->u.net.family]);
|
||||
+ } else {
|
||||
+ audit_log_format(ab, " \"unknown(%d)\"", sa->u.net.family);
|
||||
+ }
|
||||
+
|
||||
+ audit_log_format(ab, " sock_type=");
|
||||
+ if (sock_type_names[sa->aad.net.type]) {
|
||||
+ audit_log_string(ab, sock_type_names[sa->aad.net.type]);
|
||||
+ } else {
|
||||
+ audit_log_format(ab, "\"unknown(%d)\"", sa->aad.net.type);
|
||||
+ }
|
||||
+
|
||||
+ audit_log_format(ab, " protocol=%d", sa->aad.net.protocol);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * audit_net - audit network access
|
||||
+ * @profile: profile being enforced (NOT NULL)
|
||||
+ * @op: operation being checked
|
||||
+ * @family: network family
|
||||
+ * @type: network type
|
||||
+ * @protocol: network protocol
|
||||
+ * @sk: socket auditing is being applied to
|
||||
+ * @error: error code for failure else 0
|
||||
+ *
|
||||
+ * Returns: %0 or sa->error else other errorcode on failure
|
||||
+ */
|
||||
+static int audit_net(struct aa_profile *profile, int op, u16 family, int type,
|
||||
+ int protocol, struct sock *sk, int error)
|
||||
+{
|
||||
+ int audit_type = AUDIT_APPARMOR_AUTO;
|
||||
+ struct common_audit_data sa;
|
||||
+ if (sk) {
|
||||
+ COMMON_AUDIT_DATA_INIT(&sa, NET);
|
||||
+ } else {
|
||||
+ COMMON_AUDIT_DATA_INIT(&sa, NONE);
|
||||
+ }
|
||||
+ /* todo fill in socket addr info */
|
||||
+
|
||||
+ sa.aad.op = op,
|
||||
+ sa.u.net.family = family;
|
||||
+ sa.u.net.sk = sk;
|
||||
+ sa.aad.net.type = type;
|
||||
+ sa.aad.net.protocol = protocol;
|
||||
+ sa.aad.error = error;
|
||||
+
|
||||
+ if (likely(!sa.aad.error)) {
|
||||
+ u16 audit_mask = profile->net.audit[sa.u.net.family];
|
||||
+ if (likely((AUDIT_MODE(profile) != AUDIT_ALL) &&
|
||||
+ !(1 << sa.aad.net.type & audit_mask)))
|
||||
+ return 0;
|
||||
+ audit_type = AUDIT_APPARMOR_AUDIT;
|
||||
+ } else {
|
||||
+ u16 quiet_mask = profile->net.quiet[sa.u.net.family];
|
||||
+ u16 kill_mask = 0;
|
||||
+ u16 denied = (1 << sa.aad.net.type) & ~quiet_mask;
|
||||
+
|
||||
+ if (denied & kill_mask)
|
||||
+ audit_type = AUDIT_APPARMOR_KILL;
|
||||
+
|
||||
+ if ((denied & quiet_mask) &&
|
||||
+ AUDIT_MODE(profile) != AUDIT_NOQUIET &&
|
||||
+ AUDIT_MODE(profile) != AUDIT_ALL)
|
||||
+ return COMPLAIN_MODE(profile) ? 0 : sa.aad.error;
|
||||
+ }
|
||||
+
|
||||
+ return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * aa_net_perm - very course network access check
|
||||
+ * @op: operation being checked
|
||||
+ * @profile: profile being enforced (NOT NULL)
|
||||
+ * @family: network family
|
||||
+ * @type: network type
|
||||
+ * @protocol: network protocol
|
||||
+ *
|
||||
+ * Returns: %0 else error if permission denied
|
||||
+ */
|
||||
+int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type,
|
||||
+ int protocol, struct sock *sk)
|
||||
+{
|
||||
+ u16 family_mask;
|
||||
+ int error;
|
||||
+
|
||||
+ if ((family < 0) || (family >= AF_MAX))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if ((type < 0) || (type >= SOCK_MAX))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /* unix domain and netlink sockets are handled by ipc */
|
||||
+ if (family == AF_UNIX || family == AF_NETLINK)
|
||||
+ return 0;
|
||||
+
|
||||
+ family_mask = profile->net.allow[family];
|
||||
+
|
||||
+ error = (family_mask & (1 << type)) ? 0 : -EACCES;
|
||||
+
|
||||
+ return audit_net(profile, op, family, type, protocol, sk, error);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * aa_revalidate_sk - Revalidate access to a sock
|
||||
+ * @op: operation being checked
|
||||
+ * @sk: sock being revalidated (NOT NULL)
|
||||
+ *
|
||||
+ * Returns: %0 else error if permission denied
|
||||
+ */
|
||||
+int aa_revalidate_sk(int op, struct sock *sk)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ int error = 0;
|
||||
+
|
||||
+ /* aa_revalidate_sk should not be called from interrupt context
|
||||
+ * don't mediate these calls as they are not task related
|
||||
+ */
|
||||
+ if (in_interrupt())
|
||||
+ return 0;
|
||||
+
|
||||
+ profile = __aa_current_profile();
|
||||
+ if (!unconfined(profile))
|
||||
+ error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type,
|
||||
+ sk->sk_protocol, sk);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
|
||||
index 4f0eade..4d5ce13 100644
|
||||
--- a/security/apparmor/policy.c
|
||||
+++ b/security/apparmor/policy.c
|
||||
@@ -745,6 +745,7 @@ static void free_profile(struct aa_profile *profile)
|
||||
|
||||
aa_free_file_rules(&profile->file);
|
||||
aa_free_cap_rules(&profile->caps);
|
||||
+ aa_free_net_rules(&profile->net);
|
||||
aa_free_rlimit_rules(&profile->rlimits);
|
||||
|
||||
aa_free_sid(profile->sid);
|
||||
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
|
||||
index d6d9a57..f4874c4 100644
|
||||
--- a/security/apparmor/policy_unpack.c
|
||||
+++ b/security/apparmor/policy_unpack.c
|
||||
@@ -190,6 +190,19 @@ fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name)
|
||||
+{
|
||||
+ if (unpack_nameX(e, AA_U16, name)) {
|
||||
+ if (!inbounds(e, sizeof(u16)))
|
||||
+ return 0;
|
||||
+ if (data)
|
||||
+ *data = le16_to_cpu(get_unaligned((u16 *) e->pos));
|
||||
+ e->pos += sizeof(u16);
|
||||
+ return 1;
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name)
|
||||
{
|
||||
if (unpack_nameX(e, AA_U32, name)) {
|
||||
@@ -468,7 +481,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
|
||||
{
|
||||
struct aa_profile *profile = NULL;
|
||||
const char *name = NULL;
|
||||
- int error = -EPROTO;
|
||||
+ size_t size = 0;
|
||||
+ int i, error = -EPROTO;
|
||||
kernel_cap_t tmpcap;
|
||||
u32 tmp;
|
||||
|
||||
@@ -559,6 +573,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
|
||||
if (!unpack_rlimits(e, profile))
|
||||
goto fail;
|
||||
|
||||
+ size = unpack_array(e, "net_allowed_af");
|
||||
+ if (size) {
|
||||
+
|
||||
+ for (i = 0; i < size; i++) {
|
||||
+ /* discard extraneous rules that this kernel will
|
||||
+ * never request
|
||||
+ */
|
||||
+ if (i >= AF_MAX) {
|
||||
+ u16 tmp;
|
||||
+ if (!unpack_u16(e, &tmp, NULL) ||
|
||||
+ !unpack_u16(e, &tmp, NULL) ||
|
||||
+ !unpack_u16(e, &tmp, NULL))
|
||||
+ goto fail;
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (!unpack_u16(e, &profile->net.allow[i], NULL))
|
||||
+ goto fail;
|
||||
+ if (!unpack_u16(e, &profile->net.audit[i], NULL))
|
||||
+ goto fail;
|
||||
+ if (!unpack_u16(e, &profile->net.quiet[i], NULL))
|
||||
+ goto fail;
|
||||
+ }
|
||||
+ if (!unpack_nameX(e, AA_ARRAYEND, NULL))
|
||||
+ goto fail;
|
||||
+ /*
|
||||
+ * allow unix domain and netlink sockets they are handled
|
||||
+ * by IPC
|
||||
+ */
|
||||
+ }
|
||||
+ profile->net.allow[AF_UNIX] = 0xffff;
|
||||
+ profile->net.allow[AF_NETLINK] = 0xffff;
|
||||
+
|
||||
/* get file rules */
|
||||
profile->file.dfa = unpack_dfa(e);
|
||||
if (IS_ERR(profile->file.dfa)) {
|
||||
--
|
||||
1.7.5.4
|
||||
|
@@ -0,0 +1,391 @@
|
||||
From a2515f25ad5a7833ddc5a032d34eee6a5ddee3a2 Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Wed, 10 Aug 2011 22:02:40 -0700
|
||||
Subject: [PATCH 2/3] AppArmor: compatibility patch for v5 interface
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
---
|
||||
security/apparmor/Kconfig | 9 +
|
||||
security/apparmor/Makefile | 1 +
|
||||
security/apparmor/apparmorfs-24.c | 287 ++++++++++++++++++++++++++++++++
|
||||
security/apparmor/apparmorfs.c | 18 ++-
|
||||
security/apparmor/include/apparmorfs.h | 6 +
|
||||
5 files changed, 319 insertions(+), 2 deletions(-)
|
||||
create mode 100644 security/apparmor/apparmorfs-24.c
|
||||
|
||||
diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig
|
||||
index 9b9013b..51ebf96 100644
|
||||
--- a/security/apparmor/Kconfig
|
||||
+++ b/security/apparmor/Kconfig
|
||||
@@ -29,3 +29,12 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE
|
||||
boot.
|
||||
|
||||
If you are unsure how to answer this question, answer 1.
|
||||
+
|
||||
+config SECURITY_APPARMOR_COMPAT_24
|
||||
+ bool "Enable AppArmor 2.4 compatability"
|
||||
+ depends on SECURITY_APPARMOR
|
||||
+ default y
|
||||
+ help
|
||||
+ This option enables compatability with AppArmor 2.4. It is
|
||||
+ recommended if compatability with older versions of AppArmor
|
||||
+ is desired.
|
||||
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
|
||||
index 7cefef9..0bb604b 100644
|
||||
--- a/security/apparmor/Makefile
|
||||
+++ b/security/apparmor/Makefile
|
||||
@@ -5,6 +5,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
|
||||
apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
|
||||
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
|
||||
resource.o sid.o file.o net.o
|
||||
+apparmor-$(CONFIG_SECURITY_APPARMOR_COMPAT_24) += apparmorfs-24.o
|
||||
|
||||
clean-files := capability_names.h rlim_names.h af_names.h
|
||||
|
||||
diff --git a/security/apparmor/apparmorfs-24.c b/security/apparmor/apparmorfs-24.c
|
||||
new file mode 100644
|
||||
index 0000000..dc8c744
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/apparmorfs-24.c
|
||||
@@ -0,0 +1,287 @@
|
||||
+/*
|
||||
+ * AppArmor security module
|
||||
+ *
|
||||
+ * This file contains AppArmor /sys/kernel/secrutiy/apparmor interface functions
|
||||
+ *
|
||||
+ * Copyright (C) 1998-2008 Novell/SUSE
|
||||
+ * Copyright 2009-2010 Canonical Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License as
|
||||
+ * published by the Free Software Foundation, version 2 of the
|
||||
+ * License.
|
||||
+ *
|
||||
+ *
|
||||
+ * This file contain functions providing an interface for <= AppArmor 2.4
|
||||
+ * compatibility. It is dependent on CONFIG_SECURITY_APPARMOR_COMPAT_24
|
||||
+ * being set (see Makefile).
|
||||
+ */
|
||||
+
|
||||
+#include <linux/security.h>
|
||||
+#include <linux/vmalloc.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/seq_file.h>
|
||||
+#include <linux/uaccess.h>
|
||||
+#include <linux/namei.h>
|
||||
+
|
||||
+#include "include/apparmor.h"
|
||||
+#include "include/audit.h"
|
||||
+#include "include/context.h"
|
||||
+#include "include/policy.h"
|
||||
+
|
||||
+
|
||||
+/* apparmor/matching */
|
||||
+static ssize_t aa_matching_read(struct file *file, char __user *buf,
|
||||
+ size_t size, loff_t *ppos)
|
||||
+{
|
||||
+ const char matching[] = "pattern=aadfa audit perms=crwxamlk/ "
|
||||
+ "user::other";
|
||||
+
|
||||
+ return simple_read_from_buffer(buf, size, ppos, matching,
|
||||
+ sizeof(matching) - 1);
|
||||
+}
|
||||
+
|
||||
+const struct file_operations aa_fs_matching_fops = {
|
||||
+ .read = aa_matching_read,
|
||||
+};
|
||||
+
|
||||
+/* apparmor/features */
|
||||
+static ssize_t aa_features_read(struct file *file, char __user *buf,
|
||||
+ size_t size, loff_t *ppos)
|
||||
+{
|
||||
+ const char features[] = "file=3.1 capability=2.0 network=1.0 "
|
||||
+ "change_hat=1.5 change_profile=1.1 " "aanamespaces=1.1 rlimit=1.1";
|
||||
+
|
||||
+ return simple_read_from_buffer(buf, size, ppos, features,
|
||||
+ sizeof(features) - 1);
|
||||
+}
|
||||
+
|
||||
+const struct file_operations aa_fs_features_fops = {
|
||||
+ .read = aa_features_read,
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * __next_namespace - find the next namespace to list
|
||||
+ * @root: root namespace to stop search at (NOT NULL)
|
||||
+ * @ns: current ns position (NOT NULL)
|
||||
+ *
|
||||
+ * Find the next namespace from @ns under @root and handle all locking needed
|
||||
+ * while switching current namespace.
|
||||
+ *
|
||||
+ * Returns: next namespace or NULL if at last namespace under @root
|
||||
+ * NOTE: will not unlock root->lock
|
||||
+ */
|
||||
+static struct aa_namespace *__next_namespace(struct aa_namespace *root,
|
||||
+ struct aa_namespace *ns)
|
||||
+{
|
||||
+ struct aa_namespace *parent;
|
||||
+
|
||||
+ /* is next namespace a child */
|
||||
+ if (!list_empty(&ns->sub_ns)) {
|
||||
+ struct aa_namespace *next;
|
||||
+ next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list);
|
||||
+ read_lock(&next->lock);
|
||||
+ return next;
|
||||
+ }
|
||||
+
|
||||
+ /* check if the next ns is a sibling, parent, gp, .. */
|
||||
+ parent = ns->parent;
|
||||
+ while (parent) {
|
||||
+ read_unlock(&ns->lock);
|
||||
+ list_for_each_entry_continue(ns, &parent->sub_ns, base.list) {
|
||||
+ read_lock(&ns->lock);
|
||||
+ return ns;
|
||||
+ }
|
||||
+ if (parent == root)
|
||||
+ return NULL;
|
||||
+ ns = parent;
|
||||
+ parent = parent->parent;
|
||||
+ }
|
||||
+
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * __first_profile - find the first profile in a namespace
|
||||
+ * @root: namespace that is root of profiles being displayed (NOT NULL)
|
||||
+ * @ns: namespace to start in (NOT NULL)
|
||||
+ *
|
||||
+ * Returns: unrefcounted profile or NULL if no profile
|
||||
+ */
|
||||
+static struct aa_profile *__first_profile(struct aa_namespace *root,
|
||||
+ struct aa_namespace *ns)
|
||||
+{
|
||||
+ for ( ; ns; ns = __next_namespace(root, ns)) {
|
||||
+ if (!list_empty(&ns->base.profiles))
|
||||
+ return list_first_entry(&ns->base.profiles,
|
||||
+ struct aa_profile, base.list);
|
||||
+ }
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * __next_profile - step to the next profile in a profile tree
|
||||
+ * @profile: current profile in tree (NOT NULL)
|
||||
+ *
|
||||
+ * Perform a depth first taversal on the profile tree in a namespace
|
||||
+ *
|
||||
+ * Returns: next profile or NULL if done
|
||||
+ * Requires: profile->ns.lock to be held
|
||||
+ */
|
||||
+static struct aa_profile *__next_profile(struct aa_profile *p)
|
||||
+{
|
||||
+ struct aa_profile *parent;
|
||||
+ struct aa_namespace *ns = p->ns;
|
||||
+
|
||||
+ /* is next profile a child */
|
||||
+ if (!list_empty(&p->base.profiles))
|
||||
+ return list_first_entry(&p->base.profiles, typeof(*p),
|
||||
+ base.list);
|
||||
+
|
||||
+ /* is next profile a sibling, parent sibling, gp, subling, .. */
|
||||
+ parent = p->parent;
|
||||
+ while (parent) {
|
||||
+ list_for_each_entry_continue(p, &parent->base.profiles,
|
||||
+ base.list)
|
||||
+ return p;
|
||||
+ p = parent;
|
||||
+ parent = parent->parent;
|
||||
+ }
|
||||
+
|
||||
+ /* is next another profile in the namespace */
|
||||
+ list_for_each_entry_continue(p, &ns->base.profiles, base.list)
|
||||
+ return p;
|
||||
+
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * next_profile - step to the next profile in where ever it may be
|
||||
+ * @root: root namespace (NOT NULL)
|
||||
+ * @profile: current profile (NOT NULL)
|
||||
+ *
|
||||
+ * Returns: next profile or NULL if there isn't one
|
||||
+ */
|
||||
+static struct aa_profile *next_profile(struct aa_namespace *root,
|
||||
+ struct aa_profile *profile)
|
||||
+{
|
||||
+ struct aa_profile *next = __next_profile(profile);
|
||||
+ if (next)
|
||||
+ return next;
|
||||
+
|
||||
+ /* finished all profiles in namespace move to next namespace */
|
||||
+ return __first_profile(root, __next_namespace(root, profile->ns));
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * p_start - start a depth first traversal of profile tree
|
||||
+ * @f: seq_file to fill
|
||||
+ * @pos: current position
|
||||
+ *
|
||||
+ * Returns: first profile under current namespace or NULL if none found
|
||||
+ *
|
||||
+ * acquires first ns->lock
|
||||
+ */
|
||||
+static void *p_start(struct seq_file *f, loff_t *pos)
|
||||
+ __acquires(root->lock)
|
||||
+{
|
||||
+ struct aa_profile *profile = NULL;
|
||||
+ struct aa_namespace *root = aa_current_profile()->ns;
|
||||
+ loff_t l = *pos;
|
||||
+ f->private = aa_get_namespace(root);
|
||||
+
|
||||
+
|
||||
+ /* find the first profile */
|
||||
+ read_lock(&root->lock);
|
||||
+ profile = __first_profile(root, root);
|
||||
+
|
||||
+ /* skip to position */
|
||||
+ for (; profile && l > 0; l--)
|
||||
+ profile = next_profile(root, profile);
|
||||
+
|
||||
+ return profile;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * p_next - read the next profile entry
|
||||
+ * @f: seq_file to fill
|
||||
+ * @p: profile previously returned
|
||||
+ * @pos: current position
|
||||
+ *
|
||||
+ * Returns: next profile after @p or NULL if none
|
||||
+ *
|
||||
+ * may acquire/release locks in namespace tree as necessary
|
||||
+ */
|
||||
+static void *p_next(struct seq_file *f, void *p, loff_t *pos)
|
||||
+{
|
||||
+ struct aa_profile *profile = p;
|
||||
+ struct aa_namespace *root = f->private;
|
||||
+ (*pos)++;
|
||||
+
|
||||
+ return next_profile(root, profile);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * p_stop - stop depth first traversal
|
||||
+ * @f: seq_file we are filling
|
||||
+ * @p: the last profile writen
|
||||
+ *
|
||||
+ * Release all locking done by p_start/p_next on namespace tree
|
||||
+ */
|
||||
+static void p_stop(struct seq_file *f, void *p)
|
||||
+ __releases(root->lock)
|
||||
+{
|
||||
+ struct aa_profile *profile = p;
|
||||
+ struct aa_namespace *root = f->private, *ns;
|
||||
+
|
||||
+ if (profile) {
|
||||
+ for (ns = profile->ns; ns && ns != root; ns = ns->parent)
|
||||
+ read_unlock(&ns->lock);
|
||||
+ }
|
||||
+ read_unlock(&root->lock);
|
||||
+ aa_put_namespace(root);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * seq_show_profile - show a profile entry
|
||||
+ * @f: seq_file to file
|
||||
+ * @p: current position (profile) (NOT NULL)
|
||||
+ *
|
||||
+ * Returns: error on failure
|
||||
+ */
|
||||
+static int seq_show_profile(struct seq_file *f, void *p)
|
||||
+{
|
||||
+ struct aa_profile *profile = (struct aa_profile *)p;
|
||||
+ struct aa_namespace *root = f->private;
|
||||
+
|
||||
+ if (profile->ns != root)
|
||||
+ seq_printf(f, ":%s://", aa_ns_name(root, profile->ns));
|
||||
+ seq_printf(f, "%s (%s)\n", profile->base.hname,
|
||||
+ COMPLAIN_MODE(profile) ? "complain" : "enforce");
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct seq_operations aa_fs_profiles_op = {
|
||||
+ .start = p_start,
|
||||
+ .next = p_next,
|
||||
+ .stop = p_stop,
|
||||
+ .show = seq_show_profile,
|
||||
+};
|
||||
+
|
||||
+static int profiles_open(struct inode *inode, struct file *file)
|
||||
+{
|
||||
+ return seq_open(file, &aa_fs_profiles_op);
|
||||
+}
|
||||
+
|
||||
+static int profiles_release(struct inode *inode, struct file *file)
|
||||
+{
|
||||
+ return seq_release(inode, file);
|
||||
+}
|
||||
+
|
||||
+const struct file_operations aa_fs_profiles_fops = {
|
||||
+ .open = profiles_open,
|
||||
+ .read = seq_read,
|
||||
+ .llseek = seq_lseek,
|
||||
+ .release = profiles_release,
|
||||
+};
|
||||
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
|
||||
index 0848292..28c52ac 100644
|
||||
--- a/security/apparmor/apparmorfs.c
|
||||
+++ b/security/apparmor/apparmorfs.c
|
||||
@@ -187,7 +187,11 @@ void __init aa_destroy_aafs(void)
|
||||
aafs_remove(".remove");
|
||||
aafs_remove(".replace");
|
||||
aafs_remove(".load");
|
||||
-
|
||||
+#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24
|
||||
+ aafs_remove("profiles");
|
||||
+ aafs_remove("matching");
|
||||
+ aafs_remove("features");
|
||||
+#endif
|
||||
securityfs_remove(aa_fs_dentry);
|
||||
aa_fs_dentry = NULL;
|
||||
}
|
||||
@@ -218,7 +222,17 @@ int __init aa_create_aafs(void)
|
||||
aa_fs_dentry = NULL;
|
||||
goto error;
|
||||
}
|
||||
-
|
||||
+#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24
|
||||
+ error = aafs_create("matching", 0444, &aa_fs_matching_fops);
|
||||
+ if (error)
|
||||
+ goto error;
|
||||
+ error = aafs_create("features", 0444, &aa_fs_features_fops);
|
||||
+ if (error)
|
||||
+ goto error;
|
||||
+#endif
|
||||
+ error = aafs_create("profiles", 0440, &aa_fs_profiles_fops);
|
||||
+ if (error)
|
||||
+ goto error;
|
||||
error = aafs_create(".load", 0640, &aa_fs_profile_load);
|
||||
if (error)
|
||||
goto error;
|
||||
diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h
|
||||
index cb1e93a..14f955c 100644
|
||||
--- a/security/apparmor/include/apparmorfs.h
|
||||
+++ b/security/apparmor/include/apparmorfs.h
|
||||
@@ -17,4 +17,10 @@
|
||||
|
||||
extern void __init aa_destroy_aafs(void);
|
||||
|
||||
+#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24
|
||||
+extern const struct file_operations aa_fs_matching_fops;
|
||||
+extern const struct file_operations aa_fs_features_fops;
|
||||
+extern const struct file_operations aa_fs_profiles_fops;
|
||||
+#endif
|
||||
+
|
||||
#endif /* __AA_APPARMORFS_H */
|
||||
--
|
||||
1.7.5.4
|
||||
|
@@ -0,0 +1,69 @@
|
||||
From 7a10d093f9779f42cb8d6affcb6a4436d3ebd6d3 Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Wed, 10 Aug 2011 22:02:41 -0700
|
||||
Subject: [PATCH 3/3] AppArmor: Allow dfa backward compatibility with broken
|
||||
userspace
|
||||
|
||||
The apparmor_parser when compiling policy could generate invalid dfas
|
||||
that did not have sufficient padding to avoid invalid references, when
|
||||
used by the kernel. The kernels check to verify the next/check table
|
||||
size was broken meaning invalid dfas were being created by userspace
|
||||
and not caught.
|
||||
|
||||
To remain compatible with old tools that are not fixed, pad the loaded
|
||||
dfas next/check table. The dfa's themselves are valid except for the
|
||||
high padding for potentially invalid transitions (high bounds error),
|
||||
which have a maximimum is 256 entries. So just allocate an extra null filled
|
||||
256 entries for the next/check tables. This will guarentee all bounds
|
||||
are good and invalid transitions go to the null (0) state.
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
---
|
||||
security/apparmor/match.c | 17 +++++++++++++++++
|
||||
1 files changed, 17 insertions(+), 0 deletions(-)
|
||||
|
||||
diff --git a/security/apparmor/match.c b/security/apparmor/match.c
|
||||
index 94de6b4..081491e 100644
|
||||
--- a/security/apparmor/match.c
|
||||
+++ b/security/apparmor/match.c
|
||||
@@ -57,8 +57,17 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
|
||||
if (bsize < tsize)
|
||||
goto out;
|
||||
|
||||
+ /* Pad table allocation for next/check by 256 entries to remain
|
||||
+ * backwards compatible with old (buggy) tools and remain safe without
|
||||
+ * run time checks
|
||||
+ */
|
||||
+ if (th.td_id == YYTD_ID_NXT || th.td_id == YYTD_ID_CHK)
|
||||
+ tsize += 256 * th.td_flags;
|
||||
+
|
||||
table = kvmalloc(tsize);
|
||||
if (table) {
|
||||
+ /* ensure the pad is clear, else there will be errors */
|
||||
+ memset(table, 0, tsize);
|
||||
*table = th;
|
||||
if (th.td_flags == YYTD_DATA8)
|
||||
UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
|
||||
@@ -134,11 +143,19 @@ static int verify_dfa(struct aa_dfa *dfa, int flags)
|
||||
goto out;
|
||||
|
||||
if (flags & DFA_FLAG_VERIFY_STATES) {
|
||||
+ int warning = 0;
|
||||
for (i = 0; i < state_count; i++) {
|
||||
if (DEFAULT_TABLE(dfa)[i] >= state_count)
|
||||
goto out;
|
||||
/* TODO: do check that DEF state recursion terminates */
|
||||
if (BASE_TABLE(dfa)[i] + 255 >= trans_count) {
|
||||
+ if (warning)
|
||||
+ continue;
|
||||
+ printk(KERN_WARNING "AppArmor DFA next/check "
|
||||
+ "upper bounds error fixed, upgrade "
|
||||
+ "user space tools \n");
|
||||
+ warning = 1;
|
||||
+ } else if (BASE_TABLE(dfa)[i] >= trans_count) {
|
||||
printk(KERN_ERR "AppArmor DFA next/check upper "
|
||||
"bounds error\n");
|
||||
goto out;
|
||||
--
|
||||
1.7.5.4
|
||||
|
@@ -0,0 +1,553 @@
|
||||
From dc13dec93dbd04bfa7a9ba67df1b8ed3431d8d48 Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Wed, 10 Aug 2011 22:02:39 -0700
|
||||
Subject: [PATCH 1/3] AppArmor: compatibility patch for v5 network controll
|
||||
|
||||
Add compatibility for v5 network rules.
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
---
|
||||
include/linux/lsm_audit.h | 4 +
|
||||
security/apparmor/Makefile | 19 ++++-
|
||||
security/apparmor/include/net.h | 40 +++++++++
|
||||
security/apparmor/include/policy.h | 3 +
|
||||
security/apparmor/lsm.c | 112 +++++++++++++++++++++++
|
||||
security/apparmor/net.c | 170 ++++++++++++++++++++++++++++++++++++
|
||||
security/apparmor/policy.c | 1 +
|
||||
security/apparmor/policy_unpack.c | 48 ++++++++++-
|
||||
8 files changed, 394 insertions(+), 3 deletions(-)
|
||||
create mode 100644 security/apparmor/include/net.h
|
||||
create mode 100644 security/apparmor/net.c
|
||||
|
||||
diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h
|
||||
index 88e78de..c63979a 100644
|
||||
--- a/include/linux/lsm_audit.h
|
||||
+++ b/include/linux/lsm_audit.h
|
||||
@@ -124,6 +124,10 @@ struct common_audit_data {
|
||||
u32 denied;
|
||||
uid_t ouid;
|
||||
} fs;
|
||||
+ struct {
|
||||
+ int type, protocol;
|
||||
+ struct sock *sk;
|
||||
+ } net;
|
||||
};
|
||||
} apparmor_audit_data;
|
||||
#endif
|
||||
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
|
||||
index 2dafe50..7cefef9 100644
|
||||
--- a/security/apparmor/Makefile
|
||||
+++ b/security/apparmor/Makefile
|
||||
@@ -4,9 +4,9 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
|
||||
|
||||
apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
|
||||
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
|
||||
- resource.o sid.o file.o
|
||||
+ resource.o sid.o file.o net.o
|
||||
|
||||
-clean-files := capability_names.h rlim_names.h
|
||||
+clean-files := capability_names.h rlim_names.h af_names.h
|
||||
|
||||
|
||||
# Build a lower case string table of capability names
|
||||
@@ -44,9 +44,24 @@ cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ;\
|
||||
sed -r -n "s/^\# ?define[ \t]+(RLIMIT_[A-Z0-9_]+).*/\1,/p" $< >> $@ ;\
|
||||
echo "};" >> $@
|
||||
|
||||
+# Build a lower case string table of address family names.
|
||||
+# Transform lines from
|
||||
+# #define AF_INET 2 /* Internet IP Protocol */
|
||||
+# to
|
||||
+# [2] = "inet",
|
||||
+quiet_cmd_make-af = GEN $@
|
||||
+cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\
|
||||
+ sed $< >> $@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \
|
||||
+ 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+).*/[\2] = "\L\1",/p';\
|
||||
+ echo "};" >> $@
|
||||
+
|
||||
+
|
||||
$(obj)/capability.o : $(obj)/capability_names.h
|
||||
$(obj)/resource.o : $(obj)/rlim_names.h
|
||||
+$(obj)/net.o : $(obj)/af_names.h
|
||||
$(obj)/capability_names.h : $(srctree)/include/linux/capability.h
|
||||
$(call cmd,make-caps)
|
||||
$(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h
|
||||
$(call cmd,make-rlim)
|
||||
+$(obj)/af_names.h : $(srctree)/include/linux/socket.h
|
||||
+ $(call cmd,make-af)
|
||||
\ No newline at end of file
|
||||
diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h
|
||||
new file mode 100644
|
||||
index 0000000..3c7d599
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/include/net.h
|
||||
@@ -0,0 +1,40 @@
|
||||
+/*
|
||||
+ * AppArmor security module
|
||||
+ *
|
||||
+ * This file contains AppArmor network mediation definitions.
|
||||
+ *
|
||||
+ * Copyright (C) 1998-2008 Novell/SUSE
|
||||
+ * Copyright 2009-2010 Canonical Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License as
|
||||
+ * published by the Free Software Foundation, version 2 of the
|
||||
+ * License.
|
||||
+ */
|
||||
+
|
||||
+#ifndef __AA_NET_H
|
||||
+#define __AA_NET_H
|
||||
+
|
||||
+#include <net/sock.h>
|
||||
+
|
||||
+/* struct aa_net - network confinement data
|
||||
+ * @allowed: basic network families permissions
|
||||
+ * @audit_network: which network permissions to force audit
|
||||
+ * @quiet_network: which network permissions to quiet rejects
|
||||
+ */
|
||||
+struct aa_net {
|
||||
+ u16 allow[AF_MAX];
|
||||
+ u16 audit[AF_MAX];
|
||||
+ u16 quiet[AF_MAX];
|
||||
+};
|
||||
+
|
||||
+extern int aa_net_perm(int op, struct aa_profile *profile, u16 family,
|
||||
+ int type, int protocol, struct sock *sk);
|
||||
+extern int aa_revalidate_sk(int op, struct sock *sk);
|
||||
+
|
||||
+static inline void aa_free_net_rules(struct aa_net *new)
|
||||
+{
|
||||
+ /* NOP */
|
||||
+}
|
||||
+
|
||||
+#endif /* __AA_NET_H */
|
||||
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
|
||||
index aeda5cf..6776929 100644
|
||||
--- a/security/apparmor/include/policy.h
|
||||
+++ b/security/apparmor/include/policy.h
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "capability.h"
|
||||
#include "domain.h"
|
||||
#include "file.h"
|
||||
+#include "net.h"
|
||||
#include "resource.h"
|
||||
|
||||
extern const char *profile_mode_names[];
|
||||
@@ -145,6 +146,7 @@ struct aa_namespace {
|
||||
* @size: the memory consumed by this profiles rules
|
||||
* @file: The set of rules governing basic file access and domain transitions
|
||||
* @caps: capabilities for the profile
|
||||
+ * @net: network controls for the profile
|
||||
* @rlimits: rlimits for the profile
|
||||
*
|
||||
* The AppArmor profile contains the basic confinement data. Each profile
|
||||
@@ -181,6 +183,7 @@ struct aa_profile {
|
||||
|
||||
struct aa_file_rules file;
|
||||
struct aa_caps caps;
|
||||
+ struct aa_net net;
|
||||
struct aa_rlimit rlimits;
|
||||
};
|
||||
|
||||
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
|
||||
index 3d2fd14..aa293ae 100644
|
||||
--- a/security/apparmor/lsm.c
|
||||
+++ b/security/apparmor/lsm.c
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "include/context.h"
|
||||
#include "include/file.h"
|
||||
#include "include/ipc.h"
|
||||
+#include "include/net.h"
|
||||
#include "include/path.h"
|
||||
#include "include/policy.h"
|
||||
#include "include/procattr.h"
|
||||
@@ -621,6 +622,104 @@ static int apparmor_task_setrlimit(struct task_struct *task,
|
||||
return error;
|
||||
}
|
||||
|
||||
+static int apparmor_socket_create(int family, int type, int protocol, int kern)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ int error = 0;
|
||||
+
|
||||
+ if (kern)
|
||||
+ return 0;
|
||||
+
|
||||
+ profile = __aa_current_profile();
|
||||
+ if (!unconfined(profile))
|
||||
+ error = aa_net_perm(OP_CREATE, profile, family, type, protocol,
|
||||
+ NULL);
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_bind(struct socket *sock,
|
||||
+ struct sockaddr *address, int addrlen)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_BIND, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_connect(struct socket *sock,
|
||||
+ struct sockaddr *address, int addrlen)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_CONNECT, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_listen(struct socket *sock, int backlog)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_LISTEN, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_accept(struct socket *sock, struct socket *newsock)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_ACCEPT, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_sendmsg(struct socket *sock,
|
||||
+ struct msghdr *msg, int size)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_SENDMSG, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_recvmsg(struct socket *sock,
|
||||
+ struct msghdr *msg, int size, int flags)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_RECVMSG, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_getsockname(struct socket *sock)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_GETSOCKNAME, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_getpeername(struct socket *sock)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_GETPEERNAME, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_getsockopt(struct socket *sock, int level,
|
||||
+ int optname)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_GETSOCKOPT, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_setsockopt(struct socket *sock, int level,
|
||||
+ int optname)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_SETSOCKOPT, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_shutdown(struct socket *sock, int how)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk);
|
||||
+}
|
||||
+
|
||||
static struct security_operations apparmor_ops = {
|
||||
.name = "apparmor",
|
||||
|
||||
@@ -652,6 +751,19 @@ static struct security_operations apparmor_ops = {
|
||||
.getprocattr = apparmor_getprocattr,
|
||||
.setprocattr = apparmor_setprocattr,
|
||||
|
||||
+ .socket_create = apparmor_socket_create,
|
||||
+ .socket_bind = apparmor_socket_bind,
|
||||
+ .socket_connect = apparmor_socket_connect,
|
||||
+ .socket_listen = apparmor_socket_listen,
|
||||
+ .socket_accept = apparmor_socket_accept,
|
||||
+ .socket_sendmsg = apparmor_socket_sendmsg,
|
||||
+ .socket_recvmsg = apparmor_socket_recvmsg,
|
||||
+ .socket_getsockname = apparmor_socket_getsockname,
|
||||
+ .socket_getpeername = apparmor_socket_getpeername,
|
||||
+ .socket_getsockopt = apparmor_socket_getsockopt,
|
||||
+ .socket_setsockopt = apparmor_socket_setsockopt,
|
||||
+ .socket_shutdown = apparmor_socket_shutdown,
|
||||
+
|
||||
.cred_alloc_blank = apparmor_cred_alloc_blank,
|
||||
.cred_free = apparmor_cred_free,
|
||||
.cred_prepare = apparmor_cred_prepare,
|
||||
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
|
||||
new file mode 100644
|
||||
index 0000000..1765901
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/net.c
|
||||
@@ -0,0 +1,170 @@
|
||||
+/*
|
||||
+ * AppArmor security module
|
||||
+ *
|
||||
+ * This file contains AppArmor network mediation
|
||||
+ *
|
||||
+ * Copyright (C) 1998-2008 Novell/SUSE
|
||||
+ * Copyright 2009-2010 Canonical Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License as
|
||||
+ * published by the Free Software Foundation, version 2 of the
|
||||
+ * License.
|
||||
+ */
|
||||
+
|
||||
+#include "include/apparmor.h"
|
||||
+#include "include/audit.h"
|
||||
+#include "include/context.h"
|
||||
+#include "include/net.h"
|
||||
+#include "include/policy.h"
|
||||
+
|
||||
+#include "af_names.h"
|
||||
+
|
||||
+static const char *sock_type_names[] = {
|
||||
+ "unknown(0)",
|
||||
+ "stream",
|
||||
+ "dgram",
|
||||
+ "raw",
|
||||
+ "rdm",
|
||||
+ "seqpacket",
|
||||
+ "dccp",
|
||||
+ "unknown(7)",
|
||||
+ "unknown(8)",
|
||||
+ "unknown(9)",
|
||||
+ "packet",
|
||||
+};
|
||||
+
|
||||
+/* audit callback for net specific fields */
|
||||
+static void audit_cb(struct audit_buffer *ab, void *va)
|
||||
+{
|
||||
+ struct common_audit_data *sa = va;
|
||||
+
|
||||
+ audit_log_format(ab, " family=");
|
||||
+ if (address_family_names[sa->u.net.family]) {
|
||||
+ audit_log_string(ab, address_family_names[sa->u.net.family]);
|
||||
+ } else {
|
||||
+ audit_log_format(ab, " \"unknown(%d)\"", sa->u.net.family);
|
||||
+ }
|
||||
+
|
||||
+ audit_log_format(ab, " sock_type=");
|
||||
+ if (sock_type_names[sa->aad.net.type]) {
|
||||
+ audit_log_string(ab, sock_type_names[sa->aad.net.type]);
|
||||
+ } else {
|
||||
+ audit_log_format(ab, "\"unknown(%d)\"", sa->aad.net.type);
|
||||
+ }
|
||||
+
|
||||
+ audit_log_format(ab, " protocol=%d", sa->aad.net.protocol);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * audit_net - audit network access
|
||||
+ * @profile: profile being enforced (NOT NULL)
|
||||
+ * @op: operation being checked
|
||||
+ * @family: network family
|
||||
+ * @type: network type
|
||||
+ * @protocol: network protocol
|
||||
+ * @sk: socket auditing is being applied to
|
||||
+ * @error: error code for failure else 0
|
||||
+ *
|
||||
+ * Returns: %0 or sa->error else other errorcode on failure
|
||||
+ */
|
||||
+static int audit_net(struct aa_profile *profile, int op, u16 family, int type,
|
||||
+ int protocol, struct sock *sk, int error)
|
||||
+{
|
||||
+ int audit_type = AUDIT_APPARMOR_AUTO;
|
||||
+ struct common_audit_data sa;
|
||||
+ if (sk) {
|
||||
+ COMMON_AUDIT_DATA_INIT(&sa, NET);
|
||||
+ } else {
|
||||
+ COMMON_AUDIT_DATA_INIT(&sa, NONE);
|
||||
+ }
|
||||
+ /* todo fill in socket addr info */
|
||||
+
|
||||
+ sa.aad.op = op,
|
||||
+ sa.u.net.family = family;
|
||||
+ sa.u.net.sk = sk;
|
||||
+ sa.aad.net.type = type;
|
||||
+ sa.aad.net.protocol = protocol;
|
||||
+ sa.aad.error = error;
|
||||
+
|
||||
+ if (likely(!sa.aad.error)) {
|
||||
+ u16 audit_mask = profile->net.audit[sa.u.net.family];
|
||||
+ if (likely((AUDIT_MODE(profile) != AUDIT_ALL) &&
|
||||
+ !(1 << sa.aad.net.type & audit_mask)))
|
||||
+ return 0;
|
||||
+ audit_type = AUDIT_APPARMOR_AUDIT;
|
||||
+ } else {
|
||||
+ u16 quiet_mask = profile->net.quiet[sa.u.net.family];
|
||||
+ u16 kill_mask = 0;
|
||||
+ u16 denied = (1 << sa.aad.net.type) & ~quiet_mask;
|
||||
+
|
||||
+ if (denied & kill_mask)
|
||||
+ audit_type = AUDIT_APPARMOR_KILL;
|
||||
+
|
||||
+ if ((denied & quiet_mask) &&
|
||||
+ AUDIT_MODE(profile) != AUDIT_NOQUIET &&
|
||||
+ AUDIT_MODE(profile) != AUDIT_ALL)
|
||||
+ return COMPLAIN_MODE(profile) ? 0 : sa.aad.error;
|
||||
+ }
|
||||
+
|
||||
+ return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * aa_net_perm - very course network access check
|
||||
+ * @op: operation being checked
|
||||
+ * @profile: profile being enforced (NOT NULL)
|
||||
+ * @family: network family
|
||||
+ * @type: network type
|
||||
+ * @protocol: network protocol
|
||||
+ *
|
||||
+ * Returns: %0 else error if permission denied
|
||||
+ */
|
||||
+int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type,
|
||||
+ int protocol, struct sock *sk)
|
||||
+{
|
||||
+ u16 family_mask;
|
||||
+ int error;
|
||||
+
|
||||
+ if ((family < 0) || (family >= AF_MAX))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if ((type < 0) || (type >= SOCK_MAX))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /* unix domain and netlink sockets are handled by ipc */
|
||||
+ if (family == AF_UNIX || family == AF_NETLINK)
|
||||
+ return 0;
|
||||
+
|
||||
+ family_mask = profile->net.allow[family];
|
||||
+
|
||||
+ error = (family_mask & (1 << type)) ? 0 : -EACCES;
|
||||
+
|
||||
+ return audit_net(profile, op, family, type, protocol, sk, error);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * aa_revalidate_sk - Revalidate access to a sock
|
||||
+ * @op: operation being checked
|
||||
+ * @sk: sock being revalidated (NOT NULL)
|
||||
+ *
|
||||
+ * Returns: %0 else error if permission denied
|
||||
+ */
|
||||
+int aa_revalidate_sk(int op, struct sock *sk)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ int error = 0;
|
||||
+
|
||||
+ /* aa_revalidate_sk should not be called from interrupt context
|
||||
+ * don't mediate these calls as they are not task related
|
||||
+ */
|
||||
+ if (in_interrupt())
|
||||
+ return 0;
|
||||
+
|
||||
+ profile = __aa_current_profile();
|
||||
+ if (!unconfined(profile))
|
||||
+ error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type,
|
||||
+ sk->sk_protocol, sk);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
|
||||
index 4f0eade..4d5ce13 100644
|
||||
--- a/security/apparmor/policy.c
|
||||
+++ b/security/apparmor/policy.c
|
||||
@@ -745,6 +745,7 @@ static void free_profile(struct aa_profile *profile)
|
||||
|
||||
aa_free_file_rules(&profile->file);
|
||||
aa_free_cap_rules(&profile->caps);
|
||||
+ aa_free_net_rules(&profile->net);
|
||||
aa_free_rlimit_rules(&profile->rlimits);
|
||||
|
||||
aa_free_sid(profile->sid);
|
||||
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
|
||||
index d6d9a57..f4874c4 100644
|
||||
--- a/security/apparmor/policy_unpack.c
|
||||
+++ b/security/apparmor/policy_unpack.c
|
||||
@@ -190,6 +190,19 @@ fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name)
|
||||
+{
|
||||
+ if (unpack_nameX(e, AA_U16, name)) {
|
||||
+ if (!inbounds(e, sizeof(u16)))
|
||||
+ return 0;
|
||||
+ if (data)
|
||||
+ *data = le16_to_cpu(get_unaligned((u16 *) e->pos));
|
||||
+ e->pos += sizeof(u16);
|
||||
+ return 1;
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name)
|
||||
{
|
||||
if (unpack_nameX(e, AA_U32, name)) {
|
||||
@@ -468,7 +481,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
|
||||
{
|
||||
struct aa_profile *profile = NULL;
|
||||
const char *name = NULL;
|
||||
- int error = -EPROTO;
|
||||
+ size_t size = 0;
|
||||
+ int i, error = -EPROTO;
|
||||
kernel_cap_t tmpcap;
|
||||
u32 tmp;
|
||||
|
||||
@@ -559,6 +573,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
|
||||
if (!unpack_rlimits(e, profile))
|
||||
goto fail;
|
||||
|
||||
+ size = unpack_array(e, "net_allowed_af");
|
||||
+ if (size) {
|
||||
+
|
||||
+ for (i = 0; i < size; i++) {
|
||||
+ /* discard extraneous rules that this kernel will
|
||||
+ * never request
|
||||
+ */
|
||||
+ if (i >= AF_MAX) {
|
||||
+ u16 tmp;
|
||||
+ if (!unpack_u16(e, &tmp, NULL) ||
|
||||
+ !unpack_u16(e, &tmp, NULL) ||
|
||||
+ !unpack_u16(e, &tmp, NULL))
|
||||
+ goto fail;
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (!unpack_u16(e, &profile->net.allow[i], NULL))
|
||||
+ goto fail;
|
||||
+ if (!unpack_u16(e, &profile->net.audit[i], NULL))
|
||||
+ goto fail;
|
||||
+ if (!unpack_u16(e, &profile->net.quiet[i], NULL))
|
||||
+ goto fail;
|
||||
+ }
|
||||
+ if (!unpack_nameX(e, AA_ARRAYEND, NULL))
|
||||
+ goto fail;
|
||||
+ /*
|
||||
+ * allow unix domain and netlink sockets they are handled
|
||||
+ * by IPC
|
||||
+ */
|
||||
+ }
|
||||
+ profile->net.allow[AF_UNIX] = 0xffff;
|
||||
+ profile->net.allow[AF_NETLINK] = 0xffff;
|
||||
+
|
||||
/* get file rules */
|
||||
profile->file.dfa = unpack_dfa(e);
|
||||
if (IS_ERR(profile->file.dfa)) {
|
||||
--
|
||||
1.7.5.4
|
||||
|
@@ -0,0 +1,391 @@
|
||||
From a2515f25ad5a7833ddc5a032d34eee6a5ddee3a2 Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Wed, 10 Aug 2011 22:02:40 -0700
|
||||
Subject: [PATCH 2/3] AppArmor: compatibility patch for v5 interface
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
---
|
||||
security/apparmor/Kconfig | 9 +
|
||||
security/apparmor/Makefile | 1 +
|
||||
security/apparmor/apparmorfs-24.c | 287 ++++++++++++++++++++++++++++++++
|
||||
security/apparmor/apparmorfs.c | 18 ++-
|
||||
security/apparmor/include/apparmorfs.h | 6 +
|
||||
5 files changed, 319 insertions(+), 2 deletions(-)
|
||||
create mode 100644 security/apparmor/apparmorfs-24.c
|
||||
|
||||
diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig
|
||||
index 9b9013b..51ebf96 100644
|
||||
--- a/security/apparmor/Kconfig
|
||||
+++ b/security/apparmor/Kconfig
|
||||
@@ -29,3 +29,12 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE
|
||||
boot.
|
||||
|
||||
If you are unsure how to answer this question, answer 1.
|
||||
+
|
||||
+config SECURITY_APPARMOR_COMPAT_24
|
||||
+ bool "Enable AppArmor 2.4 compatability"
|
||||
+ depends on SECURITY_APPARMOR
|
||||
+ default y
|
||||
+ help
|
||||
+ This option enables compatability with AppArmor 2.4. It is
|
||||
+ recommended if compatability with older versions of AppArmor
|
||||
+ is desired.
|
||||
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
|
||||
index 7cefef9..0bb604b 100644
|
||||
--- a/security/apparmor/Makefile
|
||||
+++ b/security/apparmor/Makefile
|
||||
@@ -5,6 +5,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
|
||||
apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
|
||||
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
|
||||
resource.o sid.o file.o net.o
|
||||
+apparmor-$(CONFIG_SECURITY_APPARMOR_COMPAT_24) += apparmorfs-24.o
|
||||
|
||||
clean-files := capability_names.h rlim_names.h af_names.h
|
||||
|
||||
diff --git a/security/apparmor/apparmorfs-24.c b/security/apparmor/apparmorfs-24.c
|
||||
new file mode 100644
|
||||
index 0000000..dc8c744
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/apparmorfs-24.c
|
||||
@@ -0,0 +1,287 @@
|
||||
+/*
|
||||
+ * AppArmor security module
|
||||
+ *
|
||||
+ * This file contains AppArmor /sys/kernel/secrutiy/apparmor interface functions
|
||||
+ *
|
||||
+ * Copyright (C) 1998-2008 Novell/SUSE
|
||||
+ * Copyright 2009-2010 Canonical Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License as
|
||||
+ * published by the Free Software Foundation, version 2 of the
|
||||
+ * License.
|
||||
+ *
|
||||
+ *
|
||||
+ * This file contain functions providing an interface for <= AppArmor 2.4
|
||||
+ * compatibility. It is dependent on CONFIG_SECURITY_APPARMOR_COMPAT_24
|
||||
+ * being set (see Makefile).
|
||||
+ */
|
||||
+
|
||||
+#include <linux/security.h>
|
||||
+#include <linux/vmalloc.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/seq_file.h>
|
||||
+#include <linux/uaccess.h>
|
||||
+#include <linux/namei.h>
|
||||
+
|
||||
+#include "include/apparmor.h"
|
||||
+#include "include/audit.h"
|
||||
+#include "include/context.h"
|
||||
+#include "include/policy.h"
|
||||
+
|
||||
+
|
||||
+/* apparmor/matching */
|
||||
+static ssize_t aa_matching_read(struct file *file, char __user *buf,
|
||||
+ size_t size, loff_t *ppos)
|
||||
+{
|
||||
+ const char matching[] = "pattern=aadfa audit perms=crwxamlk/ "
|
||||
+ "user::other";
|
||||
+
|
||||
+ return simple_read_from_buffer(buf, size, ppos, matching,
|
||||
+ sizeof(matching) - 1);
|
||||
+}
|
||||
+
|
||||
+const struct file_operations aa_fs_matching_fops = {
|
||||
+ .read = aa_matching_read,
|
||||
+};
|
||||
+
|
||||
+/* apparmor/features */
|
||||
+static ssize_t aa_features_read(struct file *file, char __user *buf,
|
||||
+ size_t size, loff_t *ppos)
|
||||
+{
|
||||
+ const char features[] = "file=3.1 capability=2.0 network=1.0 "
|
||||
+ "change_hat=1.5 change_profile=1.1 " "aanamespaces=1.1 rlimit=1.1";
|
||||
+
|
||||
+ return simple_read_from_buffer(buf, size, ppos, features,
|
||||
+ sizeof(features) - 1);
|
||||
+}
|
||||
+
|
||||
+const struct file_operations aa_fs_features_fops = {
|
||||
+ .read = aa_features_read,
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * __next_namespace - find the next namespace to list
|
||||
+ * @root: root namespace to stop search at (NOT NULL)
|
||||
+ * @ns: current ns position (NOT NULL)
|
||||
+ *
|
||||
+ * Find the next namespace from @ns under @root and handle all locking needed
|
||||
+ * while switching current namespace.
|
||||
+ *
|
||||
+ * Returns: next namespace or NULL if at last namespace under @root
|
||||
+ * NOTE: will not unlock root->lock
|
||||
+ */
|
||||
+static struct aa_namespace *__next_namespace(struct aa_namespace *root,
|
||||
+ struct aa_namespace *ns)
|
||||
+{
|
||||
+ struct aa_namespace *parent;
|
||||
+
|
||||
+ /* is next namespace a child */
|
||||
+ if (!list_empty(&ns->sub_ns)) {
|
||||
+ struct aa_namespace *next;
|
||||
+ next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list);
|
||||
+ read_lock(&next->lock);
|
||||
+ return next;
|
||||
+ }
|
||||
+
|
||||
+ /* check if the next ns is a sibling, parent, gp, .. */
|
||||
+ parent = ns->parent;
|
||||
+ while (parent) {
|
||||
+ read_unlock(&ns->lock);
|
||||
+ list_for_each_entry_continue(ns, &parent->sub_ns, base.list) {
|
||||
+ read_lock(&ns->lock);
|
||||
+ return ns;
|
||||
+ }
|
||||
+ if (parent == root)
|
||||
+ return NULL;
|
||||
+ ns = parent;
|
||||
+ parent = parent->parent;
|
||||
+ }
|
||||
+
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * __first_profile - find the first profile in a namespace
|
||||
+ * @root: namespace that is root of profiles being displayed (NOT NULL)
|
||||
+ * @ns: namespace to start in (NOT NULL)
|
||||
+ *
|
||||
+ * Returns: unrefcounted profile or NULL if no profile
|
||||
+ */
|
||||
+static struct aa_profile *__first_profile(struct aa_namespace *root,
|
||||
+ struct aa_namespace *ns)
|
||||
+{
|
||||
+ for ( ; ns; ns = __next_namespace(root, ns)) {
|
||||
+ if (!list_empty(&ns->base.profiles))
|
||||
+ return list_first_entry(&ns->base.profiles,
|
||||
+ struct aa_profile, base.list);
|
||||
+ }
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * __next_profile - step to the next profile in a profile tree
|
||||
+ * @profile: current profile in tree (NOT NULL)
|
||||
+ *
|
||||
+ * Perform a depth first taversal on the profile tree in a namespace
|
||||
+ *
|
||||
+ * Returns: next profile or NULL if done
|
||||
+ * Requires: profile->ns.lock to be held
|
||||
+ */
|
||||
+static struct aa_profile *__next_profile(struct aa_profile *p)
|
||||
+{
|
||||
+ struct aa_profile *parent;
|
||||
+ struct aa_namespace *ns = p->ns;
|
||||
+
|
||||
+ /* is next profile a child */
|
||||
+ if (!list_empty(&p->base.profiles))
|
||||
+ return list_first_entry(&p->base.profiles, typeof(*p),
|
||||
+ base.list);
|
||||
+
|
||||
+ /* is next profile a sibling, parent sibling, gp, subling, .. */
|
||||
+ parent = p->parent;
|
||||
+ while (parent) {
|
||||
+ list_for_each_entry_continue(p, &parent->base.profiles,
|
||||
+ base.list)
|
||||
+ return p;
|
||||
+ p = parent;
|
||||
+ parent = parent->parent;
|
||||
+ }
|
||||
+
|
||||
+ /* is next another profile in the namespace */
|
||||
+ list_for_each_entry_continue(p, &ns->base.profiles, base.list)
|
||||
+ return p;
|
||||
+
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * next_profile - step to the next profile in where ever it may be
|
||||
+ * @root: root namespace (NOT NULL)
|
||||
+ * @profile: current profile (NOT NULL)
|
||||
+ *
|
||||
+ * Returns: next profile or NULL if there isn't one
|
||||
+ */
|
||||
+static struct aa_profile *next_profile(struct aa_namespace *root,
|
||||
+ struct aa_profile *profile)
|
||||
+{
|
||||
+ struct aa_profile *next = __next_profile(profile);
|
||||
+ if (next)
|
||||
+ return next;
|
||||
+
|
||||
+ /* finished all profiles in namespace move to next namespace */
|
||||
+ return __first_profile(root, __next_namespace(root, profile->ns));
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * p_start - start a depth first traversal of profile tree
|
||||
+ * @f: seq_file to fill
|
||||
+ * @pos: current position
|
||||
+ *
|
||||
+ * Returns: first profile under current namespace or NULL if none found
|
||||
+ *
|
||||
+ * acquires first ns->lock
|
||||
+ */
|
||||
+static void *p_start(struct seq_file *f, loff_t *pos)
|
||||
+ __acquires(root->lock)
|
||||
+{
|
||||
+ struct aa_profile *profile = NULL;
|
||||
+ struct aa_namespace *root = aa_current_profile()->ns;
|
||||
+ loff_t l = *pos;
|
||||
+ f->private = aa_get_namespace(root);
|
||||
+
|
||||
+
|
||||
+ /* find the first profile */
|
||||
+ read_lock(&root->lock);
|
||||
+ profile = __first_profile(root, root);
|
||||
+
|
||||
+ /* skip to position */
|
||||
+ for (; profile && l > 0; l--)
|
||||
+ profile = next_profile(root, profile);
|
||||
+
|
||||
+ return profile;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * p_next - read the next profile entry
|
||||
+ * @f: seq_file to fill
|
||||
+ * @p: profile previously returned
|
||||
+ * @pos: current position
|
||||
+ *
|
||||
+ * Returns: next profile after @p or NULL if none
|
||||
+ *
|
||||
+ * may acquire/release locks in namespace tree as necessary
|
||||
+ */
|
||||
+static void *p_next(struct seq_file *f, void *p, loff_t *pos)
|
||||
+{
|
||||
+ struct aa_profile *profile = p;
|
||||
+ struct aa_namespace *root = f->private;
|
||||
+ (*pos)++;
|
||||
+
|
||||
+ return next_profile(root, profile);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * p_stop - stop depth first traversal
|
||||
+ * @f: seq_file we are filling
|
||||
+ * @p: the last profile writen
|
||||
+ *
|
||||
+ * Release all locking done by p_start/p_next on namespace tree
|
||||
+ */
|
||||
+static void p_stop(struct seq_file *f, void *p)
|
||||
+ __releases(root->lock)
|
||||
+{
|
||||
+ struct aa_profile *profile = p;
|
||||
+ struct aa_namespace *root = f->private, *ns;
|
||||
+
|
||||
+ if (profile) {
|
||||
+ for (ns = profile->ns; ns && ns != root; ns = ns->parent)
|
||||
+ read_unlock(&ns->lock);
|
||||
+ }
|
||||
+ read_unlock(&root->lock);
|
||||
+ aa_put_namespace(root);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * seq_show_profile - show a profile entry
|
||||
+ * @f: seq_file to file
|
||||
+ * @p: current position (profile) (NOT NULL)
|
||||
+ *
|
||||
+ * Returns: error on failure
|
||||
+ */
|
||||
+static int seq_show_profile(struct seq_file *f, void *p)
|
||||
+{
|
||||
+ struct aa_profile *profile = (struct aa_profile *)p;
|
||||
+ struct aa_namespace *root = f->private;
|
||||
+
|
||||
+ if (profile->ns != root)
|
||||
+ seq_printf(f, ":%s://", aa_ns_name(root, profile->ns));
|
||||
+ seq_printf(f, "%s (%s)\n", profile->base.hname,
|
||||
+ COMPLAIN_MODE(profile) ? "complain" : "enforce");
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct seq_operations aa_fs_profiles_op = {
|
||||
+ .start = p_start,
|
||||
+ .next = p_next,
|
||||
+ .stop = p_stop,
|
||||
+ .show = seq_show_profile,
|
||||
+};
|
||||
+
|
||||
+static int profiles_open(struct inode *inode, struct file *file)
|
||||
+{
|
||||
+ return seq_open(file, &aa_fs_profiles_op);
|
||||
+}
|
||||
+
|
||||
+static int profiles_release(struct inode *inode, struct file *file)
|
||||
+{
|
||||
+ return seq_release(inode, file);
|
||||
+}
|
||||
+
|
||||
+const struct file_operations aa_fs_profiles_fops = {
|
||||
+ .open = profiles_open,
|
||||
+ .read = seq_read,
|
||||
+ .llseek = seq_lseek,
|
||||
+ .release = profiles_release,
|
||||
+};
|
||||
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
|
||||
index 0848292..28c52ac 100644
|
||||
--- a/security/apparmor/apparmorfs.c
|
||||
+++ b/security/apparmor/apparmorfs.c
|
||||
@@ -187,7 +187,11 @@ void __init aa_destroy_aafs(void)
|
||||
aafs_remove(".remove");
|
||||
aafs_remove(".replace");
|
||||
aafs_remove(".load");
|
||||
-
|
||||
+#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24
|
||||
+ aafs_remove("profiles");
|
||||
+ aafs_remove("matching");
|
||||
+ aafs_remove("features");
|
||||
+#endif
|
||||
securityfs_remove(aa_fs_dentry);
|
||||
aa_fs_dentry = NULL;
|
||||
}
|
||||
@@ -218,7 +222,17 @@ int __init aa_create_aafs(void)
|
||||
aa_fs_dentry = NULL;
|
||||
goto error;
|
||||
}
|
||||
-
|
||||
+#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24
|
||||
+ error = aafs_create("matching", 0444, &aa_fs_matching_fops);
|
||||
+ if (error)
|
||||
+ goto error;
|
||||
+ error = aafs_create("features", 0444, &aa_fs_features_fops);
|
||||
+ if (error)
|
||||
+ goto error;
|
||||
+#endif
|
||||
+ error = aafs_create("profiles", 0440, &aa_fs_profiles_fops);
|
||||
+ if (error)
|
||||
+ goto error;
|
||||
error = aafs_create(".load", 0640, &aa_fs_profile_load);
|
||||
if (error)
|
||||
goto error;
|
||||
diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h
|
||||
index cb1e93a..14f955c 100644
|
||||
--- a/security/apparmor/include/apparmorfs.h
|
||||
+++ b/security/apparmor/include/apparmorfs.h
|
||||
@@ -17,4 +17,10 @@
|
||||
|
||||
extern void __init aa_destroy_aafs(void);
|
||||
|
||||
+#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24
|
||||
+extern const struct file_operations aa_fs_matching_fops;
|
||||
+extern const struct file_operations aa_fs_features_fops;
|
||||
+extern const struct file_operations aa_fs_profiles_fops;
|
||||
+#endif
|
||||
+
|
||||
#endif /* __AA_APPARMORFS_H */
|
||||
--
|
||||
1.7.5.4
|
||||
|
@@ -0,0 +1,69 @@
|
||||
From 7a10d093f9779f42cb8d6affcb6a4436d3ebd6d3 Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Wed, 10 Aug 2011 22:02:41 -0700
|
||||
Subject: [PATCH 3/3] AppArmor: Allow dfa backward compatibility with broken
|
||||
userspace
|
||||
|
||||
The apparmor_parser when compiling policy could generate invalid dfas
|
||||
that did not have sufficient padding to avoid invalid references, when
|
||||
used by the kernel. The kernels check to verify the next/check table
|
||||
size was broken meaning invalid dfas were being created by userspace
|
||||
and not caught.
|
||||
|
||||
To remain compatible with old tools that are not fixed, pad the loaded
|
||||
dfas next/check table. The dfa's themselves are valid except for the
|
||||
high padding for potentially invalid transitions (high bounds error),
|
||||
which have a maximimum is 256 entries. So just allocate an extra null filled
|
||||
256 entries for the next/check tables. This will guarentee all bounds
|
||||
are good and invalid transitions go to the null (0) state.
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
---
|
||||
security/apparmor/match.c | 17 +++++++++++++++++
|
||||
1 files changed, 17 insertions(+), 0 deletions(-)
|
||||
|
||||
diff --git a/security/apparmor/match.c b/security/apparmor/match.c
|
||||
index 94de6b4..081491e 100644
|
||||
--- a/security/apparmor/match.c
|
||||
+++ b/security/apparmor/match.c
|
||||
@@ -57,8 +57,17 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
|
||||
if (bsize < tsize)
|
||||
goto out;
|
||||
|
||||
+ /* Pad table allocation for next/check by 256 entries to remain
|
||||
+ * backwards compatible with old (buggy) tools and remain safe without
|
||||
+ * run time checks
|
||||
+ */
|
||||
+ if (th.td_id == YYTD_ID_NXT || th.td_id == YYTD_ID_CHK)
|
||||
+ tsize += 256 * th.td_flags;
|
||||
+
|
||||
table = kvmalloc(tsize);
|
||||
if (table) {
|
||||
+ /* ensure the pad is clear, else there will be errors */
|
||||
+ memset(table, 0, tsize);
|
||||
*table = th;
|
||||
if (th.td_flags == YYTD_DATA8)
|
||||
UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
|
||||
@@ -134,11 +143,19 @@ static int verify_dfa(struct aa_dfa *dfa, int flags)
|
||||
goto out;
|
||||
|
||||
if (flags & DFA_FLAG_VERIFY_STATES) {
|
||||
+ int warning = 0;
|
||||
for (i = 0; i < state_count; i++) {
|
||||
if (DEFAULT_TABLE(dfa)[i] >= state_count)
|
||||
goto out;
|
||||
/* TODO: do check that DEF state recursion terminates */
|
||||
if (BASE_TABLE(dfa)[i] + 255 >= trans_count) {
|
||||
+ if (warning)
|
||||
+ continue;
|
||||
+ printk(KERN_WARNING "AppArmor DFA next/check "
|
||||
+ "upper bounds error fixed, upgrade "
|
||||
+ "user space tools \n");
|
||||
+ warning = 1;
|
||||
+ } else if (BASE_TABLE(dfa)[i] >= trans_count) {
|
||||
printk(KERN_ERR "AppArmor DFA next/check upper "
|
||||
"bounds error\n");
|
||||
goto out;
|
||||
--
|
||||
1.7.5.4
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
POD2MAN = pod2man
|
||||
|
||||
man_MANS = aa_change_hat.2 aa_change_profile.2
|
||||
man_MANS = aa_change_hat.2 aa_change_profile.2 aa_getcon.2 aa_find_mountpoint.2
|
||||
|
||||
PODS = $(subst .2,.pod,$(man_MANS))
|
||||
|
||||
@@ -14,8 +14,6 @@ BUILT_SOURCES = $(man_MANS)
|
||||
%.2: %.pod
|
||||
$(POD2MAN) \
|
||||
--section=2 \
|
||||
--release="NOVELL/SUSE" \
|
||||
--release="AppArmor $(VERSION)" \
|
||||
--center="AppArmor" \
|
||||
--date="2007-07-27" \
|
||||
$< > $@
|
||||
$
|
||||
|
@@ -29,7 +29,9 @@ aa_change_hat - change to or from a "hat" within a AppArmor profile
|
||||
B<#include E<lt>sys/apparmor.hE<gt>>
|
||||
|
||||
B<int aa_change_hat (char *subprofile, unsigned long magic_token);>
|
||||
|
||||
B<int aa_change_hatv (char *subprofiles[], unsigned long magic_token);>
|
||||
|
||||
B<int aa_change_hat_vargs (unsigned long magic_token, ...);>
|
||||
|
||||
Link with B<-lapparmor> when compiling.
|
||||
@@ -244,7 +246,8 @@ should be used.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
apparmor(7), apparmor.d(5), apparmor_parser(8), aa_change_profile(2) and
|
||||
apparmor(7), apparmor.d(5), apparmor_parser(8), aa_change_profile(2),
|
||||
aa_getcon(2) and
|
||||
L<http://wiki.apparmor.net>.
|
||||
|
||||
=cut
|
||||
|
@@ -22,14 +22,16 @@
|
||||
|
||||
=head1 NAME
|
||||
|
||||
aa_change_profile - change to another profile within an AppArmor profile
|
||||
aa_change_onexec - change to another profile at the next exec
|
||||
aa_change_profile, aa_change_onexec - change a tasks profile
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
B<#include E<lt>sys/apparmor.hE<gt>>
|
||||
|
||||
B<int aa_change_profile(const char *profile);>
|
||||
|
||||
B<int aa_change_onexec(const char *profile);>
|
||||
|
||||
Link with B<-lapparmor> when compiling.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
116
libraries/libapparmor/doc/aa_find_mountpoint.pod
Normal file
116
libraries/libapparmor/doc/aa_find_mountpoint.pod
Normal file
@@ -0,0 +1,116 @@
|
||||
# This publication is intellectual property of Canonical Ltd. Its contents
|
||||
# can be duplicated, either in part or in whole, provided that a copyright
|
||||
# label is visibly located on each copy.
|
||||
#
|
||||
# All information found in this book has been compiled with utmost
|
||||
# attention to detail. However, this does not guarantee complete accuracy.
|
||||
# Neither Canonical Ltd, the authors, nor the translators shall be held
|
||||
# liable for possible errors or the consequences thereof.
|
||||
#
|
||||
# Many of the software and hardware descriptions cited in this book
|
||||
# are registered trademarks. All trade names are subject to copyright
|
||||
# restrictions and may be registered trade marks. Canonical Ltd.
|
||||
# essentially adhere to the manufacturer's spelling.
|
||||
#
|
||||
# Names of products and trademarks appearing in this book (with or without
|
||||
# specific notation) are likewise subject to trademark and trade protection
|
||||
# laws and may thus fall under copyright restrictions.
|
||||
#
|
||||
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
aa_is_enabled - determine if apparmor is available
|
||||
|
||||
aa_find_mountpoint - find where the apparmor interface filesystem is mounted
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
B<#include E<lt>sys/apparmor.hE<gt>>
|
||||
|
||||
B<int aa_is_enabled(void);>
|
||||
|
||||
B<int aa_find_mountpoint(char **mnt);>
|
||||
|
||||
Link with B<-lapparmor> when compiling.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The aa_is_enabled function returns true (1) if apparmor is enabled. If it
|
||||
isn't it sets the errno to reflect the reason it is not enabled and returns 0.
|
||||
|
||||
The aa_find_mountpoint function finds where the apparmor filesystem is mounted
|
||||
on the system, and returns a string containing the mount path. It is the
|
||||
caller's responsibility to free(3) the returned path.
|
||||
|
||||
=head1 RETURN VALUE
|
||||
|
||||
B<aa_is_enabled>
|
||||
On success 1 is returned. On error, 0 is returned, and errno(3) is set
|
||||
appropriately.
|
||||
|
||||
B<aa_find_mountpoint>
|
||||
On success zero is returned. On error, -1 is returned, and errno(3) is set
|
||||
appropriately.
|
||||
|
||||
=head1 ERRORS
|
||||
|
||||
=over 4
|
||||
|
||||
B<aa_is_enabled>
|
||||
|
||||
=item B<ENOSYS>
|
||||
|
||||
AppArmor extensions to the system are not available.
|
||||
|
||||
=item B<ECANCELED>
|
||||
|
||||
AppArmor is available on the system but has been disabled at boot.
|
||||
|
||||
=item B<ENOENT>
|
||||
|
||||
AppArmor is available (and maybe even enforcing policy) but the interface is
|
||||
not available.
|
||||
|
||||
=item B<ENOMEM>
|
||||
|
||||
Insufficient memory was available.
|
||||
|
||||
=item B<EPERM>
|
||||
|
||||
Did not have sufficient permissions to determine if AppArmor is enabled.
|
||||
|
||||
=item B<EACCES>
|
||||
|
||||
+Did not have sufficient permissions to determine if AppArmor is enabled.
|
||||
|
||||
|
||||
B<aa_find_mountpoint>
|
||||
|
||||
=item B<ENOMEM>
|
||||
|
||||
Insufficient memory was available.
|
||||
|
||||
=item B<EACCES>
|
||||
|
||||
Access to the the required paths was denied.
|
||||
|
||||
=item B<ENOENT>
|
||||
|
||||
The apparmor filesystem mount could not be found
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
None known. If you find any, please report them at
|
||||
L<http://https://bugs.launchpad.net/apparmor/+filebug>.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
apparmor(7), apparmor.d(5), apparmor_parser(8), and
|
||||
L<http://wiki.apparmor.net>.
|
||||
|
||||
=cut
|
112
libraries/libapparmor/doc/aa_getcon.pod
Normal file
112
libraries/libapparmor/doc/aa_getcon.pod
Normal file
@@ -0,0 +1,112 @@
|
||||
# This publication is intellectual property of Canonical Ltd. Its contents
|
||||
# can be duplicated, either in part or in whole, provided that a copyright
|
||||
# label is visibly located on each copy.
|
||||
#
|
||||
# All information found in this book has been compiled with utmost
|
||||
# attention to detail. However, this does not guarantee complete accuracy.
|
||||
# Neither Canonical Ltd, the authors, nor the translators shall be held
|
||||
# liable for possible errors or the consequences thereof.
|
||||
#
|
||||
# Many of the software and hardware descriptions cited in this book
|
||||
# are registered trademarks. All trade names are subject to copyright
|
||||
# restrictions and may be registered trade marks. Canonical Ltd.
|
||||
# essentially adhere to the manufacturer's spelling.
|
||||
#
|
||||
# Names of products and trademarks appearing in this book (with or without
|
||||
# specific notation) are likewise subject to trademark and trade protection
|
||||
# laws and may thus fall under copyright restrictions.
|
||||
#
|
||||
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
aa_getprocattr_raw, aa_getprocattr - read and parse procattr data
|
||||
|
||||
aa_getcon, aa_gettaskcon - get task confinement information
|
||||
|
||||
aa_getpeercon - get the confinement of a socket's other end (peer)
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
B<#include E<lt>sys/apparmor.hE<gt>>
|
||||
|
||||
B<int aa_getprocattr_raw(pid_t tid, const char *attr, char *buf, int len,
|
||||
char **mode);>
|
||||
|
||||
B<int aa_getprocattr(pid_t tid, const char *attr, char **buf, char **mode);>
|
||||
|
||||
B<int aa_gettaskcon(pid_t target, char **con, char **mode);>
|
||||
|
||||
B<int aa_getcon(char **con, char **mode);>
|
||||
|
||||
B<int aa_getpeercon(int fd, char **con);>
|
||||
|
||||
Link with B<-lapparmor> when compiling.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The aa_getcon function gets the current AppArmor confinement context for the
|
||||
current task. The confinement context is usually just the name of the AppArmor
|
||||
profile restricting the task, but it may include the profile namespace or in
|
||||
some cases a set of profile names (known as a stack of profiles). The returned string *con should be freed using <free()>.
|
||||
|
||||
The aa_gettaskcon function is like the aa_getcon function except it will work
|
||||
for any arbitrary task in the system.
|
||||
|
||||
The aa_getpeercon function is similar to that of aa_gettaskcon except that
|
||||
it returns the confinement information for task on the other end of a socket
|
||||
connection.
|
||||
|
||||
The aa_getprocattr function is the backend for the aa_getcon and aa_gettaskcon
|
||||
functions and handles the reading and parsing of the confinement data from
|
||||
different arbitrary attr files and returns the processed results in
|
||||
an allocated buffer.
|
||||
|
||||
The aa_getprocattr_raw() is the backend for the aa_getprocattr function and
|
||||
does not handle buffer allocation.
|
||||
|
||||
=head1 RETURN VALUE
|
||||
|
||||
On success zero is returned. On error, -1 is returned, and
|
||||
errno(3) is set appropriately.
|
||||
|
||||
=head1 ERRORS
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<EINVAL>
|
||||
|
||||
The apparmor kernel module is not loaded or the communication via the
|
||||
F</proc/*/attr/file> did not conform to protocol.
|
||||
|
||||
=item B<ENOMEM>
|
||||
|
||||
Insufficient kernel memory was available.
|
||||
|
||||
=item B<EACCES>
|
||||
|
||||
Access to the specified I<file/task> was denied.
|
||||
|
||||
=item B<ENOENT>
|
||||
|
||||
The specified I<file/task> does not exist or is not visible.
|
||||
|
||||
=item B<ERANGE>
|
||||
|
||||
The confinement data is to large to fit in the supplied buffer.
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
None known. If you find any, please report them at
|
||||
L<http://https://bugs.launchpad.net/apparmor/+filebug>.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
apparmor(7), apparmor.d(5), apparmor_parser(8), aa_change_profile(2) and
|
||||
L<http://wiki.apparmor.net>.
|
||||
|
||||
=cut
|
@@ -18,8 +18,14 @@
|
||||
#ifndef _SYS_APPARMOR_H_
|
||||
#define _SYS_APPARMOR_H 1
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
/* Prototypes for apparmor state queries */
|
||||
extern int aa_is_enabled(void);
|
||||
extern int aa_find_mountpoint(char **mnt);
|
||||
|
||||
/* Prototypes for self directed domain transitions
|
||||
* see <http://apparmor.net>
|
||||
* Please see the change_hat(2) manpage for information.
|
||||
@@ -34,6 +40,17 @@ extern int aa_change_onexec(const char *profile);
|
||||
extern int aa_change_hatv(const char *subprofiles[], unsigned long token);
|
||||
extern int (aa_change_hat_vargs)(unsigned long token, int count, ...);
|
||||
|
||||
/* Protypes for introspecting task confinement
|
||||
* Please see the aa_getcon(2) manpage for information
|
||||
*/
|
||||
extern int aa_getprocattr_raw(pid_t tid, const char *attr, char *buf, int len,
|
||||
char **mode);
|
||||
extern int aa_getprocattr(pid_t tid, const char *attr, char **buf, char **mode);
|
||||
extern int aa_gettaskcon(pid_t target, char **con, char **mode);
|
||||
extern int aa_getcon(char **con, char **mode);
|
||||
extern int aa_getpeercon_raw(int fd, char *buffer, int *size);
|
||||
extern int aa_getpeercon(int fd, char **con);
|
||||
|
||||
#define __macroarg_counter(Y...) __macroarg_count1 ( , ##Y)
|
||||
#define __macroarg_count1(Y...) __macroarg_count2 (Y, 16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)
|
||||
#define __macroarg_count2(_,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,n,Y...) n
|
||||
|
@@ -246,7 +246,7 @@ key: TOK_KEY_OPERATION TOK_EQUALS TOK_QUOTED_STRING
|
||||
{ ret_record->fsuid = $3;}
|
||||
| TOK_KEY_OUID TOK_EQUALS TOK_DIGITS
|
||||
{ ret_record->ouid = $3;}
|
||||
| TOK_KEY_COMM TOK_EQUALS TOK_QUOTED_STRING
|
||||
| TOK_KEY_COMM TOK_EQUALS safe_string
|
||||
{ ret_record->comm = $3;}
|
||||
| TOK_KEY_APPARMOR TOK_EQUALS apparmor_event
|
||||
| TOK_KEY_CAPABILITY TOK_EQUALS TOK_DIGITS
|
||||
|
@@ -22,32 +22,283 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/socket.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <mntent.h>
|
||||
|
||||
#include "apparmor.h"
|
||||
|
||||
/* some non-Linux systems do not define a static value */
|
||||
#ifndef PATH_MAX
|
||||
# define PATH_MAX 4096
|
||||
#endif
|
||||
|
||||
#define symbol_version(real, name, version) \
|
||||
__asm__ (".symver " #real "," #name "@" #version)
|
||||
#define default_symbol_version(real, name, version) \
|
||||
__asm__ (".symver " #real "," #name "@@" #version)
|
||||
|
||||
static int setprocattr(const char *path, const char *buf, int len)
|
||||
/**
|
||||
* aa_find_mountpoint - find where the apparmor interface filesystem is mounted
|
||||
* @mnt: returns buffer with the mountpoint string
|
||||
*
|
||||
* Returns: 0 on success else -1 on error
|
||||
*
|
||||
* NOTE: this function only supports versions of apparmor using securityfs
|
||||
*/
|
||||
int aa_find_mountpoint(char **mnt)
|
||||
{
|
||||
struct stat statbuf;
|
||||
struct mntent *mntpt;
|
||||
FILE *mntfile;
|
||||
int rc = -1;
|
||||
|
||||
if (!mnt) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
mntfile = setmntent("/proc/mounts", "r");
|
||||
if (!mntfile)
|
||||
return -1;
|
||||
|
||||
while ((mntpt = getmntent(mntfile))) {
|
||||
char *proposed = NULL;
|
||||
if (strcmp(mntpt->mnt_type, "securityfs") != 0)
|
||||
continue;
|
||||
|
||||
if (asprintf(&proposed, "%s/apparmor", mntpt->mnt_dir) < 0)
|
||||
/* ENOMEM */
|
||||
break;
|
||||
|
||||
if (stat(proposed, &statbuf) == 0) {
|
||||
*mnt = proposed;
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
free(proposed);
|
||||
}
|
||||
endmntent(mntfile);
|
||||
if (rc == -1)
|
||||
errno = ENOENT;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_is_enabled - determine if apparmor is enabled
|
||||
*
|
||||
* Returns: 1 if enabled else reason it is not, or 0 on error
|
||||
*
|
||||
* ENOSYS - no indication apparmor is present in the system
|
||||
* ENOENT - enabled but interface could not be found
|
||||
* ECANCELED - disabled at boot
|
||||
* ENOMEM - out of memory
|
||||
*/
|
||||
int aa_is_enabled(void)
|
||||
{
|
||||
int serrno, fd, rc, size;
|
||||
char buffer[2];
|
||||
char *mnt;
|
||||
|
||||
/* if the interface mountpoint is available apparmor is enabled */
|
||||
rc = aa_find_mountpoint(&mnt);
|
||||
if (rc == 0) {
|
||||
free(mnt);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* determine why the interface mountpoint isn't available */
|
||||
fd = open("/sys/module/apparmor/parameters/enabled", O_RDONLY);
|
||||
if (fd == -1) {
|
||||
if (errno == ENOENT)
|
||||
errno = ENOSYS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size = read(fd, &buffer, 2);
|
||||
serrno = errno;
|
||||
close(fd);
|
||||
errno = serrno;
|
||||
|
||||
if (size > 0) {
|
||||
if (buffer[0] == 'Y')
|
||||
errno = ENOENT;
|
||||
else
|
||||
errno = ECANCELED;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline pid_t aa_gettid(void)
|
||||
{
|
||||
#ifdef SYS_gettid
|
||||
return syscall(SYS_gettid);
|
||||
#else
|
||||
return getpid();
|
||||
#endif
|
||||
}
|
||||
|
||||
static char *procattr_path(pid_t pid, const char *attr)
|
||||
{
|
||||
char *path = NULL;
|
||||
if (asprintf(&path, "/proc/%d/attr/%s", pid, attr) > 0)
|
||||
return path;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_getprocattr_raw - get the contents of @attr for @tid into @buf
|
||||
* @tid: tid of task to query
|
||||
* @attr: which /proc/<tid>/attr/<attr> to query
|
||||
* @buf: buffer to store the result in
|
||||
* @len: size of the buffer
|
||||
* @mode: if set will point to mode string within buffer if it is present
|
||||
*
|
||||
* Returns: size of data read or -1 on error, and sets errno
|
||||
*/
|
||||
int aa_getprocattr_raw(pid_t tid, const char *attr, char *buf, int len,
|
||||
char **mode)
|
||||
{
|
||||
int rc = -1;
|
||||
int fd, ret, ctlerr = 0;
|
||||
int fd, ret;
|
||||
char *tmp = NULL;
|
||||
int size = 0;
|
||||
|
||||
if (!buf || len <= 0) {
|
||||
errno = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
tmp = procattr_path(tid, attr);
|
||||
if (!tmp)
|
||||
goto out;
|
||||
|
||||
fd = open(tmp, O_RDONLY);
|
||||
free(tmp);
|
||||
if (fd == -1) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
tmp = buf;
|
||||
do {
|
||||
ret = read(fd, tmp, len);
|
||||
if (ret <= 0)
|
||||
break;
|
||||
tmp += ret;
|
||||
size += ret;
|
||||
len -= ret;
|
||||
if (len < 0) {
|
||||
errno = ERANGE;
|
||||
goto out2;
|
||||
}
|
||||
} while (ret > 0);
|
||||
|
||||
if (ret < 0) {
|
||||
int saved;
|
||||
if (ret != -1) {
|
||||
errno = EPROTO;
|
||||
}
|
||||
saved = errno;
|
||||
(void)close(fd);
|
||||
errno = saved;
|
||||
goto out;
|
||||
} else if (size > 0 && buf[size - 1] != 0) {
|
||||
/* check for null termination */
|
||||
if (buf[size - 1] == '\n') {
|
||||
buf[size - 1] = 0;
|
||||
} else if (len == 0) {
|
||||
errno = ERANGE;
|
||||
goto out2;
|
||||
} else {
|
||||
buf[size] = 0;
|
||||
size++;
|
||||
}
|
||||
|
||||
/*
|
||||
* now separate the mode. If we don't find it just
|
||||
* return NULL
|
||||
*/
|
||||
if (mode)
|
||||
*mode = NULL;
|
||||
if (strcmp(buf, "unconfined") != 0 &&
|
||||
size > 4 && buf[size - 2] == ')') {
|
||||
int pos = size - 3;
|
||||
while (pos > 0 &&
|
||||
!(buf[pos] == ' ' && buf[pos + 1] == '('))
|
||||
pos--;
|
||||
if (pos > 0) {
|
||||
buf[pos] = 0; /* overwrite ' ' */
|
||||
buf[size - 2] = 0; /* overwrite trailing ) */
|
||||
if (mode)
|
||||
*mode = &buf[pos + 2]; /* skip '(' */
|
||||
}
|
||||
}
|
||||
}
|
||||
rc = size;
|
||||
|
||||
out2:
|
||||
(void)close(fd);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
#define INITIAL_GUESS_SIZE 128
|
||||
|
||||
/**
|
||||
* aa_getprocattr - get the contents of @attr for @tid into @buf
|
||||
* @tid: tid of task to query
|
||||
* @attr: which /proc/<tid>/attr/<attr> to query
|
||||
* @buf: allocated buffer the result is stored in
|
||||
* @mode: if set will point to mode string within buffer if it is present
|
||||
*
|
||||
* Returns: size of data read or -1 on error, and sets errno
|
||||
*/
|
||||
int aa_getprocattr(pid_t tid, const char *attr, char **buf, char **mode)
|
||||
{
|
||||
int rc, size = INITIAL_GUESS_SIZE/2;
|
||||
char *buffer = NULL;
|
||||
|
||||
if (!buf) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
do {
|
||||
size <<= 1;
|
||||
buffer = realloc(buffer, size);
|
||||
if (!buffer)
|
||||
return -1;
|
||||
memset(buffer, 0, size);
|
||||
|
||||
rc = aa_getprocattr_raw(tid, attr, buffer, size, mode);
|
||||
} while (rc == -1 && errno == ERANGE);
|
||||
|
||||
if (rc == -1) {
|
||||
free(buffer);
|
||||
size = -1;
|
||||
} else
|
||||
*buf = buffer;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static int setprocattr(pid_t tid, const char *attr, const char *buf, int len)
|
||||
{
|
||||
int rc = -1;
|
||||
int fd, ret;
|
||||
char *ctl = NULL;
|
||||
pid_t tid = syscall(SYS_gettid);
|
||||
|
||||
if (!buf) {
|
||||
errno = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctlerr = asprintf(&ctl, path, tid);
|
||||
if (ctlerr < 0) {
|
||||
ctl = procattr_path(tid, attr);
|
||||
if (!ctl)
|
||||
goto out;
|
||||
}
|
||||
|
||||
fd = open(ctl, O_WRONLY);
|
||||
if (fd == -1) {
|
||||
@@ -99,7 +350,7 @@ int aa_change_hat(const char *subprofile, unsigned long token)
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = setprocattr("/proc/%d/attr/current", buf, len);
|
||||
rc = setprocattr(aa_gettid(), "current", buf, len);
|
||||
out:
|
||||
if (buf) {
|
||||
/* clear local copy of magic token before freeing */
|
||||
@@ -130,7 +381,7 @@ int aa_change_profile(const char *profile)
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
rc = setprocattr("/proc/%d/attr/current", buf, len);
|
||||
rc = setprocattr(aa_gettid(), "current", buf, len);
|
||||
|
||||
free(buf);
|
||||
return rc;
|
||||
@@ -151,7 +402,7 @@ int aa_change_onexec(const char *profile)
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
rc = setprocattr("/proc/%d/attr/exec", buf, len);
|
||||
rc = setprocattr(aa_gettid(), "exec", buf, len);
|
||||
|
||||
free(buf);
|
||||
return rc;
|
||||
@@ -212,7 +463,7 @@ int aa_change_hatv(const char *subprofiles[], unsigned long token)
|
||||
/* step pos past trailing \0 */
|
||||
pos++;
|
||||
|
||||
rc = setprocattr("/proc/%d/attr/current", buf, pos - buf);
|
||||
rc = setprocattr(aa_gettid(), "current", buf, pos - buf);
|
||||
|
||||
out:
|
||||
if (buf) {
|
||||
@@ -251,3 +502,124 @@ int (aa_change_hat_vargs)(unsigned long token, int nhats, ...)
|
||||
va_end(ap);
|
||||
return aa_change_hatv(argv, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_gettaskcon - get the confinement for task @target in an allocated buffer
|
||||
* @target: task to query
|
||||
* @con: pointer to returned buffer with the confinement string
|
||||
* @mode: if provided will point to the mode string in @con if present
|
||||
*
|
||||
* Returns: length of confinement data or -1 on error and sets errno
|
||||
*
|
||||
* Guarentees that @con and @mode are null terminated. The length returned
|
||||
* is for all data including both @con and @mode, and maybe > than strlen(@con)
|
||||
* even if @mode is NULL
|
||||
*
|
||||
* Caller is responsible for freeing the buffer returned in @con. @mode is
|
||||
* always contained within @con's buffer and so NEVER do free(@mode)
|
||||
*/
|
||||
int aa_gettaskcon(pid_t target, char **con, char **mode)
|
||||
{
|
||||
return aa_getprocattr(target, "current", con, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_getcon - get the confinement for current task in an allocated buffer
|
||||
* @con: pointer to return buffer with the confinement if successful
|
||||
* @mode: if provided will point to the mode string in @con if present
|
||||
*
|
||||
* Returns: length of confinement data or -1 on error and sets errno
|
||||
*
|
||||
* Guarentees that @con and @mode are null terminated. The length returned
|
||||
* is for all data including both @con and @mode, and may > than strlen(@con)
|
||||
* even if @mode is NULL
|
||||
*
|
||||
* Caller is responsible for freeing the buffer returned in @con. @mode is
|
||||
* always contained within @con's buffer and so NEVER do free(@mode)
|
||||
*/
|
||||
int aa_getcon(char **con, char **mode)
|
||||
{
|
||||
return aa_gettaskcon(aa_gettid(), con, mode);
|
||||
}
|
||||
|
||||
|
||||
#ifndef SO_PEERSEC
|
||||
#define SO_PEERSEC 31
|
||||
#endif
|
||||
|
||||
/**
|
||||
* aa_getpeercon_raw - get the confinement of the socket's peer (other end)
|
||||
* @fd: socket to get peer confinement for
|
||||
* @con: pointer to buffer to store confinement string
|
||||
* @size: initially contains size of the buffer, returns size of data read
|
||||
*
|
||||
* Returns: length of confinement data including null termination or -1 on error
|
||||
* if errno == ERANGE then @size will hold the size needed
|
||||
*/
|
||||
int aa_getpeercon_raw(int fd, char *buffer, int *size)
|
||||
{
|
||||
socklen_t optlen = *size;
|
||||
int rc;
|
||||
|
||||
if (optlen <= 0 || buffer == NULL) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, buffer, &optlen);
|
||||
if (rc == -1 || optlen <= 0)
|
||||
goto out;
|
||||
|
||||
/* check for null termination */
|
||||
if (buffer[optlen - 1] != 0) {
|
||||
if (optlen < *size) {
|
||||
buffer[optlen] = 0;
|
||||
optlen++;
|
||||
} else {
|
||||
/* buffer needs to be bigger by 1 */
|
||||
rc = -1;
|
||||
errno = ERANGE;
|
||||
optlen++;
|
||||
}
|
||||
}
|
||||
out:
|
||||
*size = optlen;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_getpeercon - get the confinement of the socket's peer (other end)
|
||||
* @fd: socket to get peer confinement for
|
||||
* @con: pointer to allocated buffer with the confinement string
|
||||
*
|
||||
* Returns: length of confinement data including null termination or -1 on error
|
||||
*
|
||||
* Caller is responsible for freeing the buffer returned.
|
||||
*/
|
||||
int aa_getpeercon(int fd, char **con)
|
||||
{
|
||||
int rc, size = INITIAL_GUESS_SIZE;
|
||||
char *buffer = NULL;
|
||||
|
||||
if (!con) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
do {
|
||||
buffer = realloc(buffer, size);
|
||||
if (!buffer)
|
||||
return -1;
|
||||
memset(buffer, 0, size);
|
||||
|
||||
rc = aa_getpeercon_raw(fd, buffer, &size);
|
||||
} while (rc == -1 && errno == ERANGE);
|
||||
|
||||
if (rc == -1) {
|
||||
free(buffer);
|
||||
size = -1;
|
||||
} else
|
||||
*con = buffer;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
@@ -16,13 +16,22 @@ APPARMOR_1.0 {
|
||||
|
||||
APPARMOR_1.1 {
|
||||
global:
|
||||
aa_is_enabled;
|
||||
aa_find_mountpoint;
|
||||
aa_change_hat;
|
||||
aa_change_hatv;
|
||||
aa_change_hat_vargs;
|
||||
aa_change_profile;
|
||||
aa_change_onexec;
|
||||
aa_gettaskcon;
|
||||
aa_getcon;
|
||||
aa_getpeercon_raw;
|
||||
aa_getpeercon;
|
||||
parse_record;
|
||||
free_record;
|
||||
aa_getprocattr_raw;
|
||||
aa_getprocattr;
|
||||
|
||||
local:
|
||||
*;
|
||||
} APPARMOR_1.0;
|
||||
|
@@ -265,7 +265,7 @@ yy_flex_debug = 0;
|
||||
{key_error} { return(TOK_KEY_ERROR); }
|
||||
{key_fsuid} { return(TOK_KEY_FSUID); }
|
||||
{key_ouid} { return(TOK_KEY_OUID); }
|
||||
{key_comm} { return(TOK_KEY_COMM); }
|
||||
{key_comm} { BEGIN(safe_string); return(TOK_KEY_COMM); }
|
||||
{key_capability} { return(TOK_KEY_CAPABILITY); }
|
||||
{key_capname} { return(TOK_KEY_CAPNAME); }
|
||||
{key_offset} { return(TOK_KEY_OFFSET); }
|
||||
|
@@ -13,9 +13,17 @@
|
||||
* are manually inserted here
|
||||
*/
|
||||
|
||||
extern int aa_is_enabled(void);
|
||||
extern int aa_find_mountpoint(char **mnt);
|
||||
extern int aa_change_hat(const char *subprofile, unsigned long magic_token);
|
||||
extern int aa_change_profile(const char *profile);
|
||||
extern int aa_change_onexec(const char *profile);
|
||||
extern int aa_change_hatv(const char *subprofiles[], unsigned long token);
|
||||
extern int aa_change_hat_vargs(unsigned long token, int count, ...);
|
||||
|
||||
extern int aa_getprocattr_raw(pid_t tid, const char *attr, char *buf, int len,
|
||||
char **mode);
|
||||
extern int aa_getprocattr(pid_t tid, const char *attr, char **buf, char **mode);
|
||||
extern int aa_gettaskcon(pid_t target, char **con, char **mode);
|
||||
extern int aa_getcon(char **con, char **mode);
|
||||
extern int aa_getpeercon_raw(int fd, char *buffer, int *size);
|
||||
extern int aa_getpeercon(int fd, char **con);
|
||||
|
@@ -13,7 +13,7 @@ setup(name = 'LibAppArmor',
|
||||
ext_package = 'LibAppArmor',
|
||||
ext_modules = [Extension('_LibAppArmor', ['libapparmor_wrap.c'],
|
||||
include_dirs=['@top_srcdir@/src'],
|
||||
extra_link_args = string.split('-L@top_builddir@/src/.libs -lapparmor'),
|
||||
# static: extra_link_args = string.split('@top_builddir@/src/.libs/libapparmor.a'),
|
||||
extra_link_args = '-L@top_builddir@/src/.libs -lapparmor'.split(),
|
||||
# static: extra_link_args = '@top_builddir@/src/.libs/libapparmor.a'.split(),
|
||||
)],
|
||||
)
|
||||
|
@@ -0,0 +1 @@
|
||||
type=AVC msg=audit(1322676143.201:455): apparmor="ALLOWED" operation="open" parent=10357 profile=2F686F6D652F73746576652F746D702F6D792070726F672E7368 name=2F686F6D652F73746576652F746D702F6D792070726F672E7368 pid=22918 comm=6D792070726F672E7368 requested_mask="r" denied_mask="r" fsuid=1000 ouid=1000
|
@@ -0,0 +1,16 @@
|
||||
START
|
||||
File: test_multi/testcase_encoded_comm.in
|
||||
Event type: AA_RECORD_ALLOWED
|
||||
Audit ID: 1322676143.201:455
|
||||
Operation: open
|
||||
Mask: r
|
||||
Denied Mask: r
|
||||
fsuid: 1000
|
||||
ouid: 1000
|
||||
Profile: /home/steve/tmp/my prog.sh
|
||||
Name: /home/steve/tmp/my prog.sh
|
||||
Command: my prog.sh
|
||||
Parent: 10357
|
||||
PID: 22918
|
||||
Epoch: 1322676143
|
||||
Audit subid: 455
|
@@ -0,0 +1 @@
|
||||
Aug 23 17:29:45 hostname kernel: [289763.843292] type=1400 audit(1322614912.304:857): apparmor="ALLOWED" operation="getattr" parent=16001 profile=74657374207370616365 name="/lib/x86_64-linux-gnu/libdl-2.13.so" pid=17011 comm="bash" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
|
@@ -0,0 +1,16 @@
|
||||
START
|
||||
File: test_multi/testcase_encoded_profile.in
|
||||
Event type: AA_RECORD_ALLOWED
|
||||
Audit ID: 1322614912.304:857
|
||||
Operation: getattr
|
||||
Mask: r
|
||||
Denied Mask: r
|
||||
fsuid: 0
|
||||
ouid: 0
|
||||
Profile: test space
|
||||
Name: /lib/x86_64-linux-gnu/libdl-2.13.so
|
||||
Command: bash
|
||||
Parent: 16001
|
||||
PID: 17011
|
||||
Epoch: 1322614912
|
||||
Audit subid: 857
|
@@ -1,15 +1,15 @@
|
||||
This license applies to all source files within the AppArmor parser
|
||||
package.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
@@ -18,7 +18,7 @@ software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
@@ -58,8 +58,8 @@ patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
@@ -113,7 +113,7 @@ above, provided that you also meet all of these conditions:
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
@@ -171,7 +171,7 @@ access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
@@ -228,7 +228,7 @@ impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
@@ -258,7 +258,7 @@ make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
@@ -280,9 +280,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
@@ -294,7 +294,7 @@ convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) 19yy <name of author>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -306,17 +306,16 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) 19yy name of author
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
@@ -339,5 +338,5 @@ necessary. Here is a sample; alter the names:
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
|
122
parser/Makefile
122
parser/Makefile
@@ -49,7 +49,7 @@ ifndef CFLAGS
|
||||
CFLAGS = -g -O2 -pipe
|
||||
|
||||
ifdef DEBUG
|
||||
CFLAGS += -pg
|
||||
CFLAGS += -pg -D DEBUG
|
||||
endif
|
||||
endif #CFLAGS
|
||||
|
||||
@@ -73,29 +73,31 @@ EXTRA_CFLAGS+=-DPACKAGE=\"${NAME}\" -DLOCALEDIR=\"${LOCALEDIR}\"
|
||||
# Compile-time configuration of the location of the config file
|
||||
EXTRA_CFLAGS+=-DSUBDOMAIN_CONFDIR=\"${CONFDIR}\"
|
||||
|
||||
SRCS = parser_include.c parser_interface.c parser_lex.c parser_main.c \
|
||||
parser_misc.c parser_merge.c parser_symtab.c parser_yacc.c \
|
||||
parser_regex.c parser_variable.c parser_policy.c parser_alias.c
|
||||
SRCS = parser_common.c parser_include.c parser_interface.c parser_lex.c \
|
||||
parser_main.c parser_misc.c parser_merge.c parser_symtab.c \
|
||||
parser_yacc.c parser_regex.c parser_variable.c parser_policy.c \
|
||||
parser_alias.c
|
||||
HDRS = parser.h parser_include.h immunix.h
|
||||
TOOLS = apparmor_parser
|
||||
|
||||
OBJECTS = parser_lex.o parser_yacc.o parser_main.o parser_interface.o \
|
||||
parser_include.o parser_merge.o parser_symtab.o parser_misc.o \
|
||||
parser_regex.o parser_variable.o parser_policy.o parser_alias.o
|
||||
OBJECTS = $(SRCS:.c=.o)
|
||||
|
||||
AAREDIR= libapparmor_re
|
||||
AAREOBJECTS = ${AAREDIR}/libapparmor_re.a
|
||||
AAREOBJECT = ${AAREDIR}/libapparmor_re.a
|
||||
AAREOBJECTS = $(AAREOBJECT) libstdc++.a
|
||||
AARE_LDFLAGS=-static-libgcc -L.
|
||||
|
||||
LEX_C_FILES = parser_lex.c
|
||||
YACC_C_FILES = parser_yacc.c parser_yacc.h
|
||||
|
||||
TESTS = tst_regex tst_misc tst_symtab tst_variable
|
||||
TEST_FLAGS = -Wl,--warn-unresolved-symbols
|
||||
DISABLED_TESTS =
|
||||
|
||||
TEST_OBJECTS = $(filter-out parser_lex.o, \
|
||||
$(filter-out parser_yacc.o, \
|
||||
$(filter-out parser_main.o, ${OBJECTS})))
|
||||
TEST_CFLAGS = $(EXTRA_CFLAGS) -DUNIT_TEST -Wno-unused-result
|
||||
TEST_OBJECTS = $(filter-out \
|
||||
parser_lex.o \
|
||||
parser_yacc.o \
|
||||
parser_main.o, ${OBJECTS}) \
|
||||
$(AAREOBJECTS)
|
||||
TEST_LDFLAGS = $(AARE_LDFLAGS)
|
||||
|
||||
ifdef V
|
||||
VERBOSE = 1
|
||||
@@ -113,7 +115,7 @@ endif
|
||||
export Q VERBOSE BUILD_OUTPUT
|
||||
|
||||
po/${NAME}.pot: ${SRCS} ${HDRS}
|
||||
make -C po ${NAME}.pot NAME=${NAME} SOURCES="${SRCS} ${HDRS}"
|
||||
$(MAKE) -C po ${NAME}.pot NAME=${NAME} SOURCES="${SRCS} ${HDRS}"
|
||||
|
||||
techdoc.pdf: techdoc.tex
|
||||
while pdflatex $< ${BUILD_OUTPUT} || exit 1 ; \
|
||||
@@ -128,8 +130,7 @@ techdoc.txt: techdoc/index.html
|
||||
|
||||
# targets arranged this way so that people who don't want full docs can
|
||||
# pick specific targets they want.
|
||||
main: $(TOOLS)
|
||||
$(Q)make -C po all
|
||||
arch: $(TOOLS)
|
||||
|
||||
manpages: $(MANPAGES)
|
||||
|
||||
@@ -139,13 +140,20 @@ pdf: techdoc.pdf
|
||||
|
||||
docs: manpages htmlmanpages pdf
|
||||
|
||||
all: main docs tests
|
||||
indep: docs
|
||||
$(Q)$(MAKE) -C po all
|
||||
|
||||
all: arch indep
|
||||
|
||||
|
||||
.PHONY: libstdc++.a
|
||||
libstdc++.a:
|
||||
rm -f ./libstdc++.a
|
||||
ln -s `$(CXX) -print-file-name=libstdc++.a`
|
||||
|
||||
apparmor_parser: $(OBJECTS) $(AAREOBJECTS)
|
||||
rm -f ./libstdc++.a
|
||||
ln -s `g++ -print-file-name=libstdc++.a`
|
||||
g++ $(EXTRA_CFLAGS) -o $@ $(OBJECTS) $(LIBS) \
|
||||
${LEXLIB} $(AAREOBJECTS) -static-libgcc -L.
|
||||
$(CXX) $(EXTRA_CFLAGS) -o $@ $(OBJECTS) $(LIBS) \
|
||||
${LEXLIB} $(AAREOBJECTS) $(AARE_LDFLAGS)
|
||||
|
||||
parser_yacc.c parser_yacc.h: parser_yacc.y parser.h
|
||||
$(YACC) $(YFLAGS) -o parser_yacc.c parser_yacc.y
|
||||
@@ -189,6 +197,9 @@ parser_policy.o: parser_policy.c parser.h parser_yacc.h
|
||||
parser_alias.o: parser_alias.c parser.h
|
||||
$(CC) $(EXTRA_CFLAGS) -c -o $@ $<
|
||||
|
||||
parser_common.o: parser_common.c parser.h
|
||||
$(CC) $(EXTRA_CFLAGS) -c -o $@ $<
|
||||
|
||||
parser_version.h: Makefile
|
||||
@echo \#define PARSER_VERSION \"$(VERSION)\" > .ver
|
||||
@mv -f .ver $@
|
||||
@@ -196,47 +207,37 @@ parser_version.h: Makefile
|
||||
# These are the families that it doesn't make sense for apparmor to mediate.
|
||||
# We use PF_ here since that is what is required in bits/socket.h, but we will
|
||||
# rewrite these as AF_.
|
||||
FILTER_FAMILIES=PF_RXRPC PF_MAX PF_UNSPEC PF_UNIX PF_LOCAL PF_NETLINK PF_LLC PF_IUCV PF_TIPC PF_CAN PF_ISDN PF_PHONET
|
||||
FILTER_FAMILIES=PF_MAX PF_UNSPEC PF_UNIX PF_LOCAL PF_NETLINK
|
||||
|
||||
|
||||
__FILTER=$(shell echo $(strip $(FILTER_FAMILIES)) | sed -e 's/ /\\\|/g')
|
||||
|
||||
af_names.h: /usr/include/bits/socket.h
|
||||
LC_ALL=C sed -n -e '/$(__FILTER)/d' -e "s/^\#define[ \\t]\\+PF_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/#ifndef AF_\\1\\n# define AF_\\1 \\2\\n#endif\\nAA_GEN_NET_ENT(\"\\L\\1\", \\UAF_\\1)\\n/p" $< > $@
|
||||
LC_ALL=C sed -n -e "s/^\#define[ \\t]\\+PF_MAX[ \\t]\\+\\([0-9]\\+\\)[ \\t]\\+.*/#define AA_AF_MAX \\1\n/p" $< >> $@
|
||||
.PHONY: af_names.h
|
||||
af_names.h:
|
||||
echo "#include <sys/socket.h>" | cpp -dM | LC_ALL=C sed -n -e '/$(__FILTER)/d' -e "s/^\#define[ \\t]\\+PF_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/#ifndef AF_\\1\\n# define AF_\\1 \\2\\n#endif\\nAA_GEN_NET_ENT(\"\\L\\1\", \\UAF_\\1)\\n/p" > $@
|
||||
echo "#include <sys/socket.h>" | cpp -dM | LC_ALL=C sed -n -e "s/^\#define[ \\t]\\+PF_MAX[ \\t]\\+\\([0-9]\\+\\)\\+.*/#define AA_AF_MAX \\1\n/p" >> $@
|
||||
# cat $@
|
||||
|
||||
cap_names.h: /usr/include/linux/capability.h
|
||||
LC_ALL=C sed -n -e "/CAP_EMPTY_SET/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9xa-f]\\+\\)\\(.*\\)\$$/\{\"\\L\\1\", \\UCAP_\\1\},/p" $< > $@
|
||||
|
||||
tst_symtab: parser_symtab.c parser.h parser_variable.o
|
||||
$(Q)$(CC) -DUNIT_TEST $(EXTRA_CFLAGS) $(TEST_FLAGS) -o $@ $< parser_variable.o $(BUILD_OUTPUT)
|
||||
|
||||
tst_variable: parser_variable.c parser.h $(filter-out parser_variable.o, ${TEST_OBJECTS})
|
||||
$(Q)$(CC) -DUNIT_TEST $(EXTRA_CFLAGS) $(TEST_FLAGS) -o $@ $< $(filter-out parser_variable.o, ${TEST_OBJECTS}) $(BUILD_OUTPUT)
|
||||
|
||||
tst_misc: parser_misc.c parser.h parser_yacc.h af_names.h cap_names.h
|
||||
$(Q)$(CC) -DUNIT_TEST $(EXTRA_CFLAGS) $(TEST_FLAGS) -o $@ $< $(BUILD_OUTPUT)
|
||||
|
||||
tst_regex: parser_regex.c parser.h parser_yacc.h
|
||||
$(Q)$(CC) -DUNIT_TEST $(EXTRA_CFLAGS) $(TEST_FLAGS) -o $@ $< $(BUILD_OUTPUT)
|
||||
tst_%: parser_%.c parser.h $(filter-out parser_%.o, ${TEST_OBJECTS})
|
||||
$(CC) $(TEST_CFLAGS) -o $@ $< $(filter-out $(<:.c=.o), ${TEST_OBJECTS}) $(TEST_LDFLAGS)
|
||||
|
||||
.SILENT: check
|
||||
.PHONY: check
|
||||
check: tests
|
||||
|
||||
.SILENT: tests
|
||||
tests: ${TESTS}
|
||||
sh -e -c 'for test in ${TESTS} ; do echo "*** running $${test}" && ./$${test} $(BUILD_OUTPUT) ; done'
|
||||
$(Q)make -s -C tst tests
|
||||
|
||||
.SILENT: check
|
||||
check: tests
|
||||
tests: apparmor_parser ${TESTS}
|
||||
sh -e -c 'for test in ${TESTS} ; do echo "*** running $${test}" && ./$${test}; done'
|
||||
$(Q)$(MAKE) -s -C tst tests
|
||||
|
||||
# always need to rebuild.
|
||||
.SILENT: $(AAREOBJECTS)
|
||||
.PHONY: $(AAREOBJECTS)
|
||||
$(AAREOBJECTS):
|
||||
make -C $(AAREDIR) CFLAGS="$(EXTRA_CXXFLAGS)"
|
||||
.SILENT: $(AAREOBJECT)
|
||||
.PHONY: $(AAREOBJECT)
|
||||
$(AAREOBJECT):
|
||||
$(MAKE) -C $(AAREDIR) CFLAGS="$(EXTRA_CXXFLAGS)"
|
||||
|
||||
.PHONY: install-rhel4
|
||||
install-rhel4: install-redhat
|
||||
@@ -245,17 +246,14 @@ install-rhel4: install-redhat
|
||||
install-redhat:
|
||||
install -m 755 -d $(DESTDIR)/etc/init.d
|
||||
install -m 755 rc.apparmor.$(subst install-,,$@) $(DESTDIR)/etc/init.d/apparmor
|
||||
install -m 755 rc.aaeventd.redhat $(DESTDIR)/etc/init.d/aaeventd
|
||||
|
||||
.PHONY: install-suse
|
||||
install-suse:
|
||||
install -m 755 -d $(DESTDIR)/etc/init.d
|
||||
install -m 755 rc.apparmor.$(subst install-,,$(@)) $(DESTDIR)/etc/init.d/boot.apparmor
|
||||
install -m 755 rc.aaeventd.$(subst install-,,$(@)) $(DESTDIR)/etc/init.d/aaeventd
|
||||
install -m 755 -d $(DESTDIR)/sbin
|
||||
ln -sf /etc/init.d/boot.apparmor $(DESTDIR)/sbin/rcapparmor
|
||||
ln -sf rcapparmor $(DESTDIR)/sbin/rcsubdomain
|
||||
ln -sf /etc/init.d/aaeventd $(DESTDIR)/sbin/rcaaeventd
|
||||
|
||||
.PHONY: install-slackware
|
||||
install-slackware:
|
||||
@@ -270,22 +268,29 @@ install-debian:
|
||||
.PHONY: install-unknown
|
||||
install-unknown:
|
||||
|
||||
INSTALLDEPS=$(TOOLS)
|
||||
INSTALLDEPS=arch
|
||||
ifdef DISTRO
|
||||
INSTALLDEPS+=install-$(DISTRO)
|
||||
endif
|
||||
|
||||
.PHONY: install
|
||||
install: $(INSTALLDEPS)
|
||||
install: install-indep install-arch
|
||||
|
||||
.PHONY: install-arch
|
||||
install-arch: $(INSTALLDEPS)
|
||||
install -m 755 -d $(DESTDIR)/sbin
|
||||
install -m 755 ${TOOLS} $(DESTDIR)/sbin
|
||||
|
||||
.PHONY: install-indep
|
||||
install-indep:
|
||||
install -m 755 -d $(INSTALL_CONFDIR)
|
||||
install -m 644 subdomain.conf $(INSTALL_CONFDIR)
|
||||
install -m 644 parser.conf $(INSTALL_CONFDIR)
|
||||
install -m 755 -d ${DESTDIR}/var/lib/apparmor
|
||||
install -m 755 -d $(APPARMOR_BIN_PREFIX)
|
||||
install -m 755 rc.apparmor.functions $(APPARMOR_BIN_PREFIX)
|
||||
make -C po install NAME=${NAME} DESTDIR=${DESTDIR}
|
||||
make install_manpages DESTDIR=${DESTDIR}
|
||||
$(MAKE) -C po install NAME=${NAME} DESTDIR=${DESTDIR}
|
||||
$(MAKE) install_manpages DESTDIR=${DESTDIR}
|
||||
|
||||
.SILENT: clean
|
||||
.PHONY: clean
|
||||
@@ -296,15 +301,14 @@ clean: _clean
|
||||
rm -f $(YACC_C_FILES)
|
||||
rm -f parser_version.h
|
||||
rm -f $(NAME)*.tar.gz $(NAME)*.tgz
|
||||
rm -f libstdc++.a
|
||||
rm -f af_names.h
|
||||
rm -f cap_names.h
|
||||
rm -rf techdoc.aux techdoc.log techdoc.pdf techdoc.toc techdor.txt techdoc/
|
||||
make -s -C $(AAREDIR) clean
|
||||
make -s -C po clean
|
||||
make -s -C tst clean
|
||||
$(MAKE) -s -C $(AAREDIR) clean
|
||||
$(MAKE) -s -C po clean
|
||||
$(MAKE) -s -C tst clean
|
||||
|
||||
.SILENT: dist_clean
|
||||
dist_clean:
|
||||
@make clean
|
||||
@$(MAKE) clean
|
||||
@rm -f $(LEX_C_FILES) $(YACC_C_FILES)
|
||||
|
@@ -103,6 +103,7 @@ make install DESTDIR=${RPM_BUILD_ROOT} \
|
||||
/etc/init.d/aaeventd
|
||||
%endif
|
||||
%config(noreplace) /etc/apparmor/subdomain.conf
|
||||
%config(noreplace) /etc/apparmor/parser.conf
|
||||
/var/lib/apparmor
|
||||
%dir %attr(-, root, root) %{apparmor_bin_prefix}
|
||||
%{apparmor_bin_prefix}/rc.apparmor.functions
|
||||
|
@@ -83,7 +83,7 @@ B<FILEGLOB> = (must start with '/' (after variable expansion), B<?*[]{}^> have s
|
||||
|
||||
B<ACCESS> = ( 'r' | 'w' | 'l' | 'ix' | 'ux' | 'Ux' | 'px' | 'Px' | 'cx -> ' I<PROGRAMCHILD> | 'Cx -> ' I<PROGRAMCHILD> | 'm' ) [ I<ACCESS> ... ] (not all combinations are allowed; see below.)
|
||||
|
||||
B<VARIABLE> = '@{' I<ALPHA> [ I<ALPHANUMERIC> ... ] '}'
|
||||
B<VARIABLE> = '@{' I<ALPHA> [ ( I<ALPHANUMERIC> | '_' ) ... ] '}'
|
||||
|
||||
B<VARIABLE ASSIGNMENT> = I<VARIABLE> ('=' | '+=') (space separated values)
|
||||
|
||||
@@ -91,7 +91,7 @@ B<ALIAS RULE> = I<ABS PATH> '->' I<REWRITTEN ABS PATH> ','
|
||||
|
||||
B<ALPHA> = ('a', 'b', 'c', ... 'z', 'A', 'B', ... 'Z')
|
||||
|
||||
B<ALPHANUMERIC> = ('1', '2', '3', ... '9', 'a', 'b', 'c', ... 'z', 'A', 'B', ... 'Z')
|
||||
B<ALPHANUMERIC> = ('0', '1', '2', ... '9', 'a', 'b', 'c', ... 'z', 'A', 'B', ... 'Z')
|
||||
|
||||
=back
|
||||
|
||||
@@ -323,10 +323,10 @@ as further information is specified.
|
||||
|
||||
eg.
|
||||
|
||||
network, #allow access to all networking
|
||||
network tcp, #allow access to tcp
|
||||
network inet tcp, #allow access to tcp only for inet4 addresses
|
||||
network inet6 tcp, #allow access to tcp only for inet6 addresses
|
||||
network, #allow access to all networking
|
||||
network tcp, #allow access to tcp
|
||||
network inet tcp, #allow access to tcp only for inet4 addresses
|
||||
network inet6 tcp, #allow access to tcp only for inet6 addresses
|
||||
|
||||
=head2 Variables
|
||||
|
||||
|
@@ -199,6 +199,38 @@ Give a quick reference guide.
|
||||
|
||||
=back
|
||||
|
||||
=head1 CONFIG FILE
|
||||
|
||||
An optional config file /etc/apparmor/parser.conf can be used to specify the
|
||||
default options for the parser, which then can be overridden using the command
|
||||
line options.
|
||||
|
||||
The config file ignores leading whitespace and treats lines that begin with #
|
||||
as comments. Config options are specified one per line using the same format
|
||||
as the longform command line options (without the preceding --).
|
||||
|
||||
Eg.
|
||||
#comment
|
||||
|
||||
optimize=no-expr-tree
|
||||
optimize=compress-fast
|
||||
|
||||
As with the command line some options accumulate and others override, ie. when
|
||||
there are conflicting versions of switch the last option is the one chosen.
|
||||
|
||||
Eg.
|
||||
Optimize=no-minimize
|
||||
Optimize=minimize
|
||||
|
||||
would result in Optimize=minimize being set.
|
||||
|
||||
The Include, Dump, and Optimize options accululate except for the inversion
|
||||
option (no-X vs. X), and a couple options that work by setting/clearing
|
||||
multiple options (compress-small). In that case the option will override
|
||||
the flags it sets but will may accumulate with others.
|
||||
|
||||
All other options override previously set values.
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
If you find any bugs, please report them at
|
||||
|
@@ -12,14 +12,21 @@ BISON := bison
|
||||
|
||||
all : ${TARGET}
|
||||
|
||||
libapparmor_re.a: regexp.o
|
||||
libapparmor_re.a: parse.o expr-tree.o hfa.o compressed_hfa.o aare_rules.o
|
||||
ar ${ARFLAGS} $@ $^
|
||||
|
||||
regexp.o : regexp.cc apparmor_re.h
|
||||
$(LINK.cc) $< -c -o $@
|
||||
expr-tree.o: expr-tree.cc expr-tree.h
|
||||
|
||||
regexp.cc : regexp.y flex-tables.h ../immunix.h
|
||||
hfa.o: hfa.cc apparmor_re.h hfa.h ../immunix.h
|
||||
|
||||
aare_rules.o: aare_rules.cc aare_rules.h apparmor_re.h expr-tree.h hfa.h compressed_hfa.h parse.h ../immunix.h
|
||||
|
||||
compressed_hfa.o: compressed_hfa.cc compressed_hfa.h ../immunix.h
|
||||
|
||||
parse.o : parse.cc apparmor_re.h expr-tree.h
|
||||
|
||||
parse.cc : parse.y parse.h flex-tables.h ../immunix.h
|
||||
${BISON} -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f regexp.o regexp.cc regexp.so regexp.a regexp ${TARGET}
|
||||
rm -f *.o parse.cc ${TARGET}
|
||||
|
314
parser/libapparmor_re/aare_rules.cc
Normal file
314
parser/libapparmor_re/aare_rules.cc
Normal file
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
|
||||
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
|
||||
* Copyright 2009-2010 Canonical Ltd.
|
||||
*
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the file
|
||||
* COPYING.LGPL.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* Wrapper around the dfa to convert aa rules into a dfa
|
||||
*/
|
||||
|
||||
#include <ostream>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <ext/stdio_filebuf.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "aare_rules.h"
|
||||
#include "expr-tree.h"
|
||||
#include "parse.h"
|
||||
#include "hfa.h"
|
||||
#include "compressed_hfa.h"
|
||||
#include "../immunix.h"
|
||||
|
||||
struct aare_ruleset {
|
||||
int reverse;
|
||||
Node *root;
|
||||
};
|
||||
|
||||
extern "C" aare_ruleset_t *aare_new_ruleset(int reverse)
|
||||
{
|
||||
aare_ruleset_t *container = (aare_ruleset_t *) malloc(sizeof(aare_ruleset_t));
|
||||
if (!container)
|
||||
return NULL;
|
||||
|
||||
container->root = NULL;
|
||||
container->reverse = reverse;
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
extern "C" void aare_delete_ruleset(aare_ruleset_t *rules)
|
||||
{
|
||||
if (rules) {
|
||||
if (rules->root)
|
||||
rules->root->release();
|
||||
free(rules);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int aare_add_rule(aare_ruleset_t *rules, char *rule, int deny,
|
||||
uint32_t perms, uint32_t audit, dfaflags_t flags)
|
||||
{
|
||||
return aare_add_rule_vec(rules, deny, perms, audit, 1, &rule, flags);
|
||||
}
|
||||
|
||||
#define FLAGS_WIDTH 2
|
||||
#define MATCH_FLAGS_SIZE (sizeof(uint32_t) * 8 - 1)
|
||||
MatchFlag *match_flags[FLAGS_WIDTH][MATCH_FLAGS_SIZE];
|
||||
DenyMatchFlag *deny_flags[FLAGS_WIDTH][MATCH_FLAGS_SIZE];
|
||||
#define EXEC_MATCH_FLAGS_SIZE (AA_EXEC_COUNT *2 * 2 * 2) /* double for each of ix pux, unsafe x bits * u::o */
|
||||
MatchFlag *exec_match_flags[FLAGS_WIDTH][EXEC_MATCH_FLAGS_SIZE]; /* mods + unsafe + ix + pux * u::o */
|
||||
ExactMatchFlag *exact_match_flags[FLAGS_WIDTH][EXEC_MATCH_FLAGS_SIZE]; /* mods + unsafe + ix + pux *u::o */
|
||||
|
||||
extern "C" void aare_reset_matchflags(void)
|
||||
{
|
||||
uint32_t i, j;
|
||||
#define RESET_FLAGS(group, size) { \
|
||||
for (i = 0; i < FLAGS_WIDTH; i++) { \
|
||||
for (j = 0; j < size; j++) { \
|
||||
if ((group)[i][j]) delete (group)[i][j]; \
|
||||
(group)[i][j] = NULL; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
RESET_FLAGS(match_flags, MATCH_FLAGS_SIZE);
|
||||
RESET_FLAGS(deny_flags, MATCH_FLAGS_SIZE);
|
||||
RESET_FLAGS(exec_match_flags, EXEC_MATCH_FLAGS_SIZE);
|
||||
RESET_FLAGS(exact_match_flags, EXEC_MATCH_FLAGS_SIZE);
|
||||
#undef RESET_FLAGS
|
||||
}
|
||||
|
||||
extern "C" int aare_add_rule_vec(aare_ruleset_t *rules, int deny,
|
||||
uint32_t perms, uint32_t audit,
|
||||
int count, char **rulev, dfaflags_t flags)
|
||||
{
|
||||
Node *tree = NULL, *accept;
|
||||
int exact_match;
|
||||
|
||||
assert(perms != 0);
|
||||
|
||||
if (regex_parse(&tree, rulev[0]))
|
||||
return 0;
|
||||
for (int i = 1; i < count; i++) {
|
||||
Node *subtree = NULL;
|
||||
Node *node = new CharNode(0);
|
||||
if (!node)
|
||||
return 0;
|
||||
tree = new CatNode(tree, node);
|
||||
if (regex_parse(&subtree, rulev[i]))
|
||||
return 0;
|
||||
tree = new CatNode(tree, subtree);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if we have an expression with or without wildcards. This
|
||||
* determines how exec modifiers are merged in accept_perms() based
|
||||
* on how we split permission bitmasks here.
|
||||
*/
|
||||
exact_match = 1;
|
||||
for (depth_first_traversal i(tree); i; i++) {
|
||||
if (dynamic_cast<StarNode *>(*i) ||
|
||||
dynamic_cast<PlusNode *>(*i) ||
|
||||
dynamic_cast<AnyCharNode *>(*i) ||
|
||||
dynamic_cast<CharSetNode *>(*i) ||
|
||||
dynamic_cast<NotCharSetNode *>(*i))
|
||||
exact_match = 0;
|
||||
}
|
||||
|
||||
if (rules->reverse)
|
||||
flip_tree(tree);
|
||||
|
||||
/* 0x7f == 4 bits x mods + 1 bit unsafe mask + 1 bit ix, + 1 pux after shift */
|
||||
#define EXTRACT_X_INDEX(perm, shift) (((perm) >> (shift + 7)) & 0x7f)
|
||||
|
||||
//if (perms & ALL_AA_EXEC_TYPE && (!perms & AA_EXEC_BITS))
|
||||
// fprintf(stderr, "adding X rule without MAY_EXEC: 0x%x %s\n", perms, rulev[0]);
|
||||
|
||||
//if (perms & ALL_EXEC_TYPE)
|
||||
// fprintf(stderr, "adding X rule %s 0x%x\n", rulev[0], perms);
|
||||
|
||||
//if (audit)
|
||||
//fprintf(stderr, "adding rule with audit bits set: 0x%x %s\n", audit, rulev[0]);
|
||||
|
||||
//if (perms & AA_CHANGE_HAT)
|
||||
// fprintf(stderr, "adding change_hat rule %s\n", rulev[0]);
|
||||
|
||||
/* the permissions set is assumed to be non-empty if any audit
|
||||
* bits are specified */
|
||||
accept = NULL;
|
||||
for (unsigned int n = 0; perms && n < (sizeof(perms) * 8); n++) {
|
||||
uint32_t mask = 1 << n;
|
||||
|
||||
if (!(perms & mask))
|
||||
continue;
|
||||
|
||||
int ai = audit & mask ? 1 : 0;
|
||||
perms &= ~mask;
|
||||
|
||||
Node *flag;
|
||||
if (mask & ALL_AA_EXEC_TYPE)
|
||||
/* these cases are covered by EXEC_BITS */
|
||||
continue;
|
||||
if (deny) {
|
||||
if (deny_flags[ai][n]) {
|
||||
flag = deny_flags[ai][n];
|
||||
} else {
|
||||
//fprintf(stderr, "Adding deny ai %d mask 0x%x audit 0x%x\n", ai, mask, audit & mask);
|
||||
deny_flags[ai][n] = new DenyMatchFlag(mask, audit & mask);
|
||||
flag = deny_flags[ai][n];
|
||||
}
|
||||
} else if (mask & AA_EXEC_BITS) {
|
||||
uint32_t eperm = 0;
|
||||
uint32_t index = 0;
|
||||
if (mask & AA_USER_EXEC) {
|
||||
eperm = mask | (perms & AA_USER_EXEC_TYPE);
|
||||
index = EXTRACT_X_INDEX(eperm, AA_USER_SHIFT);
|
||||
} else {
|
||||
eperm = mask | (perms & AA_OTHER_EXEC_TYPE);
|
||||
index = EXTRACT_X_INDEX(eperm, AA_OTHER_SHIFT) + (AA_EXEC_COUNT << 2);
|
||||
}
|
||||
//fprintf(stderr, "index %d eperm 0x%x\n", index, eperm);
|
||||
if (exact_match) {
|
||||
if (exact_match_flags[ai][index]) {
|
||||
flag = exact_match_flags[ai][index];
|
||||
} else {
|
||||
exact_match_flags[ai][index] = new ExactMatchFlag(eperm, audit & mask);
|
||||
flag = exact_match_flags[ai][index];
|
||||
}
|
||||
} else {
|
||||
if (exec_match_flags[ai][index]) {
|
||||
flag = exec_match_flags[ai][index];
|
||||
} else {
|
||||
exec_match_flags[ai][index] = new MatchFlag(eperm, audit & mask);
|
||||
flag = exec_match_flags[ai][index];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (match_flags[ai][n]) {
|
||||
flag = match_flags[ai][n];
|
||||
} else {
|
||||
match_flags[ai][n] = new MatchFlag(mask, audit & mask);
|
||||
flag = match_flags[ai][n];
|
||||
}
|
||||
}
|
||||
if (accept)
|
||||
accept = new AltNode(accept, flag);
|
||||
else
|
||||
accept = flag;
|
||||
} /* for ... */
|
||||
|
||||
if (flags & DFA_DUMP_RULE_EXPR) {
|
||||
cerr << "rule: ";
|
||||
cerr << rulev[0];
|
||||
for (int i = 1; i < count; i++) {
|
||||
cerr << "\\x00";
|
||||
cerr << rulev[i];
|
||||
}
|
||||
cerr << " -> ";
|
||||
tree->dump(cerr);
|
||||
cerr << "\n\n";
|
||||
}
|
||||
|
||||
if (rules->root)
|
||||
rules->root = new AltNode(rules->root, new CatNode(tree, accept));
|
||||
else
|
||||
rules->root = new CatNode(tree, accept);
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
/* create a dfa from the ruleset
|
||||
* returns: buffer contain dfa tables, @size set to the size of the tables
|
||||
* else NULL on failure
|
||||
*/
|
||||
extern "C" void *aare_create_dfa(aare_ruleset_t *rules, size_t *size,
|
||||
dfaflags_t flags)
|
||||
{
|
||||
char *buffer = NULL;
|
||||
|
||||
label_nodes(rules->root);
|
||||
if (flags & DFA_DUMP_TREE) {
|
||||
cerr << "\nDFA: Expression Tree\n";
|
||||
rules->root->dump(cerr);
|
||||
cerr << "\n\n";
|
||||
}
|
||||
|
||||
if (flags & DFA_CONTROL_TREE_SIMPLE) {
|
||||
rules->root = simplify_tree(rules->root, flags);
|
||||
|
||||
if (flags & DFA_DUMP_SIMPLE_TREE) {
|
||||
cerr << "\nDFA: Simplified Expression Tree\n";
|
||||
rules->root->dump(cerr);
|
||||
cerr << "\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
stringstream stream;
|
||||
try {
|
||||
DFA dfa(rules->root, flags);
|
||||
if (flags & DFA_DUMP_UNIQ_PERMS)
|
||||
dfa.dump_uniq_perms("dfa");
|
||||
|
||||
if (flags & DFA_CONTROL_MINIMIZE) {
|
||||
dfa.minimize(flags);
|
||||
|
||||
if (flags & DFA_DUMP_MIN_UNIQ_PERMS)
|
||||
dfa.dump_uniq_perms("minimized dfa");
|
||||
}
|
||||
if (flags & DFA_CONTROL_REMOVE_UNREACHABLE)
|
||||
dfa.remove_unreachable(flags);
|
||||
|
||||
if (flags & DFA_DUMP_STATES)
|
||||
dfa.dump(cerr);
|
||||
|
||||
if (flags & DFA_DUMP_GRAPH)
|
||||
dfa.dump_dot_graph(cerr);
|
||||
|
||||
map<uchar, uchar> eq;
|
||||
if (flags & DFA_CONTROL_EQUIV) {
|
||||
eq = dfa.equivalence_classes(flags);
|
||||
dfa.apply_equivalence_classes(eq);
|
||||
|
||||
if (flags & DFA_DUMP_EQUIV) {
|
||||
cerr << "\nDFA equivalence class\n";
|
||||
dump_equivalence_classes(cerr, eq);
|
||||
}
|
||||
} else if (flags & DFA_DUMP_EQUIV)
|
||||
cerr << "\nDFA did not generate an equivalence class\n";
|
||||
|
||||
TransitionTable transition_table(dfa, eq, flags);
|
||||
if (flags & DFA_DUMP_TRANS_TABLE)
|
||||
transition_table.dump(cerr);
|
||||
transition_table.flex_table(stream, "");
|
||||
}
|
||||
catch(int error) {
|
||||
*size = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stringbuf *buf = stream.rdbuf();
|
||||
|
||||
buf->pubseekpos(0);
|
||||
*size = buf->in_avail();
|
||||
|
||||
buffer = (char *)malloc(*size);
|
||||
if (!buffer)
|
||||
return NULL;
|
||||
buf->sgetn(buffer, *size);
|
||||
return buffer;
|
||||
}
|
49
parser/libapparmor_re/aare_rules.h
Normal file
49
parser/libapparmor_re/aare_rules.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
|
||||
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
|
||||
* Copyright 2009-2010 Canonical Ltd.
|
||||
*
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the file
|
||||
* COPYING.LGPL.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* Wrapper around the dfa to convert aa rules into a dfa
|
||||
*/
|
||||
#ifndef __LIBAA_RE_RULES_H
|
||||
#define __LIBAA_RE_RULES_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "apparmor_re.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct aare_ruleset;
|
||||
|
||||
typedef struct aare_ruleset aare_ruleset_t;
|
||||
|
||||
aare_ruleset_t *aare_new_ruleset(int reverse);
|
||||
void aare_delete_ruleset(aare_ruleset_t *rules);
|
||||
int aare_add_rule(aare_ruleset_t *rules, char *rule, int deny, uint32_t perms,
|
||||
uint32_t audit, dfaflags_t flags);
|
||||
int aare_add_rule_vec(aare_ruleset_t *rules, int deny, uint32_t perms,
|
||||
uint32_t audit, int count, char **rulev,
|
||||
dfaflags_t flags);
|
||||
void *aare_create_dfa(aare_ruleset_t *rules, size_t *size, dfaflags_t flags);
|
||||
void aare_reset_matchflags(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* __LIBAA_RE_RULES_H */
|
@@ -1,11 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007 Novell, Inc.
|
||||
* (All rights reserved)
|
||||
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
|
||||
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
|
||||
* Copyright 2009-2010 Canonical Ltd.
|
||||
*
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the file
|
||||
* COPYING.LGPL.
|
||||
*/
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the file
|
||||
* COPYING.LGPL.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef APPARMOR_RE_H
|
||||
#define APPARMOR_RE_H
|
||||
@@ -21,6 +30,7 @@ typedef enum dfaflags {
|
||||
DFA_CONTROL_REMOVE_UNREACHABLE = 1 << 7,
|
||||
DFA_CONTROL_TRANS_HIGH = 1 << 8,
|
||||
|
||||
DFA_DUMP_MIN_PARTS = 1 << 13,
|
||||
DFA_DUMP_UNIQ_PERMS = 1 << 14,
|
||||
DFA_DUMP_MIN_UNIQ_PERMS = 1 << 15,
|
||||
DFA_DUMP_TREE_STATS = 1 << 16,
|
||||
@@ -41,25 +51,4 @@ typedef enum dfaflags {
|
||||
DFA_DUMP_NODE_TO_DFA = 1 << 31,
|
||||
} dfaflags_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct aare_ruleset;
|
||||
|
||||
typedef struct aare_ruleset aare_ruleset_t;
|
||||
|
||||
aare_ruleset_t *aare_new_ruleset(int reverse);
|
||||
void aare_delete_ruleset(aare_ruleset_t *rules);
|
||||
int aare_add_rule(aare_ruleset_t *rules, char *rule, int deny,
|
||||
uint32_t perms, uint32_t audit, dfaflags_t flags);
|
||||
int aare_add_rule_vec(aare_ruleset_t *rules, int deny, uint32_t perms,
|
||||
uint32_t audit, int count, char **rulev, dfaflags_t flags);
|
||||
void *aare_create_dfa(aare_ruleset_t *rules, size_t *size, dfaflags_t flags);
|
||||
void aare_reset_matchflags(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* APPARMOR_RE_H */
|
||||
|
420
parser/libapparmor_re/compressed_hfa.cc
Normal file
420
parser/libapparmor_re/compressed_hfa.cc
Normal file
@@ -0,0 +1,420 @@
|
||||
/*
|
||||
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
|
||||
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
|
||||
* Copyright 2009-2010 Canonical Ltd.
|
||||
*
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the file
|
||||
* COPYING.LGPL.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* Create a compressed hfa from and hfa
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <ostream>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hfa.h"
|
||||
#include "compressed_hfa.h"
|
||||
|
||||
void TransitionTable::init_free_list(vector<pair<size_t, size_t> > &free_list,
|
||||
size_t prev, size_t start)
|
||||
{
|
||||
for (size_t i = start; i < free_list.size(); i++) {
|
||||
if (prev)
|
||||
free_list[prev].second = i;
|
||||
free_list[i].first = prev;
|
||||
prev = i;
|
||||
}
|
||||
free_list[free_list.size() - 1].second = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* new Construct the transition table.
|
||||
*/
|
||||
TransitionTable::TransitionTable(DFA &dfa, map<uchar, uchar> &eq,
|
||||
dfaflags_t flags): eq(eq)
|
||||
{
|
||||
if (flags & DFA_DUMP_TRANS_PROGRESS)
|
||||
fprintf(stderr, "Compressing trans table:\r");
|
||||
|
||||
if (eq.empty())
|
||||
max_eq = 255;
|
||||
else {
|
||||
max_eq = 0;
|
||||
for (map<uchar, uchar>::iterator i = eq.begin();
|
||||
i != eq.end(); i++) {
|
||||
if (i->second > max_eq)
|
||||
max_eq = i->second;
|
||||
}
|
||||
}
|
||||
|
||||
/* Do initial setup adding up all the transitions and sorting by
|
||||
* transition count.
|
||||
*/
|
||||
size_t optimal = 2;
|
||||
multimap<size_t, State *> order;
|
||||
vector<pair<size_t, size_t> > free_list;
|
||||
|
||||
for (Partition::iterator i = dfa.states.begin(); i != dfa.states.end(); i++) {
|
||||
if (*i == dfa.start || *i == dfa.nonmatching)
|
||||
continue;
|
||||
optimal += (*i)->cases.cases.size();
|
||||
if (flags & DFA_CONTROL_TRANS_HIGH) {
|
||||
size_t range = 0;
|
||||
if ((*i)->cases.cases.size())
|
||||
range =
|
||||
(*i)->cases.cases.rbegin()->first -
|
||||
(*i)->cases.begin()->first;
|
||||
size_t ord = ((256 - (*i)->cases.cases.size()) << 8) | (256 - range);
|
||||
/* reverse sort by entry count, most entries first */
|
||||
order.insert(make_pair(ord, *i));
|
||||
}
|
||||
}
|
||||
|
||||
/* Insert the dummy nonmatching transition by hand */
|
||||
next_check.push_back(make_pair(dfa.nonmatching, dfa.nonmatching));
|
||||
default_base.push_back(make_pair(dfa.nonmatching, 0));
|
||||
num.insert(make_pair(dfa.nonmatching, num.size()));
|
||||
|
||||
accept.resize(dfa.states.size());
|
||||
accept2.resize(dfa.states.size());
|
||||
next_check.resize(optimal);
|
||||
free_list.resize(optimal);
|
||||
|
||||
accept[0] = 0;
|
||||
accept2[0] = 0;
|
||||
first_free = 1;
|
||||
init_free_list(free_list, 0, 1);
|
||||
|
||||
insert_state(free_list, dfa.start, dfa);
|
||||
accept[1] = 0;
|
||||
accept2[1] = 0;
|
||||
num.insert(make_pair(dfa.start, num.size()));
|
||||
|
||||
int count = 2;
|
||||
|
||||
if (!(flags & DFA_CONTROL_TRANS_HIGH)) {
|
||||
for (Partition::iterator i = dfa.states.begin(); i != dfa.states.end(); i++) {
|
||||
if (*i != dfa.nonmatching && *i != dfa.start) {
|
||||
insert_state(free_list, *i, dfa);
|
||||
accept[num.size()] = (*i)->accept;
|
||||
accept2[num.size()] = (*i)->audit;
|
||||
num.insert(make_pair(*i, num.size()));
|
||||
}
|
||||
if (flags & (DFA_DUMP_TRANS_PROGRESS)) {
|
||||
count++;
|
||||
if (count % 100 == 0)
|
||||
fprintf(stderr, "\033[2KCompressing trans table: insert state: %d/%zd\r",
|
||||
count, dfa.states.size());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (multimap<size_t, State *>::iterator i = order.begin();
|
||||
i != order.end(); i++) {
|
||||
if (i->second != dfa.nonmatching &&
|
||||
i->second != dfa.start) {
|
||||
insert_state(free_list, i->second, dfa);
|
||||
accept[num.size()] = i->second->accept;
|
||||
accept2[num.size()] = i->second->audit;
|
||||
num.insert(make_pair(i->second, num.size()));
|
||||
}
|
||||
if (flags & (DFA_DUMP_TRANS_PROGRESS)) {
|
||||
count++;
|
||||
if (count % 100 == 0)
|
||||
fprintf(stderr, "\033[2KCompressing trans table: insert state: %d/%zd\r",
|
||||
count, dfa.states.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & (DFA_DUMP_TRANS_STATS | DFA_DUMP_TRANS_PROGRESS)) {
|
||||
ssize_t size = 4 * next_check.size() + 6 * dfa.states.size();
|
||||
fprintf(stderr, "\033[2KCompressed trans table: states %zd, next/check %zd, optimal next/check %zd avg/state %.2f, compression %zd/%zd = %.2f %%\n",
|
||||
dfa.states.size(), next_check.size(), optimal,
|
||||
(float)next_check.size() / (float)dfa.states.size(),
|
||||
size, 512 * dfa.states.size(),
|
||||
100.0 - ((float)size * 100.0 /(float)(512 * dfa.states.size())));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does <cases> fit into position <base> of the transition table?
|
||||
*/
|
||||
bool TransitionTable::fits_in(vector<pair<size_t, size_t> > &free_list
|
||||
__attribute__ ((unused)), size_t pos,
|
||||
Cases &cases)
|
||||
{
|
||||
size_t c, base = pos - cases.begin()->first;
|
||||
for (Cases::iterator i = cases.begin(); i != cases.end(); i++) {
|
||||
c = base + i->first;
|
||||
/* if it overflows the next_check array it fits in as we will
|
||||
* resize */
|
||||
if (c >= next_check.size())
|
||||
return true;
|
||||
if (next_check[c].second)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert <state> of <dfa> into the transition table.
|
||||
*/
|
||||
void TransitionTable::insert_state(vector<pair<size_t, size_t> > &free_list,
|
||||
State *from, DFA &dfa)
|
||||
{
|
||||
State *default_state = dfa.nonmatching;
|
||||
size_t base = 0;
|
||||
int resize;
|
||||
|
||||
Cases &cases = from->cases;
|
||||
size_t c = cases.begin()->first;
|
||||
size_t prev = 0;
|
||||
size_t x = first_free;
|
||||
|
||||
if (cases.otherwise)
|
||||
default_state = cases.otherwise;
|
||||
if (cases.cases.empty())
|
||||
goto do_insert;
|
||||
|
||||
repeat:
|
||||
resize = 0;
|
||||
/* get the first free entry that won't underflow */
|
||||
while (x && (x < c)) {
|
||||
prev = x;
|
||||
x = free_list[x].second;
|
||||
}
|
||||
|
||||
/* try inserting until we succeed. */
|
||||
while (x && !fits_in(free_list, x, cases)) {
|
||||
prev = x;
|
||||
x = free_list[x].second;
|
||||
}
|
||||
if (!x) {
|
||||
resize = 256 - cases.begin()->first;
|
||||
x = free_list.size();
|
||||
/* set prev to last free */
|
||||
} else if (x + 255 - cases.begin()->first >= next_check.size()) {
|
||||
resize = (255 - cases.begin()->first - (next_check.size() - 1 - x));
|
||||
for (size_t y = x; y; y = free_list[y].second)
|
||||
prev = y;
|
||||
}
|
||||
if (resize) {
|
||||
/* expand next_check and free_list */
|
||||
size_t old_size = free_list.size();
|
||||
next_check.resize(next_check.size() + resize);
|
||||
free_list.resize(free_list.size() + resize);
|
||||
init_free_list(free_list, prev, old_size);
|
||||
if (!first_free)
|
||||
first_free = old_size;;
|
||||
if (x == old_size)
|
||||
goto repeat;
|
||||
}
|
||||
|
||||
base = x - c;
|
||||
for (Cases::iterator j = cases.begin(); j != cases.end(); j++) {
|
||||
next_check[base + j->first] = make_pair(j->second, from);
|
||||
size_t prev = free_list[base + j->first].first;
|
||||
size_t next = free_list[base + j->first].second;
|
||||
if (prev)
|
||||
free_list[prev].second = next;
|
||||
if (next)
|
||||
free_list[next].first = prev;
|
||||
if (base + j->first == first_free)
|
||||
first_free = next;
|
||||
}
|
||||
|
||||
do_insert:
|
||||
default_base.push_back(make_pair(default_state, base));
|
||||
}
|
||||
|
||||
/**
|
||||
* Text-dump the transition table (for debugging).
|
||||
*/
|
||||
void TransitionTable::dump(ostream &os)
|
||||
{
|
||||
map<size_t, const State *> st;
|
||||
for (map<const State *, size_t>::iterator i = num.begin(); i != num.end(); i++) {
|
||||
st.insert(make_pair(i->second, i->first));
|
||||
}
|
||||
|
||||
os << "size=" << default_base.size() << " (accept, default, base): {state} -> {default state}" << "\n";
|
||||
for (size_t i = 0; i < default_base.size(); i++) {
|
||||
os << i << ": ";
|
||||
os << "(" << accept[i] << ", " << num[default_base[i].first]
|
||||
<< ", " << default_base[i].second << ")";
|
||||
if (st[i])
|
||||
os << " " << *st[i];
|
||||
if (default_base[i].first)
|
||||
os << " -> " << *default_base[i].first;
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
os << "size=" << next_check.size() << " (next, check): {check state} -> {next state} : offset from base\n";
|
||||
for (size_t i = 0; i < next_check.size(); i++) {
|
||||
if (!next_check[i].second)
|
||||
continue;
|
||||
|
||||
os << i << ": ";
|
||||
if (next_check[i].second) {
|
||||
os << "(" << num[next_check[i].first] << ", "
|
||||
<< num[next_check[i].second] << ")" << " "
|
||||
<< *next_check[i].second << " -> "
|
||||
<< *next_check[i].first << ": ";
|
||||
|
||||
size_t offs = i - default_base[num[next_check[i].second]].second;
|
||||
if (eq.size())
|
||||
os << offs;
|
||||
else
|
||||
os << (uchar) offs;
|
||||
}
|
||||
os << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a flex-style binary dump of the DFA tables. The table format
|
||||
* was partly reverse engineered from the flex sources and from
|
||||
* examining the tables that flex creates with its --tables-file option.
|
||||
* (Only the -Cf and -Ce formats are currently supported.)
|
||||
*/
|
||||
|
||||
#include "flex-tables.h"
|
||||
#define YYTH_REGEX_MAGIC 0x1B5E783D
|
||||
|
||||
static inline size_t pad64(size_t i)
|
||||
{
|
||||
return (i + (size_t) 7) & ~(size_t) 7;
|
||||
}
|
||||
|
||||
string fill64(size_t i)
|
||||
{
|
||||
const char zeroes[8] = { };
|
||||
string fill(zeroes, (i & 7) ? 8 - (i & 7) : 0);
|
||||
return fill;
|
||||
}
|
||||
|
||||
template<class Iter> size_t flex_table_size(Iter pos, Iter end)
|
||||
{
|
||||
return pad64(sizeof(struct table_header) + sizeof(*pos) * (end - pos));
|
||||
}
|
||||
|
||||
template<class Iter>
|
||||
void write_flex_table(ostream &os, int id, Iter pos, Iter end)
|
||||
{
|
||||
struct table_header td = { 0, 0, 0, 0 };
|
||||
size_t size = end - pos;
|
||||
|
||||
td.td_id = htons(id);
|
||||
td.td_flags = htons(sizeof(*pos));
|
||||
td.td_lolen = htonl(size);
|
||||
os.write((char *)&td, sizeof(td));
|
||||
|
||||
for (; pos != end; ++pos) {
|
||||
switch (sizeof(*pos)) {
|
||||
case 4:
|
||||
os.put((char)(*pos >> 24));
|
||||
os.put((char)(*pos >> 16));
|
||||
case 2:
|
||||
os.put((char)(*pos >> 8));
|
||||
case 1:
|
||||
os.put((char)*pos);
|
||||
}
|
||||
}
|
||||
|
||||
os << fill64(sizeof(td) + sizeof(*pos) * size);
|
||||
}
|
||||
|
||||
void TransitionTable::flex_table(ostream &os, const char *name)
|
||||
{
|
||||
const char th_version[] = "notflex";
|
||||
struct table_set_header th = { 0, 0, 0, 0 };
|
||||
|
||||
/**
|
||||
* Change the following two data types to adjust the maximum flex
|
||||
* table size.
|
||||
*/
|
||||
typedef uint16_t state_t;
|
||||
typedef uint32_t trans_t;
|
||||
|
||||
if (default_base.size() >= (state_t) - 1) {
|
||||
cerr << "Too many states (" << default_base.size() << ") for "
|
||||
"type state_t\n";
|
||||
exit(1);
|
||||
}
|
||||
if (next_check.size() >= (trans_t) - 1) {
|
||||
cerr << "Too many transitions (" << next_check.size()
|
||||
<< ") for " "type trans_t\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create copies of the data structures so that we can dump the tables
|
||||
* using the generic write_flex_table() routine.
|
||||
*/
|
||||
vector<uint8_t> equiv_vec;
|
||||
if (eq.size()) {
|
||||
equiv_vec.resize(256);
|
||||
for (map<uchar, uchar>::iterator i = eq.begin(); i != eq.end(); i++) {
|
||||
equiv_vec[i->first] = i->second;
|
||||
}
|
||||
}
|
||||
|
||||
vector<state_t> default_vec;
|
||||
vector<trans_t> base_vec;
|
||||
for (DefaultBase::iterator i = default_base.begin(); i != default_base.end(); i++) {
|
||||
default_vec.push_back(num[i->first]);
|
||||
base_vec.push_back(i->second);
|
||||
}
|
||||
|
||||
vector<state_t> next_vec;
|
||||
vector<state_t> check_vec;
|
||||
for (NextCheck::iterator i = next_check.begin(); i != next_check.end(); i++) {
|
||||
next_vec.push_back(num[i->first]);
|
||||
check_vec.push_back(num[i->second]);
|
||||
}
|
||||
|
||||
/* Write the actual flex parser table. */
|
||||
|
||||
size_t hsize = pad64(sizeof(th) + sizeof(th_version) + strlen(name) + 1);
|
||||
th.th_magic = htonl(YYTH_REGEX_MAGIC);
|
||||
th.th_hsize = htonl(hsize);
|
||||
th.th_ssize = htonl(hsize +
|
||||
flex_table_size(accept.begin(), accept.end()) +
|
||||
flex_table_size(accept2.begin(), accept2.end()) +
|
||||
(eq.size() ? flex_table_size(equiv_vec.begin(), equiv_vec.end()) : 0) +
|
||||
flex_table_size(base_vec.begin(), base_vec.end()) +
|
||||
flex_table_size(default_vec.begin(), default_vec.end()) +
|
||||
flex_table_size(next_vec.begin(), next_vec.end()) +
|
||||
flex_table_size(check_vec.begin(), check_vec.end()));
|
||||
os.write((char *)&th, sizeof(th));
|
||||
os << th_version << (char)0 << name << (char)0;
|
||||
os << fill64(sizeof(th) + sizeof(th_version) + strlen(name) + 1);
|
||||
|
||||
write_flex_table(os, YYTD_ID_ACCEPT, accept.begin(), accept.end());
|
||||
write_flex_table(os, YYTD_ID_ACCEPT2, accept2.begin(), accept2.end());
|
||||
if (eq.size())
|
||||
write_flex_table(os, YYTD_ID_EC, equiv_vec.begin(),
|
||||
equiv_vec.end());
|
||||
write_flex_table(os, YYTD_ID_BASE, base_vec.begin(), base_vec.end());
|
||||
write_flex_table(os, YYTD_ID_DEF, default_vec.begin(), default_vec.end());
|
||||
write_flex_table(os, YYTD_ID_NXT, next_vec.begin(), next_vec.end());
|
||||
write_flex_table(os, YYTD_ID_CHK, check_vec.begin(), check_vec.end());
|
||||
}
|
56
parser/libapparmor_re/compressed_hfa.h
Normal file
56
parser/libapparmor_re/compressed_hfa.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
|
||||
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
|
||||
* Copyright 2009-2010 Canonical Ltd.
|
||||
*
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the file
|
||||
* COPYING.LGPL.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* Create a compressed hfa from and hfa
|
||||
*/
|
||||
#ifndef __LIBAA_RE_COMPRESSED_HFA_H
|
||||
#define __LIBAA_RE_COMPRESSED_HFA_H
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "hfa.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class TransitionTable {
|
||||
typedef vector<pair<const State *, size_t> > DefaultBase;
|
||||
typedef vector<pair<const State *, const State *> > NextCheck;
|
||||
public:
|
||||
TransitionTable(DFA &dfa, map<uchar, uchar> &eq, dfaflags_t flags);
|
||||
void dump(ostream & os);
|
||||
void flex_table(ostream &os, const char *name);
|
||||
void init_free_list(vector<pair<size_t, size_t> > &free_list,
|
||||
size_t prev, size_t start);
|
||||
bool fits_in(vector<pair<size_t, size_t> > &free_list, size_t base,
|
||||
Cases &cases);
|
||||
void insert_state(vector<pair<size_t, size_t> > &free_list,
|
||||
State *state, DFA &dfa);
|
||||
|
||||
private:
|
||||
vector<uint32_t> accept;
|
||||
vector<uint32_t> accept2;
|
||||
DefaultBase default_base;
|
||||
NextCheck next_check;
|
||||
map<const State *, size_t> num;
|
||||
map<uchar, uchar> &eq;
|
||||
uchar max_eq;
|
||||
size_t first_free;
|
||||
};
|
||||
|
||||
#endif /* __LIBAA_RE_COMPRESSED_HFA_H */
|
602
parser/libapparmor_re/expr-tree.cc
Normal file
602
parser/libapparmor_re/expr-tree.cc
Normal file
@@ -0,0 +1,602 @@
|
||||
/*
|
||||
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
|
||||
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
|
||||
* Copyright 2009-2010 Canonical Ltd.
|
||||
*
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the file
|
||||
* COPYING.LGPL.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* Functions to create/manipulate an expression tree for regular expressions
|
||||
* that have been parsed.
|
||||
*
|
||||
* The expression tree can be used directly after the parse creates it, or
|
||||
* it can be factored so that the set of important nodes is smaller.
|
||||
* Having a reduced set of important nodes generally results in a dfa that
|
||||
* is closer to minimum (fewer redundant states are created). It also
|
||||
* results in fewer important nodes in a the state set during subset
|
||||
* construction resulting in less memory used to create a dfa.
|
||||
*
|
||||
* Generally it is worth doing expression tree simplification before dfa
|
||||
* construction, if the regular expression tree contains any alternations.
|
||||
* Even if the regular expression doesn't simplification should be fast
|
||||
* enough that it can be used with minimal overhead.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "expr-tree.h"
|
||||
#include "apparmor_re.h"
|
||||
|
||||
/* Use a single static EpsNode as it carries no node specific information */
|
||||
EpsNode epsnode;
|
||||
|
||||
ostream &operator<<(ostream &os, uchar c)
|
||||
{
|
||||
const char *search = "\a\033\f\n\r\t|*+[](). ",
|
||||
*replace = "aefnrt|*+[](). ", *s;
|
||||
|
||||
if ((s = strchr(search, c)) && *s != '\0') {
|
||||
os << '\\' << replace[s - search];
|
||||
} else if (c < 32 || c >= 127) {
|
||||
os << '\\' << '0' << char ('0' + (c >> 6))
|
||||
<< char ('0' + ((c >> 3) & 7)) << char ('0' + (c & 7));
|
||||
} else {
|
||||
os << (char)c;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
/**
|
||||
* Text-dump a state (for debugging).
|
||||
*/
|
||||
ostream &operator<<(ostream &os, const NodeSet &state)
|
||||
{
|
||||
os << '{';
|
||||
if (!state.empty()) {
|
||||
NodeSet::iterator i = state.begin();
|
||||
for (;;) {
|
||||
os << (*i)->label;
|
||||
if (++i == state.end())
|
||||
break;
|
||||
os << ',';
|
||||
}
|
||||
}
|
||||
os << '}';
|
||||
return os;
|
||||
}
|
||||
|
||||
ostream &operator<<(ostream &os, Node &node)
|
||||
{
|
||||
node.dump(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
/**
|
||||
* hash_NodeSet - generate a hash for the Nodes in the set
|
||||
*/
|
||||
unsigned long hash_NodeSet(NodeSet *ns)
|
||||
{
|
||||
unsigned long hash = 5381;
|
||||
|
||||
for (NodeSet::iterator i = ns->begin(); i != ns->end(); i++) {
|
||||
hash = ((hash << 5) + hash) + (unsigned long)*i;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* label_nodes - label the node positions for pretty-printing debug output
|
||||
*
|
||||
* TODO: separate - node labels should be separate and optional, if not
|
||||
* present pretty printing should use Node address
|
||||
*/
|
||||
void label_nodes(Node *root)
|
||||
{
|
||||
int nodes = 1;
|
||||
for (depth_first_traversal i(root); i; i++)
|
||||
i->label = nodes++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Text-dump the syntax tree (for debugging).
|
||||
*/
|
||||
void Node::dump_syntax_tree(ostream &os)
|
||||
{
|
||||
for (depth_first_traversal i(this); i; i++) {
|
||||
os << i->label << '\t';
|
||||
if ((*i)->child[0] == 0)
|
||||
os << **i << '\t' << (*i)->followpos << endl;
|
||||
else {
|
||||
if ((*i)->child[1] == 0)
|
||||
os << (*i)->child[0]->label << **i;
|
||||
else
|
||||
os << (*i)->child[0]->label << **i
|
||||
<< (*i)->child[1]->label;
|
||||
os << '\t' << (*i)->firstpos << (*i)->lastpos << endl;
|
||||
}
|
||||
}
|
||||
os << endl;
|
||||
}
|
||||
|
||||
/*
|
||||
* Normalize the regex parse tree for factoring and cancelations. Normalization
|
||||
* reorganizes internal (alt and cat) nodes into a fixed "normalized" form that
|
||||
* simplifies factoring code, in that it produces a canonicalized form for
|
||||
* the direction being normalized so that the factoring code does not have
|
||||
* to consider as many cases.
|
||||
*
|
||||
* left normalization (dir == 0) uses these rules
|
||||
* (E | a) -> (a | E)
|
||||
* (a | b) | c -> a | (b | c)
|
||||
* (ab)c -> a(bc)
|
||||
*
|
||||
* right normalization (dir == 1) uses the same rules but reversed
|
||||
* (a | E) -> (E | a)
|
||||
* a | (b | c) -> (a | b) | c
|
||||
* a(bc) -> (ab)c
|
||||
*
|
||||
* Note: This is written iteratively for a given node (the top node stays
|
||||
* fixed and the children are rotated) instead of recursively.
|
||||
* For a given node under examination rotate over nodes from
|
||||
* dir to !dir. Until no dir direction node meets the criterial.
|
||||
* Then recurse to the children (which will have a different node type)
|
||||
* to make sure they are normalized.
|
||||
* Normalization of a child node is guarenteed to not affect the
|
||||
* normalization of the parent.
|
||||
*
|
||||
* For cat nodes the depth first traverse order is guarenteed to be
|
||||
* maintained. This is not necessary for altnodes.
|
||||
*
|
||||
* Eg. For left normalization
|
||||
*
|
||||
* |1 |1
|
||||
* / \ / \
|
||||
* |2 T -> a |2
|
||||
* / \ / \
|
||||
* |3 c b |3
|
||||
* / \ / \
|
||||
* a b c T
|
||||
*
|
||||
*/
|
||||
static void rotate_node(Node *t, int dir)
|
||||
{
|
||||
// (a | b) | c -> a | (b | c)
|
||||
// (ab)c -> a(bc)
|
||||
Node *left = t->child[dir];
|
||||
t->child[dir] = left->child[dir];
|
||||
left->child[dir] = left->child[!dir];
|
||||
left->child[!dir] = t->child[!dir];
|
||||
t->child[!dir] = left;
|
||||
}
|
||||
|
||||
void normalize_tree(Node *t, int dir)
|
||||
{
|
||||
if (dynamic_cast<LeafNode *>(t))
|
||||
return;
|
||||
|
||||
for (;;) {
|
||||
if ((&epsnode == t->child[dir]) &&
|
||||
(&epsnode != t->child[!dir]) &&
|
||||
dynamic_cast<TwoChildNode *>(t)) {
|
||||
// (E | a) -> (a | E)
|
||||
// Ea -> aE
|
||||
Node *c = t->child[dir];
|
||||
t->child[dir] = t->child[!dir];
|
||||
t->child[!dir] = c;
|
||||
// Don't break here as 'a' may be a tree that
|
||||
// can be pulled up.
|
||||
} else if ((dynamic_cast<AltNode *>(t) &&
|
||||
dynamic_cast<AltNode *>(t->child[dir])) ||
|
||||
(dynamic_cast<CatNode *>(t) &&
|
||||
dynamic_cast<CatNode *>(t->child[dir]))) {
|
||||
// (a | b) | c -> a | (b | c)
|
||||
// (ab)c -> a(bc)
|
||||
rotate_node(t, dir);
|
||||
} else if (dynamic_cast<AltNode *>(t) &&
|
||||
dynamic_cast<CharSetNode *>(t->child[dir]) &&
|
||||
dynamic_cast<CharNode *>(t->child[!dir])) {
|
||||
// [a] | b -> b | [a]
|
||||
Node *c = t->child[dir];
|
||||
t->child[dir] = t->child[!dir];
|
||||
t->child[!dir] = c;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (t->child[dir])
|
||||
normalize_tree(t->child[dir], dir);
|
||||
if (t->child[!dir])
|
||||
normalize_tree(t->child[!dir], dir);
|
||||
}
|
||||
|
||||
//charset conversion is disabled for now,
|
||||
//it hinders tree optimization in some cases, so it need to be either
|
||||
//done post optimization, or have extra factoring rules added
|
||||
#if 0
|
||||
static Node *merge_charset(Node *a, Node *b)
|
||||
{
|
||||
if (dynamic_cast<CharNode *>(a) && dynamic_cast<CharNode *>(b)) {
|
||||
Chars chars;
|
||||
chars.insert(dynamic_cast<CharNode *>(a)->c);
|
||||
chars.insert(dynamic_cast<CharNode *>(b)->c);
|
||||
CharSetNode *n = new CharSetNode(chars);
|
||||
return n;
|
||||
} else if (dynamic_cast<CharNode *>(a) &&
|
||||
dynamic_cast<CharSetNode *>(b)) {
|
||||
Chars *chars = &dynamic_cast<CharSetNode *>(b)->chars;
|
||||
chars->insert(dynamic_cast<CharNode *>(a)->c);
|
||||
return b;
|
||||
} else if (dynamic_cast<CharSetNode *>(a) &&
|
||||
dynamic_cast<CharSetNode *>(b)) {
|
||||
Chars *from = &dynamic_cast<CharSetNode *>(a)->chars;
|
||||
Chars *to = &dynamic_cast<CharSetNode *>(b)->chars;
|
||||
for (Chars::iterator i = from->begin(); i != from->end(); i++)
|
||||
to->insert(*i);
|
||||
return b;
|
||||
}
|
||||
//return ???;
|
||||
}
|
||||
|
||||
static Node *alt_to_charsets(Node *t, int dir)
|
||||
{
|
||||
/*
|
||||
Node *first = NULL;
|
||||
Node *p = t;
|
||||
Node *i = t;
|
||||
for (;dynamic_cast<AltNode *>(i);) {
|
||||
if (dynamic_cast<CharNode *>(i->child[dir]) ||
|
||||
dynamic_cast<CharNodeSet *>(i->child[dir])) {
|
||||
if (!first) {
|
||||
first = i;
|
||||
p = i;
|
||||
i = i->child[!dir];
|
||||
} else {
|
||||
first->child[dir] = merge_charset(first->child[dir],
|
||||
i->child[dir]);
|
||||
p->child[!dir] = i->child[!dir];
|
||||
Node *tmp = i;
|
||||
i = tmp->child[!dir];
|
||||
tmp->child[!dir] = NULL;
|
||||
tmp->release();
|
||||
}
|
||||
} else {
|
||||
p = i;
|
||||
i = i->child[!dir];
|
||||
}
|
||||
}
|
||||
// last altnode of chain check other dir as well
|
||||
if (first && (dynamic_cast<charNode *>(i) ||
|
||||
dynamic_cast<charNodeSet *>(i))) {
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
if (dynamic_cast<CharNode *>(t->child[dir]) ||
|
||||
dynamic_cast<CharSetNode *>(t->child[dir]))
|
||||
char_test = true;
|
||||
(char_test &&
|
||||
(dynamic_cast<CharNode *>(i->child[dir]) ||
|
||||
dynamic_cast<CharSetNode *>(i->child[dir])))) {
|
||||
*/
|
||||
return t;
|
||||
}
|
||||
#endif
|
||||
|
||||
static Node *basic_alt_factor(Node *t, int dir)
|
||||
{
|
||||
if (!dynamic_cast<AltNode *>(t))
|
||||
return t;
|
||||
|
||||
if (t->child[dir]->eq(t->child[!dir])) {
|
||||
// (a | a) -> a
|
||||
Node *tmp = t->child[dir];
|
||||
t->child[dir] = NULL;
|
||||
t->release();
|
||||
return tmp;
|
||||
}
|
||||
// (ab) | (ac) -> a(b|c)
|
||||
if (dynamic_cast<CatNode *>(t->child[dir]) &&
|
||||
dynamic_cast<CatNode *>(t->child[!dir]) &&
|
||||
t->child[dir]->child[dir]->eq(t->child[!dir]->child[dir])) {
|
||||
// (ab) | (ac) -> a(b|c)
|
||||
Node *left = t->child[dir];
|
||||
Node *right = t->child[!dir];
|
||||
t->child[dir] = left->child[!dir];
|
||||
t->child[!dir] = right->child[!dir];
|
||||
right->child[!dir] = NULL;
|
||||
right->release();
|
||||
left->child[!dir] = t;
|
||||
return left;
|
||||
}
|
||||
// a | (ab) -> a (E | b) -> a (b | E)
|
||||
if (dynamic_cast<CatNode *>(t->child[!dir]) &&
|
||||
t->child[dir]->eq(t->child[!dir]->child[dir])) {
|
||||
Node *c = t->child[!dir];
|
||||
t->child[dir]->release();
|
||||
t->child[dir] = c->child[!dir];
|
||||
t->child[!dir] = &epsnode;
|
||||
c->child[!dir] = t;
|
||||
return c;
|
||||
}
|
||||
// ab | (a) -> a (b | E)
|
||||
if (dynamic_cast<CatNode *>(t->child[dir]) &&
|
||||
t->child[dir]->child[dir]->eq(t->child[!dir])) {
|
||||
Node *c = t->child[dir];
|
||||
t->child[!dir]->release();
|
||||
t->child[dir] = c->child[!dir];
|
||||
t->child[!dir] = &epsnode;
|
||||
c->child[!dir] = t;
|
||||
return c;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static Node *basic_simplify(Node *t, int dir)
|
||||
{
|
||||
if (dynamic_cast<CatNode *>(t) && &epsnode == t->child[!dir]) {
|
||||
// aE -> a
|
||||
Node *tmp = t->child[dir];
|
||||
t->child[dir] = NULL;
|
||||
t->release();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
return basic_alt_factor(t, dir);
|
||||
}
|
||||
|
||||
/*
|
||||
* assumes a normalized tree. reductions shown for left normalization
|
||||
* aE -> a
|
||||
* (a | a) -> a
|
||||
** factoring patterns
|
||||
* a | (a | b) -> (a | b)
|
||||
* a | (ab) -> a (E | b) -> a (b | E)
|
||||
* (ab) | (ac) -> a(b|c)
|
||||
*
|
||||
* returns t - if no simplifications were made
|
||||
* a new root node - if simplifications were made
|
||||
*/
|
||||
Node *simplify_tree_base(Node *t, int dir, bool &mod)
|
||||
{
|
||||
if (dynamic_cast<ImportantNode *>(t))
|
||||
return t;
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (t->child[i]) {
|
||||
Node *c = simplify_tree_base(t->child[i], dir, mod);
|
||||
if (c != t->child[i]) {
|
||||
t->child[i] = c;
|
||||
mod = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only iterate on loop if modification made
|
||||
for (;; mod = true) {
|
||||
|
||||
Node *tmp = basic_simplify(t, dir);
|
||||
if (tmp != t) {
|
||||
t = tmp;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* all tests after this must meet 2 alt node condition */
|
||||
if (!dynamic_cast<AltNode *>(t) ||
|
||||
!dynamic_cast<AltNode *>(t->child[!dir]))
|
||||
break;
|
||||
|
||||
// a | (a | b) -> (a | b)
|
||||
// a | (b | (c | a)) -> (b | (c | a))
|
||||
Node *p = t;
|
||||
Node *i = t->child[!dir];
|
||||
for (; dynamic_cast<AltNode *>(i); p = i, i = i->child[!dir]) {
|
||||
if (t->child[dir]->eq(i->child[dir])) {
|
||||
Node *tmp = t->child[!dir];
|
||||
t->child[!dir] = NULL;
|
||||
t->release();
|
||||
t = tmp;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// last altnode of chain check other dir as well
|
||||
if (t->child[dir]->eq(p->child[!dir])) {
|
||||
Node *tmp = t->child[!dir];
|
||||
t->child[!dir] = NULL;
|
||||
t->release();
|
||||
t = tmp;
|
||||
continue;
|
||||
}
|
||||
//exact match didn't work, try factoring front
|
||||
//a | (ac | (ad | () -> (a (E | c)) | (...)
|
||||
//ab | (ac | (...)) -> (a (b | c)) | (...)
|
||||
//ab | (a | (...)) -> (a (b | E)) | (...)
|
||||
Node *pp;
|
||||
int count = 0;
|
||||
Node *subject = t->child[dir];
|
||||
Node *a = subject;
|
||||
if (dynamic_cast<CatNode *>(subject))
|
||||
a = subject->child[dir];
|
||||
|
||||
for (pp = p = t, i = t->child[!dir];
|
||||
dynamic_cast<AltNode *>(i);) {
|
||||
if ((dynamic_cast<CatNode *>(i->child[dir]) &&
|
||||
a->eq(i->child[dir]->child[dir])) ||
|
||||
(a->eq(i->child[dir]))) {
|
||||
// extract matching alt node
|
||||
p->child[!dir] = i->child[!dir];
|
||||
i->child[!dir] = subject;
|
||||
subject = basic_simplify(i, dir);
|
||||
if (dynamic_cast<CatNode *>(subject))
|
||||
a = subject->child[dir];
|
||||
else
|
||||
a = subject;
|
||||
|
||||
i = p->child[!dir];
|
||||
count++;
|
||||
} else {
|
||||
pp = p;
|
||||
p = i;
|
||||
i = i->child[!dir];
|
||||
}
|
||||
}
|
||||
|
||||
// last altnode in chain check other dir as well
|
||||
if ((dynamic_cast<CatNode *>(i) &&
|
||||
a->eq(i->child[dir])) || (a->eq(i))) {
|
||||
count++;
|
||||
if (t == p) {
|
||||
t->child[dir] = subject;
|
||||
t = basic_simplify(t, dir);
|
||||
} else {
|
||||
t->child[dir] = p->child[dir];
|
||||
p->child[dir] = subject;
|
||||
pp->child[!dir] = basic_simplify(p, dir);
|
||||
}
|
||||
} else {
|
||||
t->child[dir] = i;
|
||||
p->child[!dir] = subject;
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
break;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
int debug_tree(Node *t)
|
||||
{
|
||||
int nodes = 1;
|
||||
|
||||
if (!dynamic_cast<ImportantNode *>(t)) {
|
||||
if (t->child[0])
|
||||
nodes += debug_tree(t->child[0]);
|
||||
if (t->child[1])
|
||||
nodes += debug_tree(t->child[1]);
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
static void count_tree_nodes(Node *t, struct node_counts *counts)
|
||||
{
|
||||
if (dynamic_cast<AltNode *>(t)) {
|
||||
counts->alt++;
|
||||
count_tree_nodes(t->child[0], counts);
|
||||
count_tree_nodes(t->child[1], counts);
|
||||
} else if (dynamic_cast<CatNode *>(t)) {
|
||||
counts->cat++;
|
||||
count_tree_nodes(t->child[0], counts);
|
||||
count_tree_nodes(t->child[1], counts);
|
||||
} else if (dynamic_cast<PlusNode *>(t)) {
|
||||
counts->plus++;
|
||||
count_tree_nodes(t->child[0], counts);
|
||||
} else if (dynamic_cast<StarNode *>(t)) {
|
||||
counts->star++;
|
||||
count_tree_nodes(t->child[0], counts);
|
||||
} else if (dynamic_cast<CharNode *>(t)) {
|
||||
counts->charnode++;
|
||||
} else if (dynamic_cast<AnyCharNode *>(t)) {
|
||||
counts->any++;
|
||||
} else if (dynamic_cast<CharSetNode *>(t)) {
|
||||
counts->charset++;
|
||||
} else if (dynamic_cast<NotCharSetNode *>(t)) {
|
||||
counts->notcharset++;
|
||||
}
|
||||
}
|
||||
|
||||
#include "stdio.h"
|
||||
#include "stdint.h"
|
||||
#include "apparmor_re.h"
|
||||
|
||||
Node *simplify_tree(Node *t, dfaflags_t flags)
|
||||
{
|
||||
bool update;
|
||||
|
||||
if (flags & DFA_DUMP_TREE_STATS) {
|
||||
struct node_counts counts = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
count_tree_nodes(t, &counts);
|
||||
fprintf(stderr,
|
||||
"expr tree: c %d, [] %d, [^] %d, | %d, + %d, * %d, . %d, cat %d\n",
|
||||
counts.charnode, counts.charset, counts.notcharset,
|
||||
counts.alt, counts.plus, counts.star, counts.any,
|
||||
counts.cat);
|
||||
}
|
||||
do {
|
||||
update = false;
|
||||
//default to right normalize first as this reduces the number
|
||||
//of trailing nodes which might follow an internal *
|
||||
//or **, which is where state explosion can happen
|
||||
//eg. in one test this makes the difference between
|
||||
// the dfa having about 7 thousands states,
|
||||
// and it having about 1.25 million states
|
||||
int dir = 1;
|
||||
if (flags & DFA_CONTROL_TREE_LEFT)
|
||||
dir = 0;
|
||||
for (int count = 0; count < 2; count++) {
|
||||
bool modified;
|
||||
do {
|
||||
modified = false;
|
||||
if (flags & DFA_CONTROL_TREE_NORMAL)
|
||||
normalize_tree(t, dir);
|
||||
t = simplify_tree_base(t, dir, modified);
|
||||
if (modified)
|
||||
update = true;
|
||||
} while (modified);
|
||||
if (flags & DFA_CONTROL_TREE_LEFT)
|
||||
dir++;
|
||||
else
|
||||
dir--;
|
||||
}
|
||||
} while (update);
|
||||
if (flags & DFA_DUMP_TREE_STATS) {
|
||||
struct node_counts counts = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
count_tree_nodes(t, &counts);
|
||||
fprintf(stderr,
|
||||
"simplified expr tree: c %d, [] %d, [^] %d, | %d, + %d, * %d, . %d, cat %d\n",
|
||||
counts.charnode, counts.charset, counts.notcharset,
|
||||
counts.alt, counts.plus, counts.star, counts.any,
|
||||
counts.cat);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flip the children of all cat nodes. This causes strings to be matched
|
||||
* back-forth.
|
||||
*/
|
||||
void flip_tree(Node *node)
|
||||
{
|
||||
for (depth_first_traversal i(node); i; i++) {
|
||||
if (CatNode *cat = dynamic_cast<CatNode *>(*i)) {
|
||||
swap(cat->child[0], cat->child[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dump_regex_rec(ostream &os, Node *tree)
|
||||
{
|
||||
if (tree->child[0])
|
||||
dump_regex_rec(os, tree->child[0]);
|
||||
os << *tree;
|
||||
if (tree->child[1])
|
||||
dump_regex_rec(os, tree->child[1]);
|
||||
}
|
||||
|
||||
void dump_regex(ostream &os, Node *tree)
|
||||
{
|
||||
dump_regex_rec(os, tree);
|
||||
os << endl;
|
||||
}
|
612
parser/libapparmor_re/expr-tree.h
Normal file
612
parser/libapparmor_re/expr-tree.h
Normal file
@@ -0,0 +1,612 @@
|
||||
/*
|
||||
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
|
||||
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
|
||||
* Copyright 2009-2010 Canonical Ltd.
|
||||
*
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the file
|
||||
* COPYING.LGPL.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* Functions to create/manipulate an expression tree for regular expressions
|
||||
* that have been parsed.
|
||||
*
|
||||
* The expression tree can be used directly after the parse creates it, or
|
||||
* it can be factored so that the set of important nodes is smaller.
|
||||
* Having a reduced set of important nodes generally results in a dfa that
|
||||
* is closer to minimum (fewer redundant states are created). It also
|
||||
* results in fewer important nodes in a the state set during subset
|
||||
* construction resulting in less memory used to create a dfa.
|
||||
*
|
||||
* Generally it is worth doing expression tree simplification before dfa
|
||||
* construction, if the regular expression tree contains any alternations.
|
||||
* Even if the regular expression doesn't simplification should be fast
|
||||
* enough that it can be used with minimal overhead.
|
||||
*/
|
||||
#ifndef __LIBAA_RE_EXPR_H
|
||||
#define __LIBAA_RE_EXPR_H
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <stack>
|
||||
#include <ostream>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "apparmor_re.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef unsigned char uchar;
|
||||
typedef set<uchar> Chars;
|
||||
|
||||
ostream &operator<<(ostream &os, uchar c);
|
||||
|
||||
/* Compute the union of two sets. */
|
||||
template<class T> set<T> operator+(const set<T> &a, const set<T> &b)
|
||||
{
|
||||
set<T> c(a);
|
||||
c.insert(b.begin(), b.end());
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* When creating DFAs from regex trees, a DFA state is constructed from
|
||||
* a set of important nodes in the syntax tree. This includes AcceptNodes,
|
||||
* which indicate that when a match ends in a particular state, the
|
||||
* regular expressions that the AcceptNode belongs to match.
|
||||
*/
|
||||
class Node;
|
||||
class ImportantNode;
|
||||
typedef set<ImportantNode *> NodeSet;
|
||||
|
||||
/**
|
||||
* Text-dump a state (for debugging).
|
||||
*/
|
||||
ostream &operator<<(ostream &os, const NodeSet &state);
|
||||
|
||||
/**
|
||||
* Out-edges from a state to another: we store the follow-set of Nodes
|
||||
* for each input character that is not a default match in
|
||||
* cases (i.e., following a CharNode or CharSetNode), and default
|
||||
* matches in otherwise as well as in all matching explicit cases
|
||||
* (i.e., following an AnyCharNode or NotCharSetNode). This avoids
|
||||
* enumerating all the explicit tranitions for default matches.
|
||||
*/
|
||||
typedef struct NodeCases {
|
||||
typedef map<uchar, NodeSet *>::iterator iterator;
|
||||
iterator begin() { return cases.begin(); }
|
||||
iterator end() { return cases.end(); }
|
||||
|
||||
NodeCases(): otherwise(0) { }
|
||||
map<uchar, NodeSet *> cases;
|
||||
NodeSet *otherwise;
|
||||
}
|
||||
|
||||
NodeCases;
|
||||
|
||||
ostream &operator<<(ostream &os, Node &node);
|
||||
|
||||
/* An abstract node in the syntax tree. */
|
||||
class Node {
|
||||
public:
|
||||
Node(): nullable(false) { child[0] = child[1] = 0; }
|
||||
Node(Node *left): nullable(false)
|
||||
{
|
||||
child[0] = left;
|
||||
child[1] = 0;
|
||||
}
|
||||
Node(Node *left, Node *right): nullable(false)
|
||||
{
|
||||
child[0] = left;
|
||||
child[1] = right;
|
||||
}
|
||||
virtual ~Node()
|
||||
{
|
||||
if (child[0])
|
||||
child[0]->release();
|
||||
if (child[1])
|
||||
child[1]->release();
|
||||
}
|
||||
|
||||
/**
|
||||
* See the "Dragon Book" for an explanation of nullable, firstpos,
|
||||
* lastpos, and followpos.
|
||||
*/
|
||||
virtual void compute_nullable() { }
|
||||
virtual void compute_firstpos() = 0;
|
||||
virtual void compute_lastpos() = 0;
|
||||
virtual void compute_followpos() { }
|
||||
virtual int eq(Node *other) = 0;
|
||||
virtual ostream &dump(ostream &os) = 0;
|
||||
void dump_syntax_tree(ostream &os);
|
||||
|
||||
bool nullable;
|
||||
NodeSet firstpos, lastpos, followpos;
|
||||
/* child 0 is left, child 1 is right */
|
||||
Node *child[2];
|
||||
|
||||
unsigned int label; /* unique number for debug etc */
|
||||
/**
|
||||
* We indirectly release Nodes through a virtual function because
|
||||
* accept and Eps Nodes are shared, and must be treated specially.
|
||||
* We could use full reference counting here but the indirect release
|
||||
* is sufficient and has less overhead
|
||||
*/
|
||||
virtual void release(void) { delete this; }
|
||||
};
|
||||
|
||||
class InnerNode: public Node {
|
||||
public:
|
||||
InnerNode(): Node() { };
|
||||
InnerNode(Node *left): Node(left) { };
|
||||
InnerNode(Node *left, Node *right): Node(left, right) { };
|
||||
};
|
||||
|
||||
class OneChildNode: public InnerNode {
|
||||
public:
|
||||
OneChildNode(Node *left): InnerNode(left) { };
|
||||
};
|
||||
|
||||
class TwoChildNode: public InnerNode {
|
||||
public:
|
||||
TwoChildNode(Node *left, Node *right): InnerNode(left, right) { };
|
||||
};
|
||||
|
||||
class LeafNode: public Node {
|
||||
public:
|
||||
LeafNode(): Node() { };
|
||||
};
|
||||
|
||||
/* Match nothing (//). */
|
||||
class EpsNode: public LeafNode {
|
||||
public:
|
||||
EpsNode(): LeafNode()
|
||||
{
|
||||
nullable = true;
|
||||
label = 0;
|
||||
}
|
||||
void release(void)
|
||||
{
|
||||
/* don't delete Eps nodes because there is a single static
|
||||
* instance shared by all trees. Look for epsnode in the code
|
||||
*/
|
||||
}
|
||||
|
||||
void compute_firstpos() { }
|
||||
void compute_lastpos() { }
|
||||
int eq(Node *other)
|
||||
{
|
||||
if (dynamic_cast<EpsNode *>(other))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
ostream &dump(ostream &os)
|
||||
{
|
||||
return os << "[]";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Leaf nodes in the syntax tree are important to us: they describe the
|
||||
* characters that the regular expression matches. We also consider
|
||||
* AcceptNodes import: they indicate when a regular expression matches.
|
||||
*/
|
||||
class ImportantNode: public LeafNode {
|
||||
public:
|
||||
ImportantNode(): LeafNode() { }
|
||||
void compute_firstpos() { firstpos.insert(this); }
|
||||
void compute_lastpos() { lastpos.insert(this); }
|
||||
virtual void follow(NodeCases &cases) = 0;
|
||||
};
|
||||
|
||||
/* common base class for all the different classes that contain
|
||||
* character information.
|
||||
*/
|
||||
class CNode: public ImportantNode {
|
||||
public:
|
||||
CNode(): ImportantNode() { }
|
||||
};
|
||||
|
||||
/* Match one specific character (/c/). */
|
||||
class CharNode: public CNode {
|
||||
public:
|
||||
CharNode(uchar c): c(c) { }
|
||||
void follow(NodeCases &cases)
|
||||
{
|
||||
NodeSet **x = &cases.cases[c];
|
||||
if (!*x) {
|
||||
if (cases.otherwise)
|
||||
*x = new NodeSet(*cases.otherwise);
|
||||
else
|
||||
*x = new NodeSet;
|
||||
}
|
||||
(*x)->insert(followpos.begin(), followpos.end());
|
||||
}
|
||||
int eq(Node *other)
|
||||
{
|
||||
CharNode *o = dynamic_cast<CharNode *>(other);
|
||||
if (o) {
|
||||
return c == o->c;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
ostream &dump(ostream &os)
|
||||
{
|
||||
return os << c;
|
||||
}
|
||||
|
||||
uchar c;
|
||||
};
|
||||
|
||||
/* Match a set of characters (/[abc]/). */
|
||||
class CharSetNode: public CNode {
|
||||
public:
|
||||
CharSetNode(Chars &chars): chars(chars) { }
|
||||
void follow(NodeCases &cases)
|
||||
{
|
||||
for (Chars::iterator i = chars.begin(); i != chars.end(); i++) {
|
||||
NodeSet **x = &cases.cases[*i];
|
||||
if (!*x) {
|
||||
if (cases.otherwise)
|
||||
*x = new NodeSet(*cases.otherwise);
|
||||
else
|
||||
*x = new NodeSet;
|
||||
}
|
||||
(*x)->insert(followpos.begin(), followpos.end());
|
||||
}
|
||||
}
|
||||
int eq(Node *other)
|
||||
{
|
||||
CharSetNode *o = dynamic_cast<CharSetNode *>(other);
|
||||
if (!o || chars.size() != o->chars.size())
|
||||
return 0;
|
||||
|
||||
for (Chars::iterator i = chars.begin(), j = o->chars.begin();
|
||||
i != chars.end() && j != o->chars.end(); i++, j++) {
|
||||
if (*i != *j)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
ostream &dump(ostream &os)
|
||||
{
|
||||
os << '[';
|
||||
for (Chars::iterator i = chars.begin(); i != chars.end(); i++)
|
||||
os << *i;
|
||||
return os << ']';
|
||||
}
|
||||
|
||||
Chars chars;
|
||||
};
|
||||
|
||||
/* Match all except one character (/[^abc]/). */
|
||||
class NotCharSetNode: public CNode {
|
||||
public:
|
||||
NotCharSetNode(Chars &chars): chars(chars) { }
|
||||
void follow(NodeCases & cases)
|
||||
{
|
||||
if (!cases.otherwise)
|
||||
cases.otherwise = new NodeSet;
|
||||
for (Chars::iterator j = chars.begin(); j != chars.end(); j++) {
|
||||
NodeSet **x = &cases.cases[*j];
|
||||
if (!*x)
|
||||
*x = new NodeSet(*cases.otherwise);
|
||||
}
|
||||
/* Note: Add to the nonmatching characters after copying away
|
||||
* the old otherwise state for the matching characters.
|
||||
*/
|
||||
cases.otherwise->insert(followpos.begin(), followpos.end());
|
||||
for (NodeCases::iterator i = cases.begin(); i != cases.end();
|
||||
i++) {
|
||||
if (chars.find(i->first) == chars.end())
|
||||
i->second->insert(followpos.begin(),
|
||||
followpos.end());
|
||||
}
|
||||
}
|
||||
int eq(Node *other)
|
||||
{
|
||||
NotCharSetNode *o = dynamic_cast<NotCharSetNode *>(other);
|
||||
if (!o || chars.size() != o->chars.size())
|
||||
return 0;
|
||||
|
||||
for (Chars::iterator i = chars.begin(), j = o->chars.begin();
|
||||
i != chars.end() && j != o->chars.end(); i++, j++) {
|
||||
if (*i != *j)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
ostream &dump(ostream &os)
|
||||
{
|
||||
os << "[^";
|
||||
for (Chars::iterator i = chars.begin(); i != chars.end(); i++)
|
||||
os << *i;
|
||||
return os << ']';
|
||||
}
|
||||
|
||||
Chars chars;
|
||||
};
|
||||
|
||||
/* Match any character (/./). */
|
||||
class AnyCharNode: public CNode {
|
||||
public:
|
||||
AnyCharNode() { }
|
||||
void follow(NodeCases &cases)
|
||||
{
|
||||
if (!cases.otherwise)
|
||||
cases.otherwise = new NodeSet;
|
||||
cases.otherwise->insert(followpos.begin(), followpos.end());
|
||||
for (NodeCases::iterator i = cases.begin(); i != cases.end();
|
||||
i++)
|
||||
i->second->insert(followpos.begin(), followpos.end());
|
||||
}
|
||||
int eq(Node *other)
|
||||
{
|
||||
if (dynamic_cast<AnyCharNode *>(other))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
ostream &dump(ostream &os) { return os << "."; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicate that a regular expression matches. An AcceptNode itself
|
||||
* doesn't match anything, so it will never generate any transitions.
|
||||
*/
|
||||
class AcceptNode: public ImportantNode {
|
||||
public:
|
||||
AcceptNode() { }
|
||||
void release(void)
|
||||
{
|
||||
/* don't delete AcceptNode via release as they are shared, and
|
||||
* will be deleted when the table the are stored in is deleted
|
||||
*/
|
||||
}
|
||||
|
||||
void follow(NodeCases &cases __attribute__ ((unused)))
|
||||
{
|
||||
/* Nothing to follow. */
|
||||
}
|
||||
|
||||
/* requires accept nodes to be common by pointer */
|
||||
int eq(Node *other)
|
||||
{
|
||||
if (dynamic_cast<AcceptNode *>(other))
|
||||
return (this == other);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/* Match a node zero or more times. (This is a unary operator.) */
|
||||
class StarNode: public OneChildNode {
|
||||
public:
|
||||
StarNode(Node *left): OneChildNode(left) { nullable = true; }
|
||||
void compute_firstpos() { firstpos = child[0]->firstpos; }
|
||||
void compute_lastpos() { lastpos = child[0]->lastpos; }
|
||||
void compute_followpos()
|
||||
{
|
||||
NodeSet from = child[0]->lastpos, to = child[0]->firstpos;
|
||||
for (NodeSet::iterator i = from.begin(); i != from.end(); i++) {
|
||||
(*i)->followpos.insert(to.begin(), to.end());
|
||||
}
|
||||
}
|
||||
int eq(Node *other)
|
||||
{
|
||||
if (dynamic_cast<StarNode *>(other))
|
||||
return child[0]->eq(other->child[0]);
|
||||
return 0;
|
||||
}
|
||||
ostream &dump(ostream &os)
|
||||
{
|
||||
os << '(';
|
||||
child[0]->dump(os);
|
||||
return os << ")*";
|
||||
}
|
||||
};
|
||||
|
||||
/* Match a node one or more times. (This is a unary operator.) */
|
||||
class PlusNode: public OneChildNode {
|
||||
public:
|
||||
PlusNode(Node *left): OneChildNode(left) {
|
||||
}
|
||||
void compute_nullable() { nullable = child[0]->nullable; }
|
||||
void compute_firstpos() { firstpos = child[0]->firstpos; }
|
||||
void compute_lastpos() { lastpos = child[0]->lastpos; }
|
||||
void compute_followpos()
|
||||
{
|
||||
NodeSet from = child[0]->lastpos, to = child[0]->firstpos;
|
||||
for (NodeSet::iterator i = from.begin(); i != from.end(); i++) {
|
||||
(*i)->followpos.insert(to.begin(), to.end());
|
||||
}
|
||||
}
|
||||
int eq(Node *other) {
|
||||
if (dynamic_cast<PlusNode *>(other))
|
||||
return child[0]->eq(other->child[0]);
|
||||
return 0;
|
||||
}
|
||||
ostream &dump(ostream &os) {
|
||||
os << '(';
|
||||
child[0]->dump(os);
|
||||
return os << ")+";
|
||||
}
|
||||
};
|
||||
|
||||
/* Match a pair of consecutive nodes. */
|
||||
class CatNode: public TwoChildNode {
|
||||
public:
|
||||
CatNode(Node *left, Node *right): TwoChildNode(left, right) { }
|
||||
void compute_nullable()
|
||||
{
|
||||
nullable = child[0]->nullable && child[1]->nullable;
|
||||
}
|
||||
void compute_firstpos()
|
||||
{
|
||||
if (child[0]->nullable)
|
||||
firstpos = child[0]->firstpos + child[1]->firstpos;
|
||||
else
|
||||
firstpos = child[0]->firstpos;
|
||||
}
|
||||
void compute_lastpos()
|
||||
{
|
||||
if (child[1]->nullable)
|
||||
lastpos = child[0]->lastpos + child[1]->lastpos;
|
||||
else
|
||||
lastpos = child[1]->lastpos;
|
||||
}
|
||||
void compute_followpos()
|
||||
{
|
||||
NodeSet from = child[0]->lastpos, to = child[1]->firstpos;
|
||||
for (NodeSet::iterator i = from.begin(); i != from.end(); i++) {
|
||||
(*i)->followpos.insert(to.begin(), to.end());
|
||||
}
|
||||
}
|
||||
int eq(Node *other)
|
||||
{
|
||||
if (dynamic_cast<CatNode *>(other)) {
|
||||
if (!child[0]->eq(other->child[0]))
|
||||
return 0;
|
||||
return child[1]->eq(other->child[1]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
ostream &dump(ostream &os)
|
||||
{
|
||||
child[0]->dump(os);
|
||||
child[1]->dump(os);
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
/* Match one of two alternative nodes. */
|
||||
class AltNode: public TwoChildNode {
|
||||
public:
|
||||
AltNode(Node *left, Node *right): TwoChildNode(left, right) { }
|
||||
void compute_nullable()
|
||||
{
|
||||
nullable = child[0]->nullable || child[1]->nullable;
|
||||
}
|
||||
void compute_lastpos()
|
||||
{
|
||||
lastpos = child[0]->lastpos + child[1]->lastpos;
|
||||
}
|
||||
void compute_firstpos()
|
||||
{
|
||||
firstpos = child[0]->firstpos + child[1]->firstpos;
|
||||
}
|
||||
int eq(Node *other)
|
||||
{
|
||||
if (dynamic_cast<AltNode *>(other)) {
|
||||
if (!child[0]->eq(other->child[0]))
|
||||
return 0;
|
||||
return child[1]->eq(other->child[1]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
ostream &dump(ostream &os)
|
||||
{
|
||||
os << '(';
|
||||
child[0]->dump(os);
|
||||
os << '|';
|
||||
child[1]->dump(os);
|
||||
os << ')';
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
/* Traverse the syntax tree depth-first in an iterator-like manner. */
|
||||
class depth_first_traversal {
|
||||
stack<Node *>pos;
|
||||
void push_left(Node *node) {
|
||||
pos.push(node);
|
||||
|
||||
while (dynamic_cast<InnerNode *>(node)) {
|
||||
pos.push(node->child[0]);
|
||||
node = node->child[0];
|
||||
}
|
||||
}
|
||||
public:
|
||||
depth_first_traversal(Node *node) { push_left(node); }
|
||||
Node *operator*() { return pos.top(); }
|
||||
Node *operator->() { return pos.top(); }
|
||||
operator bool() { return !pos.empty(); }
|
||||
void operator++(int)
|
||||
{
|
||||
Node *last = pos.top();
|
||||
pos.pop();
|
||||
|
||||
if (!pos.empty()) {
|
||||
/* no need to dynamic cast, as we just popped a node so
|
||||
* the top node must be an inner node */
|
||||
InnerNode *node = (InnerNode *) (pos.top());
|
||||
if (node->child[1] && node->child[1] != last) {
|
||||
push_left(node->child[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct node_counts {
|
||||
int charnode;
|
||||
int charset;
|
||||
int notcharset;
|
||||
int alt;
|
||||
int plus;
|
||||
int star;
|
||||
int any;
|
||||
int cat;
|
||||
};
|
||||
|
||||
extern EpsNode epsnode;
|
||||
|
||||
int debug_tree(Node *t);
|
||||
Node *simplify_tree(Node *t, dfaflags_t flags);
|
||||
void label_nodes(Node *root);
|
||||
unsigned long hash_NodeSet(NodeSet *ns);
|
||||
void flip_tree(Node *node);
|
||||
|
||||
/* Comparison operator for sets of <NodeSet *>.
|
||||
* Compare set hashes, and if the sets have the same hash
|
||||
* do compare pointer comparison on set of <Node *>, the pointer comparison
|
||||
* allows us to determine which Sets of <Node *> we have seen already from
|
||||
* new ones when constructing the DFA.
|
||||
*/
|
||||
struct deref_less_than {
|
||||
bool operator()(pair<unsigned long, NodeSet *>const &lhs,
|
||||
pair<unsigned long, NodeSet *>const &rhs)const
|
||||
{
|
||||
if (lhs.first == rhs.first)
|
||||
return *(lhs.second) < *(rhs.second);
|
||||
else
|
||||
return lhs.first < rhs.first;
|
||||
}
|
||||
};
|
||||
|
||||
class MatchFlag: public AcceptNode {
|
||||
public:
|
||||
MatchFlag(uint32_t flag, uint32_t audit): flag(flag), audit(audit) { }
|
||||
ostream &dump(ostream &os) { return os << '<' << flag << '>'; }
|
||||
|
||||
uint32_t flag;
|
||||
uint32_t audit;
|
||||
};
|
||||
|
||||
class ExactMatchFlag: public MatchFlag {
|
||||
public:
|
||||
ExactMatchFlag(uint32_t flag, uint32_t audit): MatchFlag(flag, audit) {}
|
||||
};
|
||||
|
||||
class DenyMatchFlag: public MatchFlag {
|
||||
public:
|
||||
DenyMatchFlag(uint32_t flag, uint32_t quiet): MatchFlag(flag, quiet) {}
|
||||
};
|
||||
|
||||
#endif /* __LIBAA_RE_EXPR */
|
869
parser/libapparmor_re/hfa.cc
Normal file
869
parser/libapparmor_re/hfa.cc
Normal file
@@ -0,0 +1,869 @@
|
||||
/*
|
||||
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
|
||||
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
|
||||
* Copyright 2009-2010 Canonical Ltd.
|
||||
*
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the file
|
||||
* COPYING.LGPL.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* Base of implementation based on the Lexical Analysis chapter of:
|
||||
* Alfred V. Aho, Ravi Sethi, Jeffrey D. Ullman:
|
||||
* Compilers: Principles, Techniques, and Tools (The "Dragon Book"),
|
||||
* Addison-Wesley, 1986.
|
||||
*/
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <ostream>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include "expr-tree.h"
|
||||
#include "hfa.h"
|
||||
#include "../immunix.h"
|
||||
|
||||
ostream &operator<<(ostream &os, const State &state)
|
||||
{
|
||||
/* dump the state label */
|
||||
os << '{';
|
||||
os << state.label;
|
||||
os << '}';
|
||||
return os;
|
||||
}
|
||||
|
||||
State *DFA::add_new_state(NodeMap &nodemap,
|
||||
pair<unsigned long, NodeSet *> index,
|
||||
NodeSet *nodes, dfa_stats_t &stats)
|
||||
{
|
||||
State *state = new State(nodemap.size(), nodes);
|
||||
states.push_back(state);
|
||||
nodemap.insert(make_pair(index, state));
|
||||
stats.proto_sum += nodes->size();
|
||||
if (nodes->size() > stats.proto_max)
|
||||
stats.proto_max = nodes->size();
|
||||
return state;
|
||||
}
|
||||
|
||||
State *DFA::find_target_state(NodeMap &nodemap, list<State *> &work_queue,
|
||||
NodeSet *nodes, dfa_stats_t &stats)
|
||||
{
|
||||
State *target;
|
||||
|
||||
pair<unsigned long, NodeSet *> index = make_pair(hash_NodeSet(nodes), nodes);
|
||||
|
||||
map<pair<unsigned long, NodeSet *>, State *, deref_less_than>::iterator x = nodemap.find(index);
|
||||
|
||||
if (x == nodemap.end()) {
|
||||
/* set of nodes isn't known so create new state, and nodes to
|
||||
* state mapping
|
||||
*/
|
||||
target = add_new_state(nodemap, index, nodes, stats);
|
||||
work_queue.push_back(target);
|
||||
} else {
|
||||
/* set of nodes already has a mapping so free this one */
|
||||
stats.duplicates++;
|
||||
delete(nodes);
|
||||
target = x->second;
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
void DFA::update_state_transitions(NodeMap &nodemap, list<State *> &work_queue,
|
||||
State *state, dfa_stats_t &stats)
|
||||
{
|
||||
/* Compute possible transitions for state->nodes. This is done by
|
||||
* iterating over all the nodes in state->nodes and combining the
|
||||
* transitions.
|
||||
*
|
||||
* The resultant transition set is a mapping of characters to
|
||||
* sets of nodes.
|
||||
*/
|
||||
NodeCases cases;
|
||||
for (NodeSet::iterator i = state->nodes->begin(); i != state->nodes->end(); i++)
|
||||
(*i)->follow(cases);
|
||||
|
||||
/* Now for each set of nodes in the computed transitions, make
|
||||
* sure that there is a state that maps to it, and add the
|
||||
* matching case to the state.
|
||||
*/
|
||||
|
||||
/* check the default transition first */
|
||||
if (cases.otherwise)
|
||||
state->cases.otherwise = find_target_state(nodemap, work_queue,
|
||||
cases.otherwise,
|
||||
stats);;
|
||||
|
||||
/* For each transition from *from, check if the set of nodes it
|
||||
* transitions to already has been mapped to a state
|
||||
*/
|
||||
for (NodeCases::iterator j = cases.begin(); j != cases.end(); j++) {
|
||||
State *target;
|
||||
target = find_target_state(nodemap, work_queue, j->second, stats);
|
||||
|
||||
/* Don't insert transition that the default transition
|
||||
* already covers
|
||||
*/
|
||||
if (target != state->cases.otherwise)
|
||||
state->cases.cases[j->first] = target;
|
||||
}
|
||||
}
|
||||
|
||||
/* WARNING: This routine can only be called from within DFA creation as
|
||||
* the nodes value is only valid during dfa construction.
|
||||
*/
|
||||
void DFA::dump_node_to_dfa(void)
|
||||
{
|
||||
cerr << "Mapping of States to expr nodes\n"
|
||||
" State <= Nodes\n"
|
||||
"-------------------\n";
|
||||
for (Partition::iterator i = states.begin(); i != states.end(); i++)
|
||||
cerr << " " << (*i)->label << " <= " << *(*i)->nodes << "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a DFA from a syntax tree.
|
||||
*/
|
||||
DFA::DFA(Node *root, dfaflags_t flags): root(root)
|
||||
{
|
||||
dfa_stats_t stats = { 0, 0, 0 };
|
||||
int i = 0;
|
||||
|
||||
if (flags & DFA_DUMP_PROGRESS)
|
||||
fprintf(stderr, "Creating dfa:\r");
|
||||
|
||||
for (depth_first_traversal i(root); i; i++) {
|
||||
(*i)->compute_nullable();
|
||||
(*i)->compute_firstpos();
|
||||
(*i)->compute_lastpos();
|
||||
}
|
||||
|
||||
if (flags & DFA_DUMP_PROGRESS)
|
||||
fprintf(stderr, "Creating dfa: followpos\r");
|
||||
for (depth_first_traversal i(root); i; i++) {
|
||||
(*i)->compute_followpos();
|
||||
}
|
||||
|
||||
NodeMap nodemap;
|
||||
NodeSet *emptynode = new NodeSet;
|
||||
nonmatching = add_new_state(nodemap,
|
||||
make_pair(hash_NodeSet(emptynode), emptynode),
|
||||
emptynode, stats);
|
||||
|
||||
NodeSet *first = new NodeSet(root->firstpos);
|
||||
start = add_new_state(nodemap, make_pair(hash_NodeSet(first), first),
|
||||
first, stats);
|
||||
|
||||
/* the work_queue contains the states that need to have their
|
||||
* transitions computed. This could be done with a recursive
|
||||
* algorithm instead of a work_queue, but it would be slightly slower
|
||||
* and consume more memory.
|
||||
*
|
||||
* TODO: currently the work_queue is treated in a breadth first
|
||||
* search manner. Test using the work_queue in a depth first
|
||||
* manner, this may help reduce the number of entries on the
|
||||
* work_queue at any given time, thus reducing peak memory use.
|
||||
*/
|
||||
list<State *> work_queue;
|
||||
work_queue.push_back(start);
|
||||
|
||||
while (!work_queue.empty()) {
|
||||
if (i % 1000 == 0 && (flags & DFA_DUMP_PROGRESS))
|
||||
fprintf(stderr, "\033[2KCreating dfa: queue %zd\tstates %zd\teliminated duplicates %d\r",
|
||||
work_queue.size(), states.size(),
|
||||
stats.duplicates);
|
||||
i++;
|
||||
|
||||
State *from = work_queue.front();
|
||||
work_queue.pop_front();
|
||||
|
||||
/* Update 'from's transitions, and if it transitions to any
|
||||
* unknown State create it and add it to the work_queue
|
||||
*/
|
||||
update_state_transitions(nodemap, work_queue, from, stats);
|
||||
|
||||
} /* while (!work_queue.empty()) */
|
||||
|
||||
/* cleanup Sets of nodes used computing the DFA as they are no longer
|
||||
* needed.
|
||||
*/
|
||||
for (depth_first_traversal i(root); i; i++) {
|
||||
(*i)->firstpos.clear();
|
||||
(*i)->lastpos.clear();
|
||||
(*i)->followpos.clear();
|
||||
}
|
||||
|
||||
if (flags & DFA_DUMP_NODE_TO_DFA)
|
||||
dump_node_to_dfa();
|
||||
|
||||
for (NodeMap::iterator i = nodemap.begin(); i != nodemap.end(); i++)
|
||||
delete i->first.second;
|
||||
nodemap.clear();
|
||||
|
||||
if (flags & (DFA_DUMP_STATS))
|
||||
fprintf(stderr, "\033[2KCreated dfa: states %zd,\teliminated duplicates %d,\tprotostate sets: longest %u, avg %u\n",
|
||||
states.size(), stats.duplicates, stats.proto_max,
|
||||
(unsigned int)(stats.proto_sum / states.size()));
|
||||
|
||||
}
|
||||
|
||||
DFA::~DFA()
|
||||
{
|
||||
for (Partition::iterator i = states.begin(); i != states.end(); i++)
|
||||
delete *i;
|
||||
}
|
||||
|
||||
void DFA::dump_uniq_perms(const char *s)
|
||||
{
|
||||
set<pair<uint32_t, uint32_t> > uniq;
|
||||
for (Partition::iterator i = states.begin(); i != states.end(); i++)
|
||||
uniq.insert(make_pair((*i)->accept, (*i)->audit));
|
||||
|
||||
cerr << "Unique Permission sets: " << s << " (" << uniq.size() << ")\n";
|
||||
cerr << "----------------------\n";
|
||||
for (set<pair<uint32_t, uint32_t> >::iterator i = uniq.begin();
|
||||
i != uniq.end(); i++) {
|
||||
cerr << " " << hex << i->first << " " << i->second << dec << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove dead or unreachable states */
|
||||
void DFA::remove_unreachable(dfaflags_t flags)
|
||||
{
|
||||
set<State *> reachable;
|
||||
list<State *> work_queue;
|
||||
|
||||
/* find the set of reachable states */
|
||||
reachable.insert(nonmatching);
|
||||
work_queue.push_back(start);
|
||||
while (!work_queue.empty()) {
|
||||
State *from = work_queue.front();
|
||||
work_queue.pop_front();
|
||||
reachable.insert(from);
|
||||
|
||||
if (from->cases.otherwise &&
|
||||
(reachable.find(from->cases.otherwise) == reachable.end()))
|
||||
work_queue.push_back(from->cases.otherwise);
|
||||
|
||||
for (Cases::iterator j = from->cases.begin(); j != from->cases.end(); j++) {
|
||||
if (reachable.find(j->second) == reachable.end())
|
||||
work_queue.push_back(j->second);
|
||||
}
|
||||
}
|
||||
|
||||
/* walk the set of states and remove any that aren't reachable */
|
||||
if (reachable.size() < states.size()) {
|
||||
int count = 0;
|
||||
Partition::iterator i;
|
||||
Partition::iterator next;
|
||||
for (i = states.begin(); i != states.end(); i = next) {
|
||||
next = i;
|
||||
next++;
|
||||
if (reachable.find(*i) == reachable.end()) {
|
||||
if (flags & DFA_DUMP_UNREACHABLE) {
|
||||
cerr << "unreachable: " << **i;
|
||||
if (*i == start)
|
||||
cerr << " <==";
|
||||
if ((*i)->accept) {
|
||||
cerr << " (0x" << hex
|
||||
<< (*i)->accept << " "
|
||||
<< (*i)->audit << dec
|
||||
<< ')';
|
||||
}
|
||||
cerr << "\n";
|
||||
}
|
||||
State *current = *i;
|
||||
states.erase(i);
|
||||
delete(current);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count && (flags & DFA_DUMP_STATS))
|
||||
cerr << "DFA: states " << states.size() << " removed "
|
||||
<< count << " unreachable states\n";
|
||||
}
|
||||
}
|
||||
|
||||
/* test if two states have the same transitions under partition_map */
|
||||
bool DFA::same_mappings(State *s1, State *s2)
|
||||
{
|
||||
if (s1->cases.otherwise && s1->cases.otherwise != nonmatching) {
|
||||
if (!s2->cases.otherwise || s2->cases.otherwise == nonmatching)
|
||||
return false;
|
||||
Partition *p1 = s1->cases.otherwise->partition;
|
||||
Partition *p2 = s2->cases.otherwise->partition;
|
||||
if (p1 != p2)
|
||||
return false;
|
||||
} else if (s2->cases.otherwise && s2->cases.otherwise != nonmatching) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s1->cases.cases.size() != s2->cases.cases.size())
|
||||
return false;
|
||||
for (Cases::iterator j1 = s1->cases.begin(); j1 != s1->cases.end(); j1++) {
|
||||
Cases::iterator j2 = s2->cases.cases.find(j1->first);
|
||||
if (j2 == s2->cases.end())
|
||||
return false;
|
||||
Partition *p1 = j1->second->partition;
|
||||
Partition *p2 = j2->second->partition;
|
||||
if (p1 != p2)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Do simple djb2 hashing against a States transition cases
|
||||
* this provides a rough initial guess at state equivalence as if a state
|
||||
* has a different number of transitions or has transitions on different
|
||||
* cases they will never be equivalent.
|
||||
* Note: this only hashes based off of the alphabet (not destination)
|
||||
* as different destinations could end up being equiv
|
||||
*/
|
||||
size_t DFA::hash_trans(State *s)
|
||||
{
|
||||
unsigned long hash = 5381;
|
||||
|
||||
for (Cases::iterator j = s->cases.begin(); j != s->cases.end(); j++) {
|
||||
hash = ((hash << 5) + hash) + j->first;
|
||||
State *k = j->second;
|
||||
hash = ((hash << 5) + hash) + k->cases.cases.size();
|
||||
}
|
||||
|
||||
if (s->cases.otherwise && s->cases.otherwise != nonmatching) {
|
||||
hash = ((hash << 5) + hash) + 5381;
|
||||
State *k = s->cases.otherwise;
|
||||
hash = ((hash << 5) + hash) + k->cases.cases.size();
|
||||
}
|
||||
|
||||
hash = (hash << 8) | s->cases.cases.size();
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* minimize the number of dfa states */
|
||||
void DFA::minimize(dfaflags_t flags)
|
||||
{
|
||||
map<pair<uint64_t, size_t>, Partition *> perm_map;
|
||||
list<Partition *> partitions;
|
||||
|
||||
/* Set up the initial partitions
|
||||
* minimium of - 1 non accepting, and 1 accepting
|
||||
* if trans hashing is used the accepting and non-accepting partitions
|
||||
* can be further split based on the number and type of transitions
|
||||
* a state makes.
|
||||
* If permission hashing is enabled the accepting partitions can
|
||||
* be further divided by permissions. This can result in not
|
||||
* obtaining a truely minimized dfa but comes close, and can speedup
|
||||
* minimization.
|
||||
*/
|
||||
int accept_count = 0;
|
||||
int final_accept = 0;
|
||||
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
|
||||
uint64_t perm_hash = 0;
|
||||
if (flags & DFA_CONTROL_MINIMIZE_HASH_PERMS) {
|
||||
/* make every unique perm create a new partition */
|
||||
perm_hash = ((uint64_t) (*i)->audit) << 32 |
|
||||
(uint64_t) (*i)->accept;
|
||||
} else if ((*i)->audit || (*i)->accept) {
|
||||
/* combine all perms together into a single parition */
|
||||
perm_hash = 1;
|
||||
} /* else not an accept state so 0 for perm_hash */
|
||||
size_t trans_hash = 0;
|
||||
if (flags & DFA_CONTROL_MINIMIZE_HASH_TRANS)
|
||||
trans_hash = hash_trans(*i);
|
||||
pair<uint64_t, size_t> group = make_pair(perm_hash, trans_hash);
|
||||
map<pair<uint64_t, size_t>, Partition *>::iterator p = perm_map.find(group);
|
||||
if (p == perm_map.end()) {
|
||||
Partition *part = new Partition();
|
||||
part->push_back(*i);
|
||||
perm_map.insert(make_pair(group, part));
|
||||
partitions.push_back(part);
|
||||
(*i)->partition = part;
|
||||
if (perm_hash)
|
||||
accept_count++;
|
||||
} else {
|
||||
(*i)->partition = p->second;
|
||||
p->second->push_back(*i);
|
||||
}
|
||||
|
||||
if ((flags & DFA_DUMP_PROGRESS) && (partitions.size() % 1000 == 0))
|
||||
cerr << "\033[2KMinimize dfa: partitions "
|
||||
<< partitions.size() << "\tinit " << partitions.size()
|
||||
<< " (accept " << accept_count << ")\r";
|
||||
}
|
||||
|
||||
/* perm_map is no longer needed so free the memory it is using.
|
||||
* Don't remove - doing it manually here helps reduce peak memory usage.
|
||||
*/
|
||||
perm_map.clear();
|
||||
|
||||
int init_count = partitions.size();
|
||||
if (flags & DFA_DUMP_PROGRESS)
|
||||
cerr << "\033[2KMinimize dfa: partitions " << partitions.size()
|
||||
<< "\tinit " << init_count << " (accept "
|
||||
<< accept_count << ")\r";
|
||||
|
||||
/* Now do repartitioning until each partition contains the set of
|
||||
* states that are the same. This will happen when the partition
|
||||
* splitting stables. With a worse case of 1 state per partition
|
||||
* ie. already minimized.
|
||||
*/
|
||||
Partition *new_part;
|
||||
int new_part_count;
|
||||
do {
|
||||
new_part_count = 0;
|
||||
for (list<Partition *>::iterator p = partitions.begin();
|
||||
p != partitions.end(); p++) {
|
||||
new_part = NULL;
|
||||
State *rep = *((*p)->begin());
|
||||
Partition::iterator next;
|
||||
for (Partition::iterator s = ++(*p)->begin(); s != (*p)->end();) {
|
||||
if (same_mappings(rep, *s)) {
|
||||
++s;
|
||||
continue;
|
||||
}
|
||||
if (!new_part) {
|
||||
new_part = new Partition;
|
||||
list<Partition *>::iterator tmp = p;
|
||||
partitions.insert(++tmp, new_part);
|
||||
new_part_count++;
|
||||
}
|
||||
new_part->push_back(*s);
|
||||
s = (*p)->erase(s);
|
||||
}
|
||||
/* remapping partition_map for new_part entries
|
||||
* Do not do this above as it messes up same_mappings
|
||||
*/
|
||||
if (new_part) {
|
||||
for (Partition::iterator m = new_part->begin();
|
||||
m != new_part->end(); m++) {
|
||||
(*m)->partition = new_part;
|
||||
}
|
||||
}
|
||||
if ((flags & DFA_DUMP_PROGRESS) && (partitions.size() % 100 == 0))
|
||||
cerr << "\033[2KMinimize dfa: partitions "
|
||||
<< partitions.size() << "\tinit "
|
||||
<< init_count << " (accept "
|
||||
<< accept_count << ")\r";
|
||||
}
|
||||
} while (new_part_count);
|
||||
|
||||
if (partitions.size() == states.size()) {
|
||||
if (flags & DFA_DUMP_STATS)
|
||||
cerr << "\033[2KDfa minimization no states removed: partitions "
|
||||
<< partitions.size() << "\tinit " << init_count
|
||||
<< " (accept " << accept_count << ")\n";
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Remap the dfa so it uses the representative states
|
||||
* Use the first state of a partition as the representative state
|
||||
* At this point all states with in a partion have transitions
|
||||
* to states within the same partitions, however this can slow
|
||||
* down compressed dfa compression as there are more states,
|
||||
*/
|
||||
if (flags & DFA_DUMP_MIN_PARTS)
|
||||
cerr << "Partitions after minimization\n";
|
||||
for (list<Partition *>::iterator p = partitions.begin();
|
||||
p != partitions.end(); p++) {
|
||||
/* representative state for this partition */
|
||||
State *rep = *((*p)->begin());
|
||||
if (flags & DFA_DUMP_MIN_PARTS)
|
||||
cerr << *rep << " : ";
|
||||
|
||||
/* update representative state's transitions */
|
||||
if (rep->cases.otherwise) {
|
||||
Partition *partition = rep->cases.otherwise->partition;
|
||||
rep->cases.otherwise = *partition->begin();
|
||||
}
|
||||
for (Cases::iterator c = rep->cases.begin(); c != rep->cases.end(); c++) {
|
||||
Partition *partition = c->second->partition;
|
||||
c->second = *partition->begin();
|
||||
}
|
||||
|
||||
//if ((*p)->size() > 1)
|
||||
//cerr << rep->label << ": ";
|
||||
/* clear the state label for all non representative states,
|
||||
* and accumulate permissions */
|
||||
for (Partition::iterator i = ++(*p)->begin(); i != (*p)->end(); i++) {
|
||||
//cerr << " " << (*i)->label;
|
||||
if (flags & DFA_DUMP_MIN_PARTS)
|
||||
cerr << **i << ", ";
|
||||
(*i)->label = -1;
|
||||
rep->accept |= (*i)->accept;
|
||||
rep->audit |= (*i)->audit;
|
||||
}
|
||||
if (rep->accept || rep->audit)
|
||||
final_accept++;
|
||||
//if ((*p)->size() > 1)
|
||||
//cerr << "\n";
|
||||
if (flags & DFA_DUMP_MIN_PARTS)
|
||||
cerr << "\n";
|
||||
}
|
||||
if (flags & DFA_DUMP_STATS)
|
||||
cerr << "\033[2KMinimized dfa: final partitions "
|
||||
<< partitions.size() << " (accept " << final_accept
|
||||
<< ")" << "\tinit " << init_count << " (accept "
|
||||
<< accept_count << ")\n";
|
||||
|
||||
/* make sure nonmatching and start state are up to date with the
|
||||
* mappings */
|
||||
{
|
||||
Partition *partition = nonmatching->partition;
|
||||
if (*partition->begin() != nonmatching) {
|
||||
nonmatching = *partition->begin();
|
||||
}
|
||||
|
||||
partition = start->partition;
|
||||
if (*partition->begin() != start) {
|
||||
start = *partition->begin();
|
||||
}
|
||||
}
|
||||
|
||||
/* Now that the states have been remapped, remove all states
|
||||
* that are not the representive states for their partition, they
|
||||
* will have a label == -1
|
||||
*/
|
||||
for (Partition::iterator i = states.begin(); i != states.end();) {
|
||||
if ((*i)->label == -1) {
|
||||
State *s = *i;
|
||||
i = states.erase(i);
|
||||
delete(s);
|
||||
} else
|
||||
i++;
|
||||
}
|
||||
|
||||
out:
|
||||
/* Cleanup */
|
||||
while (!partitions.empty()) {
|
||||
Partition *p = partitions.front();
|
||||
partitions.pop_front();
|
||||
delete(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* text-dump the DFA (for debugging).
|
||||
*/
|
||||
void DFA::dump(ostream & os)
|
||||
{
|
||||
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
|
||||
if (*i == start || (*i)->accept) {
|
||||
os << **i;
|
||||
if (*i == start)
|
||||
os << " <==";
|
||||
if ((*i)->accept) {
|
||||
os << " (0x" << hex << (*i)->accept << " "
|
||||
<< (*i)->audit << dec << ')';
|
||||
}
|
||||
os << "\n";
|
||||
}
|
||||
}
|
||||
os << "\n";
|
||||
|
||||
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
|
||||
if ((*i)->cases.otherwise)
|
||||
os << **i << " -> " << (*i)->cases.otherwise << "\n";
|
||||
for (Cases::iterator j = (*i)->cases.begin();
|
||||
j != (*i)->cases.end(); j++) {
|
||||
os << **i << " -> " << j->second << ": "
|
||||
<< j->first << "\n";
|
||||
}
|
||||
}
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a dot (graphviz) graph from the DFA (for debugging).
|
||||
*/
|
||||
void DFA::dump_dot_graph(ostream & os)
|
||||
{
|
||||
os << "digraph \"dfa\" {" << "\n";
|
||||
|
||||
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
|
||||
if (*i == nonmatching)
|
||||
continue;
|
||||
|
||||
os << "\t\"" << **i << "\" [" << "\n";
|
||||
if (*i == start) {
|
||||
os << "\t\tstyle=bold" << "\n";
|
||||
}
|
||||
uint32_t perms = (*i)->accept;
|
||||
if (perms) {
|
||||
os << "\t\tlabel=\"" << **i << "\\n("
|
||||
<< perms << ")\"" << "\n";
|
||||
}
|
||||
os << "\t]" << "\n";
|
||||
}
|
||||
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
|
||||
Cases &cases = (*i)->cases;
|
||||
Chars excluded;
|
||||
|
||||
for (Cases::iterator j = cases.begin(); j != cases.end(); j++) {
|
||||
if (j->second == nonmatching)
|
||||
excluded.insert(j->first);
|
||||
else {
|
||||
os << "\t\"" << **i << "\" -> \"" << *j->second
|
||||
<< "\" [" << "\n";
|
||||
os << "\t\tlabel=\"" << j->first << "\"\n";
|
||||
os << "\t]" << "\n";
|
||||
}
|
||||
}
|
||||
if (cases.otherwise && cases.otherwise != nonmatching) {
|
||||
os << "\t\"" << **i << "\" -> \"" << *cases.otherwise
|
||||
<< "\" [" << "\n";
|
||||
if (!excluded.empty()) {
|
||||
os << "\t\tlabel=\"[^";
|
||||
for (Chars::iterator i = excluded.begin();
|
||||
i != excluded.end(); i++) {
|
||||
os << *i;
|
||||
}
|
||||
os << "]\"" << "\n";
|
||||
}
|
||||
os << "\t]" << "\n";
|
||||
}
|
||||
}
|
||||
os << '}' << "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute character equivalence classes in the DFA to save space in the
|
||||
* transition table.
|
||||
*/
|
||||
map<uchar, uchar> DFA::equivalence_classes(dfaflags_t flags)
|
||||
{
|
||||
map<uchar, uchar> classes;
|
||||
uchar next_class = 1;
|
||||
|
||||
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
|
||||
Cases & cases = (*i)->cases;
|
||||
|
||||
/* Group edges to the same next state together */
|
||||
map<const State *, Chars> node_sets;
|
||||
for (Cases::iterator j = cases.begin(); j != cases.end(); j++)
|
||||
node_sets[j->second].insert(j->first);
|
||||
|
||||
for (map<const State *, Chars>::iterator j = node_sets.begin();
|
||||
j != node_sets.end(); j++) {
|
||||
/* Group edges to the same next state together by class */
|
||||
map<uchar, Chars> node_classes;
|
||||
bool class_used = false;
|
||||
for (Chars::iterator k = j->second.begin();
|
||||
k != j->second.end(); k++) {
|
||||
pair<map<uchar, uchar>::iterator, bool> x = classes.insert(make_pair(*k, next_class));
|
||||
if (x.second)
|
||||
class_used = true;
|
||||
pair<map<uchar, Chars>::iterator, bool> y = node_classes.insert(make_pair(x.first->second, Chars()));
|
||||
y.first->second.insert(*k);
|
||||
}
|
||||
if (class_used) {
|
||||
next_class++;
|
||||
class_used = false;
|
||||
}
|
||||
for (map<uchar, Chars>::iterator k = node_classes.begin();
|
||||
k != node_classes.end(); k++) {
|
||||
/**
|
||||
* If any other characters are in the same class, move
|
||||
* the characters in this class into their own new
|
||||
* class
|
||||
*/
|
||||
map<uchar, uchar>::iterator l;
|
||||
for (l = classes.begin(); l != classes.end(); l++) {
|
||||
if (l->second == k->first &&
|
||||
k->second.find(l->first) == k->second.end()) {
|
||||
class_used = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (class_used) {
|
||||
for (Chars::iterator l = k->second.begin();
|
||||
l != k->second.end(); l++) {
|
||||
classes[*l] = next_class;
|
||||
}
|
||||
next_class++;
|
||||
class_used = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & DFA_DUMP_EQUIV_STATS)
|
||||
fprintf(stderr, "Equiv class reduces to %d classes\n",
|
||||
next_class - 1);
|
||||
return classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Text-dump the equivalence classes (for debugging).
|
||||
*/
|
||||
void dump_equivalence_classes(ostream &os, map<uchar, uchar> &eq)
|
||||
{
|
||||
map<uchar, Chars> rev;
|
||||
|
||||
for (map<uchar, uchar>::iterator i = eq.begin(); i != eq.end(); i++) {
|
||||
Chars &chars = rev.insert(make_pair(i->second, Chars())).first->second;
|
||||
chars.insert(i->first);
|
||||
}
|
||||
os << "(eq):" << "\n";
|
||||
for (map<uchar, Chars>::iterator i = rev.begin(); i != rev.end(); i++) {
|
||||
os << (int)i->first << ':';
|
||||
Chars &chars = i->second;
|
||||
for (Chars::iterator j = chars.begin(); j != chars.end(); j++) {
|
||||
os << ' ' << *j;
|
||||
}
|
||||
os << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace characters with classes (which are also represented as
|
||||
* characters) in the DFA transition table.
|
||||
*/
|
||||
void DFA::apply_equivalence_classes(map<uchar, uchar> &eq)
|
||||
{
|
||||
/**
|
||||
* Note: We only transform the transition table; the nodes continue to
|
||||
* contain the original characters.
|
||||
*/
|
||||
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
|
||||
map<uchar, State *> tmp;
|
||||
tmp.swap((*i)->cases.cases);
|
||||
for (Cases::iterator j = tmp.begin(); j != tmp.end(); j++)
|
||||
(*i)->cases.cases.
|
||||
insert(make_pair(eq[j->first], j->second));
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
typedef set <ImportantNode *>AcceptNodes;
|
||||
map<ImportantNode *, AcceptNodes> dominance(DFA & dfa)
|
||||
{
|
||||
map<ImportantNode *, AcceptNodes> is_dominated;
|
||||
|
||||
for (States::iterator i = dfa.states.begin(); i != dfa.states.end(); i++) {
|
||||
AcceptNodes set1;
|
||||
for (State::iterator j = (*i)->begin(); j != (*i)->end(); j++) {
|
||||
if (AcceptNode * accept = dynamic_cast<AcceptNode *>(*j))
|
||||
set1.insert(accept);
|
||||
}
|
||||
for (AcceptNodes::iterator j = set1.begin(); j != set1.end(); j++) {
|
||||
pair<map<ImportantNode *, AcceptNodes>::iterator, bool> x = is_dominated.insert(make_pair(*j, set1));
|
||||
if (!x.second) {
|
||||
AcceptNodes & set2(x.first->second), set3;
|
||||
for (AcceptNodes::iterator l = set2.begin();
|
||||
l != set2.end(); l++) {
|
||||
if (set1.find(*l) != set1.end())
|
||||
set3.insert(*l);
|
||||
}
|
||||
set3.swap(set2);
|
||||
}
|
||||
}
|
||||
}
|
||||
return is_dominated;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int diff_qualifiers(uint32_t perm1, uint32_t perm2)
|
||||
{
|
||||
return ((perm1 & AA_EXEC_TYPE) && (perm2 & AA_EXEC_TYPE) &&
|
||||
(perm1 & AA_EXEC_TYPE) != (perm2 & AA_EXEC_TYPE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the permission flags that this state corresponds to. If we
|
||||
* have any exact matches, then they override the execute and safe
|
||||
* execute flags.
|
||||
*/
|
||||
uint32_t accept_perms(NodeSet *state, uint32_t *audit_ctl, int *error)
|
||||
{
|
||||
uint32_t perms = 0, exact_match_perms = 0;
|
||||
uint32_t audit = 0, exact_audit = 0, quiet = 0, deny = 0;
|
||||
|
||||
if (error)
|
||||
*error = 0;
|
||||
for (NodeSet::iterator i = state->begin(); i != state->end(); i++) {
|
||||
MatchFlag *match;
|
||||
if (!(match = dynamic_cast<MatchFlag *>(*i)))
|
||||
continue;
|
||||
if (dynamic_cast<ExactMatchFlag *>(match)) {
|
||||
/* exact match only ever happens with x */
|
||||
if (!is_merged_x_consistent(exact_match_perms,
|
||||
match->flag) && error)
|
||||
*error = 1;;
|
||||
exact_match_perms |= match->flag;
|
||||
exact_audit |= match->audit;
|
||||
} else if (dynamic_cast<DenyMatchFlag *>(match)) {
|
||||
deny |= match->flag;
|
||||
quiet |= match->audit;
|
||||
} else {
|
||||
if (!is_merged_x_consistent(perms, match->flag)
|
||||
&& error)
|
||||
*error = 1;
|
||||
perms |= match->flag;
|
||||
audit |= match->audit;
|
||||
}
|
||||
}
|
||||
|
||||
//if (audit || quiet)
|
||||
//fprintf(stderr, "perms: 0x%x, audit: 0x%x exact: 0x%x eaud: 0x%x deny: 0x%x quiet: 0x%x\n", perms, audit, exact_match_perms, exact_audit, deny, quiet);
|
||||
|
||||
perms |= exact_match_perms & ~(AA_USER_EXEC_TYPE | AA_OTHER_EXEC_TYPE);
|
||||
|
||||
if (exact_match_perms & AA_USER_EXEC_TYPE) {
|
||||
perms = (exact_match_perms & AA_USER_EXEC_TYPE) |
|
||||
(perms & ~AA_USER_EXEC_TYPE);
|
||||
audit = (exact_audit & AA_USER_EXEC_TYPE) |
|
||||
(audit & ~AA_USER_EXEC_TYPE);
|
||||
}
|
||||
if (exact_match_perms & AA_OTHER_EXEC_TYPE) {
|
||||
perms = (exact_match_perms & AA_OTHER_EXEC_TYPE) |
|
||||
(perms & ~AA_OTHER_EXEC_TYPE);
|
||||
audit = (exact_audit & AA_OTHER_EXEC_TYPE) |
|
||||
(audit & ~AA_OTHER_EXEC_TYPE);
|
||||
}
|
||||
if (perms & AA_USER_EXEC & deny)
|
||||
perms &= ~AA_USER_EXEC_TYPE;
|
||||
|
||||
if (perms & AA_OTHER_EXEC & deny)
|
||||
perms &= ~AA_OTHER_EXEC_TYPE;
|
||||
|
||||
perms &= ~deny;
|
||||
|
||||
if (audit_ctl)
|
||||
*audit_ctl = PACK_AUDIT_CTL(audit, quiet & deny);
|
||||
|
||||
// if (perms & AA_ERROR_BIT) {
|
||||
// fprintf(stderr, "error bit 0x%x\n", perms);
|
||||
// exit(255);
|
||||
//}
|
||||
|
||||
//if (perms & AA_EXEC_BITS)
|
||||
//fprintf(stderr, "accept perm: 0x%x\n", perms);
|
||||
/*
|
||||
if (perms & ~AA_VALID_PERMS)
|
||||
yyerror(_("Internal error accumulated invalid perm 0x%llx\n"), perms);
|
||||
*/
|
||||
|
||||
//if (perms & AA_CHANGE_HAT)
|
||||
// fprintf(stderr, "change_hat 0x%x\n", perms);
|
||||
|
||||
if (*error)
|
||||
fprintf(stderr, "profile has merged rule with conflicting x modifiers\n");
|
||||
|
||||
return perms;
|
||||
}
|
143
parser/libapparmor_re/hfa.h
Normal file
143
parser/libapparmor_re/hfa.h
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
|
||||
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
|
||||
* Copyright 2009-2010 Canonical Ltd.
|
||||
*
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the file
|
||||
* COPYING.LGPL.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* Base of implementation based on the Lexical Analysis chapter of:
|
||||
* Alfred V. Aho, Ravi Sethi, Jeffrey D. Ullman:
|
||||
* Compilers: Principles, Techniques, and Tools (The "Dragon Book"),
|
||||
* Addison-Wesley, 1986.
|
||||
*/
|
||||
#ifndef __LIBAA_RE_HFA_H
|
||||
#define __LIBAA_RE_HFA_H
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "expr-tree.h"
|
||||
|
||||
class State;
|
||||
/**
|
||||
* State cases are identical to NodesCases except they map to State *
|
||||
* instead of NodeSet.
|
||||
* Out-edges from a state to another: we store the follow State
|
||||
* for each input character that is not a default match in cases and
|
||||
* default matches in otherwise as well as in all matching explicit cases
|
||||
* This avoids enumerating all the explicit tranitions for default matches.
|
||||
*/
|
||||
typedef struct Cases {
|
||||
typedef map<uchar, State *>::iterator iterator;
|
||||
iterator begin() { return cases.begin(); }
|
||||
iterator end() { return cases.end(); }
|
||||
|
||||
Cases(): otherwise(0) { }
|
||||
|
||||
map<uchar, State *> cases;
|
||||
State *otherwise;
|
||||
} Cases;
|
||||
|
||||
typedef list<State *> Partition;
|
||||
|
||||
uint32_t accept_perms(NodeSet *state, uint32_t *audit_ctl, int *error);
|
||||
|
||||
/*
|
||||
* State - DFA individual state information
|
||||
* label: a unique label to identify the state used for pretty printing
|
||||
* the non-matching state is setup to have label == 0 and
|
||||
* the start state is setup to have label == 1
|
||||
* audit: the audit permission mask for the state
|
||||
* accept: the accept permissions for the state
|
||||
* cases: set of transitions from this state
|
||||
* parition: Is a temporary work variable used during dfa minimization.
|
||||
* it can be replaced with a map, but that is slower and uses more
|
||||
* memory.
|
||||
* nodes: Is a temporary work variable used during dfa creation. It can
|
||||
* be replaced by using the nodemap, but that is slower
|
||||
*/
|
||||
class State {
|
||||
public:
|
||||
State(): label(0), audit(0), accept(0), cases(), nodes(NULL) { };
|
||||
State(int l): label(l), audit(0), accept(0), cases(), nodes(NULL) { };
|
||||
State(int l, NodeSet * n) throw(int):
|
||||
label(l), audit(0), accept(0), cases(), nodes(n)
|
||||
{
|
||||
int error;
|
||||
|
||||
/* Compute permissions associated with the State. */
|
||||
accept = accept_perms(nodes, &audit, &error);
|
||||
if (error) {
|
||||
//cerr << "Failing on accept perms " << error << "\n";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
int label;
|
||||
uint32_t audit, accept;
|
||||
Cases cases;
|
||||
union {
|
||||
Partition *partition;
|
||||
NodeSet *nodes;
|
||||
};
|
||||
};
|
||||
|
||||
ostream &operator<<(ostream &os, const State &state);
|
||||
|
||||
typedef map<pair<unsigned long, NodeSet *>, State *, deref_less_than> NodeMap;
|
||||
/* Transitions in the DFA. */
|
||||
|
||||
/* dfa_stats - structure to group various stats about dfa creation
|
||||
* duplicates - how many duplicate NodeSets where encountered and discarded
|
||||
* proto_max - maximum length of a NodeSet encountered during dfa construction
|
||||
* proto_sum - sum of NodeSet length during dfa construction. Used to find
|
||||
* average length.
|
||||
*/
|
||||
typedef struct dfa_stats {
|
||||
unsigned int duplicates, proto_max, proto_sum;
|
||||
} dfa_stats_t;
|
||||
|
||||
class DFA {
|
||||
void dump_node_to_dfa(void);
|
||||
State *add_new_state(NodeMap &nodemap,
|
||||
pair<unsigned long, NodeSet *> index,
|
||||
NodeSet *nodes, dfa_stats_t &stats);
|
||||
void update_state_transitions(NodeMap &nodemap,
|
||||
list<State *> &work_queue,
|
||||
State *state, dfa_stats_t &stats);
|
||||
State *find_target_state(NodeMap &nodemap, list<State *> &work_queue,
|
||||
NodeSet *nodes, dfa_stats_t &stats);
|
||||
public:
|
||||
DFA(Node *root, dfaflags_t flags);
|
||||
virtual ~DFA();
|
||||
void remove_unreachable(dfaflags_t flags);
|
||||
bool same_mappings(State *s1, State *s2);
|
||||
size_t hash_trans(State *s);
|
||||
void minimize(dfaflags_t flags);
|
||||
void dump(ostream &os);
|
||||
void dump_dot_graph(ostream &os);
|
||||
void dump_uniq_perms(const char *s);
|
||||
map<uchar, uchar> equivalence_classes(dfaflags_t flags);
|
||||
void apply_equivalence_classes(map<uchar, uchar> &eq);
|
||||
Node *root;
|
||||
State *nonmatching, *start;
|
||||
Partition states;
|
||||
};
|
||||
|
||||
void dump_equivalence_classes(ostream &os, map<uchar, uchar> &eq);
|
||||
|
||||
#endif /* __LIBAA_RE_HFA_H */
|
27
parser/libapparmor_re/parse.h
Normal file
27
parser/libapparmor_re/parse.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
|
||||
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
|
||||
* Copyright 2009-2010 Canonical Ltd.
|
||||
*
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the file
|
||||
* COPYING.LGPL.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* Parsing of regular expression into expression trees as implemented in
|
||||
* expr-tree
|
||||
*/
|
||||
#ifndef __LIBAA_RE_PARSE_H
|
||||
#define __LIBAA_RE_PARSE_H
|
||||
|
||||
int regex_parse(Node **tree, const char *rule);
|
||||
|
||||
#endif /* __LIBAA_RE_PARSE_H */
|
262
parser/libapparmor_re/parse.y
Normal file
262
parser/libapparmor_re/parse.y
Normal file
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
|
||||
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
|
||||
* Copyright 2009-2010 Canonical Ltd.
|
||||
*
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the file
|
||||
* COPYING.LGPL.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* Parsing of regular expression into expression trees as implemented in
|
||||
* expr-tree
|
||||
*/
|
||||
|
||||
%{
|
||||
/* #define DEBUG_TREE */
|
||||
#include "expr-tree.h"
|
||||
|
||||
%}
|
||||
|
||||
%union {
|
||||
char c;
|
||||
Node *node;
|
||||
Chars *cset;
|
||||
}
|
||||
|
||||
%{
|
||||
|
||||
void regex_error(Node **, const char *, const char *);
|
||||
#define YYLEX_PARAM &text
|
||||
int regex_lex(YYSTYPE *, const char **);
|
||||
|
||||
static inline Chars *insert_char(Chars* cset, uchar a)
|
||||
{
|
||||
cset->insert(a);
|
||||
return cset;
|
||||
}
|
||||
|
||||
static inline Chars* insert_char_range(Chars* cset, uchar a, uchar b)
|
||||
{
|
||||
if (a > b)
|
||||
swap(a, b);
|
||||
for (uchar i = a; i <= b; i++)
|
||||
cset->insert(i);
|
||||
return cset;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%pure-parser
|
||||
/* %error-verbose */
|
||||
%parse-param {Node **root}
|
||||
%parse-param {const char *text}
|
||||
%name-prefix = "regex_"
|
||||
|
||||
%token <c> CHAR
|
||||
%type <c> regex_char cset_char1 cset_char cset_charN
|
||||
%type <cset> charset cset_chars
|
||||
%type <node> regex expr terms0 terms qterm term
|
||||
|
||||
/**
|
||||
* Note: destroy all nodes upon failure, but *not* the start symbol once
|
||||
* parsing succeeds!
|
||||
*/
|
||||
%destructor { $$->release(); } expr terms0 terms qterm term
|
||||
|
||||
%%
|
||||
|
||||
/* FIXME: Does not parse "[--]", "[---]", "[^^-x]". I don't actually know
|
||||
which precise grammer Perl regexs use, and rediscovering that
|
||||
is proving to be painful. */
|
||||
|
||||
regex : /* empty */ { *root = $$ = &epsnode; }
|
||||
| expr { *root = $$ = $1; }
|
||||
;
|
||||
|
||||
expr : terms
|
||||
| expr '|' terms0 { $$ = new AltNode($1, $3); }
|
||||
| '|' terms0 { $$ = new AltNode(&epsnode, $2); }
|
||||
;
|
||||
|
||||
terms0 : /* empty */ { $$ = &epsnode; }
|
||||
| terms
|
||||
;
|
||||
|
||||
terms : qterm
|
||||
| terms qterm { $$ = new CatNode($1, $2); }
|
||||
;
|
||||
|
||||
qterm : term
|
||||
| term '*' { $$ = new StarNode($1); }
|
||||
| term '+' { $$ = new PlusNode($1); }
|
||||
;
|
||||
|
||||
term : '.' { $$ = new AnyCharNode; }
|
||||
| regex_char { $$ = new CharNode($1); }
|
||||
| '[' charset ']' { $$ = new CharSetNode(*$2);
|
||||
delete $2; }
|
||||
| '[' '^' charset ']'
|
||||
{ $$ = new NotCharSetNode(*$3);
|
||||
delete $3; }
|
||||
| '[' '^' '^' cset_chars ']'
|
||||
{ $4->insert('^');
|
||||
$$ = new NotCharSetNode(*$4);
|
||||
delete $4; }
|
||||
| '(' regex ')' { $$ = $2; }
|
||||
;
|
||||
|
||||
regex_char : CHAR
|
||||
| '^' { $$ = '^'; }
|
||||
| '-' { $$ = '-'; }
|
||||
| ']' { $$ = ']'; }
|
||||
;
|
||||
|
||||
charset : cset_char1 cset_chars
|
||||
{ $$ = insert_char($2, $1); }
|
||||
| cset_char1 '-' cset_charN cset_chars
|
||||
{ $$ = insert_char_range($4, $1, $3); }
|
||||
;
|
||||
|
||||
cset_chars : /* nothing */ { $$ = new Chars; }
|
||||
| cset_chars cset_charN
|
||||
{ $$ = insert_char($1, $2); }
|
||||
| cset_chars cset_charN '-' cset_charN
|
||||
{ $$ = insert_char_range($1, $2, $4); }
|
||||
;
|
||||
|
||||
cset_char1 : cset_char
|
||||
| ']' { $$ = ']'; }
|
||||
| '-' { $$ = '-'; }
|
||||
;
|
||||
|
||||
cset_charN : cset_char
|
||||
| '^' { $$ = '^'; }
|
||||
;
|
||||
|
||||
cset_char : CHAR
|
||||
| '[' { $$ = '['; }
|
||||
| '*' { $$ = '*'; }
|
||||
| '+' { $$ = '+'; }
|
||||
| '.' { $$ = '.'; }
|
||||
| '|' { $$ = '|'; }
|
||||
| '(' { $$ = '('; }
|
||||
| ')' { $$ = ')'; }
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
|
||||
int octdigit(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '7')
|
||||
return c - '0';
|
||||
return -1;
|
||||
}
|
||||
|
||||
int hexdigit(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
return 10 + c - 'A';
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
return 10 + c - 'A';
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
int regex_lex(YYSTYPE *val, const char **pos)
|
||||
{
|
||||
int c;
|
||||
|
||||
val->c = **pos;
|
||||
switch(*(*pos)++) {
|
||||
case '\0':
|
||||
(*pos)--;
|
||||
return 0;
|
||||
|
||||
case '*': case '+': case '.': case '|': case '^': case '-':
|
||||
case '[': case ']': case '(' : case ')':
|
||||
return *(*pos - 1);
|
||||
|
||||
case '\\':
|
||||
val->c = **pos;
|
||||
switch(*(*pos)++) {
|
||||
case '\0':
|
||||
(*pos)--;
|
||||
/* fall through */
|
||||
case '\\':
|
||||
val->c = '\\';
|
||||
break;
|
||||
|
||||
case '0':
|
||||
val->c = 0;
|
||||
if ((c = octdigit(**pos)) >= 0) {
|
||||
val->c = c;
|
||||
(*pos)++;
|
||||
}
|
||||
if ((c = octdigit(**pos)) >= 0) {
|
||||
val->c = (val->c << 3) + c;
|
||||
(*pos)++;
|
||||
}
|
||||
if ((c = octdigit(**pos)) >= 0) {
|
||||
val->c = (val->c << 3) + c;
|
||||
(*pos)++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
val->c = 0;
|
||||
if ((c = hexdigit(**pos)) >= 0) {
|
||||
val->c = c;
|
||||
(*pos)++;
|
||||
}
|
||||
if ((c = hexdigit(**pos)) >= 0) {
|
||||
val->c = (val->c << 4) + c;
|
||||
(*pos)++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
val->c = '\a';
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
val->c = 033 /* ESC */;
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
val->c = '\f';
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
val->c = '\n';
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
val->c = '\r';
|
||||
break;
|
||||
|
||||
case 't':
|
||||
val->c = '\t';
|
||||
break;
|
||||
}
|
||||
}
|
||||
return CHAR;
|
||||
}
|
||||
|
||||
void regex_error(Node ** __attribute__((unused)),
|
||||
const char *text __attribute__((unused)),
|
||||
const char *error __attribute__((unused)))
|
||||
{
|
||||
/* We don't want the library to print error messages. */
|
||||
}
|
@@ -1,10 +0,0 @@
|
||||
#ifndef __REGEXP_H
|
||||
#define __REGEXP_H
|
||||
|
||||
/**
|
||||
* Flex file format, but without state compression and with negative
|
||||
* match results in the YYTD_ID_DEF table instead.
|
||||
*/
|
||||
#define YYTH_REGEXP_MAGIC 0x1B5E783D
|
||||
|
||||
#endif /* __REGEXP_H */
|
File diff suppressed because it is too large
Load Diff
58
parser/parser.conf
Normal file
58
parser/parser.conf
Normal file
@@ -0,0 +1,58 @@
|
||||
# parser.conf is a global AppArmor config file for the apparmor_parser
|
||||
#
|
||||
# It can be used to specify the default options for the parser, which
|
||||
# can then be overriden by options passed on the command line.
|
||||
#
|
||||
# Leading whitespace is ignored and lines that begin with # are treated
|
||||
# as comments.
|
||||
#
|
||||
# Config options are specified one per line using the same format as the
|
||||
# longform command line options (without the preceding --).
|
||||
#
|
||||
# If a value is specified twice the last version to appear is used.
|
||||
|
||||
## Suppress Warnings
|
||||
#quiet
|
||||
|
||||
## Be verbose
|
||||
#verbose
|
||||
|
||||
## Set include path
|
||||
#Include /etc/apparmor.d/abstractions
|
||||
|
||||
## Set location of apparmor filesystem
|
||||
#subdomainfs /sys/kernel/security/apparmor
|
||||
|
||||
## Set match-string to use - for forcing compiler to treat different kernels
|
||||
## the same
|
||||
# match-string "pattern=aadfa audit perms=crwxamlk/ user::other"
|
||||
|
||||
## Turn creating/updating of the cache on by default
|
||||
#write-cache
|
||||
|
||||
## Show cache hits
|
||||
#show-cache
|
||||
|
||||
## skip cached policy
|
||||
#skip-cache
|
||||
|
||||
## skip reading cache but allow updating
|
||||
#skip-read-cache
|
||||
|
||||
|
||||
#### Set Optimizaions. Multiple Optimizations can be set, one per line ####
|
||||
# For supported optimizations see
|
||||
# apparmor_parser --help=O
|
||||
|
||||
## Turn on equivalence classes
|
||||
#equiv
|
||||
|
||||
## Turn off expr tree simplification
|
||||
#Optimize=no-expr-simplify
|
||||
|
||||
## Turn off DFA minimization
|
||||
#Optimize=no-minimize
|
||||
|
||||
## Adjust compression
|
||||
#Optimize=compress-small
|
||||
#Optimize=compress-fast
|
@@ -23,6 +23,7 @@
|
||||
#include <sys/resource.h>
|
||||
#include "immunix.h"
|
||||
#include "libapparmor_re/apparmor_re.h"
|
||||
#include "libapparmor_re/aare_rules.h"
|
||||
|
||||
typedef enum pattern_t pattern_t;
|
||||
|
||||
@@ -178,12 +179,8 @@ struct var_string {
|
||||
|
||||
#define FLAG_CHANGEHAT_1_4 2
|
||||
#define FLAG_CHANGEHAT_1_5 3
|
||||
extern int kernel_supports_network;
|
||||
extern int flag_changehat_version;
|
||||
extern int read_implies_exec;
|
||||
extern dfaflags_t dfaflags;
|
||||
|
||||
extern int preprocess_only;
|
||||
extern FILE *ofile;
|
||||
|
||||
#define PATH_CHROOT_REL 0x1
|
||||
#define PATH_NS_REL 0x2
|
||||
@@ -226,23 +223,34 @@ extern FILE *ofile;
|
||||
#define list_last_entry(LIST, ENTRY) \
|
||||
for ((ENTRY) = (LIST); (ENTRY) && (ENTRY)->next; (ENTRY) = (ENTRY)->next)
|
||||
|
||||
/* Some external definitions to make b0rken programs happy */
|
||||
/* from parser_common.c */
|
||||
extern int regex_type;
|
||||
extern int perms_create;
|
||||
extern int net_af_max_override;
|
||||
extern int kernel_load;
|
||||
extern int kernel_supports_network;
|
||||
extern int flag_changehat_version;
|
||||
extern int conf_verbose;
|
||||
extern int conf_quiet;
|
||||
extern int names_only;
|
||||
extern int option;
|
||||
extern int current_lineno;
|
||||
extern dfaflags_t dfaflags;
|
||||
extern char *progname;
|
||||
extern char *subdomainbase;
|
||||
extern char *profilename;
|
||||
extern char *profile_namespace;
|
||||
|
||||
/* from parser_main */
|
||||
extern int force_complain;
|
||||
extern int conf_quiet;
|
||||
extern int conf_verbose;
|
||||
extern int kernel_load;
|
||||
extern int regex_type;
|
||||
extern int perms_create;
|
||||
extern struct timespec mru_tstamp;
|
||||
extern void update_mru_tstamp(FILE *file);
|
||||
extern char *current_filename;
|
||||
extern FILE *ofile;
|
||||
extern int read_implies_exec;
|
||||
extern void pwarn(char *fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
|
||||
|
||||
/* from parser_main (cannot be used in tst builds) */
|
||||
extern int force_complain;
|
||||
extern struct timespec mru_tstamp;
|
||||
extern void update_mru_tstamp(FILE *file);
|
||||
|
||||
/* provided by parser_lex.l (cannot be used in tst builds) */
|
||||
extern FILE *yyin;
|
||||
extern void yyrestart(FILE *fp);
|
||||
extern int yyparse(void);
|
||||
@@ -338,3 +346,32 @@ extern void dump_policy_hats(struct codomain *cod);
|
||||
extern void dump_policy_names(void);
|
||||
extern int die_if_any_regex(void);
|
||||
void free_policies(void);
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
/* For the unit-test builds, we must include function stubs for stuff that
|
||||
* only exists in the excluded object files; everything else should live
|
||||
* in parser_common.c.
|
||||
*/
|
||||
|
||||
/* parser_yacc.y */
|
||||
void yyerror(char *msg, ...)
|
||||
{
|
||||
va_list arg;
|
||||
char buf[PATH_MAX];
|
||||
|
||||
va_start(arg, msg);
|
||||
vsnprintf(buf, sizeof(buf), msg, arg);
|
||||
va_end(arg);
|
||||
|
||||
PERROR(_("AppArmor parser error: %s\n"), buf);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#define MY_TEST(statement, error) \
|
||||
if (!(statement)) { \
|
||||
PERROR("FAIL: %s\n", error); \
|
||||
rc = 1; \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
76
parser/parser_common.c
Normal file
76
parser/parser_common.c
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2011
|
||||
* Canonical, Ltd. (All rights reserved)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, contact Novell, Inc. or Canonical,
|
||||
* Ltd.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <libintl.h>
|
||||
#include <locale.h>
|
||||
#define _(s) gettext(s)
|
||||
#include "parser.h"
|
||||
|
||||
int regex_type = AARE_DFA;
|
||||
int perms_create = 0; /* perms contain create flag */
|
||||
int net_af_max_override = -1; /* use kernel to determine af_max */
|
||||
int kernel_load = 1;
|
||||
int kernel_supports_network = 1; /* kernel supports network rules */
|
||||
int flag_changehat_version = FLAG_CHANGEHAT_1_5;
|
||||
int conf_verbose = 0;
|
||||
int conf_quiet = 0;
|
||||
int names_only = 0;
|
||||
int current_lineno = 1;
|
||||
int option = OPTION_ADD;
|
||||
|
||||
dfaflags_t dfaflags = DFA_CONTROL_TREE_NORMAL | DFA_CONTROL_TREE_SIMPLE | DFA_CONTROL_MINIMIZE | DFA_CONTROL_MINIMIZE_HASH_TRANS | DFA_CONTROL_MINIMIZE_HASH_PERMS;
|
||||
|
||||
char *subdomainbase = NULL;
|
||||
char *progname = __FILE__;
|
||||
char *profile_namespace = NULL;
|
||||
char *profilename = NULL;
|
||||
char *current_filename = NULL;
|
||||
|
||||
FILE *ofile = NULL;
|
||||
|
||||
#ifdef FORCE_READ_IMPLIES_EXEC
|
||||
int read_implies_exec = 1;
|
||||
#else
|
||||
int read_implies_exec = 0;
|
||||
#endif
|
||||
|
||||
void pwarn(char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
char *newfmt;
|
||||
int rc;
|
||||
|
||||
if (conf_quiet || names_only || option == OPTION_REMOVE)
|
||||
return;
|
||||
|
||||
rc = asprintf(&newfmt, _("Warning from %s (%s%sline %d): %s"),
|
||||
profilename ? profilename : "stdin",
|
||||
current_filename ? current_filename : "",
|
||||
current_filename ? " " : "",
|
||||
current_lineno,
|
||||
fmt);
|
||||
if (!newfmt)
|
||||
return;
|
||||
|
||||
va_start(arg, fmt);
|
||||
vfprintf(stderr, newfmt, arg);
|
||||
va_end(arg);
|
||||
|
||||
free(newfmt);
|
||||
}
|
@@ -21,8 +21,6 @@
|
||||
#define PARSER_INCLUDE_H
|
||||
|
||||
extern int preprocess_only;
|
||||
extern int current_lineno;
|
||||
extern char *current_filename;
|
||||
|
||||
extern int add_search_dir(char *dir);
|
||||
extern void init_base_dir(void);
|
||||
|
@@ -54,9 +54,6 @@
|
||||
|
||||
#define YY_NO_INPUT
|
||||
|
||||
int current_lineno = 1;
|
||||
char *current_filename = NULL;
|
||||
|
||||
struct ignored_suffix_t {
|
||||
char * text;
|
||||
int len;
|
||||
@@ -511,7 +508,7 @@ LT_EQUAL <=
|
||||
{WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ }
|
||||
|
||||
|
||||
-?{NUMBER}[kKMG]? {
|
||||
-?{NUMBER}[[:alpha:]]* {
|
||||
DUMP_PREPROCESS;
|
||||
yylval.var_val = strdup(yytext);
|
||||
return TOK_VALUE;
|
||||
|
@@ -2,7 +2,7 @@
|
||||
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
|
||||
* NOVELL (All rights reserved)
|
||||
*
|
||||
* Copyright (c) 2010
|
||||
* Copyright (c) 2010, 2011
|
||||
* Canonical, Ltd. (All rights reserved)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@@ -19,6 +19,7 @@
|
||||
* Ltd.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
@@ -28,7 +29,6 @@
|
||||
#include <fcntl.h>
|
||||
#include <mntent.h>
|
||||
#include <libintl.h>
|
||||
#include <linux/limits.h>
|
||||
#include <locale.h>
|
||||
#define _(s) gettext(s)
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
/* #define DEBUG */
|
||||
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -58,45 +59,26 @@
|
||||
#define UNPRIVILEGED_OPS (!(PRIVILEGED_OPS))
|
||||
|
||||
const char *parser_title = "AppArmor parser";
|
||||
const char *parser_copyright = "Copyright (C) 1999-2008 Novell Inc.\nCopyright 2009-2010 Canonical Ltd.";
|
||||
const char *parser_copyright = "Copyright (C) 1999-2008 Novell Inc.\nCopyright 2009-2011 Canonical Ltd.";
|
||||
|
||||
char *progname;
|
||||
int option = OPTION_ADD;
|
||||
int opt_force_complain = 0;
|
||||
int binary_input = 0;
|
||||
int names_only = 0;
|
||||
int dump_vars = 0;
|
||||
int dump_expanded_vars = 0;
|
||||
dfaflags_t dfaflags = DFA_CONTROL_TREE_NORMAL | DFA_CONTROL_TREE_SIMPLE | DFA_CONTROL_MINIMIZE | DFA_CONTROL_MINIMIZE_HASH_TRANS | DFA_CONTROL_MINIMIZE_HASH_PERMS;
|
||||
int conf_verbose = 0;
|
||||
int conf_quiet = 0;
|
||||
int kernel_load = 1;
|
||||
int show_cache = 0;
|
||||
int skip_cache = 0;
|
||||
int skip_read_cache = 0;
|
||||
int write_cache = 0;
|
||||
#ifdef FORCE_READ_IMPLIES_EXEC
|
||||
int read_implies_exec = 1;
|
||||
#else
|
||||
int read_implies_exec = 0;
|
||||
#endif
|
||||
int preprocess_only = 0;
|
||||
int skip_mode_force = 0;
|
||||
struct timespec mru_tstamp;
|
||||
|
||||
char *subdomainbase = NULL;
|
||||
char *match_string = NULL;
|
||||
char *flags_string = NULL;
|
||||
int regex_type = AARE_DFA;
|
||||
int perms_create = 0; /* perms contain create flag */
|
||||
int kernel_supports_network = 1; /* kernel supports network rules */
|
||||
char *profile_namespace = NULL;
|
||||
int flag_changehat_version = FLAG_CHANGEHAT_1_5;
|
||||
FILE *ofile = NULL;
|
||||
|
||||
/* per-profile settings */
|
||||
int force_complain = 0;
|
||||
char *profilename = NULL;
|
||||
|
||||
struct option long_options[] = {
|
||||
{"add", 0, 0, 'a'},
|
||||
@@ -215,6 +197,8 @@ optflag_table_t dumpflag_table[] = {
|
||||
DFA_DUMP_UNIQ_PERMS },
|
||||
{ 1, "dfa-minimize-uniq-perms", "Dump unique perms post minimization",
|
||||
DFA_DUMP_MIN_UNIQ_PERMS },
|
||||
{ 1, "dfa-minimize-partitions", "Dump dfa minimization partitions",
|
||||
DFA_DUMP_MIN_PARTS },
|
||||
{ 1, "compress-progress", "Dump progress of compression",
|
||||
DFA_DUMP_TRANS_PROGRESS | DFA_DUMP_TRANS_STATS },
|
||||
{ 1, "compress-stats", "Dump stats on compression",
|
||||
@@ -321,29 +305,240 @@ static void display_optimize(char *command)
|
||||
print_flag_table(optflag_table);
|
||||
}
|
||||
|
||||
void pwarn(char *fmt, ...)
|
||||
|
||||
/* Treat conf file like options passed on command line
|
||||
*/
|
||||
static int getopt_long_file(FILE *f, const struct option *longopts,
|
||||
char **optarg, int *longindex)
|
||||
{
|
||||
va_list arg;
|
||||
char *newfmt;
|
||||
int rc;
|
||||
static char line[256];
|
||||
char *pos, *opt, *save;
|
||||
int i;
|
||||
|
||||
if (conf_quiet || names_only || option == OPTION_REMOVE)
|
||||
return;
|
||||
for (;;) {
|
||||
if (!fgets(line, 256, f))
|
||||
return -1;
|
||||
pos = line;
|
||||
while (isblank(*pos))
|
||||
pos++;
|
||||
if (*pos == '#')
|
||||
continue;
|
||||
opt = strtok_r(pos, " \t\r\n=", &save);
|
||||
/* blank line */
|
||||
if (!opt)
|
||||
continue;
|
||||
|
||||
rc = asprintf(&newfmt, _("Warning from %s (%s%sline %d): %s"),
|
||||
profilename ? profilename : "stdin",
|
||||
current_filename ? current_filename : "",
|
||||
current_filename ? " " : "",
|
||||
current_lineno,
|
||||
fmt);
|
||||
if (!newfmt)
|
||||
return;
|
||||
for (i = 0; longopts[i].name &&
|
||||
strcmp(longopts[i].name, opt) != 0; i++) ;
|
||||
if (!longopts[i].name) {
|
||||
PERROR("%s: unknown option (%s) in config file.\n",
|
||||
progname, opt);
|
||||
/* skip it */
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
va_start(arg, fmt);
|
||||
vfprintf(stderr, newfmt, arg);
|
||||
va_end(arg);
|
||||
if (longindex)
|
||||
*longindex = i;
|
||||
|
||||
free(newfmt);
|
||||
if (*save) {
|
||||
int len;
|
||||
while(isblank(*save))
|
||||
save++;
|
||||
len = strlen(save) - 1;
|
||||
if (save[len] == '\n')
|
||||
save[len] = 0;
|
||||
}
|
||||
|
||||
switch (longopts[i].has_arg) {
|
||||
case 0:
|
||||
*optarg = NULL;
|
||||
break;
|
||||
case 1:
|
||||
if (!strlen(save)) {
|
||||
*optarg = NULL;
|
||||
return '?';
|
||||
}
|
||||
*optarg = save;
|
||||
break;
|
||||
case 2:
|
||||
*optarg = save;
|
||||
break;
|
||||
default:
|
||||
PERROR("%s: internal error bad longopt value\n", progname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (longopts[i].flag == NULL)
|
||||
return longopts[i].val;
|
||||
else
|
||||
*longopts[i].flag = longopts[i].val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* process a single argment from getopt_long
|
||||
* Returns: 1 if an action arg, else 0
|
||||
*/
|
||||
static int process_arg(int c, char *optarg)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
switch (c) {
|
||||
case 0:
|
||||
PERROR("Assert, in getopt_long handling\n");
|
||||
display_usage(progname);
|
||||
exit(0);
|
||||
break;
|
||||
case 'a':
|
||||
count++;
|
||||
option = OPTION_ADD;
|
||||
break;
|
||||
case 'd':
|
||||
debug++;
|
||||
skip_read_cache = 1;
|
||||
break;
|
||||
case 'h':
|
||||
if (!optarg) {
|
||||
display_usage(progname);
|
||||
} else if (strcmp(optarg, "Dump") == 0 ||
|
||||
strcmp(optarg, "dump") == 0 ||
|
||||
strcmp(optarg, "D") == 0) {
|
||||
display_dump(progname);
|
||||
} else if (strcmp(optarg, "Optimize") == 0 ||
|
||||
strcmp(optarg, "optimize") == 0 ||
|
||||
strcmp(optarg, "O") == 0) {
|
||||
display_optimize(progname);
|
||||
} else {
|
||||
PERROR("%s: Invalid --help option %s\n",
|
||||
progname, optarg);
|
||||
exit(1);
|
||||
}
|
||||
exit(0);
|
||||
break;
|
||||
case 'r':
|
||||
count++;
|
||||
option = OPTION_REPLACE;
|
||||
break;
|
||||
case 'R':
|
||||
count++;
|
||||
option = OPTION_REMOVE;
|
||||
skip_cache = 1;
|
||||
break;
|
||||
case 'V':
|
||||
display_version();
|
||||
exit(0);
|
||||
break;
|
||||
case 'I':
|
||||
add_search_dir(optarg);
|
||||
break;
|
||||
case 'b':
|
||||
set_base_dir(optarg);
|
||||
break;
|
||||
case 'B':
|
||||
binary_input = 1;
|
||||
skip_cache = 1;
|
||||
break;
|
||||
case 'C':
|
||||
opt_force_complain = 1;
|
||||
skip_cache = 1;
|
||||
break;
|
||||
case 'N':
|
||||
names_only = 1;
|
||||
skip_cache = 1;
|
||||
break;
|
||||
case 'S':
|
||||
count++;
|
||||
option = OPTION_STDOUT;
|
||||
skip_read_cache = 1;
|
||||
kernel_load = 0;
|
||||
break;
|
||||
case 'o':
|
||||
count++;
|
||||
option = OPTION_OFILE;
|
||||
skip_read_cache = 1;
|
||||
kernel_load = 0;
|
||||
ofile = fopen(optarg, "w");
|
||||
if (!ofile) {
|
||||
PERROR("%s: Could not open file %s\n",
|
||||
progname, optarg);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
subdomainbase = strndup(optarg, PATH_MAX);
|
||||
break;
|
||||
case 'D':
|
||||
skip_read_cache = 1;
|
||||
if (!optarg) {
|
||||
dump_vars = 1;
|
||||
} else if (strcmp(optarg, "variables") == 0) {
|
||||
dump_vars = 1;
|
||||
} else if (strcmp(optarg, "expanded-variables") == 0) {
|
||||
dump_expanded_vars = 1;
|
||||
} else if (!handle_flag_table(dumpflag_table, optarg,
|
||||
&dfaflags)) {
|
||||
PERROR("%s: Invalid --Dump option %s\n",
|
||||
progname, optarg);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'O':
|
||||
skip_read_cache = 1;
|
||||
|
||||
if (!handle_flag_table(optflag_table, optarg,
|
||||
&dfaflags)) {
|
||||
PERROR("%s: Invalid --Optimize option %s\n",
|
||||
progname, optarg);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
match_string = strdup(optarg);
|
||||
break;
|
||||
case 'q':
|
||||
conf_verbose = 0;
|
||||
conf_quiet = 1;
|
||||
break;
|
||||
case 'v':
|
||||
conf_verbose = 1;
|
||||
conf_quiet = 0;
|
||||
break;
|
||||
case 'n':
|
||||
profile_namespace = strdup(optarg);
|
||||
break;
|
||||
case 'X':
|
||||
read_implies_exec = 1;
|
||||
break;
|
||||
case 'K':
|
||||
skip_cache = 1;
|
||||
break;
|
||||
case 'k':
|
||||
show_cache = 1;
|
||||
break;
|
||||
case 'W':
|
||||
write_cache = 1;
|
||||
break;
|
||||
case 'T':
|
||||
skip_read_cache = 1;
|
||||
break;
|
||||
case 'Q':
|
||||
kernel_load = 0;
|
||||
break;
|
||||
case 'p':
|
||||
count++;
|
||||
kernel_load = 0;
|
||||
skip_cache = 1;
|
||||
preprocess_only = 1;
|
||||
skip_mode_force = 1;
|
||||
break;
|
||||
default:
|
||||
display_usage(progname);
|
||||
exit(0);
|
||||
break;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int process_args(int argc, char *argv[])
|
||||
@@ -354,159 +549,7 @@ static int process_args(int argc, char *argv[])
|
||||
|
||||
while ((c = getopt_long(argc, argv, "adf:h::rRVvI:b:BCD:NSm:qQn:XKTWkO:po:", long_options, &o)) != -1)
|
||||
{
|
||||
switch (c) {
|
||||
case 0:
|
||||
PERROR("Assert, in getopt_long handling\n");
|
||||
display_usage(progname);
|
||||
exit(0);
|
||||
break;
|
||||
case 'a':
|
||||
count++;
|
||||
option = OPTION_ADD;
|
||||
break;
|
||||
case 'd':
|
||||
debug++;
|
||||
skip_read_cache = 1;
|
||||
break;
|
||||
case 'h':
|
||||
if (!optarg) {
|
||||
display_usage(progname);
|
||||
} else if (strcmp(optarg, "Dump") == 0 ||
|
||||
strcmp(optarg, "dump") == 0 ||
|
||||
strcmp(optarg, "D") == 0) {
|
||||
display_dump(progname);
|
||||
} else if (strcmp(optarg, "Optimize") == 0 ||
|
||||
strcmp(optarg, "optimize") == 0 ||
|
||||
strcmp(optarg, "O") == 0) {
|
||||
display_optimize(progname);
|
||||
} else {
|
||||
PERROR("%s: Invalid --help option %s\n",
|
||||
progname, optarg);
|
||||
exit(1);
|
||||
}
|
||||
exit(0);
|
||||
break;
|
||||
case 'r':
|
||||
count++;
|
||||
option = OPTION_REPLACE;
|
||||
break;
|
||||
case 'R':
|
||||
count++;
|
||||
option = OPTION_REMOVE;
|
||||
skip_cache = 1;
|
||||
break;
|
||||
case 'V':
|
||||
display_version();
|
||||
exit(0);
|
||||
break;
|
||||
case 'I':
|
||||
add_search_dir(optarg);
|
||||
break;
|
||||
case 'b':
|
||||
set_base_dir(optarg);
|
||||
break;
|
||||
case 'B':
|
||||
binary_input = 1;
|
||||
skip_cache = 1;
|
||||
break;
|
||||
case 'C':
|
||||
opt_force_complain = 1;
|
||||
skip_cache = 1;
|
||||
break;
|
||||
case 'N':
|
||||
names_only = 1;
|
||||
skip_cache = 1;
|
||||
break;
|
||||
case 'S':
|
||||
count++;
|
||||
option = OPTION_STDOUT;
|
||||
skip_read_cache = 1;
|
||||
kernel_load = 0;
|
||||
break;
|
||||
case 'o':
|
||||
count++;
|
||||
option = OPTION_OFILE;
|
||||
skip_read_cache = 1;
|
||||
kernel_load = 0;
|
||||
ofile = fopen(optarg, "w");
|
||||
if (!ofile) {
|
||||
PERROR("%s: Could not open file %s\n",
|
||||
progname, optarg);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
subdomainbase = strndup(optarg, PATH_MAX);
|
||||
break;
|
||||
case 'D':
|
||||
skip_read_cache = 1;
|
||||
if (!optarg) {
|
||||
dump_vars = 1;
|
||||
} else if (strcmp(optarg, "variables") == 0) {
|
||||
dump_vars = 1;
|
||||
} else if (strcmp(optarg, "expanded-variables") == 0) {
|
||||
dump_expanded_vars = 1;
|
||||
} else if (!handle_flag_table(dumpflag_table, optarg,
|
||||
&dfaflags)) {
|
||||
PERROR("%s: Invalid --Dump option %s\n",
|
||||
progname, optarg);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'O':
|
||||
skip_read_cache = 1;
|
||||
|
||||
if (!handle_flag_table(optflag_table, optarg,
|
||||
&dfaflags)) {
|
||||
PERROR("%s: Invalid --Optimize option %s\n",
|
||||
progname, optarg);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
match_string = strdup(optarg);
|
||||
break;
|
||||
case 'q':
|
||||
conf_verbose = 0;
|
||||
conf_quiet = 1;
|
||||
break;
|
||||
case 'v':
|
||||
conf_verbose = 1;
|
||||
conf_quiet = 0;
|
||||
break;
|
||||
case 'n':
|
||||
profile_namespace = strdup(optarg);
|
||||
break;
|
||||
case 'X':
|
||||
read_implies_exec = 1;
|
||||
break;
|
||||
case 'K':
|
||||
skip_cache = 1;
|
||||
break;
|
||||
case 'k':
|
||||
show_cache = 1;
|
||||
break;
|
||||
case 'W':
|
||||
write_cache = 1;
|
||||
break;
|
||||
case 'T':
|
||||
skip_read_cache = 1;
|
||||
break;
|
||||
case 'Q':
|
||||
kernel_load = 0;
|
||||
break;
|
||||
case 'p':
|
||||
count++;
|
||||
kernel_load = 0;
|
||||
skip_cache = 1;
|
||||
preprocess_only = 1;
|
||||
skip_mode_force = 1;
|
||||
break;
|
||||
default:
|
||||
display_usage(progname);
|
||||
exit(0);
|
||||
break;
|
||||
}
|
||||
count += process_arg(c, optarg);
|
||||
}
|
||||
|
||||
if (count > 1) {
|
||||
@@ -520,6 +563,21 @@ static int process_args(int argc, char *argv[])
|
||||
return optind;
|
||||
}
|
||||
|
||||
static int process_config_file(const char *name)
|
||||
{
|
||||
char *optarg;
|
||||
FILE *f;
|
||||
int c, o;
|
||||
|
||||
f = fopen(name, "r");
|
||||
if (!f)
|
||||
return 0;
|
||||
|
||||
while ((c = getopt_long_file(f, long_options, &optarg, &o)) != -1)
|
||||
process_arg(c, optarg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline char *try_subdomainfs_mountpoint(const char *mntpnt,
|
||||
const char *path)
|
||||
{
|
||||
@@ -804,6 +862,7 @@ int process_profile(int option, char *profilename)
|
||||
char * cachename = NULL;
|
||||
char * cachetemp = NULL;
|
||||
char *basename = NULL;
|
||||
FILE *cmd;
|
||||
|
||||
/* per-profile states */
|
||||
force_complain = opt_force_complain;
|
||||
@@ -851,6 +910,12 @@ int process_profile(int option, char *profilename)
|
||||
update_mru_tstamp(yyin);
|
||||
}
|
||||
|
||||
cmd = fopen("/proc/self/exe", "r");
|
||||
if (cmd) {
|
||||
update_mru_tstamp(cmd);
|
||||
fclose(cmd);
|
||||
}
|
||||
|
||||
retval = yyparse();
|
||||
if (retval != 0)
|
||||
goto out;
|
||||
@@ -942,8 +1007,7 @@ out:
|
||||
}
|
||||
else {
|
||||
unlink(cachetemp);
|
||||
if (show_cache)
|
||||
PERROR("Removed cache attempt: %s\n", cachetemp);
|
||||
PERROR("Warning failed to create cache: %s\n", basename);
|
||||
}
|
||||
free(cachetemp);
|
||||
}
|
||||
@@ -1025,6 +1089,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
init_base_dir();
|
||||
|
||||
process_config_file("/etc/apparmor/parser.conf");
|
||||
optind = process_args(argc, argv);
|
||||
|
||||
setlocale(LC_MESSAGES, "");
|
||||
|
@@ -29,6 +29,10 @@
|
||||
#include <linux/limits.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/capability.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "parser.h"
|
||||
#include "parser_yacc.h"
|
||||
@@ -125,6 +129,9 @@ static int get_table_token(const char *name __unused, struct keyword_table *tabl
|
||||
static struct keyword_table capability_table[] = {
|
||||
/* capabilities */
|
||||
#include "cap_names.h"
|
||||
#ifndef CAP_SYSLOG
|
||||
{"syslog", 34},
|
||||
#endif
|
||||
/* terminate */
|
||||
{NULL, 0}
|
||||
};
|
||||
@@ -203,6 +210,69 @@ static struct network_tuple network_mappings[] = {
|
||||
{NULL, 0, NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
/* The apparmor kernel patches up until 2.6.38 didn't handle networking
|
||||
* tables with sizes > AF_MAX correctly. This could happen when the
|
||||
* parser was built against newer kernel headers and then used to load
|
||||
* policy on an older kernel. This could happen during upgrades or
|
||||
* in multi-kernel boot systems.
|
||||
*
|
||||
* Try to detect the running kernel version and use that to determine
|
||||
* AF_MAX
|
||||
*/
|
||||
#define PROC_VERSION "/proc/sys/kernel/osrelease"
|
||||
static size_t kernel_af_max(void) {
|
||||
char buffer[32];
|
||||
int major;
|
||||
int fd, res;
|
||||
|
||||
if (!net_af_max_override) {
|
||||
return 0;
|
||||
}
|
||||
/* the override parameter is specifying the max value */
|
||||
if (net_af_max_override > 0)
|
||||
return net_af_max_override;
|
||||
|
||||
fd = open(PROC_VERSION, O_RDONLY);
|
||||
if (!fd)
|
||||
/* fall back to default provided during build */
|
||||
return 0;
|
||||
res = read(fd, &buffer, sizeof(buffer));
|
||||
close(fd);
|
||||
if (!res)
|
||||
return 0;
|
||||
buffer[sizeof(buffer)-1] = '\0';
|
||||
res = sscanf(buffer, "2.6.%d", &major);
|
||||
if (res != 1)
|
||||
return 0;
|
||||
|
||||
switch(major) {
|
||||
case 24:
|
||||
case 25:
|
||||
case 26:
|
||||
return 34;
|
||||
case 27:
|
||||
return 35;
|
||||
case 28:
|
||||
case 29:
|
||||
case 30:
|
||||
return 36;
|
||||
case 31:
|
||||
case 32:
|
||||
case 33:
|
||||
case 34:
|
||||
case 35:
|
||||
return 37;
|
||||
case 36:
|
||||
case 37:
|
||||
return 38;
|
||||
/* kernels .38 and later should handle this correctly so no
|
||||
* static mapping needed
|
||||
*/
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Yuck. We grab AF_* values to define above from linux/socket.h because
|
||||
* they are more accurate than sys/socket.h for what the kernel actually
|
||||
* supports. However, we can't just include linux/socket.h directly,
|
||||
@@ -213,13 +283,29 @@ static struct network_tuple network_mappings[] = {
|
||||
* hence the wrapping function.
|
||||
*/
|
||||
size_t get_af_max() {
|
||||
size_t af_max;
|
||||
/* HACK: declare that version without "create" had a static AF_MAX */
|
||||
if (!perms_create) return 36;
|
||||
if (!perms_create && !net_af_max_override)
|
||||
net_af_max_override = -1;
|
||||
|
||||
#if AA_AF_MAX > AF_MAX
|
||||
return AA_AF_MAX;
|
||||
af_max = AA_AF_MAX;
|
||||
#else
|
||||
return AF_MAX;
|
||||
af_max = AF_MAX;
|
||||
#endif
|
||||
|
||||
/* HACK: some kernels didn't handle network tables from parsers
|
||||
* compiled against newer kernel headers as they are larger than
|
||||
* the running kernel expected. If net_override is defined check
|
||||
* to see if there is a static max specified for that kernel
|
||||
*/
|
||||
if (net_af_max_override) {
|
||||
size_t max = kernel_af_max();
|
||||
if (max && max < af_max)
|
||||
return max;
|
||||
}
|
||||
|
||||
return af_max;
|
||||
}
|
||||
struct aa_network_entry *new_network_ent(unsigned int family,
|
||||
unsigned int type,
|
||||
@@ -347,7 +433,7 @@ char *processquoted(char *string, int len)
|
||||
*s = '\\';
|
||||
l++;
|
||||
break;
|
||||
case '0' - '3':
|
||||
case '0': case '1': case '2': case '3':
|
||||
if ((l < len - 4) &&
|
||||
strchr("0123456789", string[l + 2]) &&
|
||||
strchr("0123456789", string[l + 3])) {
|
||||
@@ -783,6 +869,7 @@ static const char *capnames[] = {
|
||||
"audit_control",
|
||||
"setfcap",
|
||||
"mac_override"
|
||||
"syslog",
|
||||
};
|
||||
|
||||
const char *capability_to_name(unsigned int cap)
|
||||
@@ -846,27 +933,6 @@ void debug_cod_list(struct codomain *cod)
|
||||
}
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
#define MY_TEST(statement, error) \
|
||||
if (!(statement)) { \
|
||||
PERROR("FAIL: %s\n", error); \
|
||||
rc = 1; \
|
||||
}
|
||||
|
||||
/* Guh, fake routine */
|
||||
void yyerror(char *msg, ...)
|
||||
{
|
||||
va_list arg;
|
||||
char buf[PATH_MAX];
|
||||
|
||||
va_start(arg, msg);
|
||||
vsnprintf(buf, sizeof(buf), msg, arg);
|
||||
va_end(arg);
|
||||
|
||||
PERROR(_("AppArmor parser error: %s\n"), buf);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int test_str_to_boolean(void)
|
||||
{
|
||||
int rc = 0;
|
||||
@@ -890,7 +956,7 @@ int test_str_to_boolean(void)
|
||||
int test_processunquoted(void)
|
||||
{
|
||||
int rc = 0;
|
||||
const char *teststring, *processedstring;
|
||||
char *teststring, *processedstring;
|
||||
|
||||
teststring = "";
|
||||
MY_TEST(strcmp(teststring, processunquoted(teststring, strlen(teststring))) == 0,
|
||||
@@ -915,6 +981,106 @@ int test_processunquoted(void)
|
||||
return rc;
|
||||
}
|
||||
|
||||
int test_processquoted(void)
|
||||
{
|
||||
int rc = 0;
|
||||
char *teststring, *processedstring;
|
||||
char *out;
|
||||
|
||||
teststring = "";
|
||||
out = processquoted(teststring, strlen(teststring));
|
||||
MY_TEST(strcmp(teststring, out) == 0,
|
||||
"processquoted on empty string");
|
||||
free(out);
|
||||
|
||||
teststring = "\"abcdefg\"";
|
||||
processedstring = "abcdefg";
|
||||
out = processquoted(teststring, strlen(teststring));
|
||||
MY_TEST(strcmp(processedstring, out) == 0,
|
||||
"processquoted on simple string");
|
||||
free(out);
|
||||
|
||||
teststring = "\"abcd\\tefg\"";
|
||||
processedstring = "abcd\tefg";
|
||||
out = processquoted(teststring, strlen(teststring));
|
||||
MY_TEST(strcmp(processedstring, out) == 0,
|
||||
"processquoted on string with tab");
|
||||
free(out);
|
||||
|
||||
teststring = "\"abcdefg\\\"";
|
||||
processedstring = "abcdefg\\";
|
||||
out = processquoted(teststring, strlen(teststring));
|
||||
MY_TEST(strcmp(processedstring, out) == 0,
|
||||
"processquoted on trailing slash");
|
||||
free(out);
|
||||
|
||||
teststring = "\"a\\\\bcdefg\"";
|
||||
processedstring = "a\\bcdefg";
|
||||
out = processquoted(teststring, strlen(teststring));
|
||||
MY_TEST(strcmp(processedstring, out) == 0,
|
||||
"processquoted on quoted slash");
|
||||
free(out);
|
||||
|
||||
teststring = "\"a\\\"bcde\\\"fg\"";
|
||||
processedstring = "a\"bcde\"fg";
|
||||
out = processquoted(teststring, strlen(teststring));
|
||||
MY_TEST(strcmp(processedstring, out) == 0,
|
||||
"processquoted on quoted quotes");
|
||||
free(out);
|
||||
|
||||
teststring = "\"\\rabcdefg\"";
|
||||
processedstring = "\rabcdefg";
|
||||
out = processquoted(teststring, strlen(teststring));
|
||||
MY_TEST(strcmp(processedstring, out) == 0,
|
||||
"processquoted on quoted \\r");
|
||||
free(out);
|
||||
|
||||
teststring = "\"abcdefg\\n\"";
|
||||
processedstring = "abcdefg\n";
|
||||
out = processquoted(teststring, strlen(teststring));
|
||||
MY_TEST(strcmp(processedstring, out) == 0,
|
||||
"processquoted on quoted \\n");
|
||||
free(out);
|
||||
|
||||
teststring = "\"\\Uabc\\Ndefg\\x\"";
|
||||
processedstring = "\\Uabc\\Ndefg\\x";
|
||||
out = processquoted(teststring, strlen(teststring));
|
||||
MY_TEST(strcmp(processedstring, out) == 0,
|
||||
"processquoted passthrough on invalid quoted chars");
|
||||
free(out);
|
||||
|
||||
teststring = "\"abc\\042defg\"";
|
||||
processedstring = "abc\"defg";
|
||||
out = processquoted(teststring, strlen(teststring));
|
||||
MY_TEST(strcmp(processedstring, out) == 0,
|
||||
"processquoted on quoted octal \\042");
|
||||
free(out);
|
||||
|
||||
teststring = "\"abcdefg\\176\"";
|
||||
processedstring = "abcdefg~";
|
||||
out = processquoted(teststring, strlen(teststring));
|
||||
MY_TEST(strcmp(processedstring, out) == 0,
|
||||
"processquoted on quoted octal \\176");
|
||||
free(out);
|
||||
|
||||
/* yes, our octal processing is lame; patches accepted */
|
||||
teststring = "\"abc\\42defg\"";
|
||||
processedstring = "abc\\42defg";
|
||||
out = processquoted(teststring, strlen(teststring));
|
||||
MY_TEST(strcmp(processedstring, out) == 0,
|
||||
"processquoted passthrough quoted invalid octal \\42");
|
||||
free(out);
|
||||
|
||||
teststring = "\"abcdefg\\04\"";
|
||||
processedstring = "abcdefg\\04";
|
||||
out = processquoted(teststring, strlen(teststring));
|
||||
MY_TEST(strcmp(processedstring, out) == 0,
|
||||
"processquoted passthrough quoted invalid trailing octal \\04");
|
||||
free(out);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int rc = 0;
|
||||
@@ -927,6 +1093,11 @@ int main(void)
|
||||
retval = test_processunquoted();
|
||||
if (retval != 0)
|
||||
rc = retval;
|
||||
|
||||
retval = test_processquoted();
|
||||
if (retval != 0)
|
||||
rc = retval;
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif /* UNIT_TEST */
|
||||
|
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <libintl.h>
|
||||
@@ -26,6 +27,7 @@
|
||||
|
||||
#include "parser.h"
|
||||
#include "libapparmor_re/apparmor_re.h"
|
||||
#include "libapparmor_re/aare_rules.h"
|
||||
|
||||
enum error_type {
|
||||
e_no_error,
|
||||
@@ -392,6 +394,8 @@ static int process_profile_name_xmatch(struct codomain *cod)
|
||||
name = local_name(cod->name);
|
||||
ptype = convert_aaregex_to_pcre(name, 0, tbuf, PATH_MAX + 3,
|
||||
&cod->xmatch_len);
|
||||
if (ptype == ePatternBasic)
|
||||
cod->xmatch_len = strlen(name);
|
||||
|
||||
if (ptype == ePatternInvalid) {
|
||||
PERROR(_("%s: Invalid profile name '%s' - bad regular expression\n"), progname, name);
|
||||
@@ -414,8 +418,14 @@ static int process_profile_name_xmatch(struct codomain *cod)
|
||||
struct alt_name *alt;
|
||||
list_for_each(cod->altnames, alt) {
|
||||
int len;
|
||||
convert_aaregex_to_pcre(alt->name, 0, tbuf,
|
||||
PATH_MAX + 3, &len);
|
||||
ptype = convert_aaregex_to_pcre(alt->name, 0,
|
||||
tbuf,
|
||||
PATH_MAX + 3,
|
||||
&len);
|
||||
if (ptype == ePatternBasic)
|
||||
len = strlen(alt->name);
|
||||
if (len < cod->xmatch_len)
|
||||
cod->xmatch_len = len;
|
||||
if (!aare_add_rule(rule, tbuf, 0, AA_MAY_EXEC, 0, dfaflags)) {
|
||||
aare_delete_ruleset(rule);
|
||||
return FALSE;
|
||||
@@ -607,29 +617,6 @@ void reset_regex(void)
|
||||
}
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
#define MY_TEST(statement, error) \
|
||||
if (!(statement)) { \
|
||||
PERROR("FAIL: %s\n", error); \
|
||||
rc = 1; \
|
||||
}
|
||||
|
||||
/* Guh, fake routine */
|
||||
void yyerror(char *msg, ...)
|
||||
{
|
||||
va_list arg;
|
||||
char buf[PATH_MAX];
|
||||
|
||||
va_start(arg, msg);
|
||||
vsnprintf(buf, sizeof(buf), msg, arg);
|
||||
va_end(arg);
|
||||
|
||||
PERROR(_("AppArmor parser error: %s\n"), buf);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
/* Guh, fake symbol */
|
||||
char *progname;
|
||||
|
||||
static int test_filter_slashes(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
@@ -539,30 +539,6 @@ void free_symtabs(void)
|
||||
}
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
#define MY_TEST(statement, error) \
|
||||
if (!(statement)) { \
|
||||
PERROR("FAIL: %s\n", error); \
|
||||
rc = 1; \
|
||||
}
|
||||
|
||||
/* Guh, fake symbol */
|
||||
char *progname;
|
||||
|
||||
/* Guh, fake routine */
|
||||
void yyerror(char *msg, ...)
|
||||
{
|
||||
va_list arg;
|
||||
char buf[PATH_MAX];
|
||||
|
||||
va_start(arg, msg);
|
||||
vsnprintf(buf, sizeof(buf), msg, arg);
|
||||
va_end(arg);
|
||||
|
||||
PERROR(_("AppArmor parser error: %s\n"), buf);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int rc = 0;
|
||||
@@ -589,7 +565,7 @@ int main(void)
|
||||
retval = new_set_var("test", "different value");
|
||||
MY_TEST(retval != 0, "new set variable 2");
|
||||
|
||||
retval = new_set_var("testes", "testes");
|
||||
retval = new_set_var("testing", "testing");
|
||||
MY_TEST(retval == 0, "new set variable 3");
|
||||
|
||||
retval = new_set_var("monopuff", "Mockingbird");
|
||||
|
@@ -36,8 +36,14 @@ static inline char *get_var_end(char *var)
|
||||
while (*eptr) {
|
||||
if (*eptr == '}')
|
||||
return eptr;
|
||||
if (!(*eptr == '_' || isalpha(*eptr)))
|
||||
return NULL; /* invalid char */
|
||||
/* first character must be alpha */
|
||||
if (eptr == var) {
|
||||
if (!isalpha(*eptr))
|
||||
return NULL; /* invalid char */
|
||||
} else {
|
||||
if (!(*eptr == '_' || isalnum(*eptr)))
|
||||
return NULL; /* invalid char */
|
||||
}
|
||||
eptr++;
|
||||
}
|
||||
return NULL; /* no terminating '}' */
|
||||
@@ -210,29 +216,6 @@ int process_variables(struct codomain *cod)
|
||||
}
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
#define MY_TEST(statement, error) \
|
||||
if (!(statement)) { \
|
||||
PERROR("FAIL: %s\n", error); \
|
||||
rc = 1; \
|
||||
}
|
||||
|
||||
/* Guh, fake routine */
|
||||
void yyerror(char *msg, ...)
|
||||
{
|
||||
va_list arg;
|
||||
char buf[PATH_MAX];
|
||||
|
||||
va_start(arg, msg);
|
||||
vsnprintf(buf, sizeof(buf), msg, arg);
|
||||
va_end(arg);
|
||||
|
||||
PERROR(_("AppArmor parser error: %s\n"), buf);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
/* Guh, fake symbol */
|
||||
char *progname;
|
||||
|
||||
int test_get_var_end(void)
|
||||
{
|
||||
int rc = 0;
|
||||
@@ -271,7 +254,7 @@ int test_split_string(void)
|
||||
char *var = "boogie";
|
||||
char *suffix = "suffixication";
|
||||
|
||||
(void) asprintf(&tst_string, "%s@{%s}%s", prefix, var, suffix);
|
||||
asprintf(&tst_string, "%s@{%s}%s", prefix, var, suffix);
|
||||
var_start = tst_string + strlen(prefix);
|
||||
var_end = var_start + strlen(var) + strlen("@\{");
|
||||
ret_struct = split_string(tst_string, var_start, var_end);
|
||||
@@ -317,6 +300,8 @@ int test_split_out_var(void)
|
||||
struct var_string *ret_struct;
|
||||
char *prefix = "abcdefg";
|
||||
char *var = "boogie";
|
||||
char *var2 = "V4rW1thNum5";
|
||||
char *var3 = "boogie_board";
|
||||
char *suffix = "suffixication";
|
||||
|
||||
/* simple case */
|
||||
@@ -394,6 +379,34 @@ int test_split_out_var(void)
|
||||
MY_TEST(strcmp(ret_struct->suffix, suffix) == 0, "split out var 7 suffix");
|
||||
free_var_string(ret_struct);
|
||||
|
||||
/* numeric char in var, should succeed */
|
||||
asprintf(&tst_string, "%s@{%s}%s", prefix, var2, suffix);
|
||||
ret_struct = split_out_var(tst_string);
|
||||
MY_TEST(ret_struct && strcmp(ret_struct->prefix, prefix) == 0, "split out numeric var prefix");
|
||||
MY_TEST(ret_struct && strcmp(ret_struct->var, var2) == 0, "split numeric var var");
|
||||
MY_TEST(ret_struct && strcmp(ret_struct->suffix, suffix) == 0, "split out numeric var suffix");
|
||||
free_var_string(ret_struct);
|
||||
|
||||
/* numeric first char in var, should fail */
|
||||
asprintf(&tst_string, "%s@{6%s}%s", prefix, var2, suffix);
|
||||
ret_struct = split_out_var(tst_string);
|
||||
MY_TEST(ret_struct == NULL, "split out var - numeric first char in var");
|
||||
free_var_string(ret_struct);
|
||||
|
||||
/* underscore char in var, should succeed */
|
||||
asprintf(&tst_string, "%s@{%s}%s", prefix, var3, suffix);
|
||||
ret_struct = split_out_var(tst_string);
|
||||
MY_TEST(ret_struct && strcmp(ret_struct->prefix, prefix) == 0, "split out underscore var prefix");
|
||||
MY_TEST(ret_struct && strcmp(ret_struct->var, var3) == 0, "split out underscore var");
|
||||
MY_TEST(ret_struct && strcmp(ret_struct->suffix, suffix) == 0, "split out underscore var suffix");
|
||||
free_var_string(ret_struct);
|
||||
|
||||
/* underscore first char in var, should fail */
|
||||
asprintf(&tst_string, "%s@{_%s%s}%s", prefix, var2, var3, suffix);
|
||||
ret_struct = split_out_var(tst_string);
|
||||
MY_TEST(ret_struct == NULL, "split out var - underscore first char in var");
|
||||
free_var_string(ret_struct);
|
||||
|
||||
return rc;
|
||||
}
|
||||
int main(void)
|
||||
|
@@ -240,7 +240,7 @@ profile_base: TOK_ID opt_id flags TOK_OPEN rules TOK_CLOSE
|
||||
|
||||
post_process_nt_entries(cod);
|
||||
PDEBUG("%s: flags='%s%s'\n",
|
||||
$3,
|
||||
$2,
|
||||
cod->flags.complain ? "complain, " : "",
|
||||
cod->flags.audit ? "audit" : "");
|
||||
|
||||
@@ -280,7 +280,7 @@ hat: hat_start profile_base
|
||||
{
|
||||
struct codomain *cod = $2;
|
||||
if ($2)
|
||||
PDEBUG("Matched: hat %s { ... }\n", code->name);
|
||||
PDEBUG("Matched: hat %s { ... }\n", cod->name);
|
||||
|
||||
cod->flags.hat = 1;
|
||||
$$ = cod;
|
||||
@@ -384,7 +384,7 @@ valuelist: valuelist TOK_VALUE
|
||||
struct value_list *new = calloc(1, sizeof(struct value_list));
|
||||
if (!new)
|
||||
yyerror(_("Memory allocation error."));
|
||||
PDEBUG("Matched: value (%s)\n", $1);
|
||||
PDEBUG("Matched: value list\n");
|
||||
|
||||
new->value = $2;
|
||||
new->next = $1;
|
||||
@@ -711,10 +711,31 @@ rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE TOK_END_OF_RULE
|
||||
if (strcmp($6, "infinity") == 0) {
|
||||
value = RLIM_INFINITY;
|
||||
} else {
|
||||
const char *seconds = "seconds";
|
||||
const char *minutes = "minutes";
|
||||
const char *hours = "hours";
|
||||
const char *days = "days";
|
||||
const char *kb = "KB";
|
||||
const char *mb = "MB";
|
||||
const char *gb = "GB";
|
||||
|
||||
tmp = strtoll($6, &end, 0);
|
||||
switch (limit) {
|
||||
case RLIMIT_CPU:
|
||||
yyerror("RLIMIT '%s' is currently unsupported\n", $4);
|
||||
if (!end || $6 == end || tmp < 0)
|
||||
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
|
||||
if (*end == '\0' ||
|
||||
strstr(seconds, end) == seconds) {
|
||||
value = tmp;
|
||||
} else if (strstr(minutes, end) == minutes) {
|
||||
value = tmp * 60;
|
||||
} else if (strstr(hours, end) == hours) {
|
||||
value = tmp * 60 * 60;
|
||||
} else if (strstr(days, end) == days) {
|
||||
value = tmp * 60 * 60 * 24;
|
||||
} else {
|
||||
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
|
||||
}
|
||||
break;
|
||||
case RLIMIT_NOFILE:
|
||||
case RLIMIT_NPROC:
|
||||
@@ -722,14 +743,14 @@ rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE TOK_END_OF_RULE
|
||||
case RLIMIT_SIGPENDING:
|
||||
#ifdef RLIMIT_RTPRIO
|
||||
case RLIMIT_RTPRIO:
|
||||
if ($6 == end || *end != '\0' || tmp < 0)
|
||||
if (!end || $6 == end || *end != '\0' || tmp < 0)
|
||||
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
|
||||
value = tmp;
|
||||
break;
|
||||
#endif
|
||||
#ifdef RLIMIT_NICE
|
||||
case RLIMIT_NICE:
|
||||
if ($6 == end || *end != '\0')
|
||||
if (!end || $6 == end || *end != '\0')
|
||||
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
|
||||
if (tmp < -20 || tmp > 19)
|
||||
yyerror("RLIMIT '%s' out of range (-20 .. 19) %d\n", $4, tmp);
|
||||
@@ -746,11 +767,11 @@ rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE TOK_END_OF_RULE
|
||||
case RLIMIT_MSGQUEUE:
|
||||
if ($6 == end || tmp < 0)
|
||||
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
|
||||
if (strcmp(end, "K") == 0) {
|
||||
if (strstr(kb, end) == kb) {
|
||||
tmp *= 1024;
|
||||
} else if (strcmp(end, "M") == 0) {
|
||||
} else if (strstr(mb, end) == mb) {
|
||||
tmp *= 1024*1024;
|
||||
} else if (strcmp(end, "G") == 0) {
|
||||
} else if (strstr(gb, end) == gb) {
|
||||
tmp *= 1024*1024*1024;
|
||||
} else if (*end != '\0') {
|
||||
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
|
||||
|
@@ -27,7 +27,7 @@
|
||||
### BEGIN INIT INFO
|
||||
# Provides: aaeventd
|
||||
# Required-Start: apparmor
|
||||
# Required-Stop:
|
||||
# Required-Stop: $null
|
||||
# Default-Start: 2 3 5
|
||||
# Default-Stop:
|
||||
# Short-Description: AppArmor Notification and Reporting
|
||||
@@ -78,9 +78,9 @@ usage() {
|
||||
|
||||
start_aa_event() {
|
||||
if [ -x "$AA_EV_BIN" -a "${APPARMOR_ENABLE_AAEVENTD}" = "yes" ] ; then
|
||||
sd_action "Starting AppArmor Event daemon" startproc -f -p $AA_EV_PIDFILE $AA_EV_BIN -p $AA_EV_PIDFILE
|
||||
sd_action "Starting AppArmor Event daemon" startproc -p $AA_EV_PIDFILE $AA_EV_BIN -p $AA_EV_PIDFILE
|
||||
elif [ -x "$SD_EV_BIN" -a "${APPARMOR_ENABLE_AAEVENTD}" = "yes" ] ; then
|
||||
sd_action "Starting AppArmor Event daemon" startproc -f -p $SD_EV_PIDFILE $SD_EV_BIN -p $SD_EV_PIDFILE
|
||||
sd_action "Starting AppArmor Event daemon" startproc -p $SD_EV_PIDFILE $SD_EV_BIN -p $SD_EV_PIDFILE
|
||||
fi
|
||||
}
|
||||
|
||||
|
@@ -83,15 +83,6 @@ SECURITYFS=/sys/kernel/security
|
||||
SUBDOMAINFS_MOUNTPOINT=$(grep subdomainfs /etc/fstab | \
|
||||
sed -e 's|^[[:space:]]*[^[:space:]]\+[[:space:]]\+\(/[^[:space:]]*\)[[:space:]]\+subdomainfs.*$|\1|' 2> /dev/null)
|
||||
|
||||
if [ -d "/var/lib/${MODULE}" ] ; then
|
||||
APPARMOR_TMPDIR="/var/lib/${MODULE}"
|
||||
elif [ -d "/var/lib/${OLD_MODULE}" ] ; then
|
||||
APPARMOR_TMPDIR="/var/lib/${OLD_MODULE}"
|
||||
else
|
||||
APPARMOR_TMPDIR="/tmp"
|
||||
fi
|
||||
|
||||
|
||||
# keep exit status from parser during profile load. 0 is good, 1 is bad
|
||||
STATUS=0
|
||||
|
||||
@@ -108,9 +99,7 @@ is_apparmor_present() {
|
||||
# check for subdomainfs version of module
|
||||
grep -qE "^($modules)[[:space:]]" /proc/modules
|
||||
|
||||
if [ $? -ne 0 ] ; then
|
||||
ls /sys/module/apparmor 2>/dev/null | grep -qE "^($modules)"
|
||||
fi
|
||||
[ $? -ne 0 -a -d /sys/module/apparmor ]
|
||||
|
||||
return $?
|
||||
}
|
||||
@@ -170,7 +159,7 @@ parse_profiles() {
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
aa_log_action_begin "$PARSER_MSG"
|
||||
aa_log_action_start "$PARSER_MSG"
|
||||
# run the parser on all of the apparmor profiles
|
||||
if [ ! -f "$PARSER" ]; then
|
||||
aa_log_failure_msg "AppArmor parser not found"
|
||||
@@ -198,6 +187,7 @@ parse_profiles() {
|
||||
aa_log_skipped_msg "$profile"
|
||||
logger -t "AppArmor(init)" -p daemon.warn "Skipping profile $profile"
|
||||
STATUS=2
|
||||
continue
|
||||
elif [ "$skip" -ne 0 ]; then
|
||||
continue
|
||||
fi
|
||||
@@ -222,7 +212,6 @@ parse_profiles() {
|
||||
|
||||
profiles_names_list() {
|
||||
# run the parser on all of the apparmor profiles
|
||||
TMPFILE=$1
|
||||
if [ ! -f "$PARSER" ]; then
|
||||
aa_log_failure_msg "- AppArmor parser not found"
|
||||
exit 1
|
||||
@@ -235,9 +224,9 @@ profiles_names_list() {
|
||||
|
||||
for profile in $PROFILE_DIR/*; do
|
||||
if skip_profile "${profile}" && [ -f "${profile}" ] ; then
|
||||
LIST_ADD=$($PARSER $ABSTRACTIONS -N "$profile" | grep -v '\^')
|
||||
LIST_ADD=$($PARSER $ABSTRACTIONS -N "$profile" )
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "$LIST_ADD" >>$TMPFILE
|
||||
echo "$LIST_ADD"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
@@ -297,7 +286,7 @@ is_apparmor_loaded() {
|
||||
}
|
||||
|
||||
is_securityfs_mounted() {
|
||||
grep -q securityfs /proc/filesystems && grep -q securityfs /proc/mounts
|
||||
test -d ${SECURITYFS} -a -d /sys/fs/cgroup/systemd || grep -q securityfs /proc/filesystems && grep -q securityfs /proc/mounts
|
||||
return $?
|
||||
}
|
||||
|
||||
@@ -377,10 +366,11 @@ apparmor_start() {
|
||||
configure_owlsm
|
||||
|
||||
# if there is anything in the profiles file don't load
|
||||
cat "$SFS_MOUNTPOINT/profiles" | if ! read line ; then
|
||||
if ! read line < "$SFS_MOUNTPOINT/profiles"; then
|
||||
parse_profiles load
|
||||
else
|
||||
aa_log_skipped_msg "AppArmor already loaded with profiles."
|
||||
aa_log_skipped_msg ": already loaded with profiles."
|
||||
return 0
|
||||
fi
|
||||
aa_log_end_msg 0
|
||||
return 0
|
||||
@@ -408,18 +398,16 @@ remove_profiles() {
|
||||
fi
|
||||
|
||||
retval=0
|
||||
#the list of profiles isn't stable once we start adding or removing
|
||||
#them so stor to tmp first
|
||||
MODULE_PLIST=$(mktemp ${APPARMOR_TMPDIR}/tmp.XXXXXXXX)
|
||||
sed -e "s/ (\(enforce\|complain\))$//" "$SFS_MOUNTPOINT/profiles" | sort >"$MODULE_PLIST"
|
||||
cat "$MODULE_PLIST" | while read profile ; do
|
||||
# We filter child profiles as removing the parent will remove
|
||||
# the children
|
||||
sed -e "s/ (\(enforce\|complain\))$//" "$SFS_MOUNTPOINT/profiles" | \
|
||||
LC_COLLATE=C sort | grep -v // | while read profile ; do
|
||||
echo -n "$profile" > "$SFS_MOUNTPOINT/.remove"
|
||||
rc=$?
|
||||
if [ ${rc} -ne 0 ] ; then
|
||||
retval=${rc}
|
||||
fi
|
||||
done
|
||||
rm "$MODULE_PLIST"
|
||||
return ${retval}
|
||||
}
|
||||
|
||||
@@ -427,7 +415,7 @@ apparmor_stop() {
|
||||
aa_log_daemon_msg "Unloading AppArmor profiles "
|
||||
remove_profiles
|
||||
rc=$?
|
||||
log_end_msg $rc
|
||||
aa_log_end_msg $rc
|
||||
return $rc
|
||||
}
|
||||
|
||||
@@ -459,20 +447,41 @@ __apparmor_restart() {
|
||||
return 4
|
||||
fi
|
||||
|
||||
aa_log_daemon_msg "Restarting AppArmor"
|
||||
|
||||
configure_owlsm
|
||||
parse_profiles reload
|
||||
PNAMES_LIST=$(mktemp ${APPARMOR_TMPDIR}/tmp.XXXXXXXX)
|
||||
profiles_names_list ${PNAMES_LIST}
|
||||
MODULE_PLIST=$(mktemp ${APPARMOR_TMPDIR}/tmp.XXXXXXXX)
|
||||
# Clean out running profiles not associated with the current profile
|
||||
# set, excluding the libvirt dynamically generated profiles.
|
||||
sed -e "s/ (\(enforce\|complain\))$//" "$SFS_MOUNTPOINT/profiles" | egrep -v '^libvirt-[0-9a-f\-]+$' | sort >"$MODULE_PLIST"
|
||||
sort "$PNAMES_LIST" | comm -2 -3 "$MODULE_PLIST" - | while IFS= read profile ; do
|
||||
# Note that we reverse sort the list of profiles to remove to
|
||||
# ensure that child profiles (e.g. hats) are removed before the
|
||||
# parent. We *do* need to remove the child profile and not rely
|
||||
# on removing the parent profile when the profile has had its
|
||||
# child profile names changed.
|
||||
profiles_names_list | awk '
|
||||
BEGIN {
|
||||
while (getline < "'${SFS_MOUNTPOINT}'/profiles" ) {
|
||||
str = sub(/ \((enforce|complain)\)$/, "", $0);
|
||||
if (match($0, /^libvirt-[0-9a-f\-]+$/) == 0)
|
||||
arr[$str] = $str
|
||||
}
|
||||
}
|
||||
|
||||
{ if (length(arr[$0]) > 0) { delete arr[$0] } }
|
||||
|
||||
END {
|
||||
for (key in arr)
|
||||
if (length(arr[key]) > 0) {
|
||||
printf("%s\n", arr[key])
|
||||
}
|
||||
}
|
||||
' | LC_COLLATE=C sort -r | while IFS= read profile ; do
|
||||
echo -n "$profile" > "$SFS_MOUNTPOINT/.remove"
|
||||
done
|
||||
rm "$MODULE_PLIST"
|
||||
rm "$PNAMES_LIST"
|
||||
return 0
|
||||
# will not catch all errors, but still better than nothing
|
||||
rc=$?
|
||||
aa_log_end_msg $rc
|
||||
return $rc
|
||||
}
|
||||
|
||||
apparmor_restart() {
|
||||
@@ -516,11 +525,11 @@ apparmor_status () {
|
||||
${SD_STATUS} --verbose
|
||||
return $?
|
||||
fi
|
||||
if ! is_apparmor_present apparmor subdomain ; then
|
||||
if ! is_apparmor_loaded ; then
|
||||
echo "AppArmor is not loaded."
|
||||
rc=1
|
||||
else
|
||||
echo "AppArmor is enabled,"
|
||||
echo "AppArmor is enabled."
|
||||
rc=0
|
||||
fi
|
||||
echo "Install the apparmor-utils package to receive more detailed"
|
||||
|
@@ -31,6 +31,7 @@
|
||||
# Required-Start: boot.cleanup
|
||||
# Required-Stop: $null
|
||||
# Should-Start: $local_fs
|
||||
# Should-Stop: $null
|
||||
# Default-Start: B
|
||||
# Default-Stop:
|
||||
# Short-Description: AppArmor initialization
|
||||
@@ -73,7 +74,19 @@ aa_log_warning_msg() {
|
||||
}
|
||||
|
||||
aa_log_failure_msg() {
|
||||
log_failure_msg $*
|
||||
log_failure_msg '\n'$*
|
||||
}
|
||||
|
||||
aa_log_action_start() {
|
||||
echo -n
|
||||
}
|
||||
|
||||
aa_log_action_end() {
|
||||
echo -n
|
||||
}
|
||||
|
||||
aa_log_daemon_msg() {
|
||||
echo -en "$@ "
|
||||
}
|
||||
|
||||
aa_log_skipped_msg() {
|
||||
@@ -81,6 +94,14 @@ aa_log_skipped_msg() {
|
||||
echo -e "$rc_skipped"
|
||||
}
|
||||
|
||||
aa_log_end_msg() {
|
||||
v="-v"
|
||||
if [ "$1" != '0' ]; then
|
||||
rc="-v$1"
|
||||
fi
|
||||
rc_status $v
|
||||
}
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 {start|stop|restart|try-restart|reload|force-reload|status|kill}"
|
||||
}
|
||||
|
@@ -4,9 +4,10 @@ TESTS=simple.pl
|
||||
PARSER_DIR=..
|
||||
PARSER_BIN=apparmor_parser
|
||||
PARSER=$(PARSER_DIR)/$(PARSER_BIN)
|
||||
PROVE_ARG=-f
|
||||
|
||||
ifeq ($(VERBOSE),1)
|
||||
PROVE_ARG=-v
|
||||
PROVE_ARG+=-v
|
||||
endif
|
||||
|
||||
all: tests
|
||||
@@ -14,9 +15,14 @@ all: tests
|
||||
.PHONY: tests error_output gen_xtrans parser_sanity caching
|
||||
tests: error_output gen_xtrans parser_sanity caching
|
||||
|
||||
gen_xtrans:
|
||||
GEN_TRANS_DIRS=simple_tests/generated_x/ simple_tests/generated_perms_leading/ simple_tests/generated_perms_safe/
|
||||
|
||||
gen_xtrans: $(GEN_TRANS_DIRS)
|
||||
./gen-xtrans.pl
|
||||
|
||||
$(GEN_TRANS_DIRS):
|
||||
mkdir $@
|
||||
|
||||
error_output: $(PARSER)
|
||||
$(PARSER) -S -I errors >/dev/null errors/okay.sd
|
||||
LANG=C $(PARSER) -S -I errors 2>&1 >/dev/null errors/single.sd | \
|
||||
@@ -39,6 +45,4 @@ $(PARSER):
|
||||
make -C $(PARSER_DIR) $(PARSER_BIN)
|
||||
|
||||
clean:
|
||||
rm -f simple_tests/generated_x/*
|
||||
rm -f simple_tests/generated_perms_leading/*
|
||||
rm -f simple_tests/generated_perms_safe/*
|
||||
find $(GEN_TRANS_DIRS) -type f | xargs rm -f
|
||||
|
@@ -94,3 +94,13 @@ sleep $timeout
|
||||
touch $basedir/cache/$profile
|
||||
../apparmor_parser $ARGS -v -r $basedir/$profile | grep -q 'Cached reload succeeded' || { echo "FAIL"; exit 1; }
|
||||
echo "ok"
|
||||
|
||||
echo -n "Cache reading is skipped when parser is newer: "
|
||||
mkdir $basedir/parser
|
||||
cp ../apparmor_parser $basedir/parser/
|
||||
$basedir/parser/apparmor_parser $ARGS -v -r $basedir/$profile | grep -q 'Replacement succeeded for' || { echo "FAIL"; exit 1; }
|
||||
echo "ok"
|
||||
|
||||
echo -n "Cache reading is skipped when parser in \$PATH is newer: "
|
||||
(PATH=$basedir/parser/ /bin/sh -c "apparmor_parser $ARGS -v -r $basedir/$profile") | grep -q 'Replacement succeeded for' || { echo "FAIL"; exit 1; }
|
||||
echo "ok"
|
||||
|
@@ -118,7 +118,7 @@ sub gen_file($$$$$$$$$$$$) {
|
||||
print $file "/usr/bin/foo {\n";
|
||||
print_rule($file, $leading1, $qual1, $rule1, $perm1, $target1);
|
||||
print_rule($file, $leading2, $qual2, $rule2, $perm2, $target2);
|
||||
print $file "}";
|
||||
print $file "}\n";
|
||||
close($file);
|
||||
|
||||
$count++;
|
||||
|
9
parser/tst/simple_tests/vars/vars_alternation_1.sd
Normal file
9
parser/tst/simple_tests/vars/vars_alternation_1.sd
Normal file
@@ -0,0 +1,9 @@
|
||||
#=DESCRIPTION reference variables in rules that also have alternations
|
||||
#=EXRESULT PASS
|
||||
|
||||
@{FOO}=bar baz
|
||||
@{BAR}=@{FOO} blort
|
||||
|
||||
/does/not/exist {
|
||||
/does/not/{exist,notexist}/blah/@{BAR} r,
|
||||
}
|
9
parser/tst/simple_tests/vars/vars_alternation_2.sd
Normal file
9
parser/tst/simple_tests/vars/vars_alternation_2.sd
Normal file
@@ -0,0 +1,9 @@
|
||||
#=DESCRIPTION reference variables in rules that also have alternations
|
||||
#=EXRESULT PASS
|
||||
|
||||
@{FOO}=bar baz
|
||||
@{BAR}=@{FOO} blort
|
||||
|
||||
/does/not/exist {
|
||||
/does/not/@{BAR}/blah/{exist,notexist} r,
|
||||
}
|
7
parser/tst/simple_tests/vars/vars_bad_4.sd
Normal file
7
parser/tst/simple_tests/vars/vars_bad_4.sd
Normal file
@@ -0,0 +1,7 @@
|
||||
#=DESCRIPTION don't accept variables with leading underscores
|
||||
#=EXRESULT FAIL
|
||||
@{_FOO} = /foo /bar /baz /biff
|
||||
|
||||
/usr/bin/foo {
|
||||
/@{_FOO}/.foo/* r,
|
||||
}
|
7
parser/tst/simple_tests/vars/vars_bad_5.sd
Normal file
7
parser/tst/simple_tests/vars/vars_bad_5.sd
Normal file
@@ -0,0 +1,7 @@
|
||||
#=DESCRIPTION don't accept variables with leading numeric
|
||||
#=EXRESULT FAIL
|
||||
@{4FOO} = /foo /bar /baz /biff
|
||||
|
||||
/usr/bin/foo {
|
||||
/@{4FOO}/.foo/* r,
|
||||
}
|
9
parser/tst/simple_tests/vars/vars_file_evaluation_15.sd
Normal file
9
parser/tst/simple_tests/vars/vars_file_evaluation_15.sd
Normal file
@@ -0,0 +1,9 @@
|
||||
#=DESCRIPTION simple expansions within file rules with numeric variable
|
||||
#=EXRESULT PASS
|
||||
@{FOO1} = /foo /bar /baz /biff
|
||||
@{B1A1R1} = @{FOO1}
|
||||
|
||||
/usr/bin/foo {
|
||||
/@{FOO1}/.foo/* r,
|
||||
/foo/@{B1A1R1}/.foo/* r,
|
||||
}
|
7
parser/tst/simple_tests/vars/vars_file_evaluation_16.sd
Normal file
7
parser/tst/simple_tests/vars/vars_file_evaluation_16.sd
Normal file
@@ -0,0 +1,7 @@
|
||||
#=DESCRIPTION simple expansions within file rules with underscore variable
|
||||
#=EXRESULT PASS
|
||||
@{F_OO} = /foo /bar /baz /biff
|
||||
|
||||
/usr/bin/foo {
|
||||
/@{F_OO}/.foo/* r,
|
||||
}
|
@@ -20,7 +20,7 @@
|
||||
# Makefile for LSM-based AppArmor profiles
|
||||
|
||||
NAME=apparmor-profiles
|
||||
ALL:
|
||||
ALL: local
|
||||
COMMONDIR=../common/
|
||||
|
||||
include common/Make.rules
|
||||
@@ -38,7 +38,7 @@ PROFILES_SOURCE=./apparmor.d
|
||||
EXTRAS_SOURCE=./apparmor/profiles/extras/
|
||||
SUBDIRS_MUST_BE_SKIPPED=${PROFILES_SOURCE}/abstractions ${PROFILES_SOURCE}/apache2.d ${PROFILES_SOURCE}/program-chunks ${PROFILES_SOURCE}/tunables ${PROFILES_SOURCE}/local
|
||||
PROFILES_TO_COPY=$(filter-out ${SUBDIRS_MUST_BE_SKIPPED}, $(wildcard ${PROFILES_SOURCE}/*))
|
||||
TUNABLES_TO_COPY=$(filter-out ${PROFILES_SOURCE}/tunables/home.d, $(wildcard ${PROFILES_SOURCE}/tunables/*))
|
||||
TUNABLES_TO_COPY=$(filter-out ${PROFILES_SOURCE}/tunables/home.d ${PROFILES_SOURCE}/tunables/multiarch.d, $(wildcard ${PROFILES_SOURCE}/tunables/*))
|
||||
ABSTRACTIONS_TO_COPY=$(filter-out ${PROFILES_SOURCE}/abstractions/ubuntu-browsers.d, $(wildcard ${PROFILES_SOURCE}/abstractions/*))
|
||||
|
||||
local:
|
||||
@@ -52,6 +52,7 @@ install: local
|
||||
install -m 755 -d ${PROFILES_DEST}
|
||||
install -m 755 -d ${PROFILES_DEST}/abstractions \
|
||||
${PROFILES_DEST}/apache2.d \
|
||||
${PROFILES_DEST}/disable \
|
||||
${PROFILES_DEST}/program-chunks \
|
||||
${PROFILES_DEST}/tunables \
|
||||
${PROFILES_DEST}/tunables/home.d \
|
||||
@@ -64,6 +65,7 @@ install: local
|
||||
install -m 644 ${PROFILES_SOURCE}/program-chunks/* ${PROFILES_DEST}/program-chunks
|
||||
install -m 644 ${TUNABLES_TO_COPY} ${PROFILES_DEST}/tunables
|
||||
install -m 644 ${PROFILES_SOURCE}/tunables/home.d/* ${PROFILES_DEST}/tunables/home.d
|
||||
install -m 644 ${PROFILES_SOURCE}/tunables/multiarch.d/* ${PROFILES_DEST}/tunables/multiarch.d
|
||||
install -m 755 -d ${EXTRAS_DEST}
|
||||
install -m 644 ${EXTRAS_SOURCE}/* ${EXTRAS_DEST}
|
||||
install -m 644 ${PROFILES_SOURCE}/local/* ${PROFILES_DEST}/local
|
||||
@@ -81,23 +83,22 @@ endif
|
||||
|
||||
ifndef PARSER
|
||||
# use system parser
|
||||
PARSER=/sbin/apparmor_parser
|
||||
PARSER=../parser/apparmor_parser
|
||||
endif
|
||||
|
||||
ifndef LOGPROF
|
||||
# use system logprof
|
||||
LOGPROF=/usr/sbin/aa-logprof
|
||||
# use ../utils logprof
|
||||
LOGPROF=perl -I../utils/ ../utils/aa-logprof
|
||||
endif
|
||||
|
||||
EXTRAS_PATH=${EXTRAS_SOURCE}/profiles/extras
|
||||
IGNORE_FILES=${EXTRAS_PATH}/README
|
||||
CHECK_PROFILES=$(filter-out ${IGNORE_FILES}, $(wildcard ${PROFILES_SOURCE}/*) $(wildcard ${EXTRAS_PATH}/*))
|
||||
IGNORE_FILES=${EXTRAS_SOURCE}/README
|
||||
CHECK_PROFILES=$(filter-out ${IGNORE_FILES} ${SUBDIRS_MUST_BE_SKIPPED}, $(wildcard ${PROFILES_SOURCE}/*) $(wildcard ${EXTRAS_SOURCE}/*))
|
||||
|
||||
.PHONY: check
|
||||
check:
|
||||
@echo "*** Checking profiles from ${PROFILES_SOURCE} and ${EXTRAS_PATH} against apparmor_parser"
|
||||
@echo "*** Checking profiles from ${PROFILES_SOURCE} and ${EXTRAS_SOURCE} against apparmor_parser"
|
||||
$(Q)for profile in ${CHECK_PROFILES} ; do \
|
||||
${PARSER} -S -I ${PWD}/apparmor.d $${profile} > /dev/null ; \
|
||||
${PARSER} -S -b ${PWD}/apparmor.d $${profile} > /dev/null || exit 1; \
|
||||
done
|
||||
@echo "*** Checking profiles from ${PROFILES_SOURCE} against logprof"
|
||||
$(Q)${LOGPROF} -d ${PROFILES_SOURCE} -f /dev/null
|
||||
$(Q)${LOGPROF} -d ${PROFILES_SOURCE} -f /dev/null || exit 1
|
||||
|
@@ -2,7 +2,7 @@
|
||||
# ------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (C) 2002-2009 Novell/SUSE
|
||||
# Copyright (C) 2009 Canonical Ltd.
|
||||
# Copyright (C) 2009-2011 Canonical Ltd.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2 of the GNU General Public
|
||||
@@ -17,7 +17,8 @@
|
||||
|
||||
# .Xauthority files required for X connections, per user
|
||||
@{HOME}/.Xauthority r,
|
||||
owner /var/run/gdm/*/database r,
|
||||
owner /{,var/}run/gdm/*/database r,
|
||||
owner /{,var/}run/lightdm/authority/[0-9]* r,
|
||||
|
||||
# the unix socket to use to connect to the display
|
||||
/tmp/.X11-unix/* w,
|
||||
@@ -33,6 +34,7 @@
|
||||
|
||||
# DRI
|
||||
/usr/lib{,32,64}/dri/** mr,
|
||||
/usr/lib/@{multiarch}/dri/** mr,
|
||||
/dev/dri/** rw,
|
||||
|
||||
# mouse themes
|
||||
|
@@ -50,8 +50,8 @@
|
||||
# pulse
|
||||
/etc/pulse/ r,
|
||||
/etc/pulse/* r,
|
||||
/dev/shm/ r,
|
||||
owner /dev/shm/pulse-shm* rwk,
|
||||
/{run,dev}/shm/ r,
|
||||
owner /{run,dev}/shm/pulse-shm* rwk,
|
||||
owner @{HOME}/.pulse-cookie rwk,
|
||||
owner @{HOME}/.pulse/ rw,
|
||||
owner @{HOME}/.pulse/* rwk,
|
||||
@@ -61,3 +61,7 @@ owner /tmp/pulse-*/* rw,
|
||||
# libgnome2
|
||||
/etc/sound/ r,
|
||||
/etc/sound/** r,
|
||||
|
||||
# openal
|
||||
/etc/openal/alsoft.conf r,
|
||||
owner @{HOME}/.alsoftrc r,
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# ------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (C) 2002-2009 Novell/SUSE
|
||||
# Copyright (C) 2009 Canonical Ltd
|
||||
# Copyright (C) 2009-2011 Canonical Ltd
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2 of the GNU General Public
|
||||
@@ -25,6 +25,9 @@
|
||||
/lib{,32,64}/security/pam_filter/* mr,
|
||||
/lib{,32,64}/security/pam_*.so mr,
|
||||
/lib{,32,64}/security/ r,
|
||||
/lib/@{multiarch}/security/pam_filter/* mr,
|
||||
/lib/@{multiarch}/security/pam_*.so mr,
|
||||
/lib/@{multiarch}/security/ r,
|
||||
|
||||
# kerberos
|
||||
#include <abstractions/kerberosclient>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
# ------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (C) 2002-2009 Novell/SUSE
|
||||
# Copyright (C) 2009 Canonical Ltd.
|
||||
# Copyright (C) 2009-2011 Canonical Ltd.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2 of the GNU General Public
|
||||
@@ -36,6 +36,8 @@
|
||||
/usr/lib{,32,64}/locale/** mr,
|
||||
/usr/lib{,32,64}/gconv/*.so mr,
|
||||
/usr/lib{,32,64}/gconv/gconv-modules* mr,
|
||||
/usr/lib/@{multiarch}/gconv/*.so mr,
|
||||
/usr/lib/@{multiarch}/gconv/gconv-modules mr,
|
||||
|
||||
# used by glibc when binding to ephemeral ports
|
||||
/etc/bindresvport.blacklist r,
|
||||
@@ -45,17 +47,26 @@
|
||||
/etc/ld.so.cache mr,
|
||||
/lib{,32,64}/ld{,32,64}-*.so mrix,
|
||||
/lib{,32,64}/**/ld{,32,64}-*.so mrix,
|
||||
/lib/@{multiarch}/ld{,32,64}-*.so mrix,
|
||||
/lib/tls/i686/{cmov,nosegneg}/ld-*.so mrix,
|
||||
/lib/i386-linux-gnu/tls/i686/{cmov,nosegneg}/ld-*.so mrix,
|
||||
/opt/*-linux-uclibc/lib/ld-uClibc*so* mrix,
|
||||
|
||||
# we might as well allow everything to use common libraries
|
||||
/lib{,32,64}/** r,
|
||||
/lib{,32,64}/lib*.so* mr,
|
||||
/lib{,32,64}/**/lib*.so* mr,
|
||||
/lib/@{multiarch}/** r,
|
||||
/lib/@{multiarch}/lib*.so* mr,
|
||||
/lib/@{multiarch}/**/lib*.so* mr,
|
||||
/usr/lib{,32,64}/** r,
|
||||
/usr/lib{,32,64}/*.so* mr,
|
||||
/usr/lib{,32,64}/**/lib*.so* mr,
|
||||
/usr/lib/@{multiarch}/** r,
|
||||
/usr/lib/@{multiarch}/lib*.so* mr,
|
||||
/usr/lib/@{multiarch}/**/lib*.so* mr,
|
||||
/lib/tls/i686/{cmov,nosegneg}/lib*.so* mr,
|
||||
/lib/i386-linux-gnu/tls/i686/{cmov,nosegneg}/lib*.so* mr,
|
||||
|
||||
# /dev/null is pretty harmless and frequently used
|
||||
/dev/null rw,
|
||||
|
@@ -12,4 +12,4 @@
|
||||
# discoverable system configuration for non-local cupsd
|
||||
/etc/cups/client.conf r,
|
||||
# client should be able to talk the local cupsd
|
||||
/var/run/cups/cups.sock w,
|
||||
/{,var/}run/cups/cups.sock w,
|
||||
|
@@ -10,4 +10,4 @@
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
# System socket. Be careful when including this abstraction.
|
||||
/var/run/dbus/system_bus_socket w,
|
||||
/{,var/}run/dbus/system_bus_socket w,
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# vim:syntax=apparmor
|
||||
# ------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (C) 2010 Canonical Ltd.
|
||||
# Copyright (C) 2011 Canonical Ltd.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2 of the GNU General Public
|
||||
@@ -9,5 +9,5 @@
|
||||
#
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
/usr/bin/dbus-launch Pix,
|
||||
/usr/bin/dbus-launch ix,
|
||||
/var/lib/dbus/machine-id r,
|
||||
|
@@ -2,7 +2,7 @@
|
||||
# ------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (C) 2002-2009 Novell/SUSE
|
||||
# Copyright (C) 2009-2010 Canonical Ltd.
|
||||
# Copyright (C) 2009-2011 Canonical Ltd.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2 of the GNU General Public
|
||||
@@ -19,6 +19,7 @@
|
||||
/etc/gnome/gtkrc* r,
|
||||
/etc/gtk/* r,
|
||||
/usr/lib{,32,64}/gtk/** mr,
|
||||
/usr/lib/@{multiarch}/gtk/** mr,
|
||||
/usr/share/themes/** r,
|
||||
|
||||
# for gnome 1 applications
|
||||
@@ -31,6 +32,9 @@
|
||||
/usr/lib{,32,64}/pango/** mr,
|
||||
/usr/lib{,32,64}/gtk-*/** mr,
|
||||
/usr/lib{,32,64}/gdk-pixbuf-*/** mr,
|
||||
/usr/lib/@{multiarch}/pango/** mr,
|
||||
/usr/lib/@{multiarch}/gtk-*/** mr,
|
||||
/usr/lib/@{multiarch}/gdk-pixbuf-*/** mr,
|
||||
|
||||
# per-user gtk configuration
|
||||
@{HOME}/.gnome/Gnome r,
|
||||
@@ -60,6 +64,7 @@
|
||||
/etc/gnome-vfs-2.0/modules/ r,
|
||||
/etc/gnome-vfs-2.0/modules/* r,
|
||||
/usr/lib/gnome-vfs-2.0/modules/*.so mr,
|
||||
/usr/lib/@{multiarch}/gnome-vfs-2.0/modules/*.so mr,
|
||||
|
||||
# gvfs
|
||||
/usr/share/gvfs/remote-volume-monitors/ r,
|
||||
@@ -72,7 +77,8 @@
|
||||
/usr/share/cups/charmaps/** r,
|
||||
|
||||
# holds MIT-MAGIC-COOKIE for gnome
|
||||
owner /var/run/gdm/auth*/database r,
|
||||
owner /{,var/}run/gdm/auth*/database r,
|
||||
|
||||
# mime-types
|
||||
/etc/gnome/defaults.list r,
|
||||
/usr/share/gnome/applications/mimeinfo.cache r,
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# ------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (C) 2002-2006 Novell/SUSE
|
||||
# Copyright (C) 2009-2010 Canonical Ltd.
|
||||
# Copyright (C) 2009-2011 Canonical Ltd.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2 of the GNU General Public
|
||||
@@ -38,10 +38,17 @@
|
||||
/usr/lib*/kde3/plugins/styles/ r,
|
||||
/usr/lib*/kde3/plugins/styles/* mr,
|
||||
/usr/lib*/kde3/lib*so* mr,
|
||||
/usr/lib/@{multiarch}/kde3/plugins/styles/ r,
|
||||
/usr/lib/@{multiarch}/kde3/plugins/styles/* mr,
|
||||
/usr/lib/@{multiarch}/kde3/lib*so* mr,
|
||||
/usr/lib*/qt3/lib*/lib*so* mr,
|
||||
/usr/lib*/qt3/plugins/** mr,
|
||||
/usr/lib/@{multiarch}/qt3/lib*/lib*so* mr,
|
||||
/usr/lib/@{multiarch}/qt3/plugins/** mr,
|
||||
/usr/lib*/libqt-mt*so* mr,
|
||||
/usr/lib*/libqui*so* mr,
|
||||
/usr/lib/@{multiarch}/libqt-mt*so* mr,
|
||||
/usr/lib/@{multiarch}/libqui*so* mr,
|
||||
/usr/share/qt3/lib*/libqt-mt*so* mr,
|
||||
/usr/share/qt3/lib*/libqui*so* mr,
|
||||
|
||||
@@ -49,6 +56,11 @@
|
||||
/usr/lib*/kde4/plugins/*/*.so mr,
|
||||
/usr/lib*/kde4/plugins/*/ r,
|
||||
/usr/lib*/kde4/lib*so* mr,
|
||||
/usr/lib/@{multiarch}/kde4/plugins/*/*.so mr,
|
||||
/usr/lib/@{multiarch}/kde4/plugins/*/ r,
|
||||
/usr/lib/@{multiarch}/kde4/lib*so* mr,
|
||||
/usr/lib*/qt4/lib*/lib*so* mr,
|
||||
/usr/lib*/qt4/plugins/** mr,
|
||||
/usr/lib/@{multiarch}/qt4/lib*/lib*so* mr,
|
||||
/usr/lib/@{multiarch}/qt4/plugins/** mr,
|
||||
/usr/share/qt4/** r,
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# ------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (C) 2002-2009 Novell/SUSE
|
||||
# Copyright (C) 2009 Canonical Ltd.
|
||||
# Copyright (C) 2009-2011 Canonical Ltd.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2 of the GNU General Public
|
||||
@@ -12,9 +12,13 @@
|
||||
# files required by kerberos client programs
|
||||
/usr/lib{,32,64}/krb5/plugins/libkrb5/ r,
|
||||
/usr/lib{,32,64}/krb5/plugins/libkrb5/* mr,
|
||||
/usr/lib/@{multiarch}/krb5/plugins/libkrb5/ r,
|
||||
/usr/lib/@{multiarch}/krb5/plugins/libkrb5/* mr,
|
||||
|
||||
/usr/lib{,32,64}/krb5/plugins/preauth/ r,
|
||||
/usr/lib{,32,64}/krb5/plugins/preauth/* mr,
|
||||
/usr/lib/@{multiarch}/krb5/plugins/preauth/ r,
|
||||
/usr/lib/@{multiarch}/krb5/plugins/preauth/* mr,
|
||||
|
||||
/etc/krb5.keytab r,
|
||||
/etc/krb5.conf r,
|
||||
|
21
profiles/apparmor.d/abstractions/ldapclient
Normal file
21
profiles/apparmor.d/abstractions/ldapclient
Normal file
@@ -0,0 +1,21 @@
|
||||
# ------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (C) 2011 Novell/SUSE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2 of the GNU General Public
|
||||
# License published by the Free Software Foundation.
|
||||
#
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
# files required by LDAP clients (e.g. nss_ldap/pam_ldap)
|
||||
/etc/ldap.conf r,
|
||||
/etc/ldap.secret r,
|
||||
/etc/openldap/* r,
|
||||
/etc/openldap/cacerts/* r,
|
||||
|
||||
# SASL plugins and config
|
||||
/etc/sasl2/* r,
|
||||
/usr/lib{,32,64}/sasl2/* r,
|
||||
|
||||
#include <abstractions/ssl_certs>
|
@@ -10,4 +10,4 @@
|
||||
|
||||
# mdnsd
|
||||
/etc/nss_mdns.conf r,
|
||||
/var/run/mdnsd w,
|
||||
/{,var/}run/mdnsd w,
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# ------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (C) 2002-2009 Novell/SUSE
|
||||
# Copyright (C) 2009 Canonical Ltd.
|
||||
# Copyright (C) 2009-2011 Canonical Ltd.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2 of the GNU General Public
|
||||
@@ -16,8 +16,6 @@
|
||||
/etc/group r,
|
||||
/etc/host.conf r,
|
||||
/etc/hosts r,
|
||||
/etc/ldap.conf r,
|
||||
/etc/ldap.secret r,
|
||||
/etc/nsswitch.conf r,
|
||||
/etc/gai.conf r,
|
||||
/etc/passwd r,
|
||||
@@ -25,39 +23,41 @@
|
||||
|
||||
/etc/resolv.conf r,
|
||||
# on systems using resolvconf, /etc/resolv.conf is a symlink to
|
||||
# /var/run/resolvconf/resolv.conf and a file sometimes referenced in
|
||||
# /{,var/}run/resolvconf/resolv.conf and a file sometimes referenced in
|
||||
# /etc/resolvconf/run/resolv.conf
|
||||
/var/run/resolvconf/resolv.conf r,
|
||||
/{,var/}run/resolvconf/resolv.conf r,
|
||||
/etc/resolvconf/run/resolv.conf r,
|
||||
|
||||
/etc/samba/lmhosts r,
|
||||
/etc/services r,
|
||||
# all openldap config
|
||||
/etc/openldap/* r,
|
||||
/etc/ldap/** r,
|
||||
# db backend
|
||||
/var/lib/misc/*.db r,
|
||||
# The Name Service Cache Daemon can cache lookups, sometimes leading
|
||||
# to vast speed increases when working with network-based lookups.
|
||||
/var/run/.nscd_socket rw,
|
||||
/var/run/nscd/socket rw,
|
||||
/{,var/}run/.nscd_socket rw,
|
||||
/{,var/}run/nscd/socket rw,
|
||||
/var/{db,cache,run}/nscd/{passwd,group,services,host} r,
|
||||
# nscd renames and unlinks files in it's operation that clients will
|
||||
# have open
|
||||
/var/run/nscd/db* rmix,
|
||||
/{,var/}run/nscd/db* rmix,
|
||||
|
||||
# The nss libraries are sometimes used in addition to PAM; make sure
|
||||
# they are available
|
||||
/lib{,32,64}/libnss_*.so* mr,
|
||||
/usr/lib{,32,64}/libnss_*.so* mr,
|
||||
/lib/@{multiarch}/libnss_*.so* mr,
|
||||
/usr/lib/@{multiarch}/libnss_*.so* mr,
|
||||
/etc/default/nss r,
|
||||
|
||||
# avahi-daemon is used for mdns4 resolution
|
||||
/var/run/avahi-daemon/socket w,
|
||||
/{,var/}run/avahi-daemon/socket w,
|
||||
|
||||
# nis
|
||||
#include <abstractions/nis>
|
||||
|
||||
# ldap
|
||||
#include <abstractions/ldapclient>
|
||||
|
||||
# winbind
|
||||
#include <abstractions/winbind>
|
||||
|
||||
|
13
profiles/apparmor.d/abstractions/openssl
Normal file
13
profiles/apparmor.d/abstractions/openssl
Normal file
@@ -0,0 +1,13 @@
|
||||
# ------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (C) 2011 Novell/SUSE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2 of the GNU General Public
|
||||
# License published by the Free Software Foundation.
|
||||
#
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
/etc/ssl/openssl.cnf r,
|
||||
/usr/share/ssl/openssl.cnf r,
|
||||
|
@@ -17,12 +17,21 @@
|
||||
audit deny @{HOME}/.config/autostart/** wl,
|
||||
audit deny @{HOME}/.kde/Autostart/** wl,
|
||||
|
||||
deny @{HOME}/.bash* mrk,
|
||||
audit deny @{HOME}/.bash* wl,
|
||||
|
||||
deny @{HOME}/.profile* mrk,
|
||||
audit deny @{HOME}/.profile* wl,
|
||||
|
||||
# don't allow reading/updating of run control files
|
||||
deny @{HOME}/.*rc mrk,
|
||||
audit deny @{HOME}/.*rc wl,
|
||||
|
||||
# bash
|
||||
deny @{HOME}/.bash* mrk,
|
||||
audit deny @{HOME}/.bash* wl,
|
||||
deny @{HOME}/.inputrc mrk,
|
||||
audit deny @{HOME}/.inputrc wl,
|
||||
|
||||
# sh/dash/csh/tcsh/pdksh/zsh
|
||||
deny @{HOME}/.{,z}profile* mrk,
|
||||
audit deny @{HOME}/.{,z}profile* wl,
|
||||
deny @{HOME}/.{,z}log{in,out} mrk,
|
||||
audit deny @{HOME}/.{,z}log{in,out} wl,
|
||||
|
||||
deny @{HOME}/.zshenv mrk,
|
||||
audit deny @{HOME}/.zshenv wl,
|
||||
|
@@ -29,3 +29,6 @@
|
||||
|
||||
# wx paths
|
||||
/usr/lib/wx/python/*.pth r,
|
||||
|
||||
# python build configuration and headers
|
||||
/usr/include/python{2,3}.[0-7]*/pyconfig.h
|
||||
|
@@ -9,11 +9,11 @@
|
||||
#
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
/etc/samba/smb.conf r,
|
||||
/etc/samba/* r,
|
||||
/usr/share/samba/*.dat r,
|
||||
/var/lib/samba/**.tdb rwk,
|
||||
/var/log/samba/cores/ rw,
|
||||
/var/log/samba/cores/* w,
|
||||
/var/log/samba/cores/** rw,
|
||||
/var/log/samba/log.* w,
|
||||
/var/run/samba/*.tdb rw,
|
||||
/{,var/}run/samba/*.tdb rw,
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# ------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (C) 2002-2005 Novell/SUSE
|
||||
# Copyright (C) 2010 Canonical Ltd.
|
||||
# Copyright (C) 2010-2011 Canonical Ltd.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2 of the GNU General Public
|
||||
@@ -14,3 +14,6 @@
|
||||
/etc/ssl/certs/* r,
|
||||
/usr/share/ca-certificates/ r,
|
||||
/usr/share/ca-certificates/** r,
|
||||
/usr/share/ssl/certs/ca-bundle.crt r,
|
||||
/usr/local/share/ca-certificates/ r,
|
||||
/usr/local/share/ca-certificates/** r,
|
||||
|
@@ -8,6 +8,10 @@
|
||||
# Image viewers
|
||||
/usr/bin/eog PUxr,
|
||||
/usr/bin/gimp* PUxr,
|
||||
/usr/bin/shotwell PUxr,
|
||||
/usr/bin/digikam PUxr,
|
||||
/usr/bin/f-spot PUxr,
|
||||
/usr/bin/gwenview PUxr,
|
||||
|
||||
#include <abstractions/ubuntu-media-players>
|
||||
owner @{HOME}/.macromedia/** rw,
|
||||
|
@@ -11,6 +11,7 @@
|
||||
#include <abstractions/private-files>
|
||||
audit deny @{HOME}/.ssh/** mrwkl,
|
||||
audit deny @{HOME}/.gnome2_private/** mrwkl,
|
||||
audit deny @{HOME}/.kde/share/apps/kwallet/** mrwkl,
|
||||
|
||||
# Comment this out if using gpg plugin/addons
|
||||
audit deny @{HOME}/.gnupg/** mrwkl,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user