2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-09-01 23:05:11 +00:00

Compare commits

..

12 Commits

Author SHA1 Message Date
Steve Beattie
4158b5e095 Fix up the profiles make install target for the tunables/multiarch.d/
change.
2011-03-23 16:07:55 -07:00
Steve Beattie
3706a9c31a Update version for release 2011-03-23 15:01:51 -07:00
Steve Beattie
6bd242fefc Merge from trunk rev 1700: fix typo in multi-arch comment. 2011-03-23 13:45:41 -07:00
Steve Beattie
2ef723e054 Merge from trunk rev 1699: This patch add multiarch support for common
shared library locations, as well as a tunables file and directory
to ease adding additional multiarch paths.

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

With these changes, make && make check should verify the profile set
is compilable and mostly consistent. (Alas, the current profiles are
not quite consistent).

Nominated-By: Steve Beattie <sbeattie@ubuntu.com>
Acked-By: John Johansen <john.johansen@canonical.com>
2011-03-18 23:05:38 -07:00
Steve Beattie
2a031e3d71 prep for releasing a 2.6.1 release candidate 2011-03-18 15:56:09 -07:00
Steve Beattie
1ed68f67a9 Merge from trunk rev 1695: This fixes the apparmor apache2 module
to link correctly against the built libapparmor, as well as working
around libtool so that the libapparmor library build directory does
not get added as an rpath to the module.

Nominated-By: Steve Beattie <sbeattie@ubuntu.com>
Acked-By: John Johansen <john.johansen@canonical.com>

Bug: https://launchpad.net/bugs/737074
2011-03-17 23:35:45 -07:00
Steve Beattie
ffcb113465 Merge from trunk rev 1694: reduce the number of network protocols
filtered out of the parser at build time.

Bug: https://launchpad.net/bugs/732837
2011-03-17 11:18:43 -07:00
Steve Beattie
613a449db6 Merge from trunk rev 1693: Fix from PLD/Arkadiusz Miskiewicz
<arekm@maven.pl> to the initscript helper functions to correct some
log messages and to unload hats first, before their parents.

Nominated-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-03-17 10:24:25 -07:00
Steve Beattie
a14d06f8f6 Update repo url after branching 2.6 off of trunk, as well as adjust the
version to indicate that the branch is working towards a 2.6.1 release.
2011-03-10 10:08:24 -08:00
194 changed files with 4102 additions and 9727 deletions

View File

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

View File

@@ -10,9 +10,13 @@ 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
REPO_URL?=lp:apparmor/2.6
# alternate possibilities to export from
#REPO_URL=.
#REPO_URL="bzr+ssh://bazaar.launchpad.net/~sbeattie/+junk/apparmor-dev/"
@@ -20,17 +24,12 @@ 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 --exclude deprecated -cvzf ${RELEASE_DIR}.tar.gz ${RELEASE_DIR}
tar cvzf ${RELEASE_DIR}.tar.gz ${RELEASE_DIR}
.PHONY: snapshot
snapshot: clean
@@ -38,7 +37,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 --exclude deprecated -cvzf $${SNAPSHOT_DIR}.tar.gz $${SNAPSHOT_DIR} ;
tar cvzf $${SNAPSHOT_DIR}.tar.gz $${SNAPSHOT_DIR} ;
.PHONY: export_dir
@@ -50,9 +49,6 @@ export_dir:
.PHONY: clean
clean:
-rm -rf ${RELEASE_DIR} ./apparmor-${VERSION}~*
for dir in $(DIRS); do \
make -C $$dir clean; \
done
.PHONY: setup
setup:
@@ -60,5 +56,4 @@ setup:
.PHONY: tag
tag:
bzr tag apparmor_${TAG_VERSION}
bzr tag apparmor_${VERSION}

9
README
View File

@@ -65,7 +65,6 @@ $ sh ./autogen.sh
$ 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,
@@ -75,14 +74,14 @@ $ make install
Utilities:
$ cd utils
$ make
$ make check
$ make install
parser:
$ cd parser
$ make
$ make check
$ make tests # not strictly necessary as they are run during the
# build by default
$ make install
@@ -105,10 +104,6 @@ $ 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
-------------------

View File

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

View File

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

View File

@@ -27,8 +27,7 @@ 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})
# Can override by passing LANGS=whatever here
LANGS?=$(patsubst %.po, %, $(wildcard *.po))
LANGS=$(patsubst %.po, %, $(wildcard *.po))
TARGET_MOS=$(foreach lang, $(filter-out $(DISABLED_LANGS),$(LANGS)), ${lang}.mo)
.PHONY: all

View File

@@ -1 +1 @@
2.7.1
2.6.1

View File

@@ -1,218 +0,0 @@
#!/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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -29,9 +29,7 @@ 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.
@@ -246,8 +244,7 @@ should be used.
=head1 SEE ALSO
apparmor(7), apparmor.d(5), apparmor_parser(8), aa_change_profile(2),
aa_getcon(2) and
apparmor(7), apparmor.d(5), apparmor_parser(8), aa_change_profile(2) and
L<http://wiki.apparmor.net>.
=cut

View File

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

View File

@@ -1,116 +0,0 @@
# This publication is intellectual property of Canonical Ltd. Its contents
# can be duplicated, either in part or in whole, provided that a copyright
# label is visibly located on each copy.
#
# All information found in this book has been compiled with utmost
# attention to detail. However, this does not guarantee complete accuracy.
# Neither Canonical Ltd, the authors, nor the translators shall be held
# liable for possible errors or the consequences thereof.
#
# Many of the software and hardware descriptions cited in this book
# are registered trademarks. All trade names are subject to copyright
# restrictions and may be registered trade marks. Canonical Ltd.
# essentially adhere to the manufacturer's spelling.
#
# Names of products and trademarks appearing in this book (with or without
# specific notation) are likewise subject to trademark and trade protection
# laws and may thus fall under copyright restrictions.
#
=pod
=head1 NAME
aa_is_enabled - determine if apparmor is available
aa_find_mountpoint - find where the apparmor interface filesystem is mounted
=head1 SYNOPSIS
B<#include E<lt>sys/apparmor.hE<gt>>
B<int aa_is_enabled(void);>
B<int aa_find_mountpoint(char **mnt);>
Link with B<-lapparmor> when compiling.
=head1 DESCRIPTION
The aa_is_enabled function returns true (1) if apparmor is enabled. If it
isn't it sets the errno to reflect the reason it is not enabled and returns 0.
The aa_find_mountpoint function finds where the apparmor filesystem is mounted
on the system, and returns a string containing the mount path. It is the
caller's responsibility to free(3) the returned path.
=head1 RETURN VALUE
B<aa_is_enabled>
On success 1 is returned. On error, 0 is returned, and errno(3) is set
appropriately.
B<aa_find_mountpoint>
On success zero is returned. On error, -1 is returned, and errno(3) is set
appropriately.
=head1 ERRORS
=over 4
B<aa_is_enabled>
=item B<ENOSYS>
AppArmor extensions to the system are not available.
=item B<ECANCELED>
AppArmor is available on the system but has been disabled at boot.
=item B<ENOENT>
AppArmor is available (and maybe even enforcing policy) but the interface is
not available.
=item B<ENOMEM>
Insufficient memory was available.
=item B<EPERM>
Did not have sufficient permissions to determine if AppArmor is enabled.
=item B<EACCES>
+Did not have sufficient permissions to determine if AppArmor is enabled.
B<aa_find_mountpoint>
=item B<ENOMEM>
Insufficient memory was available.
=item B<EACCES>
Access to the the required paths was denied.
=item B<ENOENT>
The apparmor filesystem mount could not be found
=back
=head1 BUGS
None known. If you find any, please report them at
L<http://https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
apparmor(7), apparmor.d(5), apparmor_parser(8), and
L<http://wiki.apparmor.net>.
=cut

View File

