2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-08-30 05:47:59 +00:00

Merge remote-tracking branch 'origin/master' into prepare-rc.apparmor.functions-for-Debian-and-Ubuntu

This commit is contained in:
intrigeri 2018-12-16 20:36:53 +00:00
commit 45bc0b8eb7
96 changed files with 809 additions and 687 deletions

View File

@ -54,12 +54,12 @@ snapshot: clean
.PHONY: coverity
coverity: snapshot
cd $(SNAPSHOT_NAME)/libraries/libapparmor && ./configure --with-python
$(foreach dir, $(filter-out utils profiles tests, $(DIRS)), \
cov-build --dir $(COVERITY_DIR) -- $(MAKE) -C $(SNAPSHOT_NAME)/$(dir); \
mv $(COVERITY_DIR)/build-log.txt $(COVERITY_DIR)/build-log-$(subst /,.,$(dir)).txt ;)
$(foreach dir, libraries/libapparmor utils, \
cov-build --dir $(COVERITY_DIR) --no-command --fs-capture-search $(SNAPSHOT_NAME)/$(dir); \
mv $(COVERITY_DIR)/build-log.txt $(COVERITY_DIR)/build-log-python-$(subst /,.,$(dir)).txt ;)
cov-build --dir $(COVERITY_DIR) -- sh -c \
"$(foreach dir, $(filter-out utils profiles tests, $(DIRS)), \
$(MAKE) -C $(SNAPSHOT_NAME)/$(dir);) "
tar -cvzf $(SNAPSHOT_NAME)-$(COVERITY_DIR).tar.gz $(COVERITY_DIR)
.PHONY: export_dir

View File

@ -139,7 +139,7 @@ them at L<https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
apparmor(7), subdomain.conf(5), apparmor_parser(8), aa_change_hat(2) and
apparmor(7), apparmor_parser(8), aa_change_hat(2) and
L<https://wiki.apparmor.net>.
=cut

View File

@ -30,7 +30,7 @@ SYSTEMD_UNIT_DIR=${DESTDIR}/usr/lib/systemd/system
CONFDIR=/etc/apparmor
INSTALL_CONFDIR=${DESTDIR}${CONFDIR}
LOCALEDIR=/usr/share/locale
MANPAGES=apparmor.d.5 apparmor.7 apparmor_parser.8 subdomain.conf.5 aa-teardown.8
MANPAGES=apparmor.d.5 apparmor.7 apparmor_parser.8 aa-teardown.8
YACC := bison
YFLAGS := -d
@ -72,9 +72,6 @@ endif
# Internationalization support. Define a package and a LOCALEDIR
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 \
@ -373,7 +370,6 @@ install-arch: $(INSTALLDEPS)
.PHONY: install-indep
install-indep: 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)

View File

@ -143,6 +143,56 @@ messages with the KERN facility. Thus, REJECTING and PERMITTING messages
may go to either F</var/log/audit/audit.log> or F</var/log/messages>,
depending upon local configuration.
=head1 DEBUGGING
AppArmor provides a few facilities to log more information,
which can help debugging profiles.
=head2 Enable debug mode
When debug mode is enabled, AppArmor will log a few extra messages to
dmesg (not via the audit subsystem). For example, the logs will tell
whether environment scrubbing has been applied.
To enable debug mode, run:
echo 1 > /sys/module/apparmor/parameters/debug
=head2 Turn off deny audit quieting
By default, operations that trigger C<deny> rules are not logged.
This is called I<deny audit quieting>.
To turn off deny audit quieting, run:
echo -n noquiet >/sys/module/apparmor/parameters/audit
=head2 Force audit mode
AppArmor can log a message for every operation that triggers a rule
configured in the policy. This is called I<force audit mode>.
B<Warning!> Force audit mode can be extremely noisy even for a single profile,
let alone when enabled globally.
To set a specific profile in force audit mode, add the C<audit> flag:
profile foo flags=(audit) { ... }
To enable force audit mode globally, run:
echo -n all > /sys/module/apparmor/parameters/audit
If auditd is not running, to avoid losing too many of the extra log
messages, you will likely have to turn off rate limiting by doing:
echo 0 > /proc/sys/kernel/printk_ratelimit
But even then the kernel ring buffer may overflow and you might
lose messages.
Else, if auditd is running, see auditd(8) and auditd.conf(5).
=head1 FILES
=over 4
@ -162,7 +212,7 @@ depending upon local configuration.
=head1 SEE ALSO
apparmor_parser(8), aa_change_hat(2), apparmor.d(5),
subdomain.conf(5), aa-autodep(1), clean(1),
aa-autodep(1), clean(1),
auditd(8),
aa-unconfined(8), aa-enforce(1), aa-complain(1), and
L<https://wiki.apparmor.net>.

View File

@ -179,7 +179,7 @@ defined as relative paths.
Add element n to the search path when resolving #include directives
defined as an absolute paths.
=item -f n, --subdomainfs n
=item -f n, --apparmorfs n
Set the location of the apparmor security filesystem (default is
"/sys/kernel/security/apparmor").
@ -408,7 +408,7 @@ L<https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
apparmor(7), apparmor.d(5), subdomain.conf(5), aa_change_hat(2), and
apparmor(7), apparmor.d(5), aa_change_hat(2), and
L<https://wiki.apparmor.net>.
=cut

View File

@ -549,9 +549,14 @@ static void count_tree_nodes(Node *t, struct node_counts *counts)
#include "stdint.h"
#include "apparmor_re.h"
// maximum number of passes to iterate on the expression tree doing
// simplification passes. Simplification may exit sooner if no changes
// are made.
#define MAX_PASSES 1
Node *simplify_tree(Node *t, dfaflags_t flags)
{
bool update;
bool update = true;
int i;
if (flags & DFA_DUMP_TREE_STATS) {
struct node_counts counts = { 0, 0, 0, 0, 0, 0, 0, 0 };
@ -562,7 +567,7 @@ Node *simplify_tree(Node *t, dfaflags_t flags)
counts.alt, counts.plus, counts.star, counts.any,
counts.cat);
}
do {
for (i = 0; update && i < MAX_PASSES; i++) {
update = false;
//default to right normalize first as this reduces the number
//of trailing nodes which might follow an internal *
@ -588,7 +593,7 @@ Node *simplify_tree(Node *t, dfaflags_t flags)
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);

View File

@ -116,13 +116,34 @@ public:
}
/**
* See the "Dragon Book" for an explanation of nullable, firstpos,
* lastpos, and followpos.
* firstpos, lastpos, and followpos are used to convert the syntax tree
* to a DFA.
*
* firstpos holds nodes that can match the first character of a string
* that matches the syntax tree. For the regex 'a*bcd', firstpos holds
* the 'a' and 'b' nodes. firstpos is used to determine the start state
* of the DFA.
*
* lastpos is the same as firstpos for the last character. For the regex
* 'a*bcd', lastpos holds the 'd' node. lastpos is used to determine the
* accepting states of the DFA.
*
* followpos holds the set of nodes that can match a character directly
* after the current node. For the regexp 'a*bcd', the followpos of the
* 'a' node are the 'b' node and the 'a' node itself. followpos is used
* to determine the transitions of the DFA.
*
* nullable indicates that a node can match the empty string. It is used
* to compute firstpos and lastpos.
*
* See the "Dragon Book" 2nd Edition section 3.9.2 for an in-depth
* explanation.
*/
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);

View File

@ -17,21 +17,21 @@
* along with this program; if not, contact Canonical, Ltd.
*/
/* Handle subdomain includes, as a straight forward preprocessing phase.
/* Handle apparmor includes, as a straight forward preprocessing phase.
While we are at it we will strip comments. Why? because it made it
easier.
We support 2 types of includes
#include <name> which searches for the first occurance of name in the
subdomain directory path.
apparmor directory path.
#include "name" which will search for a relative or absolute pathed
file
-p : preprocess only. Dump output to stdout
-I path : add a path to be search by #include < >
-b path : set the base path to something other than /etc/subdomain.d
-b path : set the base path to something other than /etc/apparmor.d
*/
@ -57,13 +57,6 @@
/* maximum depth of nesting */
#define MAX_NEST_LEVEL 100
/* Location of the subdomain.conf file */
#ifdef SUBDOMAIN_CONFDIR
#define SUBDOMAIN_CONF SUBDOMAIN_CONFDIR "/subdomain.conf"
#else /* !defined SUBDOMAIN_CONFDIR */
#define SUBDOMAIN_CONF "/etc/subdomain.conf"
#endif /* SUBDOMAIN_CONFDIR */
static char *path[MAX_PATH] = { NULL };
static int npath = 0;
@ -71,12 +64,11 @@ static int fgetline(FILE * f, char *buffer, size_t len);
static int stripcomment(char *s);
static char *stripblanks(char *s);
/* default base directory is /etc/subdomain.d, it can be overriden
/* default base directory is /etc/apparmor.d, it can be overriden
with the -b option. */
const char *basedir;
static const char *default_basedir = "/etc/apparmor.d";
static const char *old_basedir = "/etc/subdomain.d";
/* set up basedir so that it can be overridden/used later. */
@ -94,12 +86,6 @@ void init_base_dir(void)
basedir = default_basedir;
return;
}
rc = stat(old_basedir, &sbuf);
if (rc == 0 && S_ISDIR(sbuf.st_mode)) {
basedir = old_basedir;
return;
}
}
/* Set the base dir. Used to change default path for relative includes */
@ -164,53 +150,9 @@ int add_search_dir(const char *dir)
return 1;
}
/* Parse Subdomain.conf and put the default dirs in place.
subdomain.conf is a shell sourcable file
we only parse entries starting with
SUBDOMAIN_PATH=
if there are multiple entries with SUBDOMAIN_PATH=
each will get added.
SUBDOMAIN_PATH=/etc/subdomain.d:/etc/subdomain.d/include
is the same as
SUBDOMAIN_PATH=/etc/subdomain.d
SUBDOMAIN_PATH=/etc/subdomain.d/include */
void parse_default_paths(void)
{
autofclose FILE *f;
char buf[1024];
char *t, *s;
int saved_npath = npath;
f = fopen(SUBDOMAIN_CONF, "r");
if (f == NULL)
goto out;
memset(buf, 0, sizeof(buf));
while (fgetline(f, buf, 1024)) {
if (stripcomment(buf) && (t = strstr(buf, "SUBDOMAIN_PATH="))) {
t += 15;
/* handle : separating path elements */
do {
s = strchr(t, ':');
if (s)
*s = 0;
if (!add_search_dir(stripblanks(t)))
break;
if (s)
t = s + 1;
} while (s != NULL);
}
}
/* if subdomain.conf doesn't set a base search dir set it to this */
out:
if (npath - saved_npath == 0) {
add_search_dir(basedir);
}
add_search_dir(basedir);
}
FILE *search_path(char *filename, char **fullpath)

View File

@ -33,27 +33,12 @@
CONFIG_DIR=/etc/apparmor
MODULE=apparmor
OLD_MODULE=subdomain
if [ -f "${CONFIG_DIR}/${MODULE}.conf" ] ; then
APPARMOR_CONF="${CONFIG_DIR}/${MODULE}.conf"
elif [ -f "${CONFIG_DIR}/${OLD_MODULE}.conf" ] ; then
APPARMOR_CONF="${CONFIG_DIR}/${OLD_MODULE}.conf"
elif [ -f "/etc/immunix/subdomain.conf" ] ; then
aa_log_warning_msg "/etc/immunix/subdomain.conf is deprecated, use ${CONFIG_DIR}/subdomain.conf instead"
APPARMOR_CONF="/etc/immunix/subdomain.conf"
elif [ -f "/etc/subdomain.conf" ] ; then
aa_log_warning_msg "/etc/subdomain.conf is deprecated, use ${CONFIG_DIR}/subdomain.conf instead"
APPARMOR_CONF="/etc/subdomain.conf"
else
aa_log_warning_msg "Unable to find config file in ${CONFIG_DIR}, installation problem?"
fi
# Read configuration options from /etc/subdomain.conf, default is to
# warn if subdomain won't load.
SUBDOMAIN_MODULE_PANIC="warn"
SUBDOMAIN_ENABLE_OWLSM="no"
APPARMOR_ENABLE_AAEVENTD="no"
if [ -f "${APPARMOR_CONF}" ] ; then
#parse the conf file to see what we should do
. "${APPARMOR_CONF}"
@ -66,15 +51,11 @@ if [ "${QUIET:-no}" = yes ] || [ "${quiet:-n}" = y ]; then
PARSER_OPTS="$PARSER_OPTS --quiet"
fi
# SUBDOMAIN_DIR and APPARMOR_DIR might be defined in subdomain.conf|apparmor.conf
# APPARMOR_DIR might be defined in apparmor.conf
if [ -d "${APPARMOR_DIR}" ] ; then
PROFILE_DIRS=${APPARMOR_DIR}
elif [ -d "${SUBDOMAIN_DIR}" ] ; then
PROFILE_DIRS=${SUBDOMAIN_DIR}
elif [ -d /etc/apparmor.d ] ; then
PROFILE_DIRS=/etc/apparmor.d
elif [ -d /etc/subdomain.d ] ; then
PROFILE_DIRS=/etc/subdomain.d
else
aa_log_warning_msg "Unable to find profiles directory, installation problem?"
fi
@ -82,17 +63,9 @@ ADDITIONAL_PROFILE_DIR=/var/lib/snapd/apparmor/profiles
if [ -d "$ADDITIONAL_PROFILE_DIR" ]; then
PROFILE_DIRS="${PROFILE_DIRS} ${ADDITIONAL_PROFILE_DIR}"
fi
AA_EV_BIN=/usr/sbin/aa-eventd
AA_EV_PIDFILE=/var/run/aa-eventd.pid
AA_STATUS=/usr/sbin/aa-status
SD_EV_BIN=/usr/sbin/sd-event-dispatch.pl
SD_EV_PIDFILE=/var/run/sd-event-dispatch.init.pid
SD_STATUS=/usr/sbin/subdomain_status
SECURITYFS=/sys/kernel/security
SUBDOMAINFS_MOUNTPOINT=$(grep subdomainfs /etc/fstab | \
sed -e 's|^[[:space:]]*[^[:space:]]\+[[:space:]]\+\(/[^[:space:]]*\)[[:space:]]\+subdomainfs.*$|\1|' 2> /dev/null)
# keep exit status from parser during profile load. 0 is good, 1 is bad
STATUS=0
@ -106,9 +79,6 @@ is_apparmor_present() {
shift
done
# check for subdomainfs version of module
grep -qE "^($modules)[[:space:]]" /proc/modules
[ $? -ne 0 -a -d /sys/module/apparmor ]
return $?
@ -170,6 +140,8 @@ skip_profile() {
local profile=$1
if [ "${profile%.rpmnew}" != "${profile}" -o \
"${profile%.rpmsave}" != "${profile}" -o \
"${profile%.orig}" != "${profile}" -o \
"${profile%.rej}" != "${profile}" -o \
"${profile%\~}" != "${profile}" ] ; then
return 1
fi
@ -306,24 +278,10 @@ failstop_system() {
if [ $level -ne "1" ] ; then
aa_log_failure_msg "- could not start AppArmor. Changing to runlevel 1"
telinit 1;
return -1;
return 255;
fi
aa_log_failure_msg "- could not start AppArmor."
return -1
}
module_panic() {
# the module failed to load, determine what action should be taken
case "$SUBDOMAIN_MODULE_PANIC" in
"warn"|"WARN")
return 1 ;;
"panic"|"PANIC") failstop_system
rc=$?
return $rc ;;
*) aa_log_failure_msg "- invalid AppArmor module fail option"
return -1 ;;
esac
return 255
}
is_apparmor_loaded() {
@ -331,25 +289,12 @@ is_apparmor_loaded() {
mount_securityfs
fi
mount_subdomainfs
if [ -f "${SECURITYFS}/${MODULE}/profiles" ]; then
SFS_MOUNTPOINT="${SECURITYFS}/${MODULE}"
return 0
fi
if [ -f "${SECURITYFS}/${OLD_MODULE}/profiles" ]; then
SFS_MOUNTPOINT="${SECURITYFS}/${OLD_MODULE}"
return 0
fi
if [ -f "${SUBDOMAINFS_MOUNTPOINT}/profiles" ]; then
SFS_MOUNTPOINT=${SUBDOMAINFS_MOUNTPOINT}
return 0
fi
# check for subdomainfs version of module
is_apparmor_present apparmor subdomain
is_apparmor_present apparmor
return $?
}
@ -368,62 +313,16 @@ mount_securityfs() {
return 0
}
mount_subdomainfs() {
# for backwords compatibility
if grep -q subdomainfs /proc/filesystems && \
! grep -q subdomainfs /proc/mounts && \
[ -n "${SUBDOMAINFS_MOUNTPOINT}" ]; then
aa_action "Mounting subdomainfs on ${SUBDOMAINFS_MOUNTPOINT}" \
mount "${SUBDOMAINFS_MOUNTPOINT}"
return $?
fi
return 0
}
unmount_subdomainfs() {
SUBDOMAINFS=$(grep subdomainfs /proc/mounts | cut -d" " -f2 2> /dev/null)
if [ -n "${SUBDOMAINFS}" ]; then
aa_action "Unmounting subdomainfs" umount ${SUBDOMAINFS}
fi
}
load_module() {
local rc=0
if modinfo -F filename apparmor > /dev/null 2>&1 ; then
MODULE=apparmor
elif modinfo -F filename ${OLD_MODULE} > /dev/null 2>&1 ; then
MODULE=${OLD_MODULE}
fi
if ! is_apparmor_present apparmor subdomain ; then
aa_action "Loading AppArmor module" /sbin/modprobe -q $MODULE $1
rc=$?
if [ $rc -ne 0 ] ; then
module_panic
rc=$?
if [ $rc -ne 0 ] ; then
exit $rc
fi
fi
fi
if ! is_apparmor_loaded ; then
return 1
fi
return $rc
}
apparmor_start() {
aa_log_daemon_msg "Starting AppArmor"
if ! is_apparmor_loaded ; then
load_module
rc=$?
if [ $rc -ne 0 ] ; then
aa_log_end_msg $rc
return $rc
fi
if ! is_apparmor_present ; then
aa_log_failure_msg "Starting AppArmor - failed, To enable AppArmor, ensure your kernel is configured with CONFIG_SECURITY_APPARMOR=y then add 'security=apparmor apparmor=1' to the kernel command line"
aa_log_end_msg 1
return 1
elif ! is_apparmor_loaded ; then
aa_log_failure_msg "Starting AppArmor - AppArmor control files aren't available under /sys/kernel/security/, please make sure securityfs is mounted."
aa_log_end_msg 1
return 1
fi
if [ ! -w "$SFS_MOUNTPOINT/.load" ] ; then
@ -447,7 +346,7 @@ apparmor_start() {
remove_profiles() {
# removing profiles as we directly read from subdomainfs
# removing profiles as we directly read from apparmorfs
# doesn't work, since we are removing entries which screws up
# our position. Lets hope there are never enough profiles to
# overflow the variable
@ -495,11 +394,8 @@ apparmor_kill() {
return 1
fi
unmount_subdomainfs
if is_apparmor_present apparmor ; then
MODULE=apparmor
elif is_apparmor_present subdomain ; then
MODULE=subdomain
else
aa_log_failure_msg "AppArmor is builtin"
return 1
@ -546,27 +442,11 @@ apparmor_try_restart() {
return $?
}
configure_owlsm () {
if [ "${SUBDOMAIN_ENABLE_OWLSM}" = "yes" -a -f ${SFS_MOUNTPOINT}/control/owlsm ] ; then
# Sigh, the "sh -c" is necessary for the SuSE aa_action
# and it can't be abstracted out as a seperate function, as
# that breaks under RedHat's action, which needs a
# binary to invoke.
aa_action "Enabling OWLSM extension" sh -c "echo -n \"1\" > \"${SFS_MOUNTPOINT}/control/owlsm\""
elif [ -f "${SFS_MOUNTPOINT}/control/owlsm" ] ; then
aa_action "Disabling OWLSM extension" sh -c "echo -n \"0\" > \"${SFS_MOUNTPOINT}/control/owlsm\""
fi
}
apparmor_status () {
if test -x ${AA_STATUS} ; then
${AA_STATUS} --verbose
return $?
fi
if test -x ${SD_STATUS} ; then
${SD_STATUS} --verbose
return $?
fi
if ! is_apparmor_loaded ; then
echo "AppArmor is not loaded."
rc=1

View File

@ -1,53 +0,0 @@
# subdomain.conf is a shared AppArmor configuration file that is sh sourcable.
################## AppArmor init.d configuration ################
# Move this to /etc/sysconfig/apparmor eventually
## Path: System/AppArmor
## Description: Enable the OWLSM extension to AppArmor
## Type: yesno
## Default: no
#
# Enable OWLSM extension to AppArmor?
# OWLSM is an extension to AppArmor that prevents processes from
# following symlinks they don't own and creating hardlinks to files they
# don't own, in an attempt to prevent /tmp race attacks. However, OWLSM
# can break some applications, so is disabled by default.
SUBDOMAIN_ENABLE_OWLSM="no"
## Path: System/AppArmor
## Description: Enable the AppArmor event daemon for reporting
## Type: yesno
## Default: no
#
# Enable the AppArmor event daemon for reporting?
APPARMOR_ENABLE_AAEVENTD="no"
#SUBDOMAIN_MODULE_PANIC=XXX
#This option controls how subdomain behaves when the init script attempts
#to load the AppArmor module and fails. There are 4 options
#warn - log a failure message. (default behavior)
#build - attempt to build the AppArmor module is the module can't be loaded.
# If successful
# the module will be built for the running kernel and loaded.
# If the build fails
# a failure message is logged
#panic - If the AppArmor module fails to load
# a failure message will be logged
# and the machine will drop to runlevel 1 (single user)
#build-panic - If the AppArmor module fails to load
# attempt to build the module
# If building the module fails
# panic (drop to runlevel 1)
#SUBDOMAIN_MODULE_PANIC=warn
################## subdomain_parser configuration ################
#SUBDOMAIN_PATH=XXXX
#This option specifies the include path that the subdomain_parser will
#use by default. If no entry is specified /etc/subdomain.d is used by
#default.
#SUBDOMAIN_PATH=/etc/subdomain.d

View File

@ -1,104 +0,0 @@
# ----------------------------------------------------------------------
# Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
# 2008, 2009
# NOVELL (All rights reserved)
#
# Copyright (c) 2010 - 2012
# 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.
# ----------------------------------------------------------------------
=pod
=head1 NAME
/etc/apparmor/subdomain.conf - configuration file for fine-tuning the
behavior of the AppArmor security tool.
=head1 DESCRIPTION
The AppArmor security tool can be configured to have
certain default behaviors based on configuration options set
in subdomain.conf. There are two variables that can be set in
subdomain.conf: B<SUBDOMAIN_PATH>, and B<SUBDOMAIN_MODULE_PANIC>.
=begin comment
FIXME keep quiet about OWLSM support for now.
=head2 SUBDOMAIN_ENABLE_OWLSM
This veriable is a yes/no toggle and is by default set to I<no>.
This variable determines whether the AppArmor initscript will enable
or disable the OWLsm security extension to AppArmor when the AppArmor
security tool is started. When enabled the OWLsm feature prevents programs
from following symlinks in temporary directories that are not owned by
the program's UID, and prevents processes from creating hardlinks to
files not owned by their UID.
=end comment
=head2 SUBDOMAIN_PATH
This variable accepts a string (path), and is by default set to
'/etc/apparmor.d/' This variable defines where the AppArmor security
tool looks for its policy definitions (a.k.a. AppArmor profiles).
=head2 SUBDOMAIN_MODULE_PANIC
This variable accepts a string that is one of four values: I<warn>,
I<build>, I<panic>, or I<build-panic>, and is set by default to I<warn>.
This setting controls the behavior of the AppArmor initscript if it
cannot successfully load the AppArmor kernel module on startup. The four
possible settings are:
=over 4
=item I<warn>
Log a failure message (the default behavior).
=item I<build>
Attempt to build the AppArmor module against the currently running
kernel. If the compilation is successful, the module will be loaded and
AppArmor started; if the compilation fails, a failure message is logged.
=item I<panic>
Log a failure message and drop to runlevel 1 (single user).
=item I<build-panic>
Attempt to build the module against the running kernel (like I<build>)
and if the compilation fails, drop to runlevel 1 (single user).
=back
=head1 BUGS
Setting the initscript to recompile the module will fail on SUSE, as the
module source is no longer installed by default. However, the module has
been included with the SUSE kernel, so no rebuilding should be necessary.
If you find any additional bugs, please report them at
L<https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
apparmor(7), apparmor_parser(8), and
L<https://wiki.apparmor.net>.

View File

@ -131,9 +131,13 @@ sub test_profile {
} elsif ($coredump) {
ok(0, "$profile: Produced core dump (signal $signal): $description");
} elsif ($istodo) {
TODO: {
local $TODO = "Unfixed testcase.";
ok($expass ? !$result : $result, "TODO: $profile: $description");
if ($expass != $result) {
fail("TODO passed unexpectedly: $profile: $description");
} else {
TODO: {
local $TODO = "Unfixed testcase.";
ok($expass ? !$result : $result, "TODO: $profile: $description");
}
}
} else {
ok($expass ? !$result : $result, "$profile: $description");

View File

@ -2,6 +2,7 @@
#=DESCRIPTION abi testing - abi path quotes in <> with spaces
#=EXRESULT PASS
#=TODO
#=DISABLED - results in "superfluous TODO", but fails after removing TODO
abi < "abi/4.19">,

View File

@ -2,6 +2,7 @@
#=DESCRIPTION abi testing - abi path quotes in <> with spaces
#=EXRESULT PASS
#=TODO
#=DISABLED - results in "superfluous TODO", but fails after removing TODO
abi < "abi/4.19" >,

View File

@ -1,6 +1,5 @@
#=DESCRIPTION reference variables in rules that also have alternations
#=EXRESULT PASS
#=TODO
# This test needs check on @{FOO} attachment having leading / post var expansion
@{FOO}=/bar /baz

View File

@ -1,6 +1,5 @@
#=DESCRIPTION reference variables is null
#=EXRESULT FAIL
#=TODO
#needs post var expansion check that variable contained a value
@{FOO}=

View File

@ -1,6 +1,5 @@
#=DESCRIPTION reference variables is null
#=EXRESULT FAIL
#=TODO
#needs post var expansion check that variable contained a value
@{FOO}=

View File

@ -1,7 +1,6 @@
#
#=DESCRIPTION test for conflict resolution in minimization phase of dfa gen
#=EXRESULT PASS
#=TODO
#
/usr/bin/foo {

View File

@ -1,7 +1,6 @@
#
#=DESCRIPTION test for conflict resolution in minimization phase of dfa gen
#=EXRESULT FAIL
#=TODO
#
/usr/bin/foo {
/b* px,

View File

@ -90,8 +90,8 @@
@{PROC}/meminfo r,
@{PROC}/stat r,
@{PROC}/cpuinfo r,
/sys/devices/system/cpu/ r,
/sys/devices/system/cpu/online r,
@{sys}/devices/system/cpu/ r,
@{sys}/devices/system/cpu/online r,
# glibc's *printf protections read the maps file
@{PROC}/@{pid}/{maps,auxv,status} r,

View File

@ -4,6 +4,5 @@
# needs to enumerate graphic devices (as with drmParsePciDeviceInfo() from
# libdrm).
# TODO: use @{sys} after it's moved into tunables/kernelvars (LP: #1728551)
/sys/devices/pci[0-9]*/**/{device,subsystem_device,subsystem_vendor,uevent,vendor} r,
@{sys}/devices/pci[0-9]*/**/{device,subsystem_device,subsystem_vendor,uevent,vendor} r,

View File

@ -19,7 +19,7 @@
@{PROC}/driver/nvidia/params r,
@{PROC}/modules r,
/sys/devices/system/memory/block_size_bytes r,
@{sys}/devices/system/memory/block_size_bytes r,
owner @{HOME}/.nv/ w,
owner @{HOME}/.nv/GLCache/ rw,

View File

@ -4,7 +4,7 @@
# System files
/etc/OpenCL/** r,
/sys/bus/pci/devices/ r, # libpocl.so -> libhwlock.so, libnvidia-opencl.so, beignet/libcl.so -> libdrm_intel.so
/sys/devices/system/node/ r, # for clGetPlatformIDs() from libOpenCL.so
/sys/devices/system/node/node[0-9]*/meminfo r, # for clGetPlatformIDs() from libOpenCL.so
@{sys}/bus/pci/devices/ r, # libpocl.so -> libhwlock.so, libnvidia-opencl.so, beignet/libcl.so -> libdrm_intel.so
@{sys}/devices/system/node/ r, # for clGetPlatformIDs() from libOpenCL.so
@{sys}/devices/system/node/node[0-9]*/meminfo r, # for clGetPlatformIDs() from libOpenCL.so

View File

@ -12,6 +12,6 @@
# System files
/dev/dri/card[0-9]* rw, # beignet/libcl.so
/sys/devices/pci[0-9]*/**/{class,config,resource,revision} r, # libcl.so -> libdrm_intel.so -> libpciaccess.so (move to dri-enumerate ?)
@{sys}/devices/pci[0-9]*/**/{class,config,resource,revision} r, # libcl.so -> libdrm_intel.so -> libpciaccess.so (move to dri-enumerate ?)
/usr/lib/@{multiarch}/beignet/** r,

View File

@ -16,8 +16,8 @@
# libnvidia-opencl.so rules:
/dev/nvidia-uvm rw,
/dev/nvidia-uvm-tools rw,
/sys/devices/pci[0-9]*/**/config r,
/sys/devices/system/memory/block_size_bytes r,
@{sys}/devices/pci[0-9]*/**/config r,
@{sys}/devices/system/memory/block_size_bytes r,
/usr/share/nvidia/** r,
@{PROC}/devices r,
@{PROC}/sys/vm/mmap_min_addr r,

View File

@ -11,22 +11,22 @@
# System files
/ r, # libpocl.so -> libhwloc.so
/sys/bus/pci/slots/ r, # libpocl.so -> hwloc_topology_load() from libhwloc.so
/sys/bus/{cpu,node}/devices/ r, # libpocl.so -> libhwlock.so
/sys/class/net/ r, # libpocl.so -> hwloc_pci_traverse_lookuposdevices_cb() from libhwloc.so
/sys/devices/pci[0-9]*/**/ r, # for libpocl -> hwloc_linux_lookup_block_class() from libhwloc.so
/sys/devices/pci[0-9]*/**/block/*/dev r, # libpocl.so -> hwloc_linux_lookup_host_block_class() from libhwloc.so
/sys/devices/pci[0-9]*/**/{class,local_cpus} r, # libpocl.so -> libhwlock.so
/sys/devices/pci[0-9]*/*/net/*/address r, # libpocl.so -> hwloc_pci_traverse_lookuposdevices_cb() from libhwloc.so
/sys/devices/system/cpu/ r, # libpocl.so -> libnuma.so
/sys/devices/system/cpu/cpu[0-9]*/cache/index[0-9]*/* r, # libpocl.so -> libhwloc.so
/sys/devices/system/cpu/cpu[0-9]*/online r, # libpocl.so -> libhwlock.so
/sys/devices/system/cpu/cpu[0-9]*/topology/* r, # *_siblings, physical_package_id and lot's of others, for libpocl.so -> libhwloc.so
/sys/devices/system/cpu/cpufreq/policy[0-9]*/* r, # for clGetPlatformIDs() from libpocl.so
/sys/devices/system/cpu/possible r, # libpocl.so -> libhwloc.so
/sys/devices/virtual/dmi/id/{,*} r, # libpocl.so -> libhwloc.so
/sys/fs/cgroup/cpuset/cpuset.{cpus,mems} r, # libpocl.so -> libhwloc.so
/sys/kernel/mm/hugepages{/,/**} r, # libpocl.so -> libhwloc.so
@{sys}/bus/pci/slots/ r, # libpocl.so -> hwloc_topology_load() from libhwloc.so
@{sys}/bus/{cpu,node}/devices/ r, # libpocl.so -> libhwlock.so
@{sys}/class/net/ r, # libpocl.so -> hwloc_pci_traverse_lookuposdevices_cb() from libhwloc.so
@{sys}/devices/pci[0-9]*/**/ r, # for libpocl -> hwloc_linux_lookup_block_class() from libhwloc.so
@{sys}/devices/pci[0-9]*/**/block/*/dev r, # libpocl.so -> hwloc_linux_lookup_host_block_class() from libhwloc.so
@{sys}/devices/pci[0-9]*/**/{class,local_cpus} r, # libpocl.so -> libhwlock.so
@{sys}/devices/pci[0-9]*/*/net/*/address r, # libpocl.so -> hwloc_pci_traverse_lookuposdevices_cb() from libhwloc.so
@{sys}/devices/system/cpu/ r, # libpocl.so -> libnuma.so
@{sys}/devices/system/cpu/cpu[0-9]*/cache/index[0-9]*/* r, # libpocl.so -> libhwloc.so
@{sys}/devices/system/cpu/cpu[0-9]*/online r, # libpocl.so -> libhwlock.so
@{sys}/devices/system/cpu/cpu[0-9]*/topology/* r, # *_siblings, physical_package_id and lot's of others, for libpocl.so -> libhwloc.so
@{sys}/devices/system/cpu/cpufreq/policy[0-9]*/* r, # for clGetPlatformIDs() from libpocl.so
@{sys}/devices/system/cpu/possible r, # libpocl.so -> libhwloc.so
@{sys}/devices/virtual/dmi/id/{,*} r, # libpocl.so -> libhwloc.so
@{sys}/fs/cgroup/cpuset/cpuset.{cpus,mems} r, # libpocl.so -> libhwloc.so
@{sys}/kernel/mm/hugepages{/,/**} r, # libpocl.so -> libhwloc.so
/usr/share/pocl/** r,
/{,var/}run/udev/data/*:* r, # libpocl.so -> hwloc_linux_block_class_fillinfos() from libhwloc.so

View File

@ -1,7 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2005 Novell/SUSE
# Copyright (C) 2015 Canonical, Ltd.
# Copyright (C) 2015-2018 Canonical, Ltd.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
@ -16,9 +16,9 @@
capability sys_chroot,
# postfix's master can send us signals
signal receive peer=/usr/lib/postfix/master,
signal receive peer=postfix-master,
unix (send, receive) peer=(label=/usr/lib/postfix/master),
unix (send, receive) peer=(label=postfix-master),
/etc/mailname r,
/etc/postfix/*.cf r,
@ -33,3 +33,5 @@
/var/spool/postfix/etc/* r,
/var/spool/postfix/lib/lib*.so* mr,
/var/spool/postfix/lib/@{multiarch}/lib*.so* mr,
/etc/postfix/dynamicmaps.cf.d/ r,

View File

@ -6,6 +6,7 @@
# lot of false positives when reading contents of directories)
deny @{HOME}/.*history mrwkl,
deny @{HOME}/.fetchmail* mrwkl,
deny @{HOME}/.mutt** mrwkl,
deny @{HOME}/.viminfo* mrwkl,
deny @{HOME}/.*~ mrwkl,
deny @{HOME}/.*.swp mrwkl,

View File

@ -5,6 +5,7 @@
#include <abstractions/private-files>
# potentially extremely sensitive files
audit deny @{HOME}/.aws/{,**} mrwkl,
audit deny @{HOME}/.gnupg/{,**} mrwkl,
audit deny @{HOME}/.ssh/{,**} mrwkl,
audit deny @{HOME}/.gnome2_private/{,**} mrwkl,

View File

@ -32,3 +32,8 @@
/etc/dehydrated/certs/*/cert-*.pem r,
/etc/dehydrated/certs/*/chain-*.pem r,
/etc/dehydrated/certs/*/fullchain-*.pem r,
# certbot
/etc/letsencrypt/archive/*/cert*.pem r,
/etc/letsencrypt/archive/*/chain*.pem r,
/etc/letsencrypt/archive/*/fullchain*.pem r,

View File

@ -23,3 +23,6 @@
# dehydrated
/etc/dehydrated/certs/*/privkey-*.pem r,
# certbot / letsencrypt
/etc/letsencrypt/archive/*/privkey*.pem r,

View File

@ -41,8 +41,8 @@
@{PROC}/@{pid}/ r,
@{PROC}/@{pid}/fd/ r,
@{PROC}/filesystems r,
/sys/devices/system/cpu/ r,
/sys/devices/system/cpu/** r,
@{sys}/devices/system/cpu/ r,
@{sys}/devices/system/cpu/** r,
/usr/share/** r,
/var/lib/dbus/machine-id r,
@ -88,8 +88,8 @@
@{PROC}/@{pid}/ r,
@{PROC}/@{pid}/fd/ r,
@{PROC}/filesystems r,
/sys/devices/system/cpu/ r,
/sys/devices/system/cpu/** r,
@{sys}/devices/system/cpu/ r,
@{sys}/devices/system/cpu/** r,
/usr/share/** r,
/var/lib/dbus/machine-id r,

View File

@ -2,5 +2,5 @@
# video device access
# System devices
/sys/class/video4linux r,
/sys/class/video4linux/** r,
@{sys}/class/video4linux r,
@{sys}/class/video4linux/** r,

View File

@ -5,7 +5,7 @@
/dev/dri/ r, # libvulkan_radeon.so, libvulkan_intel.so (Mesa)
/etc/vulkan/{explicit,implicit}_layer.d/{,*.json} r,
# for drmGetMinorNameForFD() from libvulkan_intel.so (Mesa)
/sys/devices/pci[0-9]*/*/drm/ r,
@{sys}/devices/pci[0-9]*/*/drm/ r,
/usr/share/vulkan/icd.d/{,*.json} r,
/usr/share/vulkan/{explicit,implicit}_layer.d/{,*.json} r,

View File

@ -20,13 +20,13 @@
/etc/phpsysinfo/config.php r,
/etc/udev/udev.conf r,
@{PROC}/** r,
/sys/bus/ r,
/sys/bus/pci/devices/ r,
/sys/bus/pci/slots/ r,
/sys/bus/pci/slots/** r,
/sys/bus/usb/devices/ r,
/sys/class/ r,
/sys/devices/** r,
@{sys}/bus/ r,
@{sys}/bus/pci/devices/ r,
@{sys}/bus/pci/slots/ r,
@{sys}/bus/pci/slots/** r,
@{sys}/bus/usb/devices/ r,
@{sys}/class/ r,
@{sys}/devices/** r,
/usr/bin/ r,
/usr/bin/apt-cache ixr,
/usr/bin/dpkg-query ixr,

View File

@ -24,8 +24,8 @@ profile nvidia_modprobe {
/dev/nvidia-uvm w,
/dev/nvidia-uvm-tools w,
/sys/bus/pci/devices/ r,
/sys/devices/pci[0-9]*/**/config r,
@{sys}/bus/pci/devices/ r,
@{sys}/devices/pci[0-9]*/**/config r,
@{PROC}/devices r,
@{PROC}/modules r,
@{PROC}/sys/kernel/modprobe r,
@ -51,9 +51,9 @@ profile nvidia_modprobe {
/etc/modprobe.d/{,*.conf} r,
/etc/nvidia/current/*.conf r,
/sys/module/ipmi_devintf/initstate r,
/sys/module/ipmi_msghandler/initstate r,
/sys/module/nvidia/initstate r,
@{sys}/module/ipmi_devintf/initstate r,
@{sys}/module/ipmi_msghandler/initstate r,
@{sys}/module/nvidia/initstate r,
@{PROC}/cmdline r,
}

View File

@ -47,7 +47,7 @@ profile syslog-ng /{usr/,}{bin,sbin}/syslog-ng {
/etc/hosts.deny r,
/etc/hosts.allow r,
/{usr/,}{bin,sbin}/syslog-ng mr,
/sys/devices/system/cpu/online r,
@{sys}/devices/system/cpu/online r,
/usr/share/syslog-ng/** r,
/var/lib/syslog-ng/syslog-ng-?????.qf rw,
# chrooted applications

View File

@ -45,7 +45,7 @@ profile dnsmasq /usr/{bin,sbin}/dnsmasq flags=(attach_disconnected) {
/usr/{bin,sbin}/dnsmasq mr,
/var/log/*dnsmasq.log w,
/var/log/dnsmasq*.log w,
/usr/share/dnsmasq/ r,
/usr/share/dnsmasq/* r,
@ -96,6 +96,7 @@ profile dnsmasq /usr/{bin,sbin}/dnsmasq flags=(attach_disconnected) {
/{,var/}run/sendsigs.omit.d/*dnsmasq.pid w,
/{,var/}run/NetworkManager/dnsmasq.conf r,
/{,var/}run/NetworkManager/dnsmasq.pid w,
/{,var/}run/NetworkManager/NetworkManager.pid w,
profile libvirt_leaseshelper {
#include <abstractions/base>
@ -107,9 +108,9 @@ profile dnsmasq /usr/{bin,sbin}/dnsmasq flags=(attach_disconnected) {
owner @{PROC}/@{pid}/net/psched r,
owner @{PROC}/@{pid}/status r,
/sys/devices/system/cpu/ r,
/sys/devices/system/node/ r,
/sys/devices/system/node/*/meminfo r,
@{sys}/devices/system/cpu/ r,
@{sys}/devices/system/node/ r,
@{sys}/devices/system/node/*/meminfo r,
# libvirt lease and status files for dnsmasq
/var/lib/libvirt/dnsmasq/*.leases rw,

View File

@ -38,6 +38,7 @@ profile dovecot /usr/{bin,sbin}/dovecot flags=(attach_disconnected) {
/etc/lsb-release r,
/etc/SuSE-release r,
@{PROC}/@{pid}/mounts r,
@{PROC}/sys/fs/suid_dumpable r,
/usr/bin/doveconf rix,
/usr/lib/dovecot/anvil mrPx,
/usr/lib/dovecot/auth mrPx,

View File

@ -1,6 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2006 Novell/SUSE
# Copyright (C) 2018 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
@ -10,18 +11,14 @@
#include <tunables/global>
/usr/lib/postfix/anvil {
profile postfix-anvil /usr/lib/postfix/{sbin/,}anvil {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/postfix-common>
capability setgid,
capability setuid,
/usr/lib/postfix/anvil rmix,
/usr/lib/postfix/{sbin/,}anvil rmix,
/etc/postfix/main.cf r,
/{var/spool/postfix/,}private/anvil rw,
/{var/spool/postfix/,}pid/unix.anvil rw,
@{PROC}/net/if_inet6 r,
/{var/spool/postfix/,}pid/unix.anvil rwk,
}

View File

@ -1,6 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2006 Novell/SUSE
# Copyright (C) 2018 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
@ -10,15 +11,12 @@
#include <tunables/global>
/usr/lib/postfix/bounce {
profile postfix-bounce /usr/lib/postfix/{sbin/,}bounce {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/postfix-common>
capability setgid,
capability setuid,
/usr/lib/postfix/bounce rmix,
/usr/lib/postfix/{sbin/,}bounce rmix,
/{var/spool/postfix/,}active/[0-9A-F]/[0-9A-F]/* rwl,
/{var/spool/postfix/,}active/[0-9A-F]/[0-9A-F]/ rwl,
@ -36,10 +34,8 @@
/{var/spool/postfix/,}trace/[0-9A-F]/[0-9A-F]/ rwl,
/{var/spool/postfix/,}trace/[0-9A-F]/ rwl,
/{var/spool/postfix/,}public/cleanup w,
/{var/spool/postfix/,}pid/unix.bounce rw,
/{var/spool/postfix/,}pid/unix.defer rw,
/{var/spool/postfix/,}pid/unix.trace rw,
/etc/postfix/main.cf r,
@{PROC}/net/if_inet6 r,
/{var/spool/postfix/,}pid/unix.bounce rwk,
/{var/spool/postfix/,}pid/unix.defer rwk,
/{var/spool/postfix/,}pid/unix.trace rwk,
}

View File

@ -1,6 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2006 Novell/SUSE
# Copyright (C) 2018 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
@ -10,23 +11,27 @@
#include <tunables/global>
/usr/lib/postfix/cleanup {
profile postfix-cleanup /usr/lib/postfix/{sbin/,}cleanup {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/postfix-common>
capability net_bind_service,
/usr/lib/postfix/cleanup rmix,
/usr/lib/postfix/{sbin/,}cleanup rmix,
/{var/spool/postfix/,}incoming/[0-9]*.[0-9]* rwl,
/{var/spool/postfix/,}incoming/[0-9A-F]/[0-9A-F]/* rwl,
/{var/spool/postfix/,}incoming/[0-9A-F]/[0-9A-F]/ rwl,
/{var/spool/postfix/,}incoming/[0-9A-F]/ rwl,
/{var/spool/postfix/,}private/{rewrite,bounce} w,
/{var/spool/postfix/,}public/qmgr w,
/{var/spool/postfix/,}incoming/[0-9A-F]* rw,
/{var/spool/postfix/,}private/bounce w,
/{var/spool/postfix/,}private/rewrite rw,
/{var/spool/postfix/,}public/qmgr rw,
/{var/spool/postfix/,}hold/[0-9A-F]* w,
/{var/spool/postfix/,}pid/unix.cleanup rw,
/{var/spool/postfix/,}public/cleanup rw,
/{var/spool/postfix/,}pid/unix.cleanup rwk,
/etc/{m,fs}tab r,
/etc/postfix/* r,
}

View File

@ -1,6 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2005 Novell/SUSE
# Copyright (C) 2018 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
@ -11,8 +12,8 @@
#include <tunables/global>
/usr/lib/postfix/discard {
profile postfix-discard /usr/lib/postfix/{sbin/,}discard {
#include <abstractions/base>
/usr/lib/postfix/discard rmix,
/usr/lib/postfix/{sbin/,}discard rmix,
}

View File

@ -0,0 +1,20 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2018 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.
#
# ------------------------------------------------------------------
# vim:syntax=apparmor
#include <tunables/global>
profile postfix-dnsblog /usr/lib/postfix/{sbin/,}dnsblog {
#include <abstractions/base>
/usr/lib/postfix/{sbin/,}dnsblog rmix,
/var/spool/postfix/private/dnsblog rw,
}

View File

@ -2,6 +2,7 @@
#
# Copyright (C) 2002-2006 Novell/SUSE
# Copyright (C) 2017 Christian Boltz
# Copyright (C) 2018 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
@ -11,13 +12,13 @@
#include <tunables/global>
/usr/lib/postfix/error {
profile postfix-error /usr/lib/postfix/{sbin/,}error {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/postfix-common>
@{PROC}/sys/kernel/ngroups_max r,
/usr/lib/postfix/error mrix,
/usr/lib/postfix/{sbin/,}error rmix,
owner /var/spool/postfix/active/* rwk,
/var/spool/postfix/pid/unix.error rwk,
/var/spool/postfix/pid/unix.retry rwk,

View File

@ -1,6 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2006 Novell/SUSE
# Copyright (C) 2018 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
@ -10,15 +11,12 @@
#include <tunables/global>
/usr/lib/postfix/flush {
profile postfix-flush /usr/lib/postfix/{sbin/,}flush {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/postfix-common>
capability setgid,
capability setuid,
/usr/lib/postfix/flush rmix,
/usr/lib/postfix/{sbin/,}flush rmix,
/{var/spool/postfix/,}deferred/ r,
/{var/spool/postfix/,}deferred/[0-9A-F]/[0-9A-F]/* rwl,
@ -35,8 +33,6 @@
/{var/spool/postfix/,}public/qmgr w,
/{var/spool/postfix/,}pid/unix.flush rw,
/etc/mtab r,
/etc/postfix/main.cf r,
/etc/postfix/virtual.db r,
@{HOME}/.forward r,

View File

@ -2,6 +2,7 @@
#
# Copyright (C) 2002-2006 Novell/SUSE
# Copyright (C) 2017 Christian Boltz
# Copyright (C) 2018 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
@ -11,12 +12,13 @@
#include <tunables/global>
/usr/lib/postfix/lmtp {
profile postfix-lmtp /usr/lib/postfix/{sbin/,}lmtp {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/postfix-common>
/usr/lib/postfix/lmtp mrix,
/usr/lib/postfix/{sbin/,}lmtp rmix,
/var/spool/postfix/active/* rwk,
/var/spool/postfix/pid/unix.lmtp rwk,

View File

@ -1,6 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2006 Novell/SUSE
# Copyright (C) 2018 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
@ -10,7 +11,7 @@
#include <tunables/global>
/usr/lib/postfix/local {
profile postfix-local /usr/lib/postfix/{sbin/,}local {
#include <abstractions/base>
#include <abstractions/bash>
#include <abstractions/nameservice>
@ -23,20 +24,24 @@
/var/mailman/mail/wrapper Px,
/usr/bin/mlmmj-recieve Px,
/usr/lib/postfix/local rmix,
/usr/lib/postfix/{sbin/,}local rmix,
/{usr/,}bin/bash mixr,
/{usr/,}bin/date mixr,
/dev/tty rw,
/etc/{postfix/,}aliases.db r,
/etc/{postfix/,}aliases.db rk,
# mailman on SuSE is configed to have its own alias file
/var/lib/mailman/data/aliases.db r,
/var/lib/mailman/data/aliases.db rk,
/{var/spool/postfix/,}active/[0-9A-F]/[0-9A-F]/* rw,
/{var/spool/postfix/,}active/[0-9A-F]/[0-9A-F]/ rw,
/{var/spool/postfix/,}active/[0-9A-F]/ rw,
/{var/spool/postfix/,}pid/unix.local rw,
/{var/spool/postfix/,}private/{bounce,defer,flush,lmtp,rewrite} rw,
/{var/spool/postfix/,}active/[0-9A-F]* rwk,
/{var/spool/postfix/,}pid/unix.local rwk,
/{var/spool/postfix/,}private/{bounce,defer,flush,lmtp,local,rewrite} rw,
/{var/spool/postfix/,}public/{cleanup,flush} rw,
/etc/postfix/virtual.db r,
/etc/postfix/lists.db r,
# deliver mail
/var/mail/* wk,
}

View File

@ -0,0 +1,52 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2006 Novell/SUSE
# Copyright (C) 2018 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.
#
# ------------------------------------------------------------------
#include <tunables/global>
profile postfix-master /usr/lib/postfix/{sbin/,}master {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/postfix-common>
capability net_bind_service,
capability kill,
capability dac_override,
capability dac_read_search,
signal send peer=postfix-*,
signal peer=@{profile_name},
unix (send receive) type=stream peer=(label=postfix-*),
/etc/postfix/master.cf r,
/{var/spool/postfix/,}pid/master.pid rwk,
/{var/spool/postfix/,}private/* wl,
/{var/spool/postfix/,}private/tlsmgr rwl,
/{var/spool/postfix/,}public/{cleanup,flush,pickup,qmgr,showq,tlsmgr} rwl,
/usr/lib/postfix/{sbin/,}anvil Px,
/usr/lib/postfix/{sbin/,}bounce Px,
/usr/lib/postfix/{sbin/,}cleanup Px,
/usr/lib/postfix/{sbin/,}flush Px,
/usr/lib/postfix/{sbin/,}local Px,
/usr/lib/postfix/{sbin/,}master rmix,
/usr/lib/postfix/{sbin/,}nqmgr Px,
/usr/lib/postfix/{sbin/,}proxymap Px,
/usr/lib/postfix/{sbin/,}pickup Px,
/usr/lib/postfix/{sbin/,}pipe Px,
/usr/lib/postfix/{sbin/,}qmgr Px,
/usr/lib/postfix/{sbin/,}scache Px,
/usr/lib/postfix/{sbin/,}showq Px,
/usr/lib/postfix/{sbin/,}smtp Px,
/usr/lib/postfix/{sbin/,}smtpd Px,
/usr/lib/postfix/{sbin/,}tlsmgr Px,
/usr/lib/postfix/{sbin/,}trivial-rewrite Px,
}

View File

@ -1,6 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2006 Novell/SUSE
# Copyright (C) 2018 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
@ -10,12 +11,12 @@
#include <tunables/global>
/usr/lib/postfix/nqmgr {
profile postfix-nqmgr /usr/lib/postfix/{sbin/,}nqmgr {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/postfix-common>
/usr/lib/postfix/nqmgr rmix,
/usr/lib/postfix/{sbin/,}nqmgr rmix,
/{var/spool/postfix/,}active/ r,
/{var/spool/postfix/,}active/[0-9A-F]/ r,
@ -42,5 +43,4 @@
/{var/spool/postfix/,}private/local w,
/{var/spool/postfix/,}public/flush w,
/{var/spool/postfix/,}public/qmgr r,
/etc/postfix/main.cf r,
}

View File

@ -1,6 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2005 Novell/SUSE
# Copyright (C) 2018 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
@ -11,10 +12,10 @@
#include <tunables/global>
/usr/lib/postfix/oqmgr {
profile postfix-oqmgr /usr/lib/postfix/{sbin/,}oqmgr {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/postfix-common>
/usr/lib/postfix/oqmgr rmix,
/usr/lib/postfix/{sbin/,}oqmgr rmix,
}

View File

@ -1,6 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2006 Novell/SUSE
# Copyright (C) 2018 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
@ -10,14 +11,14 @@
#include <tunables/global>
/usr/lib/postfix/pickup {
profile postfix-pickup /usr/lib/postfix/{sbin/,}pickup {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/postfix-common>
/usr/lib/postfix/pickup rmix,
/usr/lib/postfix/{sbin/,}pickup rmix,
/{var/spool/postfix/,}public/cleanup w,
/{var/spool/postfix/,}public/cleanup rw,
/{var/spool/postfix/,}public/pickup r,
/{var/spool/postfix/,}maildrop/ r,
/{var/spool/postfix/,}maildrop/* rwl,

View File

@ -2,6 +2,7 @@
#
# Copyright (C) 2006 Novell/SUSE
# Copyright (C) 2017 Christian Boltz
# Copyright (C) 2018 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
@ -11,12 +12,13 @@
#include <tunables/global>
/usr/lib/postfix/pipe {
profile postfix-pipe /usr/lib/postfix/{sbin/,}pipe {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/postfix-common>
/usr/lib/postfix/pipe mrix,
/usr/lib/postfix/{sbin/,}pipe rmix,
/var/spool/postfix/active/* rwk,
/var/spool/postfix/private/bounce w,
/var/spool/postfix/private/defer w,

View File

@ -0,0 +1,17 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2018 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.
#
# ------------------------------------------------------------------
#include <tunables/global>
profile postfix-postscreen /usr/lib/postfix/{sbin/,}postscreen {
#include <abstractions/base>
/usr/lib/postfix/{sbin/,}postscreen rmix,
}

View File

@ -1,6 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2006 Novell/SUSE
# Copyright (C) 2018 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
@ -10,16 +11,11 @@
#include <tunables/global>
/usr/lib/postfix/proxymap {
profile postfix-proxymap /usr/lib/postfix/{sbin/,}proxymap {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/postfix-common>
capability setgid,
capability setuid,
/usr/lib/postfix/proxymap rmix,
/etc/postfix/main.cf r,
@{PROC}/net/if_inet6 r,
/usr/lib/postfix/{sbin/,}proxymap rmix,
/{var/spool/postfix/,}private/proxymap rw,
}

View File

@ -1,6 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2006 Novell/SUSE
# Copyright (C) 2018 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
@ -10,16 +11,18 @@
#include <tunables/global>
/usr/lib/postfix/qmgr {
profile postfix-qmgr /usr/lib/postfix/{sbin/,}qmgr {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/postfix-common>
/usr/lib/postfix/qmgr rmix,
/usr/lib/postfix/{sbin/,}qmgr rmix,
/{var/spool/postfix/,}active/ r,
/{var/spool/postfix/,}active/[0-9A-F]/[0-9A-F]/* rwl,
/{var/spool/postfix/,}active/[0-9A-F]/[0-9A-F]/ rwl,
/{var/spool/postfix/,}active/[0-9A-F]/ rwl,
/{var/spool/postfix/,}active/[0-9A-F]* rwlk,
/{var/spool/postfix/,}defer/ r,
/{var/spool/postfix/,}defer/[0-9A-F]/[0-9A-F]/* rwl,
/{var/spool/postfix/,}defer/[0-9A-F]/[0-9A-F]/ rwl,
@ -32,13 +35,14 @@
/{var/spool/postfix/,}incoming/[0-9A-F]/[0-9A-F]/* rwl,
/{var/spool/postfix/,}incoming/[0-9A-F]/[0-9A-F]/ rwl,
/{var/spool/postfix/,}incoming/[0-9A-F]/ rwl,
/{var/spool/postfix/,}incoming/[0-9A-F]* rwl,
/{var/spool/postfix/,}public/flush w,
/{var/spool/postfix/,}public/qmgr r,
/{var/spool/postfix/,}private/bounce w,
/{var/spool/postfix/,}private/defer w,
/{var/spool/postfix/,}private/local w,
/{var/spool/postfix/,}private/local rw,
/{var/spool/postfix/,}private/relay w,
/{var/spool/postfix/,}private/rewrite w,
/{var/spool/postfix/,}private/rewrite rw,
/{var/spool/postfix/,}private/smtp w,
/{var/spool/postfix/,}private/trace w,
/{var/spool/postfix/,}private/uucp w,

View File

@ -1,6 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2006 Novell/SUSE
# Copyright (C) 2018 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
@ -10,10 +11,10 @@
#include <tunables/global>
/usr/lib/postfix/spawn {
profile postfix-qmqpd /usr/lib/postfix/{sbin/,}qmqpd {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/postfix-common>
/usr/lib/postfix/spawn rmix,
/usr/lib/postfix/{sbin/,}qmqpd rmix,
}

View File

@ -1,6 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2005 Novell/SUSE
# Copyright (C) 2018 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,12 +13,10 @@
#include <tunables/global>
/usr/lib/postfix/scache {
profile postfix-scache /usr/lib/postfix/{sbin/,}scache {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/postfix-common>
/usr/lib/postfix/scache rmix,
/{,var/}run/nscd/group r,
/usr/lib/postfix/{sbin/,}scache rmix,
}

View File

@ -1,6 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2006 Novell/SUSE
# Copyright (C) 2018 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
@ -10,12 +11,12 @@
#include <tunables/global>
/usr/lib/postfix/showq {
profile postfix-showq /usr/lib/postfix/{sbin/,}showq {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/postfix-common>
/usr/lib/postfix/showq rmix,
/usr/lib/postfix/{sbin/,}showq rmix,
/{var/spool/postfix/,}active/ r,
/{var/spool/postfix/,}active/[0-9A-F]/[0-9A-F]/* r,

View File

@ -1,6 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2006 Novell/SUSE
# Copyright (C) 2018 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
@ -10,7 +11,7 @@
#include <tunables/global>
/usr/lib/postfix/smtp {
profile postfix-smtp /usr/lib/postfix/{sbin/,}smtp {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/postfix-common>
@ -20,7 +21,7 @@
capability dac_read_search,
capability net_bind_service,
/usr/lib/postfix/smtp rmix,
/usr/lib/postfix/{sbin/,}smtp rmix,
/{var/spool/postfix/,}active/[0-9A-F]/[0-9A-F]/* rwl,
/{var/spool/postfix/,}active/[0-9A-F]/[0-9A-F]/ rwl,

View File

@ -1,6 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2006 Novell/SUSE
# Copyright (C) 2018 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
@ -10,24 +11,24 @@
#include <tunables/global>
/usr/lib/postfix/smtpd {
profile postfix-smtpd /usr/lib/postfix/{sbin/,}smtpd {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/postfix-common>
#include <abstractions/openssl>
#include <abstractions/ssl_certs>
#include <abstractions/ssl_keys>
capability dac_override,
capability dac_read_search,
capability setgid,
capability setuid,
/usr/lib/postfix/smtpd rmix,
/usr/lib/postfix/{sbin/,}smtpd rmix,
/usr/sbin/postdrop rPx,
/dev/urandom r,
/etc/aliases.db r,
/etc/aliases.db rk,
# mailman on SuSE is configured to have its own alias db
/var/lib/mailman/data/aliases.db r,
/var/lib/mailman/data/aliases.db rk,
/etc/mtab r,
/etc/fstab r,
/etc/postfix/*.db r,
@ -37,21 +38,14 @@
/etc/postfix/main.cf r,
/etc/postfix/prng_exch rw,
/usr/lib64/sasl2/ mr,
/usr/lib64/sasl2/* mr,
/usr/lib/sasl2/ mr,
/usr/lib/sasl2/* mr,
/usr/share/ssl/certs/ca-bundle.crt r,
/{var/spool/postfix/,}pid/inet.* rw,
/{var/spool/postfix/,}private/anvil w,
/{var/spool/postfix/,}private/proxymap w,
/{var/spool/postfix/,}private/rewrite w,
/{var/spool/postfix/,}private/tlsmgr w,
/{var/spool/postfix/,}public/cleanup w,
/{var/spool/postfix/,}pid/inet.* rwk,
/{var/spool/postfix/,}private/anvil rw,
/{var/spool/postfix/,}private/proxymap rw,
/{var/spool/postfix/,}private/rewrite rw,
/{var/spool/postfix/,}private/tlsmgr rw,
/{var/spool/postfix/,}public/cleanup rw,
/{,var/}run/sasl2/mux w,
@{PROC}/net/if_inet6 r,
}

View File

@ -1,6 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2006 Novell/SUSE
# Copyright (C) 2018 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
@ -10,10 +11,10 @@
#include <tunables/global>
/usr/lib/postfix/qmqpd {
profile postfix-spawn /usr/lib/postfix/{sbin/,}spawn {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/postfix-common>
/usr/lib/postfix/qmqpd rmix,
/usr/lib/postfix/{sbin/,}spawn rmix,
}

View File

@ -1,6 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2005 Novell/SUSE
# Copyright (C) 2018 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
@ -11,15 +12,18 @@
#include <tunables/global>
/usr/lib/postfix/tlsmgr {
profile postfix-tlsmgr /usr/lib/postfix/{sbin/,}tlsmgr {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/postfix-common>
/usr/lib/postfix/tlsmgr rmix,
/usr/lib/postfix/{sbin/,}tlsmgr rmix,
/etc/postfix/prng_exch rw,
/{var/spool/postfix/,}private/tlsmgr r,
/var/spool/postfix/dev/urandom r,
/{etc,var/lib}/postfix/prng_exch rwk,
/{var/spool/postfix/,}private/tlsmgr rw,
/{,var/}run/__db.smtpd_tls_session_cache.db rw,
/{,var/}run/smtpd_tls_session_cache.db rw,
/var/lib/postfix/smtpd_scache.db rwk,
/var/lib/postfix/smtp_scache.db rwk,
}

View File

@ -1,6 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2006 Novell/SUSE
# Copyright (C) 2018 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
@ -10,16 +11,17 @@
#include <tunables/global>
/usr/lib/postfix/trivial-rewrite {
profile postfix-trivial-rewrite /usr/lib/postfix/{sbin/,}trivial-rewrite {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/postfix-common>
/usr/lib/postfix/trivial-rewrite rmix,
/usr/lib/postfix/{sbin/,}trivial-rewrite rmix,
/etc/postfix/relocated.db r,
/etc/postfix/transport.db r,
/etc/postfix/virtual.db r,
/etc/{m,fs}tab r,
/var/spool/postfix/pid/unix.rewrite rw,
/{var/spool/postfix/,}private/rewrite rw,
}

View File

@ -1,6 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2006 Novell/SUSE
# Copyright (C) 2018 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
@ -10,10 +11,10 @@
#include <tunables/global>
/usr/lib/postfix/verify {
profile postfix-verify /usr/lib/postfix/{sbin/,}verify {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/postfix-common>
/usr/lib/postfix/verify rmix,
/usr/lib/postfix/{sbin/,}verify rmix,
}

View File

@ -1,6 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2006 Novell/SUSE
# Copyright (C) 2018 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
@ -10,15 +11,13 @@
#include <tunables/global>
/usr/lib/postfix/virtual {
profile postfix-virtual /usr/lib/postfix/{sbin/,}virtual {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/postfix-common>
capability setgid,
capability setuid,
/usr/lib/postfix/{sbin/,}virtual rmix,
/usr/lib/postfix/virtual rmix,
/var/spool/postfix/active/* rw,
/var/spool/postfix/pid/unix.virtual rw,
/var/spool/postfix/private/bounce w,

View File

@ -1,46 +0,0 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-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.
#
# ------------------------------------------------------------------
#include <tunables/global>
/usr/lib/postfix/master {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/postfix-common>
capability net_bind_service,
capability kill,
capability dac_override,
/etc/postfix/master.cf r,
/{var/spool/postfix/,}pid/master.pid rwk,
/{var/spool/postfix/,}private/* wl,
/{var/spool/postfix/,}private/tlsmgr rwl,
/{var/spool/postfix/,}public/{cleanup,flush,pickup,qmgr,showq,tlsmgr} rwl,
/usr/lib/postfix/anvil Px,
/usr/lib/postfix/bounce Px,
/usr/lib/postfix/cleanup Px,
/usr/lib/postfix/flush Px,
/usr/lib/postfix/local Px,
/usr/lib/postfix/master rmix,
/usr/lib/postfix/nqmgr Px,
/usr/lib/postfix/proxymap Px,
/usr/lib/postfix/pickup Px,
/usr/lib/postfix/pipe Px,
/usr/lib/postfix/qmgr Px,
/usr/lib/postfix/scache Px,
/usr/lib/postfix/showq Px,
/usr/lib/postfix/smtp Px,
/usr/lib/postfix/smtpd Px,
/usr/lib/postfix/tlsmgr Px,
/usr/lib/postfix/trivial-rewrite Px,
/usr/lib/postfix/master rmix,
}

View File

@ -30,5 +30,5 @@
/var/spool/postfix/maildrop r,
/var/spool/postfix/maildrop/* rwl,
/var/spool/postfix/pid r,
/var/spool/postfix/public/pickup w,
/var/spool/postfix/public/pickup rw,
}

View File

@ -1,4 +1,4 @@
SUBDIRS=subdomain
SUBDIRS=apparmor
.PHONY: clean
clean:

View File

@ -107,7 +107,7 @@ apparmor.check_qualifiers(program)
apparmor.loadincludes()
profile_filename = apparmor.get_profile_filename(program)
profile_filename = apparmor.get_profile_filename_from_attachment(program, True)
if os.path.exists(profile_filename):
apparmor.helpers[program] = apparmor.get_profile_flags(profile_filename, program)
else:

View File

@ -1,7 +1,7 @@
#! /usr/bin/python3
# ----------------------------------------------------------------------
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
# Copyright (C) 2014-2017 Christian Boltz <apparmor@cboltz.de>
# Copyright (C) 2014-2018 Christian Boltz <apparmor@cboltz.de>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
@ -57,7 +57,7 @@ def reset_aa():
apparmor.aa.aa = apparmor.aa.hasher()
apparmor.aa.filelist = apparmor.aa.hasher()
apparmor.aa.include = dict()
apparmor.aa.existing_profiles = apparmor.aa.hasher()
apparmor.aa.active_profiles = apparmor.aa.ProfileList()
apparmor.aa.original_aa = apparmor.aa.hasher()
def find_profiles_from_files(files):
@ -75,7 +75,7 @@ def find_files_from_profiles(profiles):
apparmor.aa.read_profiles()
for profile_name in profiles:
profile_to_filename[profile_name] = apparmor.aa.get_profile_filename(profile_name)
profile_to_filename[profile_name] = apparmor.aa.get_profile_filename_from_profile_name(profile_name, True)
reset_aa()

View File

@ -1,6 +1,6 @@
# ----------------------------------------------------------------------
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
# Copyright (C) 2014-2017 Christian Boltz <apparmor@cboltz.de>
# Copyright (C) 2014-2018 Christian Boltz <apparmor@cboltz.de>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
@ -49,6 +49,8 @@ from apparmor.regex import (RE_PROFILE_START, RE_PROFILE_END, RE_PROFILE_LINK,
RE_PROFILE_UNIX, RE_RULE_HAS_COMMA, RE_HAS_COMMENT_SPLIT,
strip_quotes, parse_profile_start_line, re_match_include )
from apparmor.profile_list import ProfileList
from apparmor.profile_storage import (ProfileStorage, add_or_remove_flag, ruletypes, write_alias,
write_abi, write_includes, write_list_vars )
@ -89,7 +91,8 @@ extra_profile_dir = None
# To keep track of previously included profile fragments
include = dict()
existing_profiles = dict()
active_profiles = ProfileList()
extra_profiles = ProfileList()
# To store the globs entered by users so they can be provided again
# format: user_globs['/foo*'] = AARE('/foo*')
@ -217,11 +220,29 @@ def find_executable(bin_path):
return full_bin
return None
def get_profile_filename(profile):
"""Returns the full profile name"""
if existing_profiles.get(profile, False):
return existing_profiles[profile]
elif profile.startswith('/'):
def get_profile_filename_from_profile_name(profile, get_new=False):
"""Returns the full profile name for the given profile name"""
filename = active_profiles.filename_from_profile_name(profile)
if filename:
return filename
if get_new:
return get_new_profile_filename(profile)
def get_profile_filename_from_attachment(profile, get_new=False):
"""Returns the full profile name for the given attachment"""
filename = active_profiles.filename_from_attachment(profile)
if filename:
return filename
if get_new:
return get_new_profile_filename(profile)
def get_new_profile_filename(profile):
'''Compose filename for a new profile'''
if profile.startswith('/'):
# Remove leading /
profile = profile[1:]
else:
@ -238,7 +259,7 @@ def name_to_prof_filename(prof_filename):
else:
bin_path = find_executable(prof_filename)
if bin_path:
prof_filename = get_profile_filename(bin_path)
prof_filename = get_profile_filename_from_attachment(bin_path, True)
if os.path.isfile(prof_filename):
return (prof_filename, bin_path)
@ -464,7 +485,7 @@ def create_new_profile(localfile, is_stub=False):
def delete_profile(local_prof):
"""Deletes the specified file from the disk and remove it from our list"""
profile_file = get_profile_filename(local_prof)
profile_file = get_profile_filename_from_profile_name(local_prof, True)
if os.path.isfile(profile_file):
os.remove(profile_file)
if aa.get(local_prof, False):
@ -498,13 +519,15 @@ def get_profile(prof_name):
if inactive_profile:
uname = 'Inactive local profile for %s' % prof_name
inactive_profile[prof_name][prof_name]['flags'] = 'complain'
orig_filename = inactive_profile[prof_name][prof_name]['filename'] # needed for CMD_VIEW_PROFILE
inactive_profile[prof_name][prof_name]['filename'] = ''
profile_hash[uname]['username'] = uname
profile_hash[uname]['profile_type'] = 'INACTIVE_LOCAL'
profile_hash[uname]['profile'] = serialize_profile(inactive_profile[prof_name], prof_name, {})
profile_hash[uname]['profile_data'] = inactive_profile
existing_profiles.pop(prof_name) # remove profile filename from list to force storing in /etc/apparmor.d/ instead of extra_profile_dir
# no longer necessary after splitting active and extra profiles
# existing_profiles.pop(prof_name) # remove profile filename from list to force storing in /etc/apparmor.d/ instead of extra_profile_dir
# If no profiles in repo and no inactive profiles
if not profile_hash.keys():
@ -538,11 +561,7 @@ def get_profile(prof_name):
q.selected = options.index(options[arg])
if ans == 'CMD_VIEW_PROFILE':
pager = get_pager()
proc = subprocess.Popen(pager, stdin=subprocess.PIPE)
# proc.communicate('Profile submitted by %s:\n\n%s\n\n' %
# (options[arg], p['profile']))
proc.communicate(p['profile'].encode())
proc.kill()
subprocess.call([pager, orig_filename])
elif ans == 'CMD_USE_PROFILE':
if p['profile_type'] == 'INACTIVE_LOCAL':
profile_data = p['profile_data']
@ -551,21 +570,6 @@ def get_profile(prof_name):
profile_data = parse_repo_profile(prof_name, repo_url, p)
return profile_data
def activate_repo_profiles(url, profiles, complain):
read_profiles()
try:
for p in profiles:
pname = p[0]
profile_data = parse_repo_profile(pname, url, p[1])
attach_profile_data(aa, profile_data)
write_profile(pname)
if complain:
fname = get_profile_filename(pname)
change_profile_flags(profile_dir + fname, None, 'complain', True)
aaui.UI_Info(_('Setting %s to complain mode.') % pname)
except Exception as e:
sys.stderr.write(_("Error activating profiles: %s") % e)
def autodep(bin_name, pname=''):
bin_full = None
global repo_cfg
@ -592,7 +596,7 @@ def autodep(bin_name, pname=''):
# Create a new profile if no existing profile
if not profile_data:
profile_data = create_new_profile(pname)
file = get_profile_filename(pname)
file = get_profile_filename_from_profile_name(pname, True)
profile_data[pname][pname]['filename'] = None # will be stored in /etc/apparmor.d when saving, so it shouldn't carry the extra_profile_dir filename
attach_profile_data(aa, profile_data)
attach_profile_data(original_aa, profile_data)
@ -661,6 +665,7 @@ def change_profile_flags(prof_filename, program, flag, set_flag):
'flags': newflags,
'profile_keyword': matches['profile_keyword'],
'header_comment': matches['comment'] or '',
'xattrs': matches['xattrs'],
}
line = write_header(header_data, len(space)/2, profile, False, True)
line = '%s\n' % line[0]
@ -692,15 +697,16 @@ def profile_exists(program):
"""Returns True if profile exists, False otherwise"""
# Check cache of profiles
if existing_profiles.get(program, False):
if active_profiles.filename_from_attachment(program):
return True
# Check the disk for profile
prof_path = get_profile_filename(program)
prof_path = get_profile_filename_from_attachment(program, True)
#print(prof_path)
if os.path.isfile(prof_path):
# Add to cache of profile
existing_profiles[program] = prof_path
return True
raise AppArmorBug('Reached strange condition in profile_exists(), please open a bugreport!')
# active_profiles[program] = prof_path
# return True
return False
def sync_profile():
@ -1088,9 +1094,9 @@ def handle_children(profile, hat, root):
options += 'd'
# Define the default option
default = None
if 'p' in options and os.path.exists(get_profile_filename(exec_target)):
if 'p' in options and os.path.exists(get_profile_filename_from_attachment(exec_target, True)):
default = 'CMD_px'
sys.stdout.write(_('Target profile exists: %s\n') % get_profile_filename(exec_target))
sys.stdout.write(_('Target profile exists: %s\n') % get_profile_filename_from_attachment(exec_target, True))
elif 'i' in options:
default = 'CMD_ix'
elif 'c' in options:
@ -1104,7 +1110,7 @@ def handle_children(profile, hat, root):
parent_uses_ld_xxx = check_for_LD_XXX(profile)
sev_db.unload_variables()
sev_db.load_variables(get_profile_filename(profile))
sev_db.load_variables(get_profile_filename_from_profile_name(profile, True))
severity = sev_db.rank_path(exec_target, 'x')
# Prompt portion starts
@ -1228,7 +1234,7 @@ def handle_children(profile, hat, root):
profile_changes[pid] = '%s' % profile
# Check profile exists for px
if not os.path.exists(get_profile_filename(exec_target)):
if not os.path.exists(get_profile_filename_from_attachment(exec_target, True)):
ynans = 'y'
if 'i' in exec_mode:
ynans = aaui.UI_YesNo(_('A profile for %s does not exist.\nDo you want to create one?') % exec_target, 'n')
@ -1362,7 +1368,7 @@ def ask_the_questions(log_dict):
UI_SelectUpdatedRepoProfile(profile, p)
sev_db.unload_variables()
sev_db.load_variables(get_profile_filename(profile))
sev_db.load_variables(get_profile_filename_from_profile_name(profile, True))
# Sorted list of hats with the profile name coming first
hats = list(filter(lambda key: key != profile, sorted(log_dict[aamode][profile].keys())))
@ -1769,7 +1775,7 @@ def set_logfile(filename):
def do_logprof_pass(logmark='', passno=0, log_pid=log_pid):
# set up variables for this pass
# transitions = hasher()
global existing_profiles
global active_profiles
global sev_db
# aa = hasher()
# profile_changes = hasher()
@ -1786,13 +1792,13 @@ def do_logprof_pass(logmark='', passno=0, log_pid=log_pid):
if not sev_db:
sev_db = apparmor.severity.Severity(CONFDIR + '/severity.db', _('unknown'))
#print(pid)
#print(existing_profiles)
#print(active_profiles)
##if not repo_cf and cfg['repostory']['url']:
## repo_cfg = read_config('repository.conf')
## if not repo_cfg['repository'].get('enabled', False) or repo_cfg['repository]['enabled'] not in ['yes', 'no']:
## UI_ask_to_enable_repo()
log_reader = apparmor.logparser.ReadLog(log_pid, logfile, existing_profiles, profile_dir)
log_reader = apparmor.logparser.ReadLog(log_pid, logfile, active_profiles, profile_dir)
log = log_reader.read_log(logmark)
#read_log(logmark)
@ -1867,7 +1873,7 @@ def save_profiles():
if aa[which][which].get('filename', False):
oldprofile = aa[which][which]['filename']
else:
oldprofile = get_profile_filename(which)
oldprofile = get_profile_filename_from_attachment(which, True)
serialize_options = {'METADATA': True}
newprofile = serialize_profile(aa[which], which, serialize_options)
@ -2082,9 +2088,29 @@ def read_profile(file, active_profile):
if profile_data and active_profile:
attach_profile_data(aa, profile_data)
attach_profile_data(original_aa, profile_data)
for profile in profile_data: # TODO: also honor hats
name = profile_data[profile][profile]['name']
attachment = profile_data[profile][profile]['attachment']
filename = profile_data[profile][profile]['filename']
if not attachment and name.startswith('/'):
active_profiles.add(filename, name, name) # use name as name and attachment
else:
active_profiles.add(filename, name, attachment)
elif profile_data:
attach_profile_data(extras, profile_data)
for profile in profile_data: # TODO: also honor hats
name = profile_data[profile][profile]['name']
attachment = profile_data[profile][profile]['attachment']
filename = profile_data[profile][profile]['filename']
if not attachment and name.startswith('/'):
extra_profiles.add(filename, name, name) # use name as name and attachment
else:
extra_profiles.add(filename, name, attachment)
def attach_profile_data(profiles, profile_data):
# Make deep copy of data to avoid changes to
@ -2132,8 +2158,9 @@ def parse_profile_start(line, file, lineno, profile, hat):
attachment = matches['attachment']
flags = matches['flags']
xattrs = matches['xattrs']
return (profile, hat, attachment, flags, in_contained_hat, pps_set_profile, pps_set_hat_external)
return (profile, hat, attachment, xattrs, flags, in_contained_hat, pps_set_profile, pps_set_hat_external)
def parse_profile_data(data, file, do_include):
profile_data = hasher()
@ -2161,7 +2188,7 @@ def parse_profile_data(data, file, do_include):
lastline = None
# Starting line of a profile
if RE_PROFILE_START.search(line):
(profile, hat, attachment, flags, in_contained_hat, pps_set_profile, pps_set_hat_external) = parse_profile_start(line, file, lineno, profile, hat)
(profile, hat, attachment, xattrs, flags, in_contained_hat, pps_set_profile, pps_set_hat_external) = parse_profile_start(line, file, lineno, profile, hat)
if profile_data[profile].get(hat, False):
raise AppArmorException('Profile %(profile)s defined twice in %(file)s, last found in line %(line)s' %
@ -2176,14 +2203,12 @@ def parse_profile_data(data, file, do_include):
if pps_set_hat_external:
profile_data[profile][hat]['external'] = True
# Profile stored
existing_profiles[profile] = file
# save profile name and filename
profile_data[profile][hat]['name'] = profile
profile_data[profile][hat]['filename'] = file
filelist[file]['profiles'][profile][hat] = True
profile_data[profile][hat]['xattrs'] = xattrs
profile_data[profile][hat]['flags'] = flags
# Save the initial comment
@ -2504,6 +2529,11 @@ def parse_profile_data(data, file, do_include):
else:
raise AppArmorException(_('Syntax Error: Unknown line found in file %(file)s line %(lineno)s:\n %(line)s') % { 'file': file, 'lineno': lineno + 1, 'line': line })
if lastline:
# lastline gets merged into line (and reset to None) when reading the next line.
# If it isn't empty, this means there's something unparseable at the end of the profile
raise AppArmorException(_('Syntax Error: Unknown line found in file %(file)s line %(lineno)s:\n %(line)s') % { 'file': file, 'lineno': lineno + 1, 'line': lastline })
# Below is not required I'd say
if not do_include:
for hatglob in cfg['required_hats'].keys():
@ -2589,11 +2619,15 @@ def write_header(prof_data, depth, name, embedded_hat, write_flags):
if (not embedded_hat and re.search('^[^/]', unquoted_name)) or (embedded_hat and re.search('^[^^]', unquoted_name)) or prof_data['attachment'] or prof_data['profile_keyword']:
name = 'profile %s%s' % (name, attachment)
xattrs = ''
if prof_data['xattrs']:
xattrs = ' xattrs=(%s)' % prof_data['xattrs']
flags = ''
if write_flags and prof_data['flags']:
flags = ' flags=(%s)' % prof_data['flags']
data.append('%s%s%s {%s' % (pre, name, flags, comment))
data.append('%s%s%s%s {%s' % (pre, name, xattrs, flags, comment))
return data
@ -2677,7 +2711,11 @@ def serialize_profile(profile_data, name, options):
# comment.replace('\\n', '\n')
# string += comment + '\n'
prof_filename = get_profile_filename(name)
if options.get('is_attachment'):
prof_filename = get_profile_filename_from_attachment(name, True)
else:
prof_filename = get_profile_filename_from_profile_name(name, True)
if filelist.get(prof_filename, False):
data += write_abi(filelist[prof_filename], 0)
data += write_alias(filelist[prof_filename], 0)
@ -2703,16 +2741,18 @@ def serialize_profile(profile_data, name, options):
return string + '\n'
def write_profile_ui_feedback(profile):
def write_profile_ui_feedback(profile, is_attachment=False):
aaui.UI_Info(_('Writing updated profile for %s.') % profile)
write_profile(profile)
write_profile(profile, is_attachment)
def write_profile(profile):
def write_profile(profile, is_attachment=False):
prof_filename = None
if aa[profile][profile].get('filename', False):
prof_filename = aa[profile][profile]['filename']
elif is_attachment:
prof_filename = get_profile_filename_from_attachment(profile, True)
else:
prof_filename = get_profile_filename(profile)
prof_filename = get_profile_filename_from_profile_name(profile, True)
newprof = tempfile.NamedTemporaryFile('w', suffix='~', delete=False, dir=profile_dir)
if os.path.exists(prof_filename):
@ -2722,7 +2762,7 @@ def write_profile(profile):
#os.chmod(newprof.name, permission_600)
pass
serialize_options = {'METADATA': True}
serialize_options = {'METADATA': True, 'is_attachment': is_attachment}
profile_string = serialize_profile(aa[profile], profile, serialize_options)
newprof.write(profile_string)
@ -2844,7 +2884,7 @@ def reload_base(bin_path):
if not check_for_apparmor():
return None
prof_filename = get_profile_filename(bin_path)
prof_filename = get_profile_filename_from_profile_name(bin_path, True)
# XXX use reload_profile() from tools.py instead (and don't hide output in /dev/null)
subprocess.call("cat '%s' | %s -I%s -r >/dev/null 2>&1" % (prof_filename, parser, profile_dir), shell=True)

View File

@ -40,7 +40,7 @@ from apparmor.common import AppArmorException, open_file_read # , warn, msg,
# CFG = None
# REPO_CFG = None
# SHELL_FILES = ['easyprof.conf', 'notify.conf', 'parser.conf', 'subdomain.conf']
# SHELL_FILES = ['easyprof.conf', 'notify.conf', 'parser.conf']
class Config(object):
def __init__(self, conf_type, conf_dir='/etc/apparmor'):
self.CONF_DIR = conf_dir

View File

@ -1,6 +1,6 @@
# ----------------------------------------------------------------------
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
# Copyright (C) 2015-2016 Christian Boltz <apparmor@cboltz.de>
# Copyright (C) 2015-2018 Christian Boltz <apparmor@cboltz.de>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
@ -44,11 +44,11 @@ class ReadLog:
# used to pre-filter log lines so that we hand over only relevant lines to LibAppArmor parsing
RE_LOG_ALL = re.compile('(' + '|'.join(RE_log_parts) + ')')
def __init__(self, pid, filename, existing_profiles, profile_dir):
def __init__(self, pid, filename, active_profiles, profile_dir):
self.filename = filename
self.profile_dir = profile_dir
self.pid = pid
self.existing_profiles = existing_profiles
self.active_profiles = active_profiles
self.log = []
self.debug_logger = DebugLogger('ReadLog')
self.LOG = None
@ -447,15 +447,16 @@ class ReadLog:
def profile_exists(self, program):
"""Returns True if profile exists, False otherwise"""
# Check cache of profiles
if self.existing_profiles.get(program, False):
if self.active_profiles.filename_from_profile_name(program):
return True
# Check the disk for profile
prof_path = self.get_profile_filename(program)
#print(prof_path)
if os.path.isfile(prof_path):
# Add to cache of profile
self.existing_profiles[program] = prof_path
return True
raise AppArmorBug('This should never happen, please open a bugreport!')
# self.active_profiles[program] = prof_path
# return True
return False
def get_profile_filename(self, profile):

View File

@ -0,0 +1,73 @@
# ----------------------------------------------------------------------
# Copyright (C) 2018 Christian Boltz <apparmor@cboltz.de>
#
# 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 as 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.
#
# ----------------------------------------------------------------------
from apparmor.aare import AARE
from apparmor.common import AppArmorBug, AppArmorException
# setup module translations
from apparmor.translations import init_translation
_ = init_translation()
class ProfileList:
''' Stores the list of profiles (both name and attachment) and in which files they live '''
def __init__(self):
self.profile_names = {} # profile name -> filename
self.attachments = {} # attachment -> filename
self.attachments_AARE = {} # AARE(attachment) -> filename
def add(self, filename, profile_name, attachment):
''' Add the given profile and attachment to the list '''
if not filename:
raise AppArmorBug('Empty filename given to ProfileList')
if not profile_name and not attachment:
raise AppArmorBug('Neither profile name or attachment given')
if profile_name in self.profile_names:
raise AppArmorException(_('Profile %(profile_name)s exists in %(filename)s and %(filename2)s' % {'profile_name': profile_name, 'filename': filename, 'filename2': self.profile_names[profile_name]}))
if attachment in self.attachments:
raise AppArmorException(_('Profile for %(profile_name)s exists in %(filename)s and %(filename2)s' % {'profile_name': attachment, 'filename': filename, 'filename2': self.attachments[attachment]}))
if profile_name:
self.profile_names[profile_name] = filename
if attachment:
self.attachments[attachment] = filename
self.attachments_AARE[attachment] = AARE(attachment, True)
def filename_from_profile_name(self, name):
''' Return profile filename for the given profile name, or None '''
return self.profile_names.get(name, None)
def filename_from_attachment(self, attachment):
''' Return profile filename for the given attachment/executable path, or None '''
if not attachment.startswith( ('/', '@', '{') ):
raise AppArmorBug('Called filename_from_attachment with non-path attachment: %s' % attachment)
# plain path
if self.attachments.get(attachment):
return self.attachments[attachment]
# try AARE matches to cover profile names with alternations and wildcards
for path in self.attachments.keys():
if self.attachments_AARE[path].match(attachment):
return self.attachments[path] # XXX this returns the first match, not necessarily the best one
return None # nothing found

View File

@ -69,6 +69,7 @@ class ProfileStorage:
data['filename'] = ''
data['name'] = ''
data['attachment'] = ''
data['xattrs'] = ''
data['flags'] = ''
data['external'] = False
data['header_comment'] = '' # currently only set by change_profile_flags()

View File

@ -30,6 +30,8 @@ RE_PATH = '/\S*|"/[^"]*"' # filename (starting with '/') withou
RE_PROFILE_PATH = '(?P<%s>(' + RE_PATH + '))' # quoted or unquoted filename. %s is the match group name
RE_PROFILE_PATH_OR_VAR = '(?P<%s>(' + RE_PATH + '|@{\S+}\S*|"@{\S+}[^"]*"))' # quoted or unquoted filename or variable. %s is the match group name
RE_SAFE_OR_UNSAFE = '(?P<execmode>(safe|unsafe))'
RE_XATTRS = '(\s+xattrs\s*=\s*\((?P<xattrs>([^)=]+=[^)=]+\s?)+)\)\s*)?'
RE_FLAGS = '(\s+(flags\s*=\s*)?\((?P<flags>[^)]+)\))?'
RE_PROFILE_END = re.compile('^\s*\}' + RE_EOL)
RE_PROFILE_CAP = re.compile(RE_AUDIT_DENY + 'capability(?P<capability>(\s+\S+)+)?' + RE_COMMA_EOL)
@ -43,7 +45,7 @@ RE_PROFILE_CONDITIONAL_VARIABLE = re.compile('^\s*if\s+(not\s+)?defined\s+(@\{?\
RE_PROFILE_CONDITIONAL_BOOLEAN = re.compile('^\s*if\s+(not\s+)?defined\s+(\$\{?\w+\}?)\s*\{\s*(#.*)?$')
RE_PROFILE_NETWORK = re.compile(RE_AUDIT_DENY + 'network(?P<details>\s+.*)?' + RE_COMMA_EOL)
RE_PROFILE_CHANGE_HAT = re.compile('^\s*\^(\"??.+?\"??)' + RE_COMMA_EOL)
RE_PROFILE_HAT_DEF = re.compile('^(?P<leadingspace>\s*)(?P<hat_keyword>\^|hat\s+)(?P<hat>\"??.+?\"??)\s+((flags=)?\((?P<flags>.+)\)\s+)*\{' + RE_EOL)
RE_PROFILE_HAT_DEF = re.compile('^(?P<leadingspace>\s*)(?P<hat_keyword>\^|hat\s+)(?P<hat>\"??[^)]+?\"??)' + RE_XATTRS + RE_FLAGS + '\s*\{' + RE_EOL)
RE_PROFILE_DBUS = re.compile(RE_AUDIT_DENY + '(dbus\s*,|dbus(?P<details>\s+[^#]*)\s*,)' + RE_EOL)
RE_PROFILE_MOUNT = re.compile(RE_AUDIT_DENY + '((mount|remount|umount|unmount)(\s+[^#]*)?\s*,)' + RE_EOL)
RE_PROFILE_SIGNAL = re.compile(RE_AUDIT_DENY + '(signal\s*,|signal(?P<details>\s+[^#]*)\s*,)' + RE_EOL)
@ -68,7 +70,9 @@ RE_PROFILE_START = re.compile(
'|' + # or
'(' + 'profile' + '\s+' + RE_PROFILE_NAME % 'namedprofile' + '(\s+' + RE_PROFILE_PATH_OR_VAR % 'attachment' + ')?' + ')' + # 'profile', profile name, optionally attachment
')' +
'\s+((flags\s*=\s*)?\((?P<flags>.+)\)\s*)?\{' +
RE_XATTRS +
RE_FLAGS +
'\s*\{' +
RE_EOL)
@ -110,7 +114,7 @@ def parse_profile_start_line(line, filename):
result = {}
for section in [ 'leadingspace', 'plainprofile', 'namedprofile', 'attachment', 'flags', 'comment']:
for section in [ 'leadingspace', 'plainprofile', 'namedprofile', 'attachment', 'xattrs', 'flags', 'comment']:
if matches.group(section):
result[section] = matches.group(section)

View File

@ -1,5 +1,6 @@
# ----------------------------------------------------------------------
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
# Copyright (C) 2015-2018 Christian Boltz <apparmor@cboltz.de>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
@ -66,12 +67,12 @@ class aa_tools:
profile = fq_path
else:
program = fq_path
profile = apparmor.get_profile_filename(fq_path)
profile = apparmor.get_profile_filename_from_attachment(fq_path, True)
else:
which = apparmor.which(p)
if which is not None:
program = apparmor.get_full_path(which)
profile = apparmor.get_profile_filename(program)
profile = apparmor.get_profile_filename_from_attachment(program, True)
elif os.path.exists(os.path.join(apparmor.profile_dir, p)):
program = None
profile = apparmor.get_full_path(os.path.join(apparmor.profile_dir, p)).strip()
@ -190,7 +191,7 @@ class aa_tools:
apparmor.check_qualifiers(program)
if os.path.exists(apparmor.get_profile_filename(program)) and not self.force:
if os.path.exists(apparmor.get_profile_filename_from_attachment(program, True)) and not self.force:
aaui.UI_Info(_('Profile for %s already exists - skipping.') % program)
else:
apparmor.autodep(program)
@ -198,7 +199,7 @@ class aa_tools:
apparmor.reload(program)
def clean_profile(self, program):
filename = apparmor.get_profile_filename(program)
filename = apparmor.get_profile_filename_from_attachment(program, True)
import apparmor.cleanprofile as cleanprofile
prof = cleanprofile.Prof(filename)
cleanprof = cleanprofile.CleanProf(True, prof, prof)
@ -220,14 +221,14 @@ class aa_tools:
while ans != 'CMD_SAVE_CHANGES':
ans, arg = q.promptUser()
if ans == 'CMD_SAVE_CHANGES':
apparmor.write_profile_ui_feedback(program)
apparmor.write_profile_ui_feedback(program, True)
self.reload_profile(filename)
elif ans == 'CMD_VIEW_CHANGES':
#oldprofile = apparmor.serialize_profile(apparmor.original_aa[program], program, {})
newprofile = apparmor.serialize_profile(apparmor.aa[program], program, {})
newprofile = apparmor.serialize_profile(apparmor.aa[program], program, {'is_attachment': True})
aaui.UI_Changes(filename, newprofile, comments=True)
else:
apparmor.write_profile_ui_feedback(program)
apparmor.write_profile_ui_feedback(program, True)
self.reload_profile(filename)
else:
raise apparmor.AppArmorException(_('The profile for %s does not exists. Nothing to clean.') % program)

View File

@ -511,32 +511,42 @@ class AaTest_parse_profile_start(AATest):
def test_parse_profile_start_01(self):
result = self._parse('/foo {', None, None)
expected = ('/foo', '/foo', None, None, False, False, False)
expected = ('/foo', '/foo', None, None, None, False, False, False)
self.assertEqual(result, expected)
def test_parse_profile_start_02(self):
result = self._parse('/foo (complain) {', None, None)
expected = ('/foo', '/foo', None, 'complain', False, False, False)
expected = ('/foo', '/foo', None, None, 'complain', False, False, False)
self.assertEqual(result, expected)
def test_parse_profile_start_03(self):
result = self._parse('profile foo /foo {', None, None) # named profile
expected = ('foo', 'foo', '/foo', None, False, False, False)
expected = ('foo', 'foo', '/foo', None, None, False, False, False)
self.assertEqual(result, expected)
def test_parse_profile_start_04(self):
result = self._parse('profile /foo {', '/bar', '/bar') # child profile
expected = ('/bar', '/foo', None, None, True, True, False)
expected = ('/bar', '/foo', None, None, None, True, True, False)
self.assertEqual(result, expected)
def test_parse_profile_start_05(self):
result = self._parse('/foo//bar {', None, None) # external hat
expected = ('/foo', 'bar', None, None, False, False, True)
expected = ('/foo', 'bar', None, None, None, False, False, True)
self.assertEqual(result, expected)
def test_parse_profile_start_06(self):
result = self._parse('profile "/foo" (complain) {', None, None)
expected = ('/foo', '/foo', None, 'complain', False, False, False)
expected = ('/foo', '/foo', None, None, 'complain', False, False, False)
self.assertEqual(result, expected)
def test_parse_profile_start_07(self):
result = self._parse('profile "/foo" xattrs=(user.bar=bar) {', None, None)
expected = ('/foo', '/foo', None, 'user.bar=bar', None, False, False, False)
self.assertEqual(result, expected)
def test_parse_profile_start_08(self):
result = self._parse('profile "/foo" xattrs=(user.bar=bar user.foo=*) {', None, None)
expected = ('/foo', '/foo', None, 'user.bar=bar user.foo=*', None, False, False, False)
self.assertEqual(result, expected)
def test_parse_profile_start_unsupported_01(self):
@ -566,6 +576,44 @@ class AaTest_parse_profile_data(AATest):
# file contains two profiles with the same name
parse_profile_data('profile /foo {\n}\nprofile /foo {\n}\n'.split(), 'somefile', False)
def test_parse_xattrs_01(self):
prof = parse_profile_data('/foo xattrs=(user.bar=bar) {\n}\n'.split(), 'somefile', False)
self.assertEqual(list(prof.keys()), ['/foo'])
self.assertEqual(list(prof['/foo'].keys()), ['/foo'])
self.assertEqual(prof['/foo']['/foo']['name'], '/foo')
self.assertEqual(prof['/foo']['/foo']['filename'], 'somefile')
self.assertEqual(prof['/foo']['/foo']['flags'], None)
self.assertEqual(prof['/foo']['/foo']['xattrs'], 'user.bar=bar')
def test_parse_xattrs_02(self):
prof = parse_profile_data('/foo xattrs=(user.bar=bar user.foo=*) {\n}\n'.split(), 'somefile', False)
self.assertEqual(list(prof.keys()), ['/foo'])
self.assertEqual(list(prof['/foo'].keys()), ['/foo'])
self.assertEqual(prof['/foo']['/foo']['name'], '/foo')
self.assertEqual(prof['/foo']['/foo']['filename'], 'somefile')
self.assertEqual(prof['/foo']['/foo']['flags'], None)
self.assertEqual(prof['/foo']['/foo']['xattrs'], 'user.bar=bar user.foo=*')
def test_parse_xattrs_03(self):
d = '/foo xattrs=(user.bar=bar) flags=(complain) {\n}\n'
prof = parse_profile_data(d.split(), 'somefile', False)
self.assertEqual(list(prof.keys()), ['/foo'])
self.assertEqual(list(prof['/foo'].keys()), ['/foo'])
self.assertEqual(prof['/foo']['/foo']['name'], '/foo')
self.assertEqual(prof['/foo']['/foo']['filename'], 'somefile')
self.assertEqual(prof['/foo']['/foo']['flags'], 'complain')
self.assertEqual(prof['/foo']['/foo']['xattrs'], 'user.bar=bar')
def test_parse_xattrs_04(self):
with self.assertRaises(AppArmorException):
# flags before xattrs
d = '/foo flags=(complain) xattrs=(user.bar=bar) {\n}\n'
parse_profile_data(d.split(), 'somefile', False)
class AaTest_separate_vars(AATest):
tests = [
('' , set() ),
@ -669,11 +717,50 @@ class AaTest_write_header(AATest):
embedded_hat = params[1]
write_flags = params[2]
depth = params[3]
prof_data = { 'flags': params[4], 'attachment': params[5], 'profile_keyword': params[6], 'header_comment': params[7] }
prof_data = { 'flags': params[4], 'attachment': params[5], 'profile_keyword': params[6], 'header_comment': params[7], 'xattrs': '' }
result = write_header(prof_data, depth, name, embedded_hat, write_flags)
self.assertEqual(result, [expected])
class AaTest_write_header_01(AATest):
tests = [
(
{'name': '/foo', 'write_flags': True, 'depth': 1, 'flags': 'complain'},
' /foo flags=(complain) {',
),
(
{'name': '/foo', 'write_flags': True, 'depth': 1, 'flags': 'complain', 'profile_keyword': 'profile'},
' profile /foo flags=(complain) {',
),
(
{'name': '/foo', 'write_flags': True, 'flags': 'complain'},
'/foo flags=(complain) {',
),
(
{'name': '/foo', 'xattrs': 'user.foo=bar', 'write_flags': True, 'flags': 'complain'},
'/foo xattrs=(user.foo=bar) flags=(complain) {',
),
(
{'name': '/foo', 'xattrs': 'user.foo=bar', 'embedded_hat': True},
'profile /foo xattrs=(user.foo=bar) {',
),
]
def _run_test(self, params, expected):
name = params['name']
embedded_hat = params.get('embedded_hat', False)
write_flags = params.get('write_flags', False)
depth = params.get('depth', 0)
prof_data = {
'xattrs': params.get('xattrs', None),
'flags': params.get('flags', None),
'attachment': params.get('attachment', None),
'profile_keyword': params.get('profile_keyword', None),
'header_comment': params.get('header_comment', None),
}
result = write_header(prof_data, depth, name, embedded_hat, write_flags)
self.assertEqual(result, [expected])
class AaTest_get_file_perms_1(AATest):
tests = [
('/usr/share/common-licenses/foo/bar', {'allow': {'all': set(), 'owner': {'w'} }, 'deny': {'all':set(), 'owner': set()}, 'paths': {'/usr/share/common-licenses/**'} }),

View File

@ -1,7 +1,7 @@
#! /usr/bin/python3
# ------------------------------------------------------------------
#
# Copyright (C) 2015 Christian Boltz <apparmor@cboltz.de>
# Copyright (C) 2015-2018 Christian Boltz <apparmor@cboltz.de>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
@ -18,6 +18,7 @@ from apparmor.common import open_file_read
import apparmor.aa
from apparmor.logparser import ReadLog
from apparmor.profile_list import ProfileList
class TestLibapparmorTestMulti(AATest):
'''Parse all libraries/libapparmor/testsuite/test_multi tests and compare the result with the *.out files'''
@ -249,9 +250,15 @@ def logfile_to_profile(logfile):
if '//' in profile:
profile, hat = profile.split('//')
apparmor.aa.existing_profiles = {profile: profile_dummy_file}
apparmor.aa.active_profiles = ProfileList()
log_reader = ReadLog(dict(), logfile, apparmor.aa.existing_profiles, '')
# optional for now, might be needed one day
# if profile.startswith('/'):
# apparmor.aa.active_profiles.add(profile_dummy_file, profile, profile)
# else:
apparmor.aa.active_profiles.add(profile_dummy_file, profile, '')
log_reader = ReadLog(dict(), logfile, apparmor.aa.active_profiles, '')
log = log_reader.read_log('')
for root in log:

View File

@ -40,11 +40,6 @@ skip_startswith = (
# testcases that should raise an exception, but don't
exception_not_raised = [
# most abi/bad_* aren't detected as bad by the basic implementation in the tools
'abi/bad_1.sd',
'abi/bad_2.sd',
'abi/bad_3.sd',
'abi/bad_4.sd',
'abi/bad_5.sd',
'abi/bad_10.sd',
'abi/bad_11.sd',
'abi/bad_12.sd',
@ -155,13 +150,9 @@ exception_not_raised = [
'unix/bad_regex_04.sd',
'unix/bad_shutdown_1.sd',
'unix/bad_shutdown_2.sd',
'vars/boolean/boolean_bad_1.sd',
'vars/boolean/boolean_bad_2.sd',
'vars/boolean/boolean_bad_3.sd',
'vars/boolean/boolean_bad_4.sd',
'vars/boolean/boolean_bad_6.sd',
'vars/boolean/boolean_bad_7.sd',
'vars/boolean/boolean_bad_8.sd',
'vars/vars_bad_3.sd',
'vars/vars_bad_4.sd',
'vars/vars_bad_5.sd',
@ -200,7 +191,6 @@ exception_not_raised = [
'vars/vars_recursion_2.sd',
'vars/vars_recursion_3.sd',
'vars/vars_recursion_4.sd',
'vars/vars_simple_assignment_10.sd',
'vars/vars_simple_assignment_3.sd',
'vars/vars_simple_assignment_8.sd',
'vars/vars_simple_assignment_9.sd',

View File

@ -0,0 +1,114 @@
#! /usr/bin/python3
# ------------------------------------------------------------------
#
# Copyright (C) 2018 Christian Boltz <apparmor@cboltz.de>
#
# 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.
#
# ------------------------------------------------------------------
import unittest
from common_test import AATest, setup_all_loops
from apparmor.common import AppArmorBug, AppArmorException
from apparmor.profile_list import ProfileList
class TestAdd(AATest):
def AASetup(self):
self.pl = ProfileList()
def testEmpty(self):
self.assertEqual(self.pl.profile_names, {})
self.assertEqual(self.pl.attachments, {})
def testAdd_1(self):
self.pl.add('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo')
self.assertEqual(self.pl.profile_names, {'foo': '/etc/apparmor.d/bin.foo'})
self.assertEqual(self.pl.attachments, {'/bin/foo': '/etc/apparmor.d/bin.foo'})
def testAdd_2(self):
self.pl.add('/etc/apparmor.d/bin.foo', None, '/bin/foo')
self.assertEqual(self.pl.profile_names, {})
self.assertEqual(self.pl.attachments, {'/bin/foo': '/etc/apparmor.d/bin.foo'})
def testAdd_3(self):
self.pl.add('/etc/apparmor.d/bin.foo', 'foo', None)
self.assertEqual(self.pl.profile_names, {'foo': '/etc/apparmor.d/bin.foo'})
self.assertEqual(self.pl.attachments, {})
def testAddError_1(self):
with self.assertRaises(AppArmorBug):
self.pl.add('', 'foo', '/bin/foo') # no filename
def testAddError_2(self):
with self.assertRaises(AppArmorBug):
self.pl.add('/etc/apparmor.d/bin.foo', None, None) # neither attachment or profile name
def testAddError_twice_1(self):
self.pl.add('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo')
with self.assertRaises(AppArmorException):
self.pl.add('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo')
def testAddError_twice_2(self):
self.pl.add('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo')
with self.assertRaises(AppArmorException):
self.pl.add('/etc/apparmor.d/bin.foo', 'foo', None)
def testAddError_twice_3(self):
self.pl.add('/etc/apparmor.d/bin.foo', None, '/bin/foo')
with self.assertRaises(AppArmorException):
self.pl.add('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo')
def testAddError_twice_4(self):
self.pl.add('/etc/apparmor.d/bin.foo', None, '/bin/foo')
with self.assertRaises(AppArmorException):
self.pl.add('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo')
def testAddError_twice_5(self):
self.pl.add('/etc/apparmor.d/bin.foo', 'foo', None)
with self.assertRaises(AppArmorException):
self.pl.add('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo')
class TestFilename_from_profile_name(AATest):
tests = [
('foo', '/etc/apparmor.d/bin.foo'),
('/bin/foo', None),
('bar', None),
]
def AASetup(self):
self.pl = ProfileList()
self.pl.add('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo')
def _run_test(self, params, expected):
self.assertEqual(self.pl.filename_from_profile_name(params), expected)
class TestFilename_from_attachment(AATest):
tests = [
('/bin/foo', '/etc/apparmor.d/bin.foo'),
('/bin/baz', '/etc/apparmor.d/bin.baz'),
('/bin/foobar', '/etc/apparmor.d/bin.foobar'),
('@{foo}', None), # XXX variables not supported yet (and @{foo} isn't defined in this test)
('/bin/404', None),
]
def AASetup(self):
self.pl = ProfileList()
self.pl.add('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo')
self.pl.add('/etc/apparmor.d/bin.baz', 'baz', '/bin/ba*')
self.pl.add('/etc/apparmor.d/bin.foobar', 'foobar', '/bin/foo{bar,baz}')
def _run_test(self, params, expected):
self.assertEqual(self.pl.filename_from_attachment(params), expected)
def test_non_path_attachment(self):
with self.assertRaises(AppArmorBug):
self.pl.filename_from_attachment('foo')
setup_all_loops(__name__)
if __name__ == '__main__':
unittest.main(verbosity=1)