@@ -1,112 +0,0 @@
# This publication is intellectual property of Canonical Ltd. Its contents
# can be duplicated, either in part or in whole, provided that a copyright
# label is visibly located on each copy.
#
# All information found in this book has been compiled with utmost
# attention to detail. However, this does not guarantee complete accuracy.
# Neither Canonical Ltd, the authors, nor the translators shall be held
# liable for possible errors or the consequences thereof.
#
# Many of the software and hardware descriptions cited in this book
# are registered trademarks. All trade names are subject to copyright
# restrictions and may be registered trade marks. Canonical Ltd.
# essentially adhere to the manufacturer's spelling.
#
# Names of products and trademarks appearing in this book (with or without
# specific notation) are likewise subject to trademark and trade protection
# laws and may thus fall under copyright restrictions.
#
=pod
=head1 NAME
aa_getprocattr_raw, aa_getprocattr - read and parse procattr data
aa_getcon, aa_gettaskcon - get task confinement information
aa_getpeercon - get the confinement of a socket's other end (peer)
=head1 SYNOPSIS
B<#include E<lt>sys/apparmor.hE<gt>>
B<int aa_getprocattr_raw(pid_t tid, const char *attr, char *buf, int len,
char **mode);>
B<int aa_getprocattr(pid_t tid, const char *attr, char **buf, char **mode);>
B<int aa_gettaskcon(pid_t target, char **con, char **mode);>
B<int aa_getcon(char **con, char **mode);>
B<int aa_getpeercon(int fd, char **con);>
Link with B<-lapparmor> when compiling.
=head1 DESCRIPTION
The aa_getcon function gets the current AppArmor confinement context for the
current task. The confinement context is usually just the name of the AppArmor
profile restricting the task, but it may include the profile namespace or in
some cases a set of profile names (known as a stack of profiles). The returned string *con should be freed using <free()>.
The aa_gettaskcon function is like the aa_getcon function except it will work
for any arbitrary task in the system.
The aa_getpeercon function is similar to that of aa_gettaskcon except that
it returns the confinement information for task on the other end of a socket
connection.
The aa_getprocattr function is the backend for the aa_getcon and aa_gettaskcon
functions and handles the reading and parsing of the confinement data from
different arbitrary attr files and returns the processed results in
an allocated buffer.
The aa_getprocattr_raw() is the backend for the aa_getprocattr function and
does not handle buffer allocation.
=head1 RETURN VALUE
On success zero is returned. On error, -1 is returned, and
errno(3) is set appropriately.
=head1 ERRORS
=over 4
=item B<EINVAL>
The apparmor kernel module is not loaded or the communication via the
F</proc/*/attr/file> did not conform to protocol.
=item B<ENOMEM>
Insufficient kernel memory was available.
=item B<EACCES>
Access to the specified I<file/task> was denied.
=item B<ENOENT>
The specified I<file/task> does not exist or is not visible.
=item B<ERANGE>
The confinement data is to large to fit in the supplied buffer.
=back
=head1 BUGS
None known. If you find any, please report them at
L<http://https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
apparmor(7), apparmor.d(5), apparmor_parser(8), aa_change_profile(2) and
L<http://wiki.apparmor.net>.
=cut

View File

@@ -18,14 +18,8 @@
#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.
@@ -40,17 +34,6 @@ extern int aa_change_onexec(const char *profile);
extern int aa_change_hatv(const char *subprofiles[], unsigned long token);
extern int (aa_change_hat_vargs)(unsigned long token, int count, ...);
/* Protypes for introspecting task confinement
* Please see the aa_getcon(2) manpage for information
*/
extern int aa_getprocattr_raw(pid_t tid, const char *attr, char *buf, int len,
char **mode);
extern int aa_getprocattr(pid_t tid, const char *attr, char **buf, char **mode);
extern int aa_gettaskcon(pid_t target, char **con, char **mode);
extern int aa_getcon(char **con, char **mode);
extern int aa_getpeercon_raw(int fd, char *buffer, int *size);
extern int aa_getpeercon(int fd, char **con);
#define __macroarg_counter(Y...) __macroarg_count1 ( , ##Y)
#define __macroarg_count1(Y...) __macroarg_count2 (Y, 16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)
#define __macroarg_count2(_,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,n,Y...) n

View File

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

View File

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

View File

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

View File

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

View File

@@ -13,17 +13,9 @@
* are manually inserted here
*/
extern int aa_is_enabled(void);
extern int aa_find_mountpoint(char **mnt);
extern int aa_change_hat(const char *subprofile, unsigned long magic_token);
extern int aa_change_profile(const char *profile);
extern int aa_change_onexec(const char *profile);
extern int aa_change_hatv(const char *subprofiles[], unsigned long token);
extern int aa_change_hat_vargs(unsigned long token, int count, ...);
extern int aa_getprocattr_raw(pid_t tid, const char *attr, char *buf, int len,
char **mode);
extern int aa_getprocattr(pid_t tid, const char *attr, char **buf, char **mode);
extern int aa_gettaskcon(pid_t target, char **con, char **mode);
extern int aa_getcon(char **con, char **mode);
extern int aa_getpeercon_raw(int fd, char *buffer, int *size);
extern int aa_getpeercon(int fd, char **con);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,15 +1,15 @@
This license applies to all source files within the AppArmor parser
package.
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Lesser General Public License instead.) You can apply it to
the GNU Library 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) <year> <name of author>
Copyright (C) 19yy <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,16 +306,17 @@ 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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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) year name of author
Gnomovision version 69, Copyright (C) 19yy 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.
@@ -338,5 +339,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 Lesser General
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

View File

@@ -49,7 +49,7 @@ ifndef CFLAGS
CFLAGS = -g -O2 -pipe
ifdef DEBUG
CFLAGS += -pg -D DEBUG
CFLAGS += -pg
endif
endif #CFLAGS
@@ -73,31 +73,29 @@ EXTRA_CFLAGS+=-DPACKAGE=\"${NAME}\" -DLOCALEDIR=\"${LOCALEDIR}\"
# Compile-time configuration of the location of the config file
EXTRA_CFLAGS+=-DSUBDOMAIN_CONFDIR=\"${CONFDIR}\"
SRCS = parser_common.c parser_include.c parser_interface.c parser_lex.c \
parser_main.c parser_misc.c parser_merge.c parser_symtab.c \
parser_yacc.c parser_regex.c parser_variable.c parser_policy.c \
parser_alias.c
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
HDRS = parser.h parser_include.h immunix.h
TOOLS = apparmor_parser
OBJECTS = $(SRCS:.c=.o)
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
AAREDIR= libapparmor_re
AAREOBJECT = ${AAREDIR}/libapparmor_re.a
AAREOBJECTS = $(AAREOBJECT) libstdc++.a
AARE_LDFLAGS=-static-libgcc -L.
AAREOBJECTS = ${AAREDIR}/libapparmor_re.a
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_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)
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})))
ifdef V
VERBOSE = 1
@@ -115,7 +113,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 ; \
@@ -130,7 +128,8 @@ techdoc.txt: techdoc/index.html
# targets arranged this way so that people who don't want full docs can
# pick specific targets they want.
arch: $(TOOLS)
main: $(TOOLS)
$(Q)make -C po all
manpages: $(MANPAGES)
@@ -140,20 +139,13 @@ pdf: techdoc.pdf
docs: manpages htmlmanpages pdf
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`
all: main docs tests
apparmor_parser: $(OBJECTS) $(AAREOBJECTS)
$(CXX) $(EXTRA_CFLAGS) -o $@ $(OBJECTS) $(LIBS) \
${LEXLIB} $(AAREOBJECTS) $(AARE_LDFLAGS)
rm -f ./libstdc++.a
ln -s `g++ -print-file-name=libstdc++.a`
g++ $(EXTRA_CFLAGS) -o $@ $(OBJECTS) $(LIBS) \
${LEXLIB} $(AAREOBJECTS) -static-libgcc -L.
parser_yacc.c parser_yacc.h: parser_yacc.y parser.h
$(YACC) $(YFLAGS) -o parser_yacc.c parser_yacc.y
@@ -197,9 +189,6 @@ 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 $@
@@ -209,35 +198,45 @@ parser_version.h: Makefile
# rewrite these as AF_.
FILTER_FAMILIES=PF_MAX PF_UNSPEC PF_UNIX PF_LOCAL PF_NETLINK
__FILTER=$(shell echo $(strip $(FILTER_FAMILIES)) | sed -e 's/ /\\\|/g')
.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" >> $@
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" $< >> $@
# 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_%: parser_%.c parser.h $(filter-out parser_%.o, ${TEST_OBJECTS})
$(CC) $(TEST_CFLAGS) -o $@ $< $(filter-out $(<:.c=.o), ${TEST_OBJECTS}) $(TEST_LDFLAGS)
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)
.SILENT: check
.PHONY: check
check: tests
.SILENT: tests
tests: apparmor_parser ${TESTS}
sh -e -c 'for test in ${TESTS} ; do echo "*** running $${test}" && ./$${test}; done'
$(Q)$(MAKE) -s -C tst 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
# always need to rebuild.
.SILENT: $(AAREOBJECT)
.PHONY: $(AAREOBJECT)
$(AAREOBJECT):
$(MAKE) -C $(AAREDIR) CFLAGS="$(EXTRA_CXXFLAGS)"
.SILENT: $(AAREOBJECTS)
.PHONY: $(AAREOBJECTS)
$(AAREOBJECTS):
make -C $(AAREDIR) CFLAGS="$(EXTRA_CXXFLAGS)"
.PHONY: install-rhel4
install-rhel4: install-redhat
@@ -246,14 +245,17 @@ 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:
@@ -268,29 +270,22 @@ install-debian:
.PHONY: install-unknown
install-unknown:
INSTALLDEPS=arch
INSTALLDEPS=$(TOOLS)
ifdef DISTRO
INSTALLDEPS+=install-$(DISTRO)
endif
.PHONY: install
install: install-indep install-arch
.PHONY: install-arch
install-arch: $(INSTALLDEPS)
install: $(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
@@ -301,14 +296,15 @@ clean: _clean
rm -f $(YACC_C_FILES)
rm -f parser_version.h
rm -f $(NAME)*.tar.gz $(NAME)*.tgz
rm -f libstdc++.a
rm -f af_names.h
rm -f cap_names.h
rm -rf techdoc.aux techdoc.log techdoc.pdf techdoc.toc techdor.txt techdoc/
$(MAKE) -s -C $(AAREDIR) clean
$(MAKE) -s -C po clean
$(MAKE) -s -C tst clean
make -s -C $(AAREDIR) clean
make -s -C po clean
make -s -C tst clean
.SILENT: dist_clean
dist_clean:
@$(MAKE) clean
@make clean
@rm -f $(LEX_C_FILES) $(YACC_C_FILES)

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,314 +0,0 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
* Copyright 2009-2010 Canonical Ltd.
*
* The libapparmor library is licensed under the terms of the GNU
* Lesser General Public License, version 2.1. Please see the file
* COPYING.LGPL.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Wrapper around the dfa to convert aa rules into a dfa
*/
#include <ostream>
#include <iostream>
#include <fstream>
#include <sstream>
#include <ext/stdio_filebuf.h>
#include <assert.h>
#include <stdlib.h>
#include "aare_rules.h"
#include "expr-tree.h"
#include "parse.h"
#include "hfa.h"
#include "compressed_hfa.h"
#include "../immunix.h"
struct aare_ruleset {
int reverse;
Node *root;
};
extern "C" aare_ruleset_t *aare_new_ruleset(int reverse)
{
aare_ruleset_t *container = (aare_ruleset_t *) malloc(sizeof(aare_ruleset_t));
if (!container)
return NULL;
container->root = NULL;
container->reverse = reverse;
return container;
}
extern "C" void aare_delete_ruleset(aare_ruleset_t *rules)
{
if (rules) {
if (rules->root)
rules->root->release();
free(rules);
}
}
extern "C" int aare_add_rule(aare_ruleset_t *rules, char *rule, int deny,
uint32_t perms, uint32_t audit, dfaflags_t flags)
{
return aare_add_rule_vec(rules, deny, perms, audit, 1, &rule, flags);
}
#define FLAGS_WIDTH 2
#define MATCH_FLAGS_SIZE (sizeof(uint32_t) * 8 - 1)
MatchFlag *match_flags[FLAGS_WIDTH][MATCH_FLAGS_SIZE];
DenyMatchFlag *deny_flags[FLAGS_WIDTH][MATCH_FLAGS_SIZE];
#define EXEC_MATCH_FLAGS_SIZE (AA_EXEC_COUNT *2 * 2 * 2) /* double for each of ix pux, unsafe x bits * u::o */
MatchFlag *exec_match_flags[FLAGS_WIDTH][EXEC_MATCH_FLAGS_SIZE]; /* mods + unsafe + ix + pux * u::o */
ExactMatchFlag *exact_match_flags[FLAGS_WIDTH][EXEC_MATCH_FLAGS_SIZE]; /* mods + unsafe + ix + pux *u::o */
extern "C" void aare_reset_matchflags(void)
{
uint32_t i, j;
#define RESET_FLAGS(group, size) { \
for (i = 0; i < FLAGS_WIDTH; i++) { \
for (j = 0; j < size; j++) { \
if ((group)[i][j]) delete (group)[i][j]; \
(group)[i][j] = NULL; \
} \
} \
}
RESET_FLAGS(match_flags, MATCH_FLAGS_SIZE);
RESET_FLAGS(deny_flags, MATCH_FLAGS_SIZE);
RESET_FLAGS(exec_match_flags, EXEC_MATCH_FLAGS_SIZE);
RESET_FLAGS(exact_match_flags, EXEC_MATCH_FLAGS_SIZE);
#undef RESET_FLAGS
}
extern "C" int aare_add_rule_vec(aare_ruleset_t *rules, int deny,
uint32_t perms, uint32_t audit,
int count, char **rulev, dfaflags_t flags)
{
Node *tree = NULL, *accept;
int exact_match;
assert(perms != 0);
if (regex_parse(&tree, rulev[0]))
return 0;
for (int i = 1; i < count; i++) {
Node *subtree = NULL;
Node *node = new CharNode(0);
if (!node)
return 0;
tree = new CatNode(tree, node);
if (regex_parse(&subtree, rulev[i]))
return 0;
tree = new CatNode(tree, subtree);
}
/*
* Check if we have an expression with or without wildcards. This
* determines how exec modifiers are merged in accept_perms() based
* on how we split permission bitmasks here.
*/
exact_match = 1;
for (depth_first_traversal i(tree); i; i++) {
if (dynamic_cast<StarNode *>(*i) ||
dynamic_cast<PlusNode *>(*i) ||
dynamic_cast<AnyCharNode *>(*i) ||
dynamic_cast<CharSetNode *>(*i) ||
dynamic_cast<NotCharSetNode *>(*i))
exact_match = 0;
}
if (rules->reverse)
flip_tree(tree);
/* 0x7f == 4 bits x mods + 1 bit unsafe mask + 1 bit ix, + 1 pux after shift */
#define EXTRACT_X_INDEX(perm, shift) (((perm) >> (shift + 7)) & 0x7f)
//if (perms & ALL_AA_EXEC_TYPE && (!perms & AA_EXEC_BITS))
// fprintf(stderr, "adding X rule without MAY_EXEC: 0x%x %s\n", perms, rulev[0]);
//if (perms & ALL_EXEC_TYPE)
// fprintf(stderr, "adding X rule %s 0x%x\n", rulev[0], perms);
//if (audit)
//fprintf(stderr, "adding rule with audit bits set: 0x%x %s\n", audit, rulev[0]);
//if (perms & AA_CHANGE_HAT)
// fprintf(stderr, "adding change_hat rule %s\n", rulev[0]);
/* the permissions set is assumed to be non-empty if any audit
* bits are specified */
accept = NULL;
for (unsigned int n = 0; perms && n < (sizeof(perms) * 8); n++) {
uint32_t mask = 1 << n;
if (!(perms & mask))
continue;
int ai = audit & mask ? 1 : 0;
perms &= ~mask;
Node *flag;
if (mask & ALL_AA_EXEC_TYPE)
/* these cases are covered by EXEC_BITS */
continue;
if (deny) {
if (deny_flags[ai][n]) {
flag = deny_flags[ai][n];
} else {
//fprintf(stderr, "Adding deny ai %d mask 0x%x audit 0x%x\n", ai, mask, audit & mask);
deny_flags[ai][n] = new DenyMatchFlag(mask, audit & mask);
flag = deny_flags[ai][n];
}
} else if (mask & AA_EXEC_BITS) {
uint32_t eperm = 0;
uint32_t index = 0;
if (mask & AA_USER_EXEC) {
eperm = mask | (perms & AA_USER_EXEC_TYPE);
index = EXTRACT_X_INDEX(eperm, AA_USER_SHIFT);
} else {
eperm = mask | (perms & AA_OTHER_EXEC_TYPE);
index = EXTRACT_X_INDEX(eperm, AA_OTHER_SHIFT) + (AA_EXEC_COUNT << 2);
}
//fprintf(stderr, "index %d eperm 0x%x\n", index, eperm);
if (exact_match) {
if (exact_match_flags[ai][index]) {
flag = exact_match_flags[ai][index];
} else {
exact_match_flags[ai][index] = new ExactMatchFlag(eperm, audit & mask);
flag = exact_match_flags[ai][index];
}
} else {
if (exec_match_flags[ai][index]) {
flag = exec_match_flags[ai][index];
} else {
exec_match_flags[ai][index] = new MatchFlag(eperm, audit & mask);
flag = exec_match_flags[ai][index];
}
}
} else {
if (match_flags[ai][n]) {
flag = match_flags[ai][n];
} else {
match_flags[ai][n] = new MatchFlag(mask, audit & mask);
flag = match_flags[ai][n];
}
}
if (accept)
accept = new AltNode(accept, flag);
else
accept = flag;
} /* for ... */
if (flags & DFA_DUMP_RULE_EXPR) {
cerr << "rule: ";
cerr << rulev[0];
for (int i = 1; i < count; i++) {
cerr << "\\x00";
cerr << rulev[i];
}
cerr << " -> ";
tree->dump(cerr);
cerr << "\n\n";
}
if (rules->root)
rules->root = new AltNode(rules->root, new CatNode(tree, accept));
else
rules->root = new CatNode(tree, accept);
return 1;
}
/* create a dfa from the ruleset
* returns: buffer contain dfa tables, @size set to the size of the tables
* else NULL on failure
*/
extern "C" void *aare_create_dfa(aare_ruleset_t *rules, size_t *size,
dfaflags_t flags)
{
char *buffer = NULL;
label_nodes(rules->root);
if (flags & DFA_DUMP_TREE) {
cerr << "\nDFA: Expression Tree\n";
rules->root->dump(cerr);
cerr << "\n\n";
}
if (flags & DFA_CONTROL_TREE_SIMPLE) {
rules->root = simplify_tree(rules->root, flags);
if (flags & DFA_DUMP_SIMPLE_TREE) {
cerr << "\nDFA: Simplified Expression Tree\n";
rules->root->dump(cerr);
cerr << "\n\n";
}
}
stringstream stream;
try {
DFA dfa(rules->root, flags);
if (flags & DFA_DUMP_UNIQ_PERMS)
dfa.dump_uniq_perms("dfa");
if (flags & DFA_CONTROL_MINIMIZE) {
dfa.minimize(flags);
if (flags & DFA_DUMP_MIN_UNIQ_PERMS)
dfa.dump_uniq_perms("minimized dfa");
}
if (flags & DFA_CONTROL_REMOVE_UNREACHABLE)
dfa.remove_unreachable(flags);
if (flags & DFA_DUMP_STATES)
dfa.dump(cerr);
if (flags & DFA_DUMP_GRAPH)
dfa.dump_dot_graph(cerr);
map<uchar, uchar> eq;
if (flags & DFA_CONTROL_EQUIV) {
eq = dfa.equivalence_classes(flags);
dfa.apply_equivalence_classes(eq);
if (flags & DFA_DUMP_EQUIV) {
cerr << "\nDFA equivalence class\n";
dump_equivalence_classes(cerr, eq);
}
} else if (flags & DFA_DUMP_EQUIV)
cerr << "\nDFA did not generate an equivalence class\n";
TransitionTable transition_table(dfa, eq, flags);
if (flags & DFA_DUMP_TRANS_TABLE)
transition_table.dump(cerr);
transition_table.flex_table(stream, "");
}
catch(int error) {
*size = 0;
return NULL;
}
stringbuf *buf = stream.rdbuf();
buf->pubseekpos(0);
*size = buf->in_avail();
buffer = (char *)malloc(*size);
if (!buffer)
return NULL;
buf->sgetn(buffer, *size);
return buffer;
}

View File

@@ -1,49 +0,0 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
* Copyright 2009-2010 Canonical Ltd.
*
* The libapparmor library is licensed under the terms of the GNU
* Lesser General Public License, version 2.1. Please see the file
* COPYING.LGPL.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Wrapper around the dfa to convert aa rules into a dfa
*/
#ifndef __LIBAA_RE_RULES_H
#define __LIBAA_RE_RULES_H
#include <stdint.h>
#include "apparmor_re.h"
#ifdef __cplusplus
extern "C" {
#endif
struct aare_ruleset;
typedef struct aare_ruleset aare_ruleset_t;
aare_ruleset_t *aare_new_ruleset(int reverse);
void aare_delete_ruleset(aare_ruleset_t *rules);
int aare_add_rule(aare_ruleset_t *rules, char *rule, int deny, uint32_t perms,
uint32_t audit, dfaflags_t flags);
int aare_add_rule_vec(aare_ruleset_t *rules, int deny, uint32_t perms,
uint32_t audit, int count, char **rulev,
dfaflags_t flags);
void *aare_create_dfa(aare_ruleset_t *rules, size_t *size, dfaflags_t flags);
void aare_reset_matchflags(void);
#ifdef __cplusplus
}
#endif
#endif /* __LIBAA_RE_RULES_H */

View File

@@ -1,20 +1,11 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
* Copyright 2009-2010 Canonical Ltd.
* Copyright (c) 2003, 2004, 2005, 2006, 2007 Novell, Inc.
* (All rights reserved)
*
* 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/>.
*/
* The libapparmor library is licensed under the terms of the GNU
* Lesser General Public License, version 2.1. Please see the file
* COPYING.LGPL.
*/
#ifndef APPARMOR_RE_H
#define APPARMOR_RE_H
@@ -30,7 +21,6 @@ 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,
@@ -51,4 +41,25 @@ typedef enum dfaflags {
DFA_DUMP_NODE_TO_DFA = 1 << 31,
} dfaflags_t;
#ifdef __cplusplus
extern "C" {
#endif
struct aare_ruleset;
typedef struct aare_ruleset aare_ruleset_t;
aare_ruleset_t *aare_new_ruleset(int reverse);
void aare_delete_ruleset(aare_ruleset_t *rules);
int aare_add_rule(aare_ruleset_t *rules, char *rule, int deny,
uint32_t perms, uint32_t audit, dfaflags_t flags);
int aare_add_rule_vec(aare_ruleset_t *rules, int deny, uint32_t perms,
uint32_t audit, int count, char **rulev, dfaflags_t flags);
void *aare_create_dfa(aare_ruleset_t *rules, size_t *size, dfaflags_t flags);
void aare_reset_matchflags(void);
#ifdef __cplusplus
}
#endif
#endif /* APPARMOR_RE_H */

View File

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

View File

@@ -1,56 +0,0 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
* Copyright 2009-2010 Canonical Ltd.
*
* The libapparmor library is licensed under the terms of the GNU
* Lesser General Public License, version 2.1. Please see the file
* COPYING.LGPL.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Create a compressed hfa from and hfa
*/
#ifndef __LIBAA_RE_COMPRESSED_HFA_H
#define __LIBAA_RE_COMPRESSED_HFA_H
#include <map>
#include <vector>
#include "hfa.h"
using namespace std;
class TransitionTable {
typedef vector<pair<const State *, size_t> > DefaultBase;
typedef vector<pair<const State *, const State *> > NextCheck;
public:
TransitionTable(DFA &dfa, map<uchar, uchar> &eq, dfaflags_t flags);
void dump(ostream & os);
void flex_table(ostream &os, const char *name);
void init_free_list(vector<pair<size_t, size_t> > &free_list,
size_t prev, size_t start);
bool fits_in(vector<pair<size_t, size_t> > &free_list, size_t base,
Cases &cases);
void insert_state(vector<pair<size_t, size_t> > &free_list,
State *state, DFA &dfa);
private:
vector<uint32_t> accept;
vector<uint32_t> accept2;
DefaultBase default_base;
NextCheck next_check;
map<const State *, size_t> num;
map<uchar, uchar> &eq;
uchar max_eq;
size_t first_free;
};
#endif /* __LIBAA_RE_COMPRESSED_HFA_H */

View File

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

View File

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

View File

@@ -1,869 +0,0 @@
/*
* (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;
}

View File

@@ -1,143 +0,0 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
* Copyright 2009-2010 Canonical Ltd.
*
* The libapparmor library is licensed under the terms of the GNU
* Lesser General Public License, version 2.1. Please see the file
* COPYING.LGPL.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Base of implementation based on the Lexical Analysis chapter of:
* Alfred V. Aho, Ravi Sethi, Jeffrey D. Ullman:
* Compilers: Principles, Techniques, and Tools (The "Dragon Book"),
* Addison-Wesley, 1986.
*/
#ifndef __LIBAA_RE_HFA_H
#define __LIBAA_RE_HFA_H
#include <list>
#include <map>
#include <vector>
#include <stdint.h>
#include "expr-tree.h"
class State;
/**
* State cases are identical to NodesCases except they map to State *
* instead of NodeSet.
* Out-edges from a state to another: we store the follow State
* for each input character that is not a default match in cases and
* default matches in otherwise as well as in all matching explicit cases
* This avoids enumerating all the explicit tranitions for default matches.
*/
typedef struct Cases {
typedef map<uchar, State *>::iterator iterator;
iterator begin() { return cases.begin(); }
iterator end() { return cases.end(); }
Cases(): otherwise(0) { }
map<uchar, State *> cases;
State *otherwise;
} Cases;
typedef list<State *> Partition;
uint32_t accept_perms(NodeSet *state, uint32_t *audit_ctl, int *error);
/*
* State - DFA individual state information
* label: a unique label to identify the state used for pretty printing
* the non-matching state is setup to have label == 0 and
* the start state is setup to have label == 1
* audit: the audit permission mask for the state
* accept: the accept permissions for the state
* cases: set of transitions from this state
* parition: Is a temporary work variable used during dfa minimization.
* it can be replaced with a map, but that is slower and uses more
* memory.
* nodes: Is a temporary work variable used during dfa creation. It can
* be replaced by using the nodemap, but that is slower
*/
class State {
public:
State(): label(0), audit(0), accept(0), cases(), nodes(NULL) { };
State(int l): label(l), audit(0), accept(0), cases(), nodes(NULL) { };
State(int l, NodeSet * n) throw(int):
label(l), audit(0), accept(0), cases(), nodes(n)
{
int error;
/* Compute permissions associated with the State. */
accept = accept_perms(nodes, &audit, &error);
if (error) {
//cerr << "Failing on accept perms " << error << "\n";
throw error;
}
};
int label;
uint32_t audit, accept;
Cases cases;
union {
Partition *partition;
NodeSet *nodes;
};
};
ostream &operator<<(ostream &os, const State &state);
typedef map<pair<unsigned long, NodeSet *>, State *, deref_less_than> NodeMap;
/* Transitions in the DFA. */
/* dfa_stats - structure to group various stats about dfa creation
* duplicates - how many duplicate NodeSets where encountered and discarded
* proto_max - maximum length of a NodeSet encountered during dfa construction
* proto_sum - sum of NodeSet length during dfa construction. Used to find
* average length.
*/
typedef struct dfa_stats {
unsigned int duplicates, proto_max, proto_sum;
} dfa_stats_t;
class DFA {
void dump_node_to_dfa(void);
State *add_new_state(NodeMap &nodemap,
pair<unsigned long, NodeSet *> index,
NodeSet *nodes, dfa_stats_t &stats);
void update_state_transitions(NodeMap &nodemap,
list<State *> &work_queue,
State *state, dfa_stats_t &stats);
State *find_target_state(NodeMap &nodemap, list<State *> &work_queue,
NodeSet *nodes, dfa_stats_t &stats);
public:
DFA(Node *root, dfaflags_t flags);
virtual ~DFA();
void remove_unreachable(dfaflags_t flags);
bool same_mappings(State *s1, State *s2);
size_t hash_trans(State *s);
void minimize(dfaflags_t flags);
void dump(ostream &os);
void dump_dot_graph(ostream &os);
void dump_uniq_perms(const char *s);
map<uchar, uchar> equivalence_classes(dfaflags_t flags);
void apply_equivalence_classes(map<uchar, uchar> &eq);
Node *root;
State *nonmatching, *start;
Partition states;
};
void dump_equivalence_classes(ostream &os, map<uchar, uchar> &eq);
#endif /* __LIBAA_RE_HFA_H */

View File

@@ -1,27 +0,0 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
* Copyright 2009-2010 Canonical Ltd.
*
* The libapparmor library is licensed under the terms of the GNU
* Lesser General Public License, version 2.1. Please see the file
* COPYING.LGPL.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Parsing of regular expression into expression trees as implemented in
* expr-tree
*/
#ifndef __LIBAA_RE_PARSE_H
#define __LIBAA_RE_PARSE_H
int regex_parse(Node **tree, const char *rule);
#endif /* __LIBAA_RE_PARSE_H */

View File

@@ -1,262 +0,0 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
* Copyright 2009-2010 Canonical Ltd.
*
* The libapparmor library is licensed under the terms of the GNU
* Lesser General Public License, version 2.1. Please see the file
* COPYING.LGPL.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Parsing of regular expression into expression trees as implemented in
* expr-tree
*/
%{
/* #define DEBUG_TREE */
#include "expr-tree.h"
%}
%union {
char c;
Node *node;
Chars *cset;
}
%{
void regex_error(Node **, const char *, const char *);
#define YYLEX_PARAM &text
int regex_lex(YYSTYPE *, const char **);
static inline Chars *insert_char(Chars* cset, uchar a)
{
cset->insert(a);
return cset;
}
static inline Chars* insert_char_range(Chars* cset, uchar a, uchar b)
{
if (a > b)
swap(a, b);
for (uchar i = a; i <= b; i++)
cset->insert(i);
return cset;
}
%}
%pure-parser
/* %error-verbose */
%parse-param {Node **root}
%parse-param {const char *text}
%name-prefix = "regex_"
%token <c> CHAR
%type <c> regex_char cset_char1 cset_char cset_charN
%type <cset> charset cset_chars
%type <node> regex expr terms0 terms qterm term
/**
* Note: destroy all nodes upon failure, but *not* the start symbol once
* parsing succeeds!
*/
%destructor { $$->release(); } expr terms0 terms qterm term
%%
/* FIXME: Does not parse "[--]", "[---]", "[^^-x]". I don't actually know
which precise grammer Perl regexs use, and rediscovering that
is proving to be painful. */
regex : /* empty */ { *root = $$ = &epsnode; }
| expr { *root = $$ = $1; }
;
expr : terms
| expr '|' terms0 { $$ = new AltNode($1, $3); }
| '|' terms0 { $$ = new AltNode(&epsnode, $2); }
;
terms0 : /* empty */ { $$ = &epsnode; }
| terms
;
terms : qterm
| terms qterm { $$ = new CatNode($1, $2); }
;
qterm : term
| term '*' { $$ = new StarNode($1); }
| term '+' { $$ = new PlusNode($1); }
;
term : '.' { $$ = new AnyCharNode; }
| regex_char { $$ = new CharNode($1); }
| '[' charset ']' { $$ = new CharSetNode(*$2);
delete $2; }
| '[' '^' charset ']'
{ $$ = new NotCharSetNode(*$3);
delete $3; }
| '[' '^' '^' cset_chars ']'
{ $4->insert('^');
$$ = new NotCharSetNode(*$4);
delete $4; }
| '(' regex ')' { $$ = $2; }
;
regex_char : CHAR
| '^' { $$ = '^'; }
| '-' { $$ = '-'; }
| ']' { $$ = ']'; }
;
charset : cset_char1 cset_chars
{ $$ = insert_char($2, $1); }
| cset_char1 '-' cset_charN cset_chars
{ $$ = insert_char_range($4, $1, $3); }
;
cset_chars : /* nothing */ { $$ = new Chars; }
| cset_chars cset_charN
{ $$ = insert_char($1, $2); }
| cset_chars cset_charN '-' cset_charN
{ $$ = insert_char_range($1, $2, $4); }
;
cset_char1 : cset_char
| ']' { $$ = ']'; }
| '-' { $$ = '-'; }
;
cset_charN : cset_char
| '^' { $$ = '^'; }
;
cset_char : CHAR
| '[' { $$ = '['; }
| '*' { $$ = '*'; }
| '+' { $$ = '+'; }
| '.' { $$ = '.'; }
| '|' { $$ = '|'; }
| '(' { $$ = '('; }
| ')' { $$ = ')'; }
;
%%
int octdigit(char c)
{
if (c >= '0' && c <= '7')
return c - '0';
return -1;
}
int hexdigit(char c)
{
if (c >= '0' && c <= '9')
return c - '0';
else if (c >= 'A' && c <= 'F')
return 10 + c - 'A';
else if (c >= 'a' && c <= 'f')
return 10 + c - 'A';
else
return -1;
}
int regex_lex(YYSTYPE *val, const char **pos)
{
int c;
val->c = **pos;
switch(*(*pos)++) {
case '\0':
(*pos)--;
return 0;
case '*': case '+': case '.': case '|': case '^': case '-':
case '[': case ']': case '(' : case ')':
return *(*pos - 1);
case '\\':
val->c = **pos;
switch(*(*pos)++) {
case '\0':
(*pos)--;
/* fall through */
case '\\':
val->c = '\\';
break;
case '0':
val->c = 0;
if ((c = octdigit(**pos)) >= 0) {
val->c = c;
(*pos)++;
}
if ((c = octdigit(**pos)) >= 0) {
val->c = (val->c << 3) + c;
(*pos)++;
}
if ((c = octdigit(**pos)) >= 0) {
val->c = (val->c << 3) + c;
(*pos)++;
}
break;
case 'x':
val->c = 0;
if ((c = hexdigit(**pos)) >= 0) {
val->c = c;
(*pos)++;
}
if ((c = hexdigit(**pos)) >= 0) {
val->c = (val->c << 4) + c;
(*pos)++;
}
break;
case 'a':
val->c = '\a';
break;
case 'e':
val->c = 033 /* ESC */;
break;
case 'f':
val->c = '\f';
break;
case 'n':
val->c = '\n';
break;
case 'r':
val->c = '\r';
break;
case 't':
val->c = '\t';
break;
}
}
return CHAR;
}
void regex_error(Node ** __attribute__((unused)),
const char *text __attribute__((unused)),
const char *error __attribute__((unused)))
{
/* We don't want the library to print error messages. */
}

View File

@@ -0,0 +1,10 @@
#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

View File

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

View File

@@ -23,7 +23,6 @@
#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;
@@ -179,8 +178,13 @@ struct var_string {
#define FLAG_CHANGEHAT_1_4 2
#define FLAG_CHANGEHAT_1_5 3
extern int kernel_supports_network;
extern int net_af_max_override;
extern int flag_changehat_version;
extern int read_implies_exec;
extern dfaflags_t dfaflags;
extern int preprocess_only;
extern FILE *ofile;
#define PATH_CHROOT_REL 0x1
#define PATH_NS_REL 0x2
@@ -223,34 +227,23 @@ extern int preprocess_only;
#define list_last_entry(LIST, ENTRY) \
for ((ENTRY) = (LIST); (ENTRY) && (ENTRY)->next; (ENTRY) = (ENTRY)->next)
/* 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;
/* Some external definitions to make b0rken programs happy */
extern char *progname;
extern char *subdomainbase;
extern char *profilename;
extern char *profile_namespace;
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) */
/* 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 void pwarn(char *fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
/* provided by parser_lex.l (cannot be used in tst builds) */
extern FILE *yyin;
extern void yyrestart(FILE *fp);
extern int yyparse(void);
@@ -346,32 +339,3 @@ 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

View File

@@ -1,76 +0,0 @@
/*
* Copyright (c) 2010, 2011
* Canonical, Ltd. (All rights reserved)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc. or Canonical,
* Ltd.
*/
#include <stdlib.h>
#include <stdarg.h>
#include <libintl.h>
#include <locale.h>
#define _(s) gettext(s)
#include "parser.h"
int regex_type = AARE_DFA;
int perms_create = 0; /* perms contain create flag */
int net_af_max_override = -1; /* use kernel to determine af_max */
int kernel_load = 1;
int kernel_supports_network = 1; /* kernel supports network rules */
int flag_changehat_version = FLAG_CHANGEHAT_1_5;
int conf_verbose = 0;
int conf_quiet = 0;
int names_only = 0;
int current_lineno = 1;
int option = OPTION_ADD;
dfaflags_t dfaflags = DFA_CONTROL_TREE_NORMAL | DFA_CONTROL_TREE_SIMPLE | DFA_CONTROL_MINIMIZE | DFA_CONTROL_MINIMIZE_HASH_TRANS | DFA_CONTROL_MINIMIZE_HASH_PERMS;
char *subdomainbase = NULL;
char *progname = __FILE__;
char *profile_namespace = NULL;
char *profilename = NULL;
char *current_filename = NULL;
FILE *ofile = NULL;
#ifdef FORCE_READ_IMPLIES_EXEC
int read_implies_exec = 1;
#else
int read_implies_exec = 0;
#endif
void pwarn(char *fmt, ...)
{
va_list arg;
char *newfmt;
int rc;
if (conf_quiet || names_only || option == OPTION_REMOVE)
return;
rc = asprintf(&newfmt, _("Warning from %s (%s%sline %d): %s"),
profilename ? profilename : "stdin",
current_filename ? current_filename : "",
current_filename ? " " : "",
current_lineno,
fmt);
if (!newfmt)
return;
va_start(arg, fmt);
vfprintf(stderr, newfmt, arg);
va_end(arg);
free(newfmt);
}

View File

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

View File

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

View File

@@ -2,7 +2,7 @@
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
* NOVELL (All rights reserved)
*
* Copyright (c) 2010, 2011
* Copyright (c) 2010
* Canonical, Ltd. (All rights reserved)
*
* This program is free software; you can redistribute it and/or
@@ -19,7 +19,6 @@
* Ltd.
*/
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
@@ -29,6 +28,7 @@
#include <fcntl.h>
#include <mntent.h>
#include <libintl.h>
#include <linux/limits.h>
#include <locale.h>
#define _(s) gettext(s)
@@ -36,7 +36,6 @@
/* #define DEBUG */
#include <unistd.h>
#include <limits.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -59,26 +58,46 @@
#define UNPRIVILEGED_OPS (!(PRIVILEGED_OPS))
const char *parser_title = "AppArmor parser";
const char *parser_copyright = "Copyright (C) 1999-2008 Novell Inc.\nCopyright 2009-2011 Canonical Ltd.";
const char *parser_copyright = "Copyright (C) 1999-2008 Novell Inc.\nCopyright 2009-2010 Canonical Ltd.";
char *progname;
int option = OPTION_ADD;
int opt_force_complain = 0;
int binary_input = 0;
int names_only = 0;
int dump_vars = 0;
int dump_expanded_vars = 0;
dfaflags_t dfaflags = DFA_CONTROL_TREE_NORMAL | DFA_CONTROL_TREE_SIMPLE | DFA_CONTROL_MINIMIZE | DFA_CONTROL_MINIMIZE_HASH_TRANS | DFA_CONTROL_MINIMIZE_HASH_PERMS;
int conf_verbose = 0;
int conf_quiet = 0;
int kernel_load = 1;
int show_cache = 0;
int skip_cache = 0;
int skip_read_cache = 0;
int write_cache = 0;
#ifdef FORCE_READ_IMPLIES_EXEC
int read_implies_exec = 1;
#else
int read_implies_exec = 0;
#endif
int preprocess_only = 0;
int skip_mode_force = 0;
struct timespec mru_tstamp;
char *subdomainbase = NULL;
char *match_string = NULL;
char *flags_string = NULL;
int regex_type = AARE_DFA;
int perms_create = 0; /* perms contain create flag */
int kernel_supports_network = 1; /* kernel supports network rules */
int net_af_max_override = -1; /* use kernel to determine af_max */
char *profile_namespace = NULL;
int flag_changehat_version = FLAG_CHANGEHAT_1_5;
FILE *ofile = NULL;
/* per-profile settings */
int force_complain = 0;
char *profilename = NULL;
struct option long_options[] = {
{"add", 0, 0, 'a'},
@@ -197,8 +216,6 @@ 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",
@@ -305,240 +322,29 @@ static void display_optimize(char *command)
print_flag_table(optflag_table);
}
/* Treat conf file like options passed on command line
*/
static int getopt_long_file(FILE *f, const struct option *longopts,
char **optarg, int *longindex)
void pwarn(char *fmt, ...)
{
static char line[256];
char *pos, *opt, *save;
int i;
va_list arg;
char *newfmt;
int rc;
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;
if (conf_quiet || names_only || option == OPTION_REMOVE)
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;
}
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;
if (longindex)
*longindex = i;
va_start(arg, fmt);
vfprintf(stderr, newfmt, arg);
va_end(arg);
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;
free(newfmt);
}
static int process_args(int argc, char *argv[])
@@ -549,7 +355,159 @@ 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)
{
count += process_arg(c, optarg);
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;
}
}
if (count > 1) {
@@ -563,21 +521,6 @@ 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)
{
@@ -1007,7 +950,8 @@ out:
}
else {
unlink(cachetemp);
PERROR("Warning failed to create cache: %s\n", basename);
if (show_cache)
PERROR("Removed cache attempt: %s\n", cachetemp);
}
free(cachetemp);
}
@@ -1089,7 +1033,6 @@ int main(int argc, char *argv[])
init_base_dir();
process_config_file("/etc/apparmor/parser.conf");
optind = process_args(argc, argv);
setlocale(LC_MESSAGES, "");

View File

@@ -129,9 +129,6 @@ 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}
};
@@ -433,7 +430,7 @@ char *processquoted(char *string, int len)
*s = '\\';
l++;
break;
case '0': case '1': case '2': case '3':
case '0' - '3':
if ((l < len - 4) &&
strchr("0123456789", string[l + 2]) &&
strchr("0123456789", string[l + 3])) {
@@ -869,7 +866,6 @@ static const char *capnames[] = {
"audit_control",
"setfcap",
"mac_override"
"syslog",
};
const char *capability_to_name(unsigned int cap)
@@ -933,6 +929,27 @@ 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;
@@ -956,7 +973,7 @@ int test_str_to_boolean(void)
int test_processunquoted(void)
{
int rc = 0;
char *teststring, *processedstring;
const char *teststring, *processedstring;
teststring = "";
MY_TEST(strcmp(teststring, processunquoted(teststring, strlen(teststring))) == 0,
@@ -981,106 +998,6 @@ 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;
@@ -1093,11 +1010,6 @@ int main(void)
retval = test_processunquoted();
if (retval != 0)
rc = retval;
retval = test_processquoted();
if (retval != 0)
rc = retval;
return rc;
}
#endif /* UNIT_TEST */

View File

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

View File

@@ -539,6 +539,30 @@ 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;
@@ -565,7 +589,7 @@ int main(void)
retval = new_set_var("test", "different value");
MY_TEST(retval != 0, "new set variable 2");
retval = new_set_var("testing", "testing");
retval = new_set_var("testes", "testes");
MY_TEST(retval == 0, "new set variable 3");
retval = new_set_var("monopuff", "Mockingbird");

View File

@@ -36,14 +36,8 @@ static inline char *get_var_end(char *var)
while (*eptr) {
if (*eptr == '}')
return eptr;
/* first character must be alpha */
if (eptr == var) {
if (!isalpha(*eptr))
return NULL; /* invalid char */
} else {
if (!(*eptr == '_' || isalnum(*eptr)))
return NULL; /* invalid char */
}
if (!(*eptr == '_' || isalpha(*eptr)))
return NULL; /* invalid char */
eptr++;
}
return NULL; /* no terminating '}' */
@@ -216,6 +210,29 @@ 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;
@@ -254,7 +271,7 @@ int test_split_string(void)
char *var = "boogie";
char *suffix = "suffixication";
asprintf(&tst_string, "%s@{%s}%s", prefix, var, suffix);
(void) 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);
@@ -300,8 +317,6 @@ 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 */
@@ -379,34 +394,6 @@ int test_split_out_var(void)
MY_TEST(strcmp(ret_struct->suffix, suffix) == 0, "split out var 7 suffix");
free_var_string(ret_struct);
/* numeric char in var, should succeed */
asprintf(&tst_string, "%s@{%s}%s", prefix, var2, suffix);
ret_struct = split_out_var(tst_string);
MY_TEST(ret_struct && strcmp(ret_struct->prefix, prefix) == 0, "split out numeric var prefix");
MY_TEST(ret_struct && strcmp(ret_struct->var, var2) == 0, "split numeric var var");
MY_TEST(ret_struct && strcmp(ret_struct->suffix, suffix) == 0, "split out numeric var suffix");
free_var_string(ret_struct);
/* numeric first char in var, should fail */
asprintf(&tst_string, "%s@{6%s}%s", prefix, var2, suffix);
ret_struct = split_out_var(tst_string);
MY_TEST(ret_struct == NULL, "split out var - numeric first char in var");
free_var_string(ret_struct);
/* underscore char in var, should succeed */
asprintf(&tst_string, "%s@{%s}%s", prefix, var3, suffix);
ret_struct = split_out_var(tst_string);
MY_TEST(ret_struct && strcmp(ret_struct->prefix, prefix) == 0, "split out underscore var prefix");
MY_TEST(ret_struct && strcmp(ret_struct->var, var3) == 0, "split out underscore var");
MY_TEST(ret_struct && strcmp(ret_struct->suffix, suffix) == 0, "split out underscore var suffix");
free_var_string(ret_struct);
/* underscore first char in var, should fail */
asprintf(&tst_string, "%s@{_%s%s}%s", prefix, var2, var3, suffix);
ret_struct = split_out_var(tst_string);
MY_TEST(ret_struct == NULL, "split out var - underscore first char in var");
free_var_string(ret_struct);
return rc;
}
int main(void)

View File

@@ -240,7 +240,7 @@ profile_base: TOK_ID opt_id flags TOK_OPEN rules TOK_CLOSE
post_process_nt_entries(cod);
PDEBUG("%s: flags='%s%s'\n",
$2,
$3,
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", cod->name);
PDEBUG("Matched: hat %s { ... }\n", code->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 list\n");
PDEBUG("Matched: value (%s)\n", $1);
new->value = $2;
new->next = $1;
@@ -711,31 +711,10 @@ 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:
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);
}
yyerror("RLIMIT '%s' is currently unsupported\n", $4);
break;
case RLIMIT_NOFILE:
case RLIMIT_NPROC:
@@ -743,14 +722,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 (!end || $6 == end || *end != '\0' || tmp < 0)
if ($6 == end || *end != '\0' || tmp < 0)
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
value = tmp;
break;
#endif
#ifdef RLIMIT_NICE
case RLIMIT_NICE:
if (!end || $6 == end || *end != '\0')
if ($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);
@@ -767,11 +746,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 (strstr(kb, end) == kb) {
if (strcmp(end, "K") == 0) {
tmp *= 1024;
} else if (strstr(mb, end) == mb) {
} else if (strcmp(end, "M") == 0) {
tmp *= 1024*1024;
} else if (strstr(gb, end) == gb) {
} else if (strcmp(end, "G") == 0) {
tmp *= 1024*1024*1024;
} else if (*end != '\0') {
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);

View File

@@ -27,7 +27,7 @@
### BEGIN INIT INFO
# Provides: aaeventd
# Required-Start: apparmor
# Required-Stop: $null
# Required-Stop:
# 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 -p $AA_EV_PIDFILE $AA_EV_BIN -p $AA_EV_PIDFILE
sd_action "Starting AppArmor Event daemon" startproc -f -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 -p $SD_EV_PIDFILE $SD_EV_BIN -p $SD_EV_PIDFILE
sd_action "Starting AppArmor Event daemon" startproc -f -p $SD_EV_PIDFILE $SD_EV_BIN -p $SD_EV_PIDFILE
fi
}

View File

@@ -83,6 +83,15 @@ 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
@@ -99,7 +108,9 @@ is_apparmor_present() {
# check for subdomainfs version of module
grep -qE "^($modules)[[:space:]]" /proc/modules
[ $? -ne 0 -a -d /sys/module/apparmor ]
if [ $? -ne 0 ] ; then
ls /sys/module/apparmor 2>/dev/null | grep -qE "^($modules)"
fi
return $?
}
@@ -187,7 +198,6 @@ 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
@@ -212,6 +222,7 @@ 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
@@ -224,9 +235,9 @@ profiles_names_list() {
for profile in $PROFILE_DIR/*; do
if skip_profile "${profile}" && [ -f "${profile}" ] ; then
LIST_ADD=$($PARSER $ABSTRACTIONS -N "$profile" )
LIST_ADD=$($PARSER $ABSTRACTIONS -N "$profile" | grep -v '\^')
if [ $? -eq 0 ]; then
echo "$LIST_ADD"
echo "$LIST_ADD" >>$TMPFILE
fi
fi
done
@@ -286,7 +297,7 @@ is_apparmor_loaded() {
}
is_securityfs_mounted() {
test -d ${SECURITYFS} -a -d /sys/fs/cgroup/systemd || grep -q securityfs /proc/filesystems && grep -q securityfs /proc/mounts
grep -q securityfs /proc/filesystems && grep -q securityfs /proc/mounts
return $?
}
@@ -366,11 +377,10 @@ apparmor_start() {
configure_owlsm
# if there is anything in the profiles file don't load
if ! read line < "$SFS_MOUNTPOINT/profiles"; then
cat "$SFS_MOUNTPOINT/profiles" | if ! read line ; then
parse_profiles load
else
aa_log_skipped_msg ": already loaded with profiles."
return 0
aa_log_skipped_msg "AppArmor already loaded with profiles."
fi
aa_log_end_msg 0
return 0
@@ -398,16 +408,18 @@ remove_profiles() {
fi
retval=0
# 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
#the list of profiles isn't stable once we start adding or removing
#them so store to tmp first (in reverse order so hat profiles are removed first)
MODULE_PLIST=$(mktemp ${APPARMOR_TMPDIR}/tmp.XXXXXXXX)
sed -e "s/ (\(enforce\|complain\))$//" "$SFS_MOUNTPOINT/profiles" | sort -r > "$MODULE_PLIST"
cat "$MODULE_PLIST" | 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}
}
@@ -447,41 +459,20 @@ __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.
# 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
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
echo -n "$profile" > "$SFS_MOUNTPOINT/.remove"
done
# will not catch all errors, but still better than nothing
rc=$?
aa_log_end_msg $rc
return $rc
rm "$MODULE_PLIST"
rm "$PNAMES_LIST"
return 0
}
apparmor_restart() {
@@ -525,11 +516,11 @@ apparmor_status () {
${SD_STATUS} --verbose
return $?
fi
if ! is_apparmor_loaded ; then
if ! is_apparmor_present apparmor subdomain ; then
echo "AppArmor is not loaded."
rc=1
else
echo "AppArmor is enabled."
echo "AppArmor is enabled,"
rc=0
fi
echo "Install the apparmor-utils package to receive more detailed"

View File

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

View File

@@ -4,10 +4,9 @@ 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
@@ -15,14 +14,9 @@ all: tests
.PHONY: tests error_output gen_xtrans parser_sanity caching
tests: error_output gen_xtrans parser_sanity caching
GEN_TRANS_DIRS=simple_tests/generated_x/ simple_tests/generated_perms_leading/ simple_tests/generated_perms_safe/
gen_xtrans: $(GEN_TRANS_DIRS)
gen_xtrans:
./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 | \
@@ -45,4 +39,6 @@ $(PARSER):
make -C $(PARSER_DIR) $(PARSER_BIN)
clean:
find $(GEN_TRANS_DIRS) -type f | xargs rm -f
rm -f simple_tests/generated_x/*
rm -f simple_tests/generated_perms_leading/*
rm -f simple_tests/generated_perms_safe/*

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -52,11 +52,9 @@ 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 \
${PROFILES_DEST}/tunables/multiarch.d \
${PROFILES_DEST}/local
install -m 644 ${PROFILES_TO_COPY} ${PROFILES_DEST}
install -m 644 ${ABSTRACTIONS_TO_COPY} ${PROFILES_DEST}/abstractions

View File

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

View File

@@ -1,20 +1,9 @@
# vim:syntax=apparmor
# This file contains basic permissions for Apache and every vHost
#include <abstractions/nameservice>
# Apache
network inet stream,
network inet6 stream,
# apache manual, error pages and icons
/usr/share/apache2/** r,
# changehat itself
/proc/*/attr/current w,
# htaccess files - for what ever it is worth
/**/.htaccess r,
/dev/urandom r,

View File

@@ -2,7 +2,7 @@
# aspell permissions
# per-user settings and dictionaries
owner @{HOME}/.aspell.*.{pws,prepl} rwk,
@{HOME}/.aspell.*.{pws,prepl} rk,
# system libraries and dictionaries
/usr/lib/aspell/ r,

View File

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

View File

@@ -1,7 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2009 Novell/SUSE
# Copyright (C) 2009-2012 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
@@ -47,5 +47,3 @@
# smbpass
#include <abstractions/smbpass>
# p11-kit (PKCS#11 modules configuration)
#include <abstractions/p11-kit>

View File

@@ -36,8 +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,
/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,

View File

@@ -1,7 +1,7 @@
# vim:syntax=apparmor
# ------------------------------------------------------------------
#
# Copyright (C) 2009-2012 Canonical Ltd.
# Copyright (C) 2009 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,7 +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,
# client should be able to read user-specified cups configuration
owner @{HOME}/.cups/client.conf r,
owner @{HOME}/.cups/lpoptions r,
/var/run/cups/cups.sock w,

View File

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

View File

@@ -1,7 +1,7 @@
# vim:syntax=apparmor
# ------------------------------------------------------------------
#
# Copyright (C) 2011 Canonical Ltd.
# Copyright (C) 2010 Canonical Ltd.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
@@ -9,5 +9,5 @@
#
# ------------------------------------------------------------------
/usr/bin/dbus-launch ix,
/usr/bin/dbus-launch Pix,
/var/lib/dbus/machine-id r,

View File

@@ -52,5 +52,5 @@
/usr/share/java/zemberek-tr-[0-9]*.jar r,
# per-user dictionaries
owner @{HOME}/.config/enchant/ rw,
owner @{HOME}/.config/enchant/ r,
owner @{HOME}/.config/enchant/* rwk,

View File

@@ -39,8 +39,6 @@
@{HOME}/.fonts.cache-2 mr,
@{HOME}/.fontconfig/ r,
@{HOME}/.fontconfig/** mrl,
@{HOME}/.fonts.conf.d/ r,
@{HOME}/.fonts.conf.d/** r,
/usr/local/share/fonts/ r,
/usr/local/share/fonts/** r,

View File

@@ -77,8 +77,7 @@
/usr/share/cups/charmaps/** r,
# holds MIT-MAGIC-COOKIE for gnome
owner /{,var/}run/gdm/auth*/database r,
owner /var/run/gdm/auth*/database r,
# mime-types
/etc/gnome/defaults.list r,
/usr/share/gnome/applications/mimeinfo.cache r,

View File

@@ -25,8 +25,8 @@
@{HOME}/.DCOPserver_* r,
@{HOME}/.ICEauthority r,
@{HOME}/.fonts.* lrw,
@{HOME}/.kde{,4}/share/config/kdeglobals rw,
@{HOME}/.kde{,4}/share/config/*.lock rwl,
@{HOME}/.kde/share/config/kdeglobals rw,
@{HOME}/.kde/share/config/*.lock rwl,
@{HOME}/.qt/** rw,
@{HOME}/.config/Trolltech.conf rwk,

View File

@@ -1,21 +0,0 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2011 Novell/SUSE
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
# License published by the Free Software Foundation.
#
# ------------------------------------------------------------------
# files required by LDAP clients (e.g. nss_ldap/pam_ldap)
/etc/ldap.conf r,
/etc/ldap.secret r,
/etc/openldap/* r,
/etc/openldap/cacerts/* r,
# SASL plugins and config
/etc/sasl2/* r,
/usr/lib{,32,64}/sasl2/* r,
#include <abstractions/ssl_certs>

View File

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

View File

@@ -16,6 +16,8 @@
/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,
@@ -23,23 +25,26 @@
/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
@@ -50,14 +55,11 @@
/etc/default/nss r,
# avahi-daemon is used for mdns4 resolution
/{,var/}run/avahi-daemon/socket w,
/var/run/avahi-daemon/socket w,
# nis
#include <abstractions/nis>
# ldap
#include <abstractions/ldapclient>
# winbind
#include <abstractions/winbind>

View File

@@ -1,13 +0,0 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2011 Novell/SUSE
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
# License published by the Free Software Foundation.
#
# ------------------------------------------------------------------
/etc/ssl/openssl.cnf r,
/usr/share/ssl/openssl.cnf r,

View File

@@ -1,21 +0,0 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2012 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
# License published by the Free Software Foundation.
#
# ------------------------------------------------------------------
/etc/pkcs11/ r,
/etc/pkcs11/pkcs11.conf r,
/etc/pkcs11/modules/ r,
/etc/pkcs11/modules/* r,
/usr/lib{,32,64}/pkcs11/*.so mr,
/usr/lib/@{multiarch}/pkcs11/*.so mr,
# p11-kit also supports reading user configuration from ~/.pkcs11 depending
# on how /etc/pkcs11/pkcs11.conf is configured. This should generally not be
# included in this abstraction.

View File

@@ -1,6 +1,6 @@
# vim:syntax=apparmor
# privacy-violations contains rules for common files that you want to
# explicitly deny access
# privacy-violations contains rules for common files that you want to explicity
# deny access
# privacy violations (don't audit files under $HOME otherwise get a
# lot of false positives when reading contents of directories)
@@ -15,25 +15,14 @@
# special attention to (potentially) executable files
audit deny @{HOME}/bin/** wl,
audit deny @{HOME}/.config/autostart/** wl,
audit deny @{HOME}/.kde{,4}/Autostart/** wl,
audit deny @{HOME}/.kde{,4}/env/** wl,
audit deny @{HOME}/.pki/nssdb/*.so{,.[0-9]*} wl,
audit deny @{HOME}/.kde/Autostart/** wl,
deny @{HOME}/.bash* mrk,
audit deny @{HOME}/.bash* wl,
deny @{HOME}/.profile* mrk,
audit deny @{HOME}/.profile* wl,
# don't allow reading/updating of run control files
deny @{HOME}/.*rc mrk,
audit deny @{HOME}/.*rc wl,
# bash
deny @{HOME}/.bash* mrk,
audit deny @{HOME}/.bash* wl,
deny @{HOME}/.inputrc mrk,
audit deny @{HOME}/.inputrc wl,
# sh/dash/csh/tcsh/pdksh/zsh
deny @{HOME}/.{,z}profile* mrk,
audit deny @{HOME}/.{,z}profile* wl,
deny @{HOME}/.{,z}log{in,out} mrk,
audit deny @{HOME}/.{,z}log{in,out} wl,
deny @{HOME}/.zshenv mrk,
audit deny @{HOME}/.zshenv wl,

View File

@@ -1,6 +1,6 @@
# vim:syntax=apparmor
# privacy-violations-strict contains additional rules for sensitive
# files that you want to explicitly deny access
# files that you want to explicity deny access
#include <abstractions/private-files>
@@ -13,6 +13,6 @@
audit deny @{HOME}/.{,mozilla-}thunderbird/** mrwkl,
audit deny @{HOME}/.evolution/** mrwkl,
audit deny @{HOME}/.config/evolution/** mrwkl,
audit deny @{HOME}/.kde{,4}/share/apps/kmail{,2}/** mrwkl,
audit deny @{HOME}/.kde{,4}/share/apps/kwallet/** mrwkl,
audit deny @{HOME}/.kde/share/apps/kmail/** mrwkl,
audit deny @{HOME}/.kde/share/apps/kwallet/** mrwkl,

View File

@@ -29,9 +29,3 @@
# wx paths
/usr/lib/wx/python/*.pth r,
# python build configuration and headers
/usr/include/python{2,3}.[0-7]*/pyconfig.h r,
# python setup script used by apport
/etc/python{2,3}.[0-7]*/sitecustomize.py r,

View File

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

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