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

Merge in collected changes from trunk since branching off the

translations branch.
This commit is contained in:
Steve Beattie
2014-01-14 10:36:36 -08:00
91 changed files with 1967 additions and 757 deletions

View File

@@ -12,7 +12,9 @@ DIRS=parser \
changehat/pam_apparmor \
tests
REPO_URL?=lp:apparmor
#REPO_URL?=lp:apparmor
# --per-file-timestamps is failing over SSH, https://bugs.launchpad.net/bzr/+bug/1257078
REPO_URL?=https://code.launchpad.net/~apparmor-dev/apparmor/master
# alternate possibilities to export from
#REPO_URL=.
#REPO_URL="bzr+ssh://bazaar.launchpad.net/~sbeattie/+junk/apparmor-dev/"

View File

@@ -41,12 +41,43 @@ APXS:=$(shell if [ -x "/usr/sbin/apxs2" ] ; then \
fi )
APXS_INSTALL_DIR=$(shell ${APXS} -q LIBEXECDIR)
DESTDIR=
# Need to pass -Wl twice here to get past both apxs2 and libtool, as
# libtool will add the path to the RPATH of the library if passed -L/some/path
LIBAPPARMOR_FLAGS=-I../../libraries/libapparmor/src -Wl,-Wl,-L../../libraries/libapparmor/src/.libs
LDLIBS=-lapparmor
ifdef USE_SYSTEM
LIBAPPARMOR = $(shell if pkg-config --exists libapparmor ; then \
pkg-config --silence-errors --libs libapparmor ; \
elif ldconfig -p | grep -q libapparmor\.so$$ ; then \
echo -lapparmor ; \
fi )
ifeq ($(strip $(LIBAPPARMOR)),)
ERROR_MESSAGE = Unable to find libapparmor installed on this system; either \
install libapparmor devel packages, set the LIBAPPARMOR variable \
manually, or build against in-tree libapparmor)
endif # LIBAPPARMOR not set
LDLIBS += $(LIBAPPARMOR)
else
LIBAPPARMOR_SRC := ../../libraries/libapparmor/
LIBAPPARMOR_INCLUDE = $(LIBAPPARMOR_SRC)/include
LIBAPPARMOR_PATH := $(LIBAPPARMOR_SRC)/src/.libs/
ifeq ($(realpath $(LIBAPPARMOR_PATH)/libapparmor.a),)
ERROR_MESSAGE = $(LIBAPPARMOR_PATH)/libapparmor.a is missing; either build against \
the in-tree libapparmor by building it first and then trying again \
(see the top-level README for help) or build against the system \
libapparmor by adding USE_SYSTEM=1 to your make command.)
endif
# Need to pass -Wl twice here to get past both apxs2 and libtool, as
# libtool will add the path to the RPATH of the library if passed -L/some/path
LIBAPPARMOR_FLAGS = -I$(LIBAPPARMOR_INCLUDE) -Wl,-Wl,-L$(LIBAPPARMOR_PATH)
LDLIBS = -lapparmor
endif
all: $(TARGET) ${MANPAGES} ${HTMLMANPAGES}
.PHONY: libapparmor_check
.SILENT: libapparmor_check
libapparmor_check:
@if [ -n "$(ERROR_MESSAGE)" ] ; then \
echo "$(ERROR_MESSAGE)" 1>&2 ; \
return 1 ; \
fi
all: libapparmor_check $(TARGET) ${MANPAGES} ${HTMLMANPAGES}
%.so: %.c
${APXS} ${LIBAPPARMOR_FLAGS} -c $< ${LDLIBS}

View File

@@ -23,7 +23,7 @@
#include "apr_strings.h"
#include "apr_lib.h"
#include <apparmor.h>
#include <sys/apparmor.h>
#include <unistd.h>
/* #define DEBUG */

View File

@@ -26,12 +26,48 @@ common/Make.rules: $(COMMONDIR)/Make.rules
ln -sf $(COMMONDIR) .
endif
EXTRA_CFLAGS=$(CFLAGS) -fPIC -shared -Wall -I../../libraries/libapparmor/src/
LINK_FLAGS=-Xlinker -x -L../../libraries/libapparmor/src/.libs
LIBS=-lpam -lapparmor
ifdef USE_SYSTEM
LIBAPPARMOR = $(shell if pkg-config --exists libapparmor ; then \
pkg-config --silence-errors --libs libapparmor ; \
elif ldconfig -p | grep -q libapparmor\.so$$ ; then \
echo -lapparmor ; \
fi )
ifeq ($(strip $(LIBAPPARMOR)),)
ERROR_MESSAGE = Unable to find libapparmor installed on this system; either \
install libapparmor devel packages, set the LIBAPPARMOR variable \
manually, or build against in-tree libapparmor)
endif
LIBAPPARMOR_INCLUDE =
AA_LDLIBS = $(LIBAPPARMOR)
AA_LINK_FLAGS =
else
LIBAPPARMOR_SRC := ../../libraries/libapparmor/
LIBAPPARMOR_INCLUDE_PATH = $(LIBAPPARMOR_SRC)/include
LIBAPPARMOR_PATH := $(LIBAPPARMOR_SRC)/src/.libs/
ifeq ($(realpath $(LIBAPPARMOR_PATH)/libapparmor.a),)
ERROR_MESSAGE = $(LIBAPPARMOR_PATH)/libapparmor.a is missing; either build against \
the in-tree libapparmor by building it first and then trying again \
(see the top-level README for help) or build against the system \
libapparmor by adding USE_SYSTEM=1 to your make command.)
endif
LIBAPPARMOR_INCLUDE = -I$(LIBAPPARMOR_INCLUDE_PATH)
AA_LINK_FLAGS = -L$(LIBAPPARMOR_PATH)
AA_LDLIBS = -lapparmor
endif
EXTRA_CFLAGS=$(CFLAGS) -fPIC -shared -Wall $(LIBAPPARMOR_INCLUDE)
LINK_FLAGS=-Xlinker -x $(AA_LINK_FLAGS)
LIBS=-lpam $(AA_LDLIBS)
OBJECTS=${NAME}.o get_options.o
all: $(NAME).so
.PHONY: libapparmor_check
.SILENT: libapparmor_check
libapparmor_check:
@if [ -n "$(ERROR_MESSAGE)" ] ; then \
echo "$(ERROR_MESSAGE)" 1>&2 ; \
return 1 ; \
fi
all: libapparmor_check $(NAME).so
$(NAME).so: ${OBJECTS}
$(CC) $(EXTRA_CFLAGS) $(LINK_FLAGS) -o $@ ${OBJECTS} $(LIBS)

View File

@@ -27,7 +27,7 @@
#include <grp.h>
#include <syslog.h>
#include <errno.h>
#include <apparmor.h>
#include <sys/apparmor.h>
#include <security/pam_ext.h>
#include <security/pam_modutil.h>

View File

@@ -13,7 +13,7 @@
#include "jni.h"
#include <errno.h>
#include "sys/apparmor.h"
#include <sys/apparmor.h>
#include "com_novell_apparmor_JNIChangeHat.h"
/* c intermediate lib call for Java -> JNI -> c library execution of the change_hat call */

View File

@@ -13,7 +13,7 @@
#include "jni.h"
#include <errno.h>
#include <apparmor.h>
#include <sys/apparmor.h>
#include "com_novell_apparmor_JNIChangeHat.h"
/* c intermediate lib call for Java -> JNI -> c library execution of the change_hat call */

View File

@@ -2,7 +2,7 @@ AUTOMAKE_OPTIONS = foreign 1.4
NAME = libapparmor
SRCDIR = src
SUBDIRS = doc src swig testsuite
SUBDIRS = doc src include swig testsuite
REPO_VERSION=$(shell if [ -x /usr/bin/svn ] ; then \
/usr/bin/svn info . 2> /dev/null | grep "^Last Changed Rev:" | sed "s/^Last Changed Rev: //" ; \

View File

@@ -1,6 +1,7 @@
#!/bin/sh
DIE=0
package=libapparmor
(autoconf --version) < /dev/null > /dev/null 2>&1 || {
echo
@@ -19,7 +20,7 @@ DIE=0
DIE=1
}
(libtool --version) < /dev/null > /dev/null 2>&1 || {
(libtoolize --version) < /dev/null > /dev/null 2>&1 || {
echo
echo "You must have libtool installed to compile $package."
echo "Download the appropriate package for your system,"
@@ -37,6 +38,6 @@ aclocal
echo "Running autoconf"
autoconf --force
echo "Running libtoolize"
libtoolize --automake
echo "Running automake -ac"
libtoolize --automake -c
echo "Running automake"
automake -ac

View File

@@ -82,4 +82,6 @@ testsuite/Makefile
testsuite/config/Makefile
testsuite/libaalogparse.test/Makefile
testsuite/lib/Makefile
include/Makefile
include/sys/Makefile
)

View File

@@ -0,0 +1,4 @@
SUBDIRS = sys
aalogparsedir = $(includedir)/aalogparse
aalogparse_HEADERS = aalogparse.h

View File

@@ -0,0 +1,3 @@
apparmor_hdrdir = $(includedir)/sys
apparmor_hdr_HEADERS = apparmor.h

View File

@@ -24,33 +24,19 @@
__BEGIN_DECLS
/*
* Class of mediation types in the AppArmor policy db
* Class of public mediation types in the AppArmor policy db
*/
#define AA_CLASS_COND 0
#define AA_CLASS_UNKNOWN 1
#define AA_CLASS_FILE 2
#define AA_CLASS_CAP 3
#define AA_CLASS_NET 4
#define AA_CLASS_RLIMITS 5
#define AA_CLASS_DOMAIN 6
#define AA_CLASS_MOUNT 7
#define AA_CLASS_NS_DOMAIN 8
#define AA_CLASS_PTRACE 9
#define AA_CLASS_ENV 16
#define AA_CLASS_DBUS 32
#define AA_CLASS_X 33
/* Permission Flags for Mediation classes */
#define AA_MAY_WRITE (1 << 1)
#define AA_MAY_READ (1 << 2)
#define AA_MAY_BIND (1 << 6)
#define AA_DBUS_SEND AA_MAY_WRITE
#define AA_DBUS_RECEIVE AA_MAY_READ
#define AA_DBUS_BIND AA_MAY_BIND
/* Permission flags for the AA_CLASS_DBUS mediation class */
#define AA_DBUS_SEND (1 << 1)
#define AA_DBUS_RECEIVE (1 << 2)
#define AA_DBUS_EAVESDROP (1 << 5)
#define AA_DBUS_BIND (1 << 6)
#define AA_VALID_DBUS_PERMS (AA_DBUS_SEND | AA_DBUS_RECEIVE | \
AA_DBUS_BIND | AA_DBUS_EAVESDROP)
/* Prototypes for apparmor state queries */

View File

@@ -28,7 +28,7 @@ BUILT_SOURCES = grammar.h scanner.h af_protos.h
AM_LFLAGS = -v
AM_YFLAGS = -d -p aalogparse_
AM_CFLAGS = -Wall
AM_CPPFLAGS = -D_GNU_SOURCE
AM_CPPFLAGS = -D_GNU_SOURCE -I$(top_srcdir)/include/
scanner.h: scanner.l
$(LEX) -v $<
@@ -37,12 +37,6 @@ scanner.c: scanner.l
af_protos.h: /usr/include/netinet/in.h
LC_ALL=C sed -n -e "/IPPROTO_MAX/d" -e "s/^\#define[ \\t]\\+IPPROTO_\\([A-Z0-9_]\\+\\)\\(.*\\)$$/AA_GEN_PROTO_ENT(\\UIPPROTO_\\1, \"\\L\\1\")/p" $< > $@
changehatdir = $(includedir)/sys
changehat_HEADERS = apparmor.h
aalogparsedir = $(includedir)/aalogparse
aalogparse_HEADERS = aalogparse.h
lib_LTLIBRARIES = libapparmor.la
noinst_HEADERS = grammar.h parser.h scanner.h af_protos.h

View File

@@ -22,7 +22,7 @@
* information about tokens given and rules matched. */
#define YYDEBUG 0
#include <string.h>
#include "aalogparse.h"
#include <aalogparse.h>
#include "parser.h"
#include "grammar.h"
#include "scanner.h"

View File

@@ -31,7 +31,7 @@
#include <inttypes.h>
#include <pthread.h>
#include "apparmor.h"
#include <sys/apparmor.h>
/* some non-Linux systems do not define a static value */
#ifndef PATH_MAX

View File

@@ -31,7 +31,7 @@
#include <string.h>
#include <stdio.h>
#include <netinet/in.h>
#include "aalogparse.h"
#include <aalogparse.h>
#include "parser.h"
/* This is mostly just a wrapper around the code in grammar.y */

View File

@@ -27,7 +27,7 @@
%{
#include "grammar.h"
#include "aalogparse.h"
#include <aalogparse.h>
#include "parser.h"
#include <assert.h>

View File

@@ -18,7 +18,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "aalogparse.h"
#include <aalogparse.h>
#include "parser.h"

View File

@@ -1,13 +1,13 @@
%module LibAppArmor
%{
#include "aalogparse.h"
#include "apparmor.h"
#include <aalogparse.h>
#include <sys/apparmor.h>
%}
%include "typemaps.i"
%include "aalogparse.h"
%include <aalogparse.h>
/* swig doesn't like the macro magic we do in apparmor.h so the fn prototypes
* are manually inserted here

View File

@@ -10,7 +10,7 @@ WriteMakefile(
'FIRST_MAKEFILE' => 'Makefile.perl',
'ABSTRACT' => q[Perl interface to AppArmor] ,
'VERSION' => q[@VERSION@],
'INC' => q[@CPPFLAGS@ -I@top_srcdir@/src @CFLAGS@],
'INC' => q[@CPPFLAGS@ -I@top_srcdir@/include @CFLAGS@],
'LIBS' => q[-L@top_builddir@/src/.libs/ -lapparmor @LIBS@],
'OBJECT' => 'libapparmor_wrap.o', # $(OBJ_EXT)
) ;

View File

@@ -4,7 +4,7 @@ if HAVE_PERL
noinst_DATA =LibAppArmor.so
libapparmor_wrap.c: $(srcdir)/../SWIG/libapparmor.i
$(SWIG) -perl -I$(srcdir)/../../src -module LibAppArmor -o $@ $(srcdir)/../SWIG/libapparmor.i
$(SWIG) -perl -I$(srcdir)/../../include -module LibAppArmor -o $@ $(srcdir)/../SWIG/libapparmor.i
MOSTLYCLEANFILES=libapparmor_wrap.c LibAppArmor.pm

View File

@@ -5,7 +5,7 @@ EXTRA_DIST = libapparmor_wrap.c
SUBDIRS = test
libapparmor_wrap.c: $(srcdir)/../SWIG/libapparmor.i
$(SWIG) -python -I$(srcdir)/../../src -module LibAppArmor -o $@ $(srcdir)/../SWIG/libapparmor.i
$(SWIG) -python -I$(srcdir)/../../include -module LibAppArmor -o $@ $(srcdir)/../SWIG/libapparmor.i
mv LibAppArmor.py __init__.py
MOSTLYCLEANFILES=libapparmor_wrap.c __init__.py

View File

@@ -12,7 +12,7 @@ setup(name = 'LibAppArmor',
packages = [ 'LibAppArmor' ],
ext_package = 'LibAppArmor',
ext_modules = [Extension('_LibAppArmor', ['libapparmor_wrap.c'],
include_dirs=['@top_srcdir@/src'],
include_dirs=['@top_srcdir@/include'],
extra_link_args = '-L@top_builddir@/src/.libs -lapparmor'.split(),
)],
scripts = [],

View File

@@ -4,12 +4,12 @@ EXTRA_DIST = extconf.rb LibAppArmor_wrap.c examples/*.rb
noinst_DATA = LibAppArmor.so
LibAppArmor_wrap.c : $(srcdir)/../SWIG/libapparmor.i
$(SWIG) -ruby -module LibAppArmor -I$(top_srcdir)/src -o $@ $(srcdir)/../SWIG/libapparmor.i
$(SWIG) -ruby -module LibAppArmor -I$(top_srcdir)/include -o $@ $(srcdir)/../SWIG/libapparmor.i
MOSTLYCLEANFILES=LibAppArmor_wrap.c
Makefile.ruby: extconf.rb
PREFIX=$(prefix) $(RUBY) $< --with-LibAppArmor-include=$(top_srcdir)/src
PREFIX=$(prefix) $(RUBY) $< --with-LibAppArmor-include=$(top_srcdir)/include
LibAppArmor.so: LibAppArmor_wrap.c Makefile.ruby
$(MAKE) -fMakefile.ruby

View File

@@ -2,7 +2,7 @@ SUBDIRS = lib config libaalogparse.test
PACKAGE = libaalogparse
AUTOMAKE_OPTIONS = dejagnu
INCLUDES = -I. -I$(top_srcdir)/src
INCLUDES = -I. -I$(top_srcdir)/include
AM_CPPFLAGS = $(DEBUG_FLAGS) -DLOCALEDIR=\"${localedir}\"
AM_CFLAGS = -Wall

View File

@@ -5,7 +5,7 @@
#include <string.h>
#include <errno.h>
#include "aalogparse.h"
#include <aalogparse.h>
int print_results(aa_log_record *record);

View File

@@ -51,11 +51,12 @@ CFLAGS = -g -O2 -pipe
ifdef DEBUG
CFLAGS += -pg -D DEBUG
endif
ifdef COVERAGE
CFLAGS = -g -pg -fprofile-arcs -ftest-coverage
endif
endif #CFLAGS
LIBAPPARMOR_PATH=../libraries/libapparmor/src/
LIBAPPARMOR_LDPATH=$(LIBAPPARMOR_PATH)/.libs/
EXTRA_CXXFLAGS = ${CFLAGS} ${CXX_WARNINGS} -D_GNU_SOURCE -I$(LIBAPPARMOR_PATH)
EXTRA_CXXFLAGS = ${CFLAGS} ${CXX_WARNINGS} -std=gnu++0x -D_GNU_SOURCE
EXTRA_CFLAGS = ${EXTRA_CXXFLAGS} ${CPP_WARNINGS}
#LEXLIB := -lfl
@@ -87,9 +88,26 @@ OBJECTS = $(SRCS:.c=.o)
AAREDIR= libapparmor_re
AAREOBJECT = ${AAREDIR}/libapparmor_re.a
AAREOBJECTS = $(AAREOBJECT)
AARE_LDFLAGS=-static-libgcc -static-libstdc++ -L. -L$(LIBAPPARMOR_LDPATH)
AARE_LDFLAGS = -static-libgcc -static-libstdc++ -L.
AALIB = -Wl,-Bstatic -lapparmor -Wl,-Bdynamic -lpthread
ifdef USE_SYSTEM
# Using the system libapparmor so Makefile dependencies can't be used
LIBAPPARMOR_A =
INCLUDE_APPARMOR =
APPARMOR_H =
else
LIBAPPARMOR_SRC = ../libraries/libapparmor/
LOCAL_LIBAPPARMOR_INCLUDE = $(LIBAPPARMOR_SRC)/include
LOCAL_LIBAPPARMOR_LDPATH = $(LIBAPPARMOR_SRC)/src/.libs
LIBAPPARMOR_A = $(LOCAL_LIBAPPARMOR_LDPATH)/libapparmor.a
INCLUDE_APPARMOR = -I$(LOCAL_LIBAPPARMOR_INCLUDE)
AARE_LDFLAGS += -L$(LOCAL_LIBAPPARMOR_LDPATH)
APPARMOR_H = $(LOCAL_LIBAPPARMOR_INCLUDE)/sys/apparmor.h
endif
EXTRA_CFLAGS += $(INCLUDE_APPARMOR)
LEX_C_FILES = parser_lex.c
YACC_C_FILES = parser_yacc.c parser_yacc.h
@@ -149,8 +167,21 @@ indep: docs
all: arch indep
.PHONY: coverage
coverage:
$(MAKE) clean apparmor_parser COVERAGE=1
apparmor_parser: $(OBJECTS) $(AAREOBJECTS)
ifndef USE_SYSTEM
$(LIBAPPARMOR_A):
@if [ ! -f $@ ]; then \
echo "error: $@ is missing. Pick one of these possible solutions:" 1>&2; \
echo " 1) Build against the in-tree libapparmor by building it first and then trying again. See the top-level README for help." 1>&2; \
echo " 2) Build against the system libapparmor by adding USE_SYSTEM=1 to your make command." 1>&2;\
return 1; \
fi
endif
apparmor_parser: $(OBJECTS) $(AAREOBJECTS) $(LIBAPPARMOR_A)
$(CXX) $(LDFLAGS) $(EXTRA_CFLAGS) -o $@ $(OBJECTS) $(LIBS) \
${LEXLIB} $(AAREOBJECTS) $(AARE_LDFLAGS) $(AALIB)
@@ -163,13 +194,13 @@ parser_lex.c: parser_lex.l parser_yacc.h parser.h profile.h
parser_lex.o: parser_lex.c parser.h parser_yacc.h
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
parser_misc.o: parser_misc.c parser.h parser_yacc.h profile.h af_names.h cap_names.h
parser_misc.o: parser_misc.c parser.h parser_yacc.h profile.h af_names.h cap_names.h $(APPARMOR_H)
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
parser_yacc.o: parser_yacc.c parser_yacc.h
parser_yacc.o: parser_yacc.c parser_yacc.h $(APPARMOR_H)
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
parser_main.o: parser_main.c parser.h parser_version.h libapparmor_re/apparmor_re.h
parser_main.o: parser_main.c parser.h parser_version.h libapparmor_re/apparmor_re.h $(APPARMOR_H)
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
parser_interface.o: parser_interface.c parser.h profile.h libapparmor_re/apparmor_re.h
@@ -181,7 +212,7 @@ parser_include.o: parser_include.c parser.h parser_include.h
parser_merge.o: parser_merge.c parser.h profile.h
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
parser_regex.o: parser_regex.c parser.h profile.h libapparmor_re/apparmor_re.h
parser_regex.o: parser_regex.c parser.h profile.h libapparmor_re/apparmor_re.h $(APPARMOR_H)
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
parser_symtab.o: parser_symtab.c parser.h
@@ -205,7 +236,7 @@ mount.o: mount.c mount.h parser.h immunix.h
lib.o: lib.c lib.h parser.h
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
dbus.o: dbus.c dbus.h parser.h immunix.h parser_yacc.h
dbus.o: dbus.c dbus.h parser.h immunix.h parser_yacc.h $(APPARMOR_H)
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
profile.o: profile.cc profile.h parser.h
@@ -302,7 +333,8 @@ install-indep:
.SILENT: clean
.PHONY: clean
clean: _clean
rm -f core core.* *.o *.s *.a *~
rm -f core core.* *.o *.s *.a *~ *.gcda *.gcno
rm -f gmon.out
rm -f $(TOOLS) $(TESTS)
rm -f $(LEX_C_FILES)
rm -f $(YACC_C_FILES)

96
parser/README.devel Normal file
View File

@@ -0,0 +1,96 @@
AppArmor parser development notes and tips
==========================================
Debugging the build
-------------------
Adding V=1 as argument to make should result in build commands that are
normally quieter generating more verbose output. This can help diagnose
when something is going wrong at build time.
Distribution vendors may wish to enable this option all
the time to assist debugging build failures in remote build
environments. Generally, they should not need to enable any other
build flags/options.
Building the parser with debugging information
----------------------------------------------
Setting DEBUG=1 with make will enable debugging output in the parser
(i.e. PDEBUG statements will be emitted instead of dropped). Usually,
partial compilation between debug and non-debug will cause compilation
problems, so it's usually best to do something like
make clean all DEBUG=1
Test Coverage
-------------
The parser can be built to generate test coverage information, by
setting the COVERAGE variable for make. As with debugging, partial
compilation is usually problematic, so it's recommended to do:
make clean all COVERAGE=1
and then run whatever tests. Because the unit tests are built with the
make check target, in order to see what coverage they provide, they will
also need to be built with the COVERAGE flag set:
make tests COVERAGE=1
or, if running the unit tests with all the other parser tests:
make check COVERAGE=1
Coverage information will be written out in files in the source build
tree (*.gcno and *.gcda files). The 'gcovr' utility is useful for
reading and summarizing the results; to do so, after running your
tests, do something like:
gcovr -r /PATH/TO/YOUR/PARSER/BUILD/TREE
Of course, having test coverage over a given line of code
won't indicate that the code is bug free; however, not having
coverage indicates that the tests do not exercise the given code at
all. That said, 100% coverage is unlikely to be possible in a testing
environment, given checks for things like error handling for failed
memory allocations.
Finding memory leaks
--------------------
The tst/ subdirectory has a python script for running valgrind on the
parser over the test files in the simple_tests/ tree. This can take
over 24 hours to complete, so it is not part of the default tests;
however, it can be run manually or via the 'valgrind' make target.
Valgrind reports some false positives for some additional checks it
makes; the script attempts to suppress those (read the valgrind
documentation for more details on suppressions). It can also emit the
suppressions via the --dump-suppressions argument, to be used for manual
valgrind runs.
An example manual valgrind run could be something like:
./tst/valgrind_simply.py --dump-suppressions > /tmp/valgrind.suppression
valgrind --leak-check=full --suppressions=/tmp/valgrind.suppression ./apparmor_parser -QK /path/to/profile
Profiling (for performance) the parser
--------------------------------------
# Using valgrind's callgrind tool
Valgrind provides the callgrind tool to give some indication where
hot spots are in the parser. To do so, do:
valgrind --tool=callgrind ./apparmor_parser PARSER_ARGS
This will generate a data file that a tool like kcachegrind can read.
# Using gprof
This can be enabled by adding the -pg argument as a CFLAG [1] at build
time and then exercising the parser. A data file will be generated
that gprof(1) can then read to show where the parser is spending the
most time.
[1] Unfortunately, only the DEBUG option in the Makefile does this,
which enables other debugging options that alters the parser in
ways that make it a less accurate representation of real world
performance. This needs to be fixed.

View File

@@ -99,12 +99,14 @@ B<MOUNT FLAGS> = ( 'ro' | 'rw' | 'nosuid' | 'suid' | 'nodev' | 'dev' | 'noexec'
B<MOUNT EXPRESSION> = ( I<ALPHANUMERIC> | I<AARE> ) ...
B<DBUS RULE> = ( I<DBUS MESSAGE RULE> | I<DBUS SERVICE RULE> | I<DBUS COMBINED RULE> )
B<DBUS RULE> = ( I<DBUS MESSAGE RULE> | I<DBUS SERVICE RULE> | I<DBUS EAVESDROP RULE> | I<DBUS COMBINED RULE> )
B<DBUS MESSAGE RULE> = [ 'audit' ] [ 'deny' ] 'dbus' [ I<DBUS ACCESS EXPRESSION> ] [ I<DBUS BUS> ] [ I<DBUS PATH> ] [ I<DBUS INTERFACE> ] [ I<DBUS MEMBER> ] [ I<DBUS PEER> ]
B<DBUS SERVICE RULE> = [ 'audit' ] [ 'deny' ] 'dbus' [ I<DBUS ACCESS EXPRESSION> ] [ I<DBUS BUS> ] [ I<DBUS NAME> ]
B<DBUS EAVESDROP RULE> = [ 'audit' ] [ 'deny' ] 'dbus' [ I<DBUS ACCESS EXPRESSION> ] [ I<DBUS BUS> ]
B<DBUS COMBINED RULE> = [ 'audit' ] [ 'deny' ] 'dbus' [ I<DBUS ACCESS EXPRESSION> ] [ I<DBUS BUS> ]
B<DBUS ACCESS EXPRESSION> = ( I<DBUS ACCESS> | '(' I<DBUS ACCESS LIST> ')' )
@@ -125,7 +127,7 @@ B<DBUS LABEL> = 'label' '=' '(' '"' I<AARE> '"' | I<AARE> ')'
B<DBUS ACCESS LIST> = Comma separated list of I<DBUS ACCESS>
B<DBUS ACCESS> = ( 'send' | 'receive' | 'bind' ) (some accesses are incompatible with some rules; see below.)
B<DBUS ACCESS> = ( 'send' | 'receive' | 'bind' | 'eavesdrop' ) (some accesses are incompatible with some rules; see below.)
B<AARE> = B<?*[]{}^> (see below for meanings)
@@ -669,7 +671,8 @@ examined.
Some AppArmor DBus permissions are not compatible with all AppArmor DBus rules.
The 'bind' permission cannot be used in message rules. The 'send' and 'receive'
permissions cannot be used in service rules.
permissions cannot be used in service rules. The 'eavesdrop' permission cannot
be used in rules containing any conditionals outside of the 'bus' conditional.
AppArmor DBus permissions are implied when a rule does not explicitly state an
access list. By default, all DBus permissions are implied. Only message
@@ -705,6 +708,12 @@ Example AppArmor DBus rules:
member=ExampleMethod
peer=(name=(com.example.ExampleName1|com.example.ExampleName2)),
# Allow eavesdropping on the system bus
dbus eavesdrop bus=system,
# Allow and audit all eavesdropping
audit dbus eavesdrop,
=head2 Variables
AppArmor's policy language allows embedding variables into file rules

View File

@@ -6,6 +6,9 @@
# Copyright (c) 2010
# Canonical Ltd. (All rights reserved)
#
# Copyright (c) 2013
# Christian Boltz (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.
@@ -89,43 +92,46 @@ may execute, even if the process is running as root. A confined process
cannot call the following system calls:
create_module(2) delete_module(2) init_module(2) ioperm(2)
iopl(2) mount(2) umount(2) ptrace(2) reboot(2) setdomainname(2)
iopl(2) ptrace(2) reboot(2) setdomainname(2)
sethostname(2) swapoff(2) swapon(2) sysctl(2)
A confined process can not call mknod(2) to create character or block devices.
=head1 ERRORS
When a confined process tries to access a file it does not have permission
to access, the kernel will report a message through audit, similar to:
audit(1148420912.879:96): REJECTING x access to /bin/uname
(sh(6646) profile /tmp/sh active /tmp/sh)
audit(1386511672.612:238): apparmor="DENIED" operation="exec"
parent=7589 profile="/tmp/sh" name="/bin/uname" pid=7605
comm="sh" requested_mask="x" denied_mask="x" fsuid=0 ouid=0
audit(1148420912.879:97): REJECTING r access to /bin/uname
(sh(6646) profile /tmp/sh active /tmp/sh)
audit(1386511672.613:239): apparmor="DENIED" operation="open"
parent=7589 profile="/tmp/sh" name="/bin/uname" pid=7605
comm="sh" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
audit(1148420944.837:98): REJECTING access to capability
'dac_override' (sh(6641) profile /tmp/sh active /tmp/sh)
audit(1386511772.804:246): apparmor="DENIED" operation="capable"
parent=7246 profile="/tmp/sh" pid=7589 comm="sh" pid=7589
comm="sh" capability=2 capname="dac_override"
The permissions requested by the process are immediately after
REJECTING. The "name" and process id of the running program are reported,
as well as the profile name and any "hat" that may be active. ("Name"
The permissions requested by the process are described in the operation=
and denied_mask= (for files - capabilities etc. use a slightly different
log format).
The "name" and process id of the running program are reported,
as well as the profile name including any "hat" that may be active,
separated by "//". ("Name"
is in quotes, because the process name is limited to 15 bytes; it is the
same as reported through the Berkeley process accounting.) If no hat is
active (see aa_change_hat(2)) then the profile name is printed for "active".
same as reported through the Berkeley process accounting.)
For confined processes running under a profile that has been loaded in
complain mode, enforcement will not take place and the log messages
reported to audit will be of the form:
audit(1146868287.904:237): PERMITTING r access to
/etc/apparmor.d/tunables (du(3811) profile /usr/bin/du active
/usr/bin/du)
audit(1386512577.017:275): apparmor="ALLOWED" operation="open"
parent=8012 profile="/usr/bin/du" name="/etc/apparmor.d/tunables/"
pid=8049 comm="du" requested_mask="r" denied_mask="r" fsuid=1000 ouid=0
audit(1146868287.904:238): PERMITTING r access to /etc/apparmor.d
(du(3811) profile /usr/bin/du active /usr/bin/du)
audit(1386512577.017:276): apparmor="ALLOWED" operation="open"
parent=8012 profile="/usr/bin/du" name="/etc/apparmor.d/tunables/"
pid=8049 comm="du" requested_mask="r" denied_mask="r" fsuid=1000 ouid=0
If the userland auditd is not running, the kernel will send audit events

View File

@@ -18,6 +18,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/apparmor.h>
#include "parser.h"
#include "profile.h"
@@ -129,12 +130,18 @@ struct dbus_entry *new_dbus_entry(int mode, struct cond_entry *conds,
yyerror("dbus \"bind\" access cannot be used with message rule conditionals\n");
else if (service_rule && (ent->mode & (AA_DBUS_SEND | AA_DBUS_RECEIVE)))
yyerror("dbus \"send\" and/or \"receive\" accesses cannot be used with service rule conditionals\n");
else if (ent->mode & AA_DBUS_EAVESDROP &&
(ent->path || ent->interface || ent->member ||
ent->peer_label || ent->name)) {
yyerror("dbus \"eavesdrop\" access can only contain a bus conditional\n");
}
} else {
ent->mode = AA_VALID_DBUS_PERMS;
if (message_rule)
ent->mode &= ~AA_DBUS_BIND;
ent->mode = (AA_DBUS_SEND | AA_DBUS_RECEIVE);
else if (service_rule)
ent->mode &= ~(AA_DBUS_SEND | AA_DBUS_RECEIVE);
ent->mode = (AA_DBUS_BIND);
else
ent->mode = AA_VALID_DBUS_PERMS;
}
out:
@@ -184,6 +191,8 @@ void print_dbus_entry(struct dbus_entry *ent)
fprintf(stderr, "receive ");
if (ent->mode & AA_DBUS_BIND)
fprintf(stderr, "bind ");
if (ent->mode & AA_DBUS_EAVESDROP)
fprintf(stderr, "eavesdrop ");
fprintf(stderr, ")");
if (ent->bus)

View File

@@ -40,13 +40,6 @@
#define AA_EXEC_MOD_2 (1 << 12)
#define AA_EXEC_MOD_3 (1 << 13)
#define AA_DBUS_SEND AA_MAY_WRITE
#define AA_DBUS_RECEIVE AA_MAY_READ
#define AA_DBUS_BIND (1 << 6)
#define AA_VALID_DBUS_PERMS (AA_DBUS_SEND | AA_DBUS_RECEIVE | \
AA_DBUS_BIND)
#define AA_BASE_PERMS (AA_MAY_EXEC | AA_MAY_WRITE | \
AA_MAY_READ | AA_MAY_APPEND | \
AA_MAY_LINK | AA_MAY_LOCK | \

View File

@@ -3,8 +3,8 @@
TARGET=libapparmor_re.a
CFLAGS ?= -g -Wall -O2 ${EXTRA_CFLAGS}
CXXFLAGS := ${CFLAGS} -std=c++0x
CFLAGS ?= -g -Wall -O2 ${EXTRA_CFLAGS} -std=gnu++0x
CXXFLAGS := ${CFLAGS}
ARFLAGS=-rcs
@@ -29,4 +29,4 @@ parse.cc : parse.y parse.h flex-tables.h ../immunix.h
${BISON} -o $@ $<
clean:
rm -f *.o parse.cc ${TARGET}
rm -f *.o parse.cc ${TARGET} *.gcda *.gcno

View File

@@ -315,6 +315,13 @@ void *aare_create_dfa(aare_ruleset_t *rules, size_t *size,
} else if (flags & DFA_DUMP_EQUIV)
cerr << "\nDFA did not generate an equivalence class\n";
if (flags & DFA_CONTROL_DIFF_ENCODE) {
dfa.diff_encode(flags);
if (flags & DFA_DUMP_DIFF_ENCODE)
dfa.dump_diff_encode(cerr);
}
CHFA chfa(dfa, eq, flags);
if (flags & DFA_DUMP_TRANS_TABLE)
chfa.dump(cerr);

View File

@@ -27,11 +27,14 @@ typedef int dfaflags_t;
#define DFA_CONTROL_TREE_SIMPLE (1 << 2)
#define DFA_CONTROL_TREE_LEFT (1 << 3)
#define DFA_CONTROL_MINIMIZE (1 << 4)
#define DFA_CONTROL_MINIMIZE_HASH_TRANS (1 << 5)
#define DFA_CONTROL_FILTER_DENY (1 << 6)
#define DFA_CONTROL_REMOVE_UNREACHABLE (1 << 7)
#define DFA_CONTROL_TRANS_HIGH (1 << 8)
#define DFA_CONTROL_DIFF_ENCODE (1 << 9)
#define DFA_DUMP_DIFF_PROGRESS (1 << 10)
#define DFA_DUMP_DIFF_ENCODE (1 << 11)
#define DFA_DUMP_DIFF_STATS (1 << 12)
#define DFA_DUMP_MIN_PARTS (1 << 13)
#define DFA_DUMP_UNIQ_PERMS (1 << 14)
#define DFA_DUMP_MIN_UNIQ_PERMS (1 << 15)

View File

@@ -32,6 +32,7 @@
#include "hfa.h"
#include "chfa.h"
#include "../immunix.h"
#include "flex-tables.h"
void CHFA::init_free_list(vector<pair<size_t, size_t> > &free_list,
size_t prev, size_t start)
@@ -53,6 +54,11 @@ CHFA::CHFA(DFA &dfa, map<uchar, uchar> &eq, dfaflags_t flags): eq(eq)
if (flags & DFA_DUMP_TRANS_PROGRESS)
fprintf(stderr, "Compressing HFA:\r");
if (dfa.diffcount)
chfaflags = YYTH_FLAG_DIFF_ENCODE;
else
chfaflags = 0;
if (eq.empty())
max_eq = 255;
else {
@@ -92,10 +98,10 @@ CHFA::CHFA(DFA &dfa, map<uchar, uchar> &eq, dfaflags_t flags): eq(eq)
default_base.push_back(make_pair(dfa.nonmatching, 0));
num.insert(make_pair(dfa.nonmatching, num.size()));
accept.resize(dfa.states.size());
accept2.resize(dfa.states.size());
next_check.resize(optimal);
free_list.resize(optimal);
accept.resize(max(dfa.states.size(), (size_t) 2));
accept2.resize(max(dfa.states.size(), (size_t) 2));
next_check.resize(max(optimal, (size_t) 256));
free_list.resize(next_check.size());
accept[0] = 0;
accept2[0] = 0;
@@ -242,6 +248,8 @@ repeat:
}
do_insert:
if (from->flags & DiffEncodeFlag)
base |= DiffEncodeBit32;
default_base.push_back(make_pair(default_state, base));
}
@@ -279,7 +287,7 @@ void CHFA::dump(ostream &os)
<< *next_check[i].second << " -> "
<< *next_check[i].first << ": ";
size_t offs = i - default_base[num[next_check[i].second]].second;
size_t offs = i - base_mask_size(default_base[num[next_check[i].second]].second);
if (eq.size())
os << offs;
else
@@ -296,7 +304,6 @@ void CHFA::dump(ostream &os)
* (Only the -Cf and -Ce formats are currently supported.)
*/
#include "flex-tables.h"
#define YYTH_REGEX_MAGIC 0x1B5E783D
static inline size_t pad64(size_t i)
@@ -395,6 +402,7 @@ void CHFA::flex_table(ostream &os, const char *name)
size_t hsize = pad64(sizeof(th) + sizeof(th_version) + strlen(name) + 1);
th.th_magic = htonl(YYTH_REGEX_MAGIC);
th.th_flags = htonl(chfaflags);
th.th_hsize = htonl(hsize);
th.th_ssize = htonl(hsize +
flex_table_size(accept.begin(), accept.end()) +

View File

@@ -26,6 +26,10 @@
#include "hfa.h"
#define BASE32_FLAGS 0xff000000
#define DiffEncodeBit32 0x80000000
#define base_mask_size(X) ((X) & ~BASE32_FLAGS)
using namespace std;
class CHFA {
@@ -51,6 +55,7 @@ class CHFA {
map<uchar, uchar> &eq;
uchar max_eq;
size_t first_free;
unsigned int chfaflags;
};
#endif /* __LIBAA_RE_CHFA_H */

View File

@@ -1,7 +1,7 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
* Copyright 2009-2012 Canonical Ltd.
* Copyright 2009-2013 Canonical Ltd.
*
* The libapparmor library is licensed under the terms of the GNU
* Lesser General Public License, version 2.1. Please see the file
@@ -181,52 +181,72 @@ static void rotate_node(Node *t, int dir)
t->child[!dir] = left;
}
void normalize_tree(Node *t, int dir)
/* return False if no work done */
int TwoChildNode::normalize_eps(int dir)
{
if (dynamic_cast<LeafNode *>(t))
return;
if ((&epsnode == child[dir]) &&
(&epsnode != child[!dir])) {
// (E | a) -> (a | E)
// Ea -> aE
// Test for E | (E | E) and E . (E . E) which will
// result in an infinite loop
Node *c = child[!dir];
if (dynamic_cast<TwoChildNode *>(c) &&
&epsnode == c->child[dir] &&
&epsnode == c->child[!dir]) {
c->release();
c = &epsnode;
}
child[!dir] = child[dir];
child[dir] = c;
return 1;
}
return 0;
}
void CatNode::normalize(int dir)
{
for (;;) {
if (dynamic_cast<TwoChildNode *>(t) &&
(&epsnode == t->child[dir]) &&
(&epsnode != t->child[!dir])) {
// (E | a) -> (a | E)
// Ea -> aE
// Test for E | (E | E) and E . (E . E) which will
// result in an infinite loop
Node *c = t->child[!dir];
if (dynamic_cast<TwoChildNode *>(c) &&
&epsnode == c->child[dir] &&
&epsnode == c->child[!dir]) {
c->release();
c = &epsnode;
}
t->child[dir] = c;
t->child[!dir] = &epsnode;
// Don't break here as 'a' may be a tree that
// can be pulled up.
} else if ((dynamic_cast<AltNode *>(t) &&
dynamic_cast<AltNode *>(t->child[dir])) ||
(dynamic_cast<CatNode *>(t) &&
dynamic_cast<CatNode *>(t->child[dir]))) {
// (a | b) | c -> a | (b | c)
if (normalize_eps(dir)) {
continue;
} else if (dynamic_cast<CatNode *>(child[dir])) {
// (ab)c -> a(bc)
rotate_node(t, dir);
} else if (dynamic_cast<AltNode *>(t) &&
dynamic_cast<CharSetNode *>(t->child[dir]) &&
dynamic_cast<CharNode *>(t->child[!dir])) {
// [a] | b -> b | [a]
Node *c = t->child[dir];
t->child[dir] = t->child[!dir];
t->child[!dir] = c;
rotate_node(this, dir);
} else {
break;
}
}
if (t->child[dir])
normalize_tree(t->child[dir], dir);
if (t->child[!dir])
normalize_tree(t->child[!dir], dir);
if (child[dir])
child[dir]->normalize(dir);
if (child[!dir])
child[!dir]->normalize(dir);
}
void AltNode::normalize(int dir)
{
for (;;) {
if (normalize_eps(dir)) {
continue;
} else if (dynamic_cast<AltNode *>(child[dir])) {
// (a | b) | c -> a | (b | c)
rotate_node(this, dir);
} else if (dynamic_cast<CharSetNode *>(child[dir]) &&
dynamic_cast<CharNode *>(child[!dir])) {
// [a] | b -> b | [a]
Node *c = child[dir];
child[dir] = child[!dir];
child[!dir] = c;
} else {
break;
}
}
if (child[dir])
child[dir]->normalize(dir);
if (child[!dir])
child[!dir]->normalize(dir);
}
//charset conversion is disabled for now,
@@ -558,7 +578,7 @@ Node *simplify_tree(Node *t, dfaflags_t flags)
do {
modified = false;
if (flags & DFA_CONTROL_TREE_NORMAL)
normalize_tree(t, dir);
t->normalize(dir);
t = simplify_tree_base(t, dir, modified);
if (modified)
update = true;

View File

@@ -1,7 +1,7 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
* Copyright 2009-2012 Canonical Ltd.
* Copyright 2009-2013 Canonical Ltd.
*
* The libapparmor library is licensed under the terms of the GNU
* Lesser General Public License, version 2.1. Please see the file
@@ -126,6 +126,15 @@ public:
virtual int eq(Node *other) = 0;
virtual ostream &dump(ostream &os) = 0;
void dump_syntax_tree(ostream &os);
virtual void normalize(int dir)
{
if (child[dir])
child[dir]->normalize(dir);
if (child[!dir])
child[!dir]->normalize(dir);
}
/* return false if no work done */
virtual int normalize_eps(int dir __attribute__((unused))) { return 0; }
bool nullable;
NodeSet firstpos, lastpos, followpos;
@@ -157,11 +166,13 @@ public:
class TwoChildNode: public InnerNode {
public:
TwoChildNode(Node *left, Node *right): InnerNode(left, right) { };
virtual int normalize_eps(int dir);
};
class LeafNode: public Node {
public:
LeafNode(): Node() { };
virtual void normalize(int dir __attribute__((unused))) { return; }
};
/* Match nothing (//). */
@@ -485,6 +496,7 @@ public:
child[1]->dump(os);
return os;
}
void normalize(int dir);
};
/* Match one of two alternative nodes. */
@@ -521,6 +533,7 @@ public:
os << ')';
return os;
}
void normalize(int dir);
};
/* Traverse the syntax tree depth-first in an iterator-like manner. */
@@ -578,7 +591,7 @@ void flip_tree(Node *node);
class MatchFlag: public AcceptNode {
public:
MatchFlag(uint32_t flag, uint32_t audit): flag(flag), audit(audit) { }
ostream &dump(ostream &os) { return os << '<' << flag << '>'; }
ostream &dump(ostream &os) { return os << "< 0x" << hex << flag << '>'; }
uint32_t flag;
uint32_t audit;

View File

@@ -5,6 +5,7 @@
#include <stdint.h>
#define YYTH_MAGIC 0xF13C57B1
#define YYTH_FLAG_DIFF_ENCODE 1
struct table_set_header {
uint32_t th_magic; /* TH_MAGIC */

View File

@@ -73,6 +73,194 @@ ostream &operator<<(ostream &os, const State &state)
return os;
}
/**
* diff_weight - Find differential compression distance between @rel and @this
* @rel: State to compare too
* Returns: An integer indicating how good rel is as a base, larger == better
*
* Find the relative weighted difference for differential state compression
* with queried state being compressed against @rel
*
* +1 for each transition that matches (char and dest - saves a transition)
* 0 for each transition that doesn't match and exists in both states
* 0 for transition that self has and @other doesn't (no extra required)
* -1 for each transition that is in @rel and not in @this (have to override)
*
* @rel should not be a state that has already been made differential or it may
* introduce extra transitions as it does not recurse to find all transitions
*
* Should be applied after state minimization
*/
int State::diff_weight(State *rel)
{
int weight = 0;
if (this == rel)
return 0;
if (rel->diff->rel) {
/* Can only be diff encoded against states that are relative
* to a state of a lower depth. ie, at most one sibling in
* the chain
*/
if (rel->diff->rel->diff->depth >= this->diff->depth)
return 0;
} else if (rel->diff->depth >= this->diff->depth)
return 0;
if (rel->flags & DiffEncodeFlag) {
for (int i = 0; i < 256; i++) {
State *state = rel->next(i);
StateTrans::iterator j = trans.find(i);
if (j != trans.end()) {
if (state == j->second)
weight++;
/* else
0 - keep transition to mask
*/
} else if (state == otherwise) {
/* 0 - match of default against @rel
* We don't save a transition but don't have
* to mask either
*/
} else {
/* @rel has transition not covered by @this.
* Need to add a transition to mask it
*/
weight--;
}
}
return weight;
}
unsigned int count = 0;
for (StateTrans::iterator i = rel->trans.begin(); i != rel->trans.end();
i++) {
StateTrans::iterator j = trans.find(i->first);
if (j != trans.end()) {
if (i->second == j->second)
weight++;
/* } else {
0 - keep transition to mask
*/
count++;
} else if (i->second == otherwise) {
/* 0 - match of default against @rel
* We don't save a transition but don't have to
* mask either
*/
} else {
/* rel has transition not covered by @this. Need to
* add a transition to mask
*/
weight--;
}
}
/* cover transitions in @this but not in @rel */
unsigned int this_count = 0;
if (count < trans.size()) {
for (StateTrans::iterator i = trans.begin(); i != trans.end(); i++) {
StateTrans::iterator j = rel->trans.find(i->first);
if (j == rel->trans.end()) {
this_count++;
if (i->second == rel->otherwise)
/* replaced by rel->cases.otherwise */
weight++;
}
}
}
if (rel->otherwise != otherwise) {
/* rel default transitions have to be masked with transitions
* This covers all transitions not covered above
*/
weight -= 256 - (rel->trans.size() + this_count);
}
return weight;
}
/**
* make_relative - Make this state relative to @rel
* @rel: state to make this state relative too
*
* @rel can be a relative (differentially compressed state)
*/
int State::make_relative(State *rel)
{
int weight = 0;
if (this == rel || !rel)
return 0;
if (flags & DiffEncodeFlag)
return 0;
flags |= DiffEncodeFlag;
for (int i = 0; i < 256 ; i++) {
State *next = rel->next(i);
StateTrans::iterator j = trans.find(i);
if (j != trans.end()) {
if (j->second == next) {
trans.erase(j);
weight++;
}
/* else keep transition to mask */
} else if (otherwise == next) {
/* do nothing, otherwise transition disappears when
* reassigned
*/
} else {
/* need a new transition to mask those in lower state */
trans[i] = otherwise;
weight--;
}
}
otherwise = rel;
return weight;
}
/**
* flatten_differential - remove differential encode from this state
*/
void State::flatten_relative(void)
{
if (!(flags & DiffEncodeFlag))
return;
map<State *, int> count;
for (int i = 0; i < 256; i++)
count[next(i)] += 1;
int j = 0;
State *def = next(0);
for (int i = 1; i < 256; i++) {
if (count[next(i)] > count[next(j)]) {
j = i;
def = next(i);
}
}
for (int i = 0; i < 256; i++) {
if (trans.find(i) != trans.end()) {
if (trans[i] == def)
trans.erase(i);
} else {
if (trans[i] != def)
trans[i] = next(i);
}
}
otherwise = def;
flags = flags & ~DiffEncodeFlag;
}
static void split_node_types(NodeSet *nodes, NodeSet **anodes, NodeSet **nnodes
)
{
@@ -175,6 +363,7 @@ void DFA::dump_node_to_dfa(void)
DFA::DFA(Node *root, dfaflags_t flags): root(root)
{
int i = 0;
diffcount = 0; /* set by diff_encode */
if (flags & DFA_DUMP_PROGRESS)
fprintf(stderr, "Creating dfa:\r");
@@ -361,50 +550,46 @@ void DFA::remove_unreachable(dfaflags_t flags)
/* test if two states have the same transitions under partition_map */
bool DFA::same_mappings(State *s1, State *s2)
{
/* assumes otherwise is set to best choice, if there are multiple
* otherwise choices this will fail to fully minimize the dfa
* if we are not careful. Make sure in cases with multiple
* equiv otherwise we always choose the same otherwise to avoid
*/
if (s1->otherwise->partition != s2->otherwise->partition)
return false;
if (s1->trans.size() != s2->trans.size())
return false;
for (StateTrans::iterator j1 = s1->trans.begin(); j1 != s1->trans.end(); j1++) {
StateTrans::iterator j2 = s2->trans.find(j1->first);
if (j2 == s2->trans.end())
StateTrans::iterator j1;
StateTrans::iterator j2;
for (j1 = s1->trans.begin(), j2 = s2->trans.begin();
j1 != s1->trans.end() && j2 != s2->trans.end();
/*inc inline*/) {
if (j1->first < j2->first) {
if (j1->second->partition != s2->otherwise->partition)
return false;
j1++;
} else if (j1->first == j2->first) {
if (j1->second->partition != j2->second->partition)
return false;
j1++;
j2++;
} else {
if (s1->otherwise->partition != j2->second->partition)
return false;
j2++;
}
}
for ( ; j1 != s1->trans.end(); j1++) {
if (j1->second->partition != s2->otherwise->partition)
return false;
if (j1->second->partition != j2->second->partition)
}
for ( ; j2 != s2->trans.end(); j2++) {
if (j2->second->partition != s1->otherwise->partition)
return false;
}
return true;
}
/* Do simple djb2 hashing against a States transition cases
* this provides a rough initial guess at state equivalence as if a state
* has a different number of transitions or has transitions on different
* trans they will never be equivalent.
* Note: this only hashes based off of the alphabet (not destination)
* as different destinations could end up being equiv
*/
size_t DFA::hash_trans(State *s)
{
unsigned long hash = 5381;
for (StateTrans::iterator j = s->trans.begin(); j != s->trans.end(); j++) {
hash = ((hash << 5) + hash) + j->first;
State *k = j->second;
hash = ((hash << 5) + hash) + k->trans.size();
}
if (s->otherwise != nonmatching) {
hash = ((hash << 5) + hash) + 5381;
State *k = s->otherwise;
hash = ((hash << 5) + hash) + k->trans.size();
}
hash = (hash << 8) | s->trans.size();
return hash;
}
int DFA::apply_and_clear_deny(void)
{
int c = 0;
@@ -435,8 +620,6 @@ void DFA::minimize(dfaflags_t flags)
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
size_t hash = 0;
uint64_t permtype = ((uint64_t) (PACK_AUDIT_CTL((*i)->perms.audit, (*i)->perms.quiet & (*i)->perms.deny)) << 32) | (uint64_t) (*i)->perms.allow;
if (flags & DFA_CONTROL_MINIMIZE_HASH_TRANS)
hash |= hash_trans(*i);
pair<uint64_t, size_t> group = make_pair(permtype, hash);
map<pair<uint64_t, size_t>, Partition *>::iterator p = perm_map.find(group);
if (p == perm_map.end()) {
@@ -541,9 +724,14 @@ void DFA::minimize(dfaflags_t flags)
/* update representative state's transitions */
rep->otherwise = *rep->otherwise->partition->begin();
for (StateTrans::iterator c = rep->trans.begin(); c != rep->trans.end(); c++) {
for (StateTrans::iterator c = rep->trans.begin(); c != rep->trans.end(); ) {
Partition *partition = c->second->partition;
c->second = *partition->begin();
if (rep->otherwise != *partition->begin()) {
c->second = *partition->begin();
c++;
} else
/* transition is now covered by otherwise */
c = rep->trans.erase(c);
}
//if ((*p)->size() > 1)
@@ -606,6 +794,239 @@ out:
}
}
/* diff_encode helper functions */
static unsigned int add_to_dag(DiffDag *dag, State *state,
State *parent)
{
unsigned int rc = 0;
if (!state->diff) {
dag->rel = NULL;
if (parent)
dag->depth = parent->diff->depth + 1;
else
dag->depth = 1;
dag->state = state;
state->diff = dag;
rc = 1;
}
if (parent && parent->diff->depth < state->diff->depth)
state->diff->parents.push_back(parent);
return rc;
}
static int diff_partition(State *state, Partition &part, State **candidate)
{
int weight = 0;
*candidate = NULL;
for (Partition::iterator i = part.begin(); i != part.end(); i++) {
if (*i == state)
continue;
int tmp = state->diff_weight(*i);
if (tmp > weight) {
weight = tmp;
*candidate = *i;
}
}
return weight;
}
/**
* diff_encode - compress dfa by differentially encoding state transitions
* @dfa_flags: flags controling dfa creation
*
* This function reduces the number of transitions that need to be stored
* by encoding transitions as the difference between the state and a
* another transitions that is set as the states default.
*
* For performance reasons this function does not try to compute the
* absolute best encoding (maximal spanning tree) but instead computes
* a very good encoding within the following limitations.
* - Not all states have to be differentially encoded. This allows for
* multiple states to be used as a terminating basis.
* - The number of state transitions needed to match an input of length
* m will be 2m
*
* To guarentee this the ordering and distance calculation is done in the
* following manner.
* - A DAG of the DFA is created starting with the start state(s).
* - A state can only be relative (have a differential encoding) to
* another state if that state has
* - a lower depth in the DAG
* - is a sibling (same depth) that is not relative
* - is a sibling that is relative to a state with lower depth in the DAG
*
* The run time constraints are maintained by the DAG ordering + relative
* state constraints. For any input character C when at state S with S being
* at level N in the DAG then at most 2N states must be traversed to find the
* transition for C. However on the maximal number of transitions is not m*m,
* because when a character is matched and forward movement is made through
* the DFA any relative transition search will move back through the DAG order.
* So say for character C we start matching on a state S that is at depth 10
* in the DAG. The transition for C is not found in S and we recurse backwards
* to a depth of 6. A transition is found and it steps to the next state, but
* the state transition at most will only move 1 deeper into the DAG so for
* the next state the maximum number of states traversed is 2*7.
*/
void DFA::diff_encode(dfaflags_t flags)
{
DiffDag *dag;
unsigned int xcount = 0, xweight = 0, transitions = 0, depth = 0;
/* clear the depth flag */
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
(*i)->diff = NULL;
transitions += (*i)->trans.size();
}
/* Prealloc structures we need. We know the exact number of elements,
* and once setup they don't change so we don't need the flexibility
* or overhead of stl, just allocate the needed data as an array
*/
dag = new DiffDag [states.size()];
/* Generate DAG ordering and parent sets */
add_to_dag(&dag[0], nonmatching, NULL);
add_to_dag(&dag[1], start, NULL);
unsigned int tail = 2;
for (unsigned int i = 1; i < tail; i++) {
State *state = dag[i].state;
State *child = dag[i].state->otherwise;
if (child)
tail += add_to_dag(&dag[tail], child, state);
for (StateTrans::iterator j = state->trans.begin(); j != state->trans.end(); j++) {
child = j->second;
tail += add_to_dag(&dag[tail], child, state);
}
}
depth = dag[tail - 1].depth;
/* calculate which state to make a transitions relative too */
for (unsigned int i = 2; i < tail; i++) {
State *state = dag[i].state;
State *candidate = NULL;
int weight = diff_partition(state,
state->otherwise->diff->parents,
&candidate);
for (StateTrans::iterator j = state->trans.begin(); j != state->trans.end(); j++) {
State *tmp_candidate;
int tmp = diff_partition(state,
j->second->diff->parents,
&tmp_candidate);
if (tmp > weight) {
weight = tmp;
candidate = tmp_candidate;
}
}
if ((flags & DFA_DUMP_DIFF_PROGRESS) && (i % 100 == 0))
cerr << "\033[2KDiff Encode: " << i << " of "
<< tail << ". Diff states " << xcount
<< " Savings " << xweight << "\r";
state->diff->rel = candidate;
if (candidate) {
xcount++;
xweight += weight;
}
}
/* now make transitions relative, start at the back of the list so
* as to start with the last transitions and work backwards to avoid
* having to traverse multiple previous states (that have been made
* relative already) to reconstruct previous state transition table
*/
unsigned int aweight = 0;
diffcount = 0;
for (int i = tail - 1; i > 1; i--) {
if (dag[i].rel) {
int weight = dag[i].state->make_relative(dag[i].rel);
aweight += weight;
diffcount++;
}
}
if (flags & DFA_DUMP_DIFF_STATS)
cerr << "Diff encode states: " << diffcount << " of "
<< tail << " reached @ depth " << depth << ". "
<< aweight << " trans removed\n";
if (xweight != aweight)
cerr << "Diff encode error: actual savings " << aweight
<< " != expected " << xweight << "\n";
if (xcount != diffcount)
cerr << "Diff encode error: actual count " << diffcount
<< " != expected " << xcount << " \n";
/* cleanup */
for (unsigned int i = 0; i < tail; i++)
dag[i].parents.clear();
delete [] dag;
}
/**
* flatten_differential - remove differential state encoding
*
* Flatten the dfa back into a flat encoding.
*/
void DFA::undiff_encode(void)
{
for (Partition::iterator i = states.begin(); i != states.end(); i++)
(*i)->flatten_relative();
diffcount = 0;
}
void DFA::dump_diff_chain(ostream &os, map<State *, Partition> &relmap,
Partition &chain, State *state, unsigned int &count,
unsigned int &total, unsigned int &max)
{
if (relmap[state].size() == 0) {
for (Partition::iterator i = chain.begin(); i != chain.end(); i++)
os << **i << " <- ";
os << *state << "\n";
count++;
total += chain.size() + 1;
if (chain.size() + 1 > max)
max = chain.size() + 1;
}
chain.push_back(state);
for (Partition::iterator i = relmap[state].begin(); i != relmap[state].end(); i++)
dump_diff_chain(os, relmap, chain, *i, count, total, max);
chain.pop_back();
}
/* Dump the DFA diff_encoding chains */
void DFA::dump_diff_encode(ostream &os)
{
map<State *, Partition> rel;
Partition base, chain;
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
if ((*i)->flags & DiffEncodeFlag)
rel[(*i)->otherwise].push_back(*i);
else
base.push_back(*i);
}
unsigned int count = 0, total = 0, max = 0;
for (Partition::iterator i = base.begin(); i != base.end(); i++)
dump_diff_chain(os, rel, chain, *i, count, total, max);
os << base.size() << " non-differentially encoded states\n";
os << "chains: " << count - base.size() << "\n";
os << "average chain size: " << (double) (total - base.size()) / (double) (count - base.size()) << "\n";
os << "longest chain: " << max << "\n";
}
/**
* text-dump the DFA (for debugging).
*/

View File

@@ -28,10 +28,13 @@
#include <map>
#include <vector>
#include <assert.h>
#include <stdint.h>
#include "expr-tree.h"
#define DiffEncodeFlag 1
class State;
typedef map<uchar, State *> StateTrans;
@@ -334,6 +337,19 @@ public:
}
};
/* Temporary state structure used when building differential encoding
* @parents - set of states that have transitions to this state
* @depth - level in the DAG
* @state - back reference to state this DAG entry belongs
* @rel - state that this state is relative to for differential encoding
*/
struct DiffDag {
Partition parents;
int depth;
State *state;
State *rel;
};
/*
* State - DFA individual state information
* label: a unique label to identify the state used for pretty printing
@@ -352,7 +368,7 @@ public:
class State {
public:
State(int l, ProtoState &n, State *other) throw(int):
label(l), perms(), trans()
label(l), flags(0), perms(), trans()
{
int error;
@@ -372,15 +388,30 @@ public:
};
State *next(uchar c) {
StateTrans::iterator i = trans.find(c);
if (i != trans.end())
return i->second;
return otherwise;
};
State *state = this;
do {
StateTrans::iterator i = state->trans.find(c);
if (i != state->trans.end())
return i->second;
if (!(state->flags & DiffEncodeFlag))
return state->otherwise;
state = state->otherwise;
} while (state);
/* never reached */
assert(0);
return NULL;
}
int diff_weight(State *rel);
int make_relative(State *rel);
void flatten_relative(void);
int apply_and_clear_deny(void) { return perms.apply_and_clear_deny(); }
int label;
int flags;
perms_t perms;
StateTrans trans;
State *otherwise;
@@ -389,6 +420,7 @@ public:
union {
Partition *partition; /* used during minimization */
ProtoState proto; /* used during creation */
DiffDag *diff; /* used during diff encoding */
};
};
@@ -429,12 +461,16 @@ public:
}
};
/* Transitions in the DFA. */
/* Transitions in the DFA. */
class DFA {
void dump_node_to_dfa(void);
State *add_new_state(NodeSet *nodes, State *other);
void update_state_transitions(State *state);
void dump_diff_chain(ostream &os, map<State *, Partition> &relmap,
Partition &chain, State *state,
unsigned int &count, unsigned int &total,
unsigned int &max);
/* temporary values used during computations */
NodeCache anodes_cache;
@@ -452,14 +488,21 @@ public:
void remove_unreachable(dfaflags_t flags);
bool same_mappings(State *s1, State *s2);
size_t hash_trans(State *s);
void minimize(dfaflags_t flags);
int apply_and_clear_deny(void);
void diff_encode(dfaflags_t flags);
void undiff_encode(void);
void dump_diff_encode(ostream &os);
void dump(ostream &os);
void dump_dot_graph(ostream &os);
void dump_uniq_perms(const char *s);
map<uchar, uchar> equivalence_classes(dfaflags_t flags);
void apply_equivalence_classes(map<uchar, uchar> &eq);
unsigned int diffcount;
Node *root;
State *nonmatching, *start;
Partition states;

View File

@@ -34,7 +34,7 @@ int names_only = 0;
int current_lineno = 1;
int option = OPTION_ADD;
dfaflags_t dfaflags = (dfaflags_t)(DFA_CONTROL_TREE_NORMAL | DFA_CONTROL_TREE_SIMPLE | DFA_CONTROL_MINIMIZE | DFA_CONTROL_MINIMIZE_HASH_TRANS);
dfaflags_t dfaflags = (dfaflags_t)(DFA_CONTROL_TREE_NORMAL | DFA_CONTROL_TREE_SIMPLE | DFA_CONTROL_MINIMIZE );
char *subdomainbase = NULL;
const char *progname = __FILE__;

View File

@@ -363,7 +363,7 @@ inline int sd_write_blob(sd_serialize *p, void *b, int buf_size, char *name)
return 1;
}
#define align64(X) (((size_t) (X) + (size_t) 7) & ~((size_t) 7))
#define align64(X) (((X) + (typeof(X)) 7) & ~((typeof(X)) 7))
inline int sd_write_aligned_blob(sd_serialize *p, void *b, int buf_size,
const char *name)
{
@@ -371,7 +371,7 @@ inline int sd_write_aligned_blob(sd_serialize *p, void *b, int buf_size,
u32 tmp;
if (!sd_write_name(p, name))
return 0;
pad = align64(((long)(p->pos + 5) - (long)(p->buffer)) - ((long)(p->pos + 5) - (long)(p->buffer)));
pad = align64(p->pos + 5 - p->buffer) - (p->pos + 5 - p->buffer);
if (!sd_prepare_write(p, SD_BLOB, 4 + buf_size + pad))
return 0;
tmp = cpu_to_le32(buf_size + pad);

View File

@@ -34,6 +34,10 @@
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <unordered_map>
#include <string>
#define _(s) gettext(s)
#include "parser.h"
@@ -48,7 +52,7 @@
/* #define DEBUG */
#ifdef DEBUG
static int yy_top_state(void);
#define PDEBUG(fmt, args...) printf("Lexer (Line %d) (state %s): " fmt, current_lineno, state_names[YY_START], ## args)
#define PDEBUG(fmt, args...) printf("Lexer (Line %d) (state %s): " fmt, current_lineno, state_names[YY_START].c_str(), ## args)
#else
#define PDEBUG(fmt, args...) /* Do nothing */
#endif
@@ -72,13 +76,13 @@ do { \
#define POP() \
do { \
DUMP_AND_DEBUG(" (pop_to(%s)): Matched: %s\n", state_names[yy_top_state()], yytext); \
DUMP_AND_DEBUG(" (pop_to(%s)): Matched: %s\n", state_names[yy_top_state()].c_str(), yytext); \
yy_pop_state(); \
} while (0)
#define PUSH(X) \
do { \
DUMP_AND_DEBUG(" (push(%s)): Matched: %s\n", state_names[(X)], yytext); \
DUMP_AND_DEBUG(" (push(%s)): Matched: %s\n", state_names[(X)].c_str(), yytext); \
yy_push_state(X); \
} while (0)
@@ -96,7 +100,7 @@ do { \
#define BEGIN_AND_RETURN(X, Y) \
do { \
DUMP_AND_DEBUG(" (begin(%s)): Matched: %s\n", state_names[(X)], yytext); \
DUMP_AND_DEBUG(" (begin(%s)): Matched: %s\n", state_names[(X)].c_str(), yytext); \
BEGIN(X); \
return (Y); \
} while (0)
@@ -104,9 +108,8 @@ do { \
#define YY_NO_INPUT
#define STATE_TABLE_ENT(X) [(X)] = #X
/* static char *const state_names[]; */
#define STATE_TABLE_ENT(X) {X, #X }
extern unordered_map<int, string> state_names;
struct cb_struct {
const char *fullpath;
@@ -465,6 +468,7 @@ LT_EQUAL <=
bind { RETURN_TOKEN(TOK_BIND); }
read { RETURN_TOKEN(TOK_READ); }
write { RETURN_TOKEN(TOK_WRITE); }
eavesdrop { RETURN_TOKEN(TOK_EAVESDROP); }
{OPEN_PAREN} {
yy_push_state(LIST_VAL_MODE);
RETURN_TOKEN(TOK_OPENPAREN);
@@ -591,7 +595,7 @@ LT_EQUAL <=
/* Create a table mapping lexer state number to the name used in the
* in the code. This allows for better debug output
*/
static const char *const state_names[] = {
unordered_map<int, string> state_names = {
STATE_TABLE_ENT(INITIAL),
STATE_TABLE_ENT(SUB_ID),
STATE_TABLE_ENT(SUB_VALUE),

View File

@@ -41,7 +41,7 @@
#include <sys/sysctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <apparmor.h>
#include <sys/apparmor.h>
#include "lib.h"
#include "parser.h"
@@ -194,10 +194,10 @@ optflag_table_t dumpflag_table[] = {
DFA_DUMP_SIMPLE_TREE },
{ 1, "stats", "Dump all compile stats",
DFA_DUMP_TREE_STATS | DFA_DUMP_STATS | DFA_DUMP_TRANS_STATS |
DFA_DUMP_EQUIV_STATS },
DFA_DUMP_EQUIV_STATS | DFA_DUMP_DIFF_STATS },
{ 1, "progress", "Dump progress for all compile phases",
DFA_DUMP_PROGRESS | DFA_DUMP_STATS | DFA_DUMP_TRANS_PROGRESS |
DFA_DUMP_TRANS_STATS },
DFA_DUMP_TRANS_STATS | DFA_DUMP_DIFF_PROGRESS | DFA_DUMP_DIFF_STATS },
{ 1, "dfa-progress", "Dump dfa creation as in progress",
DFA_DUMP_PROGRESS | DFA_DUMP_STATS },
{ 1, "dfa-stats", "Dump dfa creation stats", DFA_DUMP_STATS },
@@ -222,13 +222,20 @@ optflag_table_t dumpflag_table[] = {
{ 1, "equiv-stats", "Dump equivance class stats",
DFA_DUMP_EQUIV_STATS },
{ 1, "equiv", "Dump equivance class", DFA_DUMP_EQUIV },
{ 1, "diff-encode", "Dump differential encoding",
DFA_DUMP_DIFF_ENCODE },
{ 1, "diff-stats", "Dump differential encoding stats",
DFA_DUMP_DIFF_STATS },
{ 1, "diff-progress", "Dump progress of differential encoding",
DFA_DUMP_DIFF_PROGRESS | DFA_DUMP_DIFF_STATS },
{ 0, NULL, NULL, 0 },
};
optflag_table_t optflag_table[] = {
{ 2, "0", "no optimizations",
DFA_CONTROL_TREE_NORMAL | DFA_CONTROL_TREE_SIMPLE |
DFA_CONTROL_MINIMIZE | DFA_CONTROL_REMOVE_UNREACHABLE
DFA_CONTROL_MINIMIZE | DFA_CONTROL_REMOVE_UNREACHABLE |
DFA_CONTROL_DIFF_ENCODE
},
{ 1, "equiv", "use equivalent classes", DFA_CONTROL_EQUIV },
{ 1, "expr-normalize", "expression tree normalization",
@@ -240,8 +247,6 @@ optflag_table_t optflag_table[] = {
{ 2, "expr-right-simplify", "right simplification first",
DFA_CONTROL_TREE_LEFT },
{ 1, "minimize", "dfa state minimization", DFA_CONTROL_MINIMIZE },
{ 1, "hash-trans", "minimization - hash transitions during setup",
DFA_CONTROL_MINIMIZE_HASH_TRANS },
{ 1, "filter-deny", "filter out deny information from final dfa",
DFA_CONTROL_FILTER_DENY },
{ 1, "remove-unreachable", "dfa unreachable state removal",
@@ -251,6 +256,8 @@ optflag_table_t optflag_table[] = {
DFA_CONTROL_TRANS_HIGH },
{ 2, "compress-fast", "do faster dfa transition table compression",
DFA_CONTROL_TRANS_HIGH },
{ 1, "diff-encode", "Differentially encode transitions",
DFA_CONTROL_DIFF_ENCODE },
{ 0, NULL, NULL, 0 },
};

View File

@@ -37,6 +37,7 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/apparmor.h>
#include "parser.h"
#include "profile.h"
@@ -146,6 +147,7 @@ static struct keyword_table keyword_table[] = {
{"bind", TOK_BIND},
{"read", TOK_READ},
{"write", TOK_WRITE},
{"eavesdrop", TOK_EAVESDROP},
{"peer", TOK_PEER},
/* terminate */
@@ -311,11 +313,11 @@ static size_t kernel_af_max(void) {
if (!fd)
/* fall back to default provided during build */
return 0;
res = read(fd, &buffer, sizeof(buffer));
res = read(fd, &buffer, sizeof(buffer) - 1);
close(fd);
if (!res)
if (res <= 0)
return 0;
buffer[sizeof(buffer)-1] = '\0';
buffer[res] = '\0';
res = sscanf(buffer, "2.6.%d", &major);
if (res != 1)
return 0;

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,8 @@
#include <libintl.h>
#include <linux/limits.h>
#include <string>
#define _(s) gettext(s)
/* #define DEBUG */
@@ -133,13 +135,89 @@ void free_var_string(struct var_string *var)
free(var);
}
static void trim_trailing_slash(std::string& str)
{
for (std::string::reverse_iterator rit = str.rbegin();
rit != str.rend() && *rit == '/'; ++rit) {
/* yuck, reverse_iterators are ugly */
str.erase(--rit.base());
}
}
static void write_replacement(const char separator, const char* value,
std::string& replacement, bool filter_leading_slash,
bool filter_trailing_slash)
{
const char *p = value;
replacement.append(1, separator);
if (filter_leading_slash)
while (*p == '/')
p++;
replacement.append(p);
if (filter_trailing_slash)
trim_trailing_slash(replacement);
}
static int expand_by_alternations(struct set_value **valuelist,
struct var_string *split_var,
char **name)
{
char *value, *first_value;
std::string replacement;
bool filter_leading_slash = false;
bool filter_trailing_slash = false;
first_value = get_next_set_value(valuelist);
if (!first_value) {
PERROR("ASSERT: set variable (%s) should always have at least one value assigned to it\n",
split_var->var);
exit(1);
}
value = get_next_set_value(valuelist);
if (!value) {
/* only one entry for the variable, so just sub it in */
free(*name);
if (asprintf(name, "%s%s%s",
split_var->prefix ? split_var->prefix : "",
first_value,
split_var->suffix ? split_var->suffix : "") == -1)
return -1;
return 0;
}
if (split_var->prefix && split_var->prefix[strlen(split_var->prefix) - 1] == '/')
filter_leading_slash = true;
if (split_var->suffix && *split_var->suffix == '/')
filter_trailing_slash = true;
write_replacement('{', first_value, replacement, filter_leading_slash, filter_trailing_slash);
write_replacement(',', value, replacement, filter_leading_slash, filter_trailing_slash);
while ((value = get_next_set_value(valuelist))) {
write_replacement(',', value, replacement, filter_leading_slash, filter_trailing_slash);
}
free(*name);
if (asprintf(name, "%s%s}%s",
split_var->prefix ? split_var->prefix : "",
replacement.c_str(),
split_var->suffix ? split_var->suffix : "") == -1) {
return -1;
}
return 0;
}
/* doesn't handle variables in options atm */
static int expand_entry_variables(char **name, void *entry,
int (dup_and_chain)(void *))
static int expand_entry_variables(char **name, void *entry)
{
struct set_value *valuelist;
char *value;
struct var_string *split_var;
int ret;
if (!entry) /* can happen when entry is optional */
return 0;
@@ -157,84 +235,23 @@ static int expand_entry_variables(char **name, void *entry,
exit(1);
}
value = get_next_set_value(&valuelist);
if (!value) {
PERROR("ASSERT: set variable (%s) should always have at least one value assigned to them\n",
split_var->var);
exit(1);
}
free(*name);
if (asprintf(name, "%s%s%s",
split_var->prefix ? split_var->prefix : "",
value,
split_var->suffix ? split_var->suffix : "") == -1)
return -1;
while ((value = get_next_set_value(&valuelist))) {
if (!dup_and_chain(entry)) {
PERROR("Memory allocation error while handling set variable %s\n",
split_var->var);
exit(1);
}
free(*name);
if (asprintf(name, "%s%s%s",
split_var->prefix ? split_var->prefix : "", value,
split_var->suffix ? split_var->suffix : "") == -1)
return -1;
}
ret = expand_by_alternations(&valuelist, split_var, name);
free_var_string(split_var);
if (ret != 0)
return -1;
}
return 0;
}
int clone_and_chain_cod(void *v)
{
struct cod_entry *entry = (struct cod_entry *) v;
struct cod_entry *dup = copy_cod_entry(entry);
if (!dup)
return 0;
entry->next = dup;
return 1;
}
int clone_and_chain_mnt(void *v)
{
struct mnt_entry *entry = (struct mnt_entry *) v;
struct mnt_entry *dup = dup_mnt_entry(entry);
if (!dup)
return 0;
entry->next = dup;
return 1;
}
int clone_and_chain_dbus(void *v)
{
struct dbus_entry *entry = (struct dbus_entry *) v;
struct dbus_entry *dup = dup_dbus_entry(entry);
if (!dup)
return 0;
entry->next = dup;
return 1;
}
static int process_variables_in_entries(struct cod_entry *entry_list)
{
int error = 0;
struct cod_entry *entry;
list_for_each(entry_list, entry) {
error = expand_entry_variables(&entry->name, entry,
clone_and_chain_cod);
error = expand_entry_variables(&entry->name, entry);
if (error)
return error;
}
@@ -249,16 +266,13 @@ static int process_variables_in_mnt_entries(struct mnt_entry *entry_list)
struct mnt_entry *entry;
list_for_each(entry_list, entry) {
error = expand_entry_variables(&entry->mnt_point, entry,
clone_and_chain_mnt);
error = expand_entry_variables(&entry->mnt_point, entry);
if (error)
return error;
error = expand_entry_variables(&entry->device, entry,
clone_and_chain_mnt);
error = expand_entry_variables(&entry->device, entry);
if (error)
return error;
error = expand_entry_variables(&entry->trans, entry,
clone_and_chain_mnt);
error = expand_entry_variables(&entry->trans, entry);
if (error)
return error;
@@ -273,28 +287,22 @@ static int process_dbus_variables(struct dbus_entry *entry_list)
struct dbus_entry *entry;
list_for_each(entry_list, entry) {
error = expand_entry_variables(&entry->bus, entry,
clone_and_chain_dbus);
error = expand_entry_variables(&entry->bus, entry);
if (error)
return error;
error = expand_entry_variables(&entry->name, entry,
clone_and_chain_dbus);
error = expand_entry_variables(&entry->name, entry);
if (error)
return error;
error = expand_entry_variables(&entry->peer_label, entry,
clone_and_chain_dbus);
error = expand_entry_variables(&entry->peer_label, entry);
if (error)
return error;
error = expand_entry_variables(&entry->path, entry,
clone_and_chain_dbus);
error = expand_entry_variables(&entry->path, entry);
if (error)
return error;
error = expand_entry_variables(&entry->interface, entry,
clone_and_chain_dbus);
error = expand_entry_variables(&entry->interface, entry);
if (error)
return error;
error = expand_entry_variables(&entry->member, entry,
clone_and_chain_dbus);
error = expand_entry_variables(&entry->member, entry);
if (error)
return error;

View File

@@ -27,6 +27,7 @@
#include <errno.h>
#include <fcntl.h>
#include <libintl.h>
#include <sys/apparmor.h>
#define _(s) gettext(s)
/* #define DEBUG */
@@ -132,6 +133,7 @@ void add_local_entry(Profile *prof);
%token TOK_BIND
%token TOK_READ
%token TOK_WRITE
%token TOK_EAVESDROP
%token TOK_PEER
/* rlimits */
@@ -657,7 +659,7 @@ rules: rules opt_prefix network_rule
rules: rules opt_prefix mnt_rule
{
if ($2.owner)
yyerror(_("owner prefix not allow on mount rules"));
yyerror(_("owner prefix not allowed on mount rules"));
if ($2.deny && $2.audit) {
$3->deny = 1;
} else if ($2.deny) {
@@ -674,7 +676,7 @@ rules: rules opt_prefix mnt_rule
rules: rules opt_prefix dbus_rule
{
if ($2.owner)
yyerror(_("owner prefix not allow on dbus rules"));
yyerror(_("owner prefix not allowed on dbus rules"));
if ($2.deny && $2.audit) {
$3->deny = 1;
} else if ($2.deny) {
@@ -701,7 +703,7 @@ rules: rules change_profile
rules: rules opt_prefix capability
{
if ($2.owner)
yyerror(_("owner prefix not allow on capability rules"));
yyerror(_("owner prefix not allowed on capability rules"));
if ($2.deny)
$1->caps.deny |= $3;
@@ -1165,6 +1167,8 @@ dbus_perm: TOK_VALUE
$$ = AA_DBUS_SEND;
else if (strcmp($1, "receive") == 0 || strcmp($1, "read") == 0)
$$ = AA_DBUS_RECEIVE;
else if (strcmp($1, "eavesdrop") == 0)
$$ = AA_DBUS_EAVESDROP;
else if ($1) {
parse_dbus_mode($1, &$$, 1);
} else
@@ -1178,6 +1182,7 @@ dbus_perm: TOK_VALUE
| TOK_RECEIVE { $$ = AA_DBUS_RECEIVE; }
| TOK_READ { $$ = AA_DBUS_RECEIVE; }
| TOK_WRITE { $$ = AA_DBUS_SEND; }
| TOK_EAVESDROP { $$ = AA_DBUS_EAVESDROP; }
| TOK_MODE
{
parse_dbus_mode($1, &$$, 1);

View File

@@ -17,7 +17,9 @@
#define __AA_POLICYDB_H
/*
* Class of mediation types in the AppArmor policy db
* Class of private mediation types in the AppArmor policy db
*
* See libapparmor's apparmor.h for public mediation types
*/
#define AA_CLASS_COND 0
#define AA_CLASS_UNKNOWN 1
@@ -32,7 +34,6 @@
#define AA_CLASS_ENV 16
#define AA_CLASS_DBUS 32
#define AA_CLASS_X 33
#endif /* __AA_POLICYDB_H */

View File

@@ -174,7 +174,7 @@ public:
parent = NULL;
flags = { 0, 0, 0, 0};
rlimits = { 0 };
rlimits = {0, {}};
std::fill(exec_table, exec_table + AA_EXEC_COUNT, (char *)NULL);

View File

@@ -8,12 +8,13 @@ PROVE_ARG=-f
ifeq ($(VERBOSE),1)
PROVE_ARG+=-v
PYTEST_ARG = -v
endif
all: tests
.PHONY: tests error_output gen_dbus gen_xtrans parser_sanity caching minimize equality
tests: error_output parser_sanity caching minimize equality
.PHONY: tests error_output gen_dbus gen_xtrans parser_sanity caching minimize equality valgrind
tests: error_output caching minimize equality parser_sanity
GEN_TRANS_DIRS=simple_tests/generated_x/ simple_tests/generated_perms_leading/ simple_tests/generated_perms_safe/ simple_tests/generated_dbus
@@ -42,7 +43,7 @@ parser_sanity: $(PARSER) gen_xtrans gen_dbus
$(Q)LANG=C APPARMOR_PARSER="$(PARSER)" ${PROVE} ${PROVE_ARG} ${TESTS}
caching: $(PARSER)
LANG=C APPARMOR_PARSER="$(PARSER)" ./caching.sh
LANG=C ./caching.py -p "$(PARSER)" $(PYTEST_ARG)
minimize: $(PARSER)
LANG=C APPARMOR_PARSER="$(PARSER)" ./minimize.sh
@@ -58,3 +59,4 @@ $(PARSER):
clean:
find $(GEN_TRANS_DIRS) -type f | xargs rm -f
rm -f gmon.out

View File

@@ -51,6 +51,11 @@ class AAParserCachingCommon(testlib.AATestTemplate):
# REPORT ALL THE OUTPUT
self.maxDiff = None
# skip all the things if apparmor securityfs isn't mounted
if not os.path.exists("/sys/kernel/security/apparmor"):
raise unittest.SkipTest("WARNING: /sys/kernel/security/apparmor does not exist. "
"Skipping tests")
self.tmp_dir = tempfile.mkdtemp(prefix='aa-caching-')
os.chmod(self.tmp_dir, 0o755)

View File

@@ -1,173 +0,0 @@
#!/bin/bash
# These tests will stop running as soon as a failure is seen since they tend to build
# on the actions and results of the prior tests.
set -e
# This test requires introspection
if [ ! -d /sys/kernel/security/apparmor ]; then
echo "WARNING: /sys/kernel/security/apparmor does not exist. Skipping tests"
echo "requiring introspection."
exit 0
fi
APPARMOR_PARSER="${APPARMOR_PARSER:-../apparmor_parser}"
# fake base directory
basedir=$(mktemp -d -t aa-cache-XXXXXX)
altcachedir=$(mktemp -d -t aa-alt-cache-XXXXXXXX)
trap "rm -rf $basedir $altcachedir" EXIT
mkdir -p $basedir/cache
ARGS="--base $basedir --skip-kernel-load"
profile=sbin.pingy
cp caching.profile $basedir/$profile
# Detect and slow down cache test when filesystem can't represent nanosecond delays.
timeout=0.1
_count=10
for ((i = 0; i < ${_count} ; i++)) ; do
touch $basedir/test${i}
sleep $timeout
done
TIMES=$(stat $basedir/test* -c %z | cut -d" " -f2 | cut -d: -f3 | sort -u | wc -l)
if [ $TIMES -ne ${_count} ]; then
echo "WARNING: $basedir lacks nanosecond timestamp resolution, falling back to slower test"
timeout=1
fi
rm -f $basedir/test*
echo -n "Profiles are not cached by default: "
${APPARMOR_PARSER} $ARGS -q -r $basedir/$profile
[ -f $basedir/cache/$profile ] && echo "FAIL ($basedir/cache/$profile exists)" && exit 1
echo "ok"
echo -n "Profiles are not cached when using --skip-cache: "
${APPARMOR_PARSER} $ARGS -q --write-cache --skip-cache -r $basedir/$profile
[ -f $basedir/cache/$profile ] && echo "FAIL ($basedir/cache/$profile exists)" && exit 1
echo "ok"
sleep $timeout
echo -n "Profiles are cached when requested: "
${APPARMOR_PARSER} $ARGS -q --write-cache -r $basedir/$profile
[ ! -f $basedir/cache/$profile ] && echo "FAIL ($basedir/cache/$profile does not exist)" && exit 1
echo "ok"
read_features_dir()
{
directory="$1"
if [ ! -d "$directory" ] ; then
return
fi
for f in `ls -AU "$directory"` ; do
if [ -f "$directory/$f" ] ; then
read -r -d "" KF < "$directory/$f" || true
echo -e "$f {$KF\n}"
elif [ -d "$directory/$f" ] ; then
echo -n "$f {"
KF=`read_features_dir "$directory/$f" "$KF"` || true
echo "$KF"
echo -e "}"
fi
done
}
echo -n "Kernel features are written to cache: "
[ ! -f $basedir/cache/.features ] && echo "FAIL ($basedir/cache/.features missing)" && exit 1
read -r -d "" CF < $basedir/cache/.features || true
if [ -d /sys/kernel/security/apparmor/features ] ; then
KF=`read_features_dir /sys/kernel/security/apparmor/features`
else
read -r -d "" KF < /sys/kernel/security/apparmor/features || true
fi
[ "$CF" != "$KF" ] && echo -e "FAIL (feature text mismatch:\n cache '$CF'\nvs\n kernel '$KF')" && exit 1
echo "ok"
echo -n "Cache is loaded when it exists and features match: "
${APPARMOR_PARSER} $ARGS -v -r $basedir/$profile | grep -q 'Cached reload succeeded' || { echo "FAIL"; exit 1; }
echo "ok"
echo -n "Cache is not loaded when skipping is requested: "
${APPARMOR_PARSER} $ARGS -v --skip-read-cache -r $basedir/$profile | grep -q 'Replacement succeeded for' || { echo "FAIL"; exit 1; }
${APPARMOR_PARSER} $ARGS -v --skip-cache -r $basedir/$profile | grep -q 'Replacement succeeded for' || { echo "FAIL"; exit 1; }
echo "ok"
echo -n "Cache reading is skipped when features do not match cache: "
echo -n "monkey" > $basedir/cache/.features
${APPARMOR_PARSER} $ARGS -v -r $basedir/$profile | grep -q 'Replacement succeeded for' || { echo "FAIL"; exit 1; }
echo "ok"
echo -n "Cache writing is skipped when features do not match and not cleared: "
rm $basedir/cache/$profile
${APPARMOR_PARSER} $ARGS -v --write-cache --skip-bad-cache -r $basedir/$profile | grep -q 'Replacement succeeded for' || { echo "FAIL"; exit 1; }
[ -f $basedir/cache/$profile ] && echo "FAIL ($basedir/cache/$profile exists)" && exit 1
echo "ok"
rm -f $basedir/cache/.features || true
rm -f $basedir/cache/$profile || true
echo -n "monkey" > $basedir/cache/.features
echo -n "monkey" > $basedir/cache/$profile
echo -n "monkey" > $basedir/cache/monkey
${APPARMOR_PARSER} $ARGS -v --write-cache -r $basedir/$profile | grep -q 'Replacement succeeded for' || { echo "Cache clear setup FAIL"; exit 1; }
echo -n "Cache clear updates features: "
echo -n "monkey" | diff -q $basedir/cache/.features - | grep -q 'differ' || { echo "FAIL"; exit 1; }
echo "ok"
echo -n "Cache clear writes updated profile: "
echo -n "monkey" | diff -q $basedir/cache/$profile - | grep -q 'differ' || { echo "FAIL"; exit 1; }
echo "ok"
echo -n "Cache clear cleans out all files: "
[ -f $basedir/cache/monkey ] && { echo "FAIL"; exit 1; }
echo "ok"
rm -f $basedir/cache/monkey
rm -f $basedir/cache/.features || true
rm -f $basedir/cache/$profile || true
echo -n "monkey" > $basedir/cache/.features
echo -n "monkey" > $basedir/cache/$profile
echo -n "monkey" > $basedir/cache/monkey
echo -n "Cache purge remove profiles unconditionally: "
${APPARMOR_PARSER} $ARGS -v --purge-cache -r $basedir/$profile || { echo "Cache purge setup FAIL"; exit 1; }
[ -f $basedir/cache/.features ] && { echo "FAIL"; exit 1; }
[ -f $basedir/cache/$profile ] && { echo "FAIL"; exit 1; }
[ -f $basedir/cache/monkey ] && { echo "FAIL"; exit 1; }
echo "ok"
echo -n "Profiles are cached when requested (again): "
rm -f $basedir/cache/.features || true
rm -f $basedir/cache/$profile || true
${APPARMOR_PARSER} $ARGS -q --write-cache -r $basedir/$profile
[ ! -f $basedir/cache/$profile ] && echo "FAIL ($basedir/cache/$profile does not exist)" && exit 1
echo "ok"
echo -n "Cache reading is skipped when profile is newer: "
sleep $timeout
touch $basedir/$profile
${APPARMOR_PARSER} $ARGS -v -r $basedir/$profile | grep -q 'Replacement succeeded for' || { echo "FAIL"; exit 1; }
echo "ok"
echo -n "Cache is used when cache is newer: "
sleep $timeout
touch $basedir/cache/$profile
${APPARMOR_PARSER} $ARGS -v -r $basedir/$profile | grep -q 'Cached reload succeeded' || { echo "FAIL"; exit 1; }
echo "ok"
echo -n "Cache reading is skipped when parser is newer: "
mkdir $basedir/parser
cp ${APPARMOR_PARSER} $basedir/parser/
$basedir/parser/apparmor_parser $ARGS -v -r $basedir/$profile | grep -q 'Replacement succeeded for' || { echo "FAIL"; exit 1; }
echo "ok"
echo -n "Cache reading is skipped when parser in \$PATH is newer: "
(PATH=$basedir/parser/ /bin/sh -c "apparmor_parser $ARGS -v -r $basedir/$profile") | grep -q 'Replacement succeeded for' || { echo "FAIL"; exit 1; }
echo "ok"
echo -n "Profiles are cached in alternate location when requested: "
${APPARMOR_PARSER} $ARGS -q --write-cache --cache-loc $altcachedir -r $basedir/$profile
[ ! -f $altcachedir/$profile ] && echo "FAIL ($altcachedir/$profile does not exist)" && exit 1
echo "ok"
echo -n "Cache is loaded from alt location when it exists and features match: "
${APPARMOR_PARSER} $ARGS -v -r $basedir/$profile --cache-loc $altcachedir | grep -q 'Cached reload succeeded' || { echo "FAIL"; exit 1; }
echo "ok"

View File

@@ -104,13 +104,21 @@ verify_binary_equality "dbus send + receive" \
"/t { dbus rw, }" \
verify_binary_equality "dbus all accesses" \
"/t { dbus (send, receive, bind), }" \
"/t { dbus (read, write, bind), }" \
"/t { dbus (r, w, bind), }" \
"/t { dbus (rw, bind), }" \
"/t { dbus (send, receive, bind, eavesdrop), }" \
"/t { dbus (read, write, bind, eavesdrop), }" \
"/t { dbus (r, w, bind, eavesdrop), }" \
"/t { dbus (rw, bind, eavesdrop), }" \
"/t { dbus (), }" \
"/t { dbus, }" \
verify_binary_equality "dbus implied accesses with a bus conditional" \
"/t { dbus (send, receive, bind, eavesdrop) bus=session, }" \
"/t { dbus (read, write, bind, eavesdrop) bus=session, }" \
"/t { dbus (r, w, bind, eavesdrop) bus=session, }" \
"/t { dbus (rw, bind, eavesdrop) bus=session, }" \
"/t { dbus () bus=session, }" \
"/t { dbus bus=session, }" \
verify_binary_equality "dbus implied accesses for services" \
"/t { dbus bind name=com.foo, }" \
"/t { dbus name=com.foo, }"
@@ -141,12 +149,12 @@ verify_binary_equality "dbus element parsing" \
verify_binary_equality "dbus access parsing" \
"/t { dbus, }" \
"/t { dbus (), }" \
"/t { dbus (send, receive, bind), }" \
"/t { dbus (send receive bind), }" \
"/t { dbus (send, receive bind), }" \
"/t { dbus (send,receive,bind), }" \
"/t { dbus (send,receive,,,,,,,,,,,,,,,,bind), }" \
"/t { dbus (send,send,send,send send receive,bind), }" \
"/t { dbus (send, receive, bind, eavesdrop), }" \
"/t { dbus (send receive bind eavesdrop), }" \
"/t { dbus (send, receive bind, eavesdrop), }" \
"/t { dbus (send,receive,bind,eavesdrop), }" \
"/t { dbus (send,receive,,,,,,,,,,,,,,,,bind,eavesdrop), }" \
"/t { dbus (send,send,send,send send receive,bind eavesdrop), }" \
verify_binary_equality "dbus variable expansion" \
"/t { dbus (send, receive) path=/com/foo member=spork interface=org.foo peer=(name=com.foo label=/com/foo), }" \
@@ -162,12 +170,19 @@ verify_binary_equality "dbus variable expansion" \
verify_binary_equality "dbus variable expansion, multiple values/rules" \
"/t { dbus (send, receive) path=/com/foo, dbus (send, receive) path=/com/bar, }" \
"/t { dbus (send, receive) path=/com/{foo,bar}, }" \
"/t { dbus (send, receive) path={/com/foo,/com/bar}, }" \
"@{FOO}=foo
/t { dbus (send, receive) path=/com/@{FOO}, dbus (send, receive) path=/com/bar, }" \
"@{FOO}=foo bar
/t { dbus (send, receive) path=/com/@{FOO}, }" \
"@{FOO}=bar foo
/t { dbus (send, receive) path=/com/@{FOO}, }"
/t { dbus (send, receive) path=/com/@{FOO}, }" \
"@{FOO}={bar,foo}
/t { dbus (send, receive) path=/com/@{FOO}, }" \
"@{FOO}=foo
@{BAR}=bar
/t { dbus (send, receive) path=/com/{@{FOO},@{BAR}}, }" \
verify_binary_equality "dbus variable expansion, ensure rule de-duping occurs" \
"/t { dbus (send, receive) path=/com/foo, dbus (send, receive) path=/com/bar, }" \
@@ -177,6 +192,29 @@ verify_binary_equality "dbus variable expansion, ensure rule de-duping occurs" \
"@{FOO}=bar foo bar foo
/t { dbus (send, receive) path=/com/@{FOO}, dbus (send, receive) path=/com/@{FOO}, }"
verify_binary_equality "dbus minimization with all perms" \
"/t { dbus, }" \
"/t { dbus bus=session, dbus, }" \
"/t { dbus (send, receive, bind, eavesdrop), dbus, }"
verify_binary_equality "dbus minimization with bind" \
"/t { dbus bind, }" \
"/t { dbus bind bus=session, dbus bind, }" \
"/t { dbus bind bus=system name=com.foo, dbus bind, }"
verify_binary_equality "dbus minimization with send and a bus conditional" \
"/t { dbus send bus=system, }" \
"/t { dbus send bus=system path=/com/foo interface=com.foo member=bar, dbus send bus=system, }" \
"/t { dbus send bus=system peer=(label=/usr/bin/foo), dbus send bus=system, }"
verify_binary_equality "dbus minimization with an audit modifier" \
"/t { audit dbus eavesdrop, }" \
"/t { audit dbus eavesdrop bus=session, audit dbus eavesdrop, }"
verify_binary_equality "dbus minimization with a deny modifier" \
"/t { deny dbus send bus=system peer=(name=com.foo), }" \
"/t { deny dbus send bus=system peer=(name=com.foo label=/usr/bin/foo), deny dbus send bus=system peer=(name=com.foo), }" \
if [ $fails -ne 0 -o $errors -ne 0 ]
then
printf "ERRORS: %d\nFAILS: %d\n" $errors $fails 2>&1

View File

@@ -49,11 +49,15 @@ gen_files("message-rules", "PASS", \@quantifier, \@msg_perms, \@session,
[""], \@path, \@interface, \@member, \@peer);
gen_files("service-rules", "PASS", \@quantifier, ["bind"], \@session,
\@name, [""], [""], [""], [""]);
gen_files("eavesdrop-rules", "PASS", \@quantifier, ["eavesdrop"], \@session,
[""], [""], [""], [""], [""]);
gen_file("sloppy-formatting", "PASS", "", "(send , receive )", "bus=session",
"", "path =\"/foo/bar\"", "interface = com.foo", " member=bar",
"peer =( label= /usr/bin/app name =\"com.foo\")");
gen_file("sloppy-formatting", "PASS", "", "bind", "bus =session",
"name= com.foo", "", "", "", "");
gen_file("sloppy-formatting", "PASS", "", "eavesdrop", "bus = system",
"", "", "", "", "");
# Don't use the first element, which is empty, from each array since all empty
# conditionals would PASS but we want all FAILs
@@ -73,6 +77,8 @@ gen_files("service-incompat", "FAIL", \@quantifier, ["bind"], \@session,
\@name, [""], [""], \@member, [""]);
gen_files("service-incompat", "FAIL", \@quantifier, ["bind"], \@session,
\@name, [""], [""], [""], \@peer);
gen_files("eavesdrop-incompat", "FAIL", \@quantifier, ["eavesdrop"], \@session,
\@name, \@path, \@interface, \@member, \@peer);
gen_files("pairing-unsupported", "FAIL", \@quantifier, ["send", "bind"],
\@session, ["name=sn", "label=sl"], [""], [""], [""],

View File

@@ -0,0 +1,8 @@
#
#=DESCRIPTION regex with empty character class (brace)
#=EXRESULT FAIL
#
/usr/bin/foo {
/alpha/[]beta rw,
}

View File

@@ -0,0 +1,7 @@
#
#=Description basic file rule w/alternations
#=EXRESULT PASS
#
/usr/bin/foo {
file /a/b/c/**{cache,data,download,/ext,fileadmin,files,images,joomla,moodledata/sessions}/** rw,
}

View File

@@ -0,0 +1,7 @@
#
#=Description basic file rule w/nested alternations
#=EXRESULT PASS
#
/usr/bin/foo {
file /a/b/c/**{cache,data,download,/ext,file{admin,s},images,joomla,moodledata/sessions}/** rw,
}

View File

@@ -0,0 +1,7 @@
#
#=Description basic file rule w/alternations
#=EXRESULT PASS
#
/usr/bin/foo {
/a/b/c/**{cache,data,download,/ext,fileadmin,files,images,joomla,moodledata/sessions}/** rw,
}

View File

@@ -0,0 +1,7 @@
#
#=Description basic file rule w/nested alternations
#=EXRESULT PASS
#
/usr/bin/foo {
/a/b/c/**{cache,data,download,/ext,file{admin,s},images,joomla,moodledata/sessions}/** rw,
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,7 @@
#
#=Description basic file rule w/alternations
#=EXRESULT PASS
#
/usr/bin/foo {
owner /a/b/c/**{cache,data,download,/ext,fileadmin,files,images,joomla,moodledata/sessions}/** rw,
}

View File

@@ -0,0 +1,7 @@
#
#=Description basic file rule w/nested alternations
#=EXRESULT PASS
#
/usr/bin/foo {
owner /a/b/c/**{cache,data,download,/ext,file{admin,s},images,joomla,moodledata/sessions}/** rw,
}

View File

@@ -0,0 +1,8 @@
#=DESCRIPTION variable w/part of an alternation included
#=EXRESULT PASS
@{BAR}={bar,baz,blort
/does/not/exist {
/does/not/@{BAR},exist,notexist} r,
}

View File

@@ -0,0 +1,8 @@
#=DESCRIPTION variable w/part of an alternation included
#=EXRESULT PASS
@{BAR}=bar,baz,blort}
/does/not/exist {
/does/not/{exist,notexist@{BAR}/meep r,
}

View File

@@ -0,0 +1,8 @@
#=DESCRIPTION variable w/part of an alternation included
#=EXRESULT PASS
@{BAR}=bar,baz,blort
/does/not/exist {
/does/not/{exist@{BAR}notexist}/meep r,
}

View File

@@ -0,0 +1,14 @@
#=DESCRIPTION reference variables in dbus rules, var containing alternation
#=EXRESULT PASS
@{BUSES}=session system
@{TLDS}=com org
@{MEMBERS}={Get,Set}
/does/not/exist {
dbus (send, receive)
bus=@{BUSES}
path=/@{TLDS}/foo
member=@{MEMBERS}.bar,
}

View File

@@ -0,0 +1,14 @@
#=DESCRIPTION reference variables in dbus rules, nested embedded alternations
#=EXRESULT PASS
@{BUSES}=session system
@{TLDS}=com org
@{MEMBERS}={Get,Set}
/does/not/exist {
dbus (send, receive)
bus=@{BUSES}
path=/@{TLDS}/foo
member={@{MEMBERS}.bar,List.baz},
}

View File

@@ -0,0 +1,13 @@
#=DESCRIPTION reference variables in dbus rules, embedded within alternation
#=EXRESULT PASS
@{TLDS}=com org
@{DOMAINS}=gnome freedesktop
/does/not/exist {
dbus (send, receive)
bus=session
path={/@{TLDS}/foo,/com/@{DOMAINS}}
interface=@{TLDS}.freedesktop
peer=(name=@{TLDS}.freedesktop label=/@{TLDS}/freedesktop),
}

View File

@@ -0,0 +1,18 @@
#=DESCRIPTION reference variables in dbus rules, multiple expansions
#=EXRESULT PASS
@{BUSES}=session system
@{TLDS}=com org
@{DOMAINS}=gnome freedesktop
@{FOO}=bar baz
@{BAR}=@{FOO}/blort
@{MEMBERS}=Get Set
/does/not/exist {
dbus (send, receive)
bus=@{BUSES}
path=/@{TLDS}/foo
member=@{MEMBERS}.bar
interface=@{TLDS}.@{DOMAINS}
peer=(name=@{TLDS}.@{DOMAINS} label=/@{TLDS}/@{BAR}),
}

View File

@@ -45,15 +45,10 @@ VALGRIND_SUPPRESSIONS = '''
valgrind-serialize_profile-obsessive-overreads
Memcheck:Addr4
fun:_Z*sd_serialize_profile*
fun:_Z*sd_serialize_codomain*
fun:_Z*load_codomain*
fun:_Z*__load_flattened_hat*
...
fun:twalk
fun:_Z*load_flattened_hats*
fun:_Z*sd_serialize_codomain*
fun:_Z*load_codomain*
fun:_Z*__load_policy*
fun:_Z*__sd_serialize_profile*
fun:_Z*load_profile*
fun:_Z*load_policy_list*
}'''

View File

@@ -12,7 +12,7 @@
# discoverable system configuration for non-local cupsd
/etc/cups/client.conf r,
# client should be able to talk the local cupsd
/{,var/}run/cups/cups.sock w,
/{,var/}run/cups/cups.sock rw,
# client should be able to read user-specified cups configuration
owner @{HOME}/.cups/client.conf r,
owner @{HOME}/.cups/lpoptions r,

View File

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

View File

@@ -0,0 +1,12 @@
# vim:syntax=apparmor
# ------------------------------------------------------------------
#
# Copyright (C) 2013 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.
#
# ------------------------------------------------------------------
dbus bus=accessibility,

View File

@@ -14,3 +14,4 @@
# unique per-machine identifier
/etc/machine-id r,
/var/lib/dbus/machine-id r,
dbus bus=session,

View File

@@ -20,7 +20,7 @@
/usr/lib/@{multiarch}/krb5/plugins/preauth/ r,
/usr/lib/@{multiarch}/krb5/plugins/preauth/* mr,
/etc/krb5.keytab r,
/etc/krb5.keytab rk,
/etc/krb5.conf r,
# config files found via strings on libs

View File

@@ -10,4 +10,5 @@
/etc/ssl/openssl.cnf r,
/usr/share/ssl/openssl.cnf r,
@{PROC}/sys/crypto/fips_enabled r,

View File

@@ -19,6 +19,9 @@
/usr/share/p11-kit/modules/ r,
/usr/share/p11-kit/modules/* r,
# gnome-keyring pkcs11 module
owner /{,var/}run/user/[0-9]*/keyring*/pkcs11 rw,
# p11-kit also supports reading user configuration from ~/.pkcs11 depending
# on how /etc/pkcs11/pkcs11.conf is configured. This should generally not be
# included in this abstraction.

View File

@@ -9,6 +9,8 @@
audit deny @{HOME}/.ssh/** mrwkl,
audit deny @{HOME}/.gnome2_private/** mrwkl,
audit deny @{HOME}/.gnome2/keyrings/** mrwkl,
# don't allow access to any gnome-keyring modules
audit deny /{,var/}run/user/[0-9]*/keyring** mrwkl,
audit deny @{HOME}/.mozilla/** mrwkl,
audit deny @{HOME}/.config/chromium/** mrwkl,
audit deny @{HOME}/.{,mozilla-}thunderbird/** mrwkl,

View File

@@ -11,9 +11,12 @@
/etc/samba/* r,
/usr/share/samba/*.dat r,
/usr/share/samba/codepages/{lowcase,upcase,valid}.dat r,
/var/cache/samba/ w,
/var/lib/samba/**.tdb rwk,
/var/log/samba/cores/ rw,
/var/log/samba/cores/** rw,
/var/log/samba/log.* w,
/{,var/}run/samba/ w,
/{,var/}run/samba/*.tdb rw,

View File

@@ -17,3 +17,5 @@
/usr/share/ssl/certs/ca-bundle.crt r,
/usr/local/share/ca-certificates/ r,
/usr/local/share/ca-certificates/** r,
/var/lib/ca-certificates/ r,
/var/lib/ca-certificates/** r,

View File

@@ -11,7 +11,9 @@
/usr/sbin/nmbd mr,
/var/cache/samba/gencache.tdb rwk,
/var/{cache,lib}/samba/browse.dat* rw,
/var/{cache,lib}/samba/gencache.dat rw,
/var/{cache,lib}/samba/wins.dat* rw,
/var/{cache,lib}/samba/smb_krb5/ rw,
/var/{cache,lib}/samba/smb_krb5/krb5.conf* rw,

View File

@@ -29,15 +29,20 @@
/usr/lib*/samba/vfs/*.so mr,
/usr/lib*/samba/charset/*.so mr,
/usr/lib*/samba/auth/script.so mr,
/usr/lib*/samba/{lowercase,upcase,valid}.dat r,
/usr/lib*/samba/pdb/*.so mr,
/usr/lib*/samba/{lowcase,upcase,valid}.dat r,
/usr/sbin/smbd mr,
/usr/sbin/smbldap-useradd Px,
/var/cache/samba/** rwk,
/var/cache/samba/printing/printers.tdb mrw,
/var/lib/samba/** rwk,
/var/lib/sss/mc/passwd r,
/var/lib/sss/pubconf/kdcinfo.* r,
/{,var/}run/cups/cups.sock rw,
/{,var/}run/dbus/system_bus_socket rw,
/{,var/}run/samba/** rk,
/{,var/}run/samba/ncalrpc/ rw,
/{,var/}run/samba/ncalrpc/** rw,
/{,var/}run/samba/smbd.pid rw,
/var/spool/samba/** rw,

View File

@@ -1,10 +1,43 @@
# Copyright (C) 2002-2005 Novell/SUSE
# Copyright (C) 2013 Canonical, Ltd
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, version 2 of the
# License.
ifdef USE_SYSTEM
# use the system libapparmor headers and library
LIBAPPARMOR = $(shell if pkg-config --exists libapparmor ; then \
pkg-config --silence-errors --libs libapparmor ; \
elif ldconfig -p | grep -q libapparmor\.so$$ ; then \
echo -lapparmor ; \
fi )
ifeq ($(strip $(LIBAPPARMOR)),)
ERROR_MESSAGE = Unable to find libapparmor installed on this system; either \
install libapparmor devel packages, set the LIBAPPARMOR variable \
manually, or build against in-tree libapparmor)
endif # LIBAPPARMOR not set
LDLIBS += $(LIBAPPARMOR)
else # !USE_SYSTEM
# use in-tree versions
LIBAPPARMOR_SRC := ../../../libraries/libapparmor/
LIBAPPARMOR_INCLUDE = $(LIBAPPARMOR_SRC)/include
LIBAPPARMOR_PATH := $(LIBAPPARMOR_SRC)/src/.libs/
ifeq ($(realpath $(LIBAPPARMOR_PATH)/libapparmor.a),)
ERROR_MESSAGE = $(LIBAPPARMOR_PATH)/libapparmor.a is missing; either build against \
the in-tree libapparmor by building it first and then trying again \
(see the top-level README for help) or build against the system \
libapparmor by adding USE_SYSTEM=1 to your make command.)
endif
CFLAGS += -L$(LIBAPPARMOR_PATH) -I$(LIBAPPARMOR_INCLUDE)
LDLIBS += -Wl,-Bstatic -lapparmor -Wl,-Bdynamic -lpthread
endif # USE_SYSTEM
CFLAGS += -Wall -Wstrict-prototypes
SRC=access.c \
introspect.c \
changeprofile.c \
@@ -23,6 +56,7 @@ SRC=access.c \
chown.c \
clone.c \
coredump.c \
dbus_eavesdrop.c \
dbus_message.c \
dbus_service.c \
deleted.c \
@@ -81,13 +115,6 @@ ifneq (,$(findstring $(shell uname -i),i386 i486 i586 i686 x86 x86_64))
SRC+=syscall_ioperm.c syscall_iopl.c
endif
LIBAPPARMOR:=$(shell if ldconfig -p | grep -q libapparmor\.so ; then \
echo -lapparmor ; \
fi )
CFLAGS+=-Wall -Wstrict-prototypes
LDLIBS+=$(LIBAPPARMOR)
EXEC=$(SRC:%.c=%)
TESTS=access \
@@ -101,6 +128,7 @@ TESTS=access \
chdir \
clone \
coredump \
dbus_eavesdrop \
dbus_message \
dbus_service \
deleted \
@@ -144,7 +172,15 @@ TESTS=access \
# Tests that can crash the kernel should be placed here
RISKY_TESTS=
all: $(EXEC) changehat.h
.PHONY: libapparmor_check
.SILENT: libapparmor_check
libapparmor_check:
@if [ -n "$(ERROR_MESSAGE)" ] ; then \
echo "$(ERROR_MESSAGE)" 1>&2 ; \
return 1 ; \
fi
all: libapparmor_check $(EXEC) changehat.h
changehat_pthread: changehat_pthread.c changehat.h
${CC} ${CFLAGS} ${LDFLAGS} $< -o $@ ${LDLIBS} -pthread
@@ -152,6 +188,9 @@ changehat_pthread: changehat_pthread.c changehat.h
dbus_common.o: dbus_common.c dbus_common.h
${CC} ${CFLAGS} ${LDFLAGS} $^ -c ${LDLIBS} $(shell pkg-config --cflags --libs dbus-1)
dbus_eavesdrop: dbus_eavesdrop.c dbus_common.o
${CC} ${CFLAGS} ${LDFLAGS} $^ -o dbus_eavesdrop ${LDLIBS} $(shell pkg-config --cflags --libs dbus-1)
dbus_message: dbus_message.c dbus_common.o
${CC} ${CFLAGS} ${LDFLAGS} $^ -o dbus_message ${LDLIBS} $(shell pkg-config --cflags --libs dbus-1)

View File

@@ -0,0 +1,148 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* dbus_service.c Utility program to attempt to eavesdrop on a bus
*
* Copyright (C) 2003 Philip Blundell <philb@gnu.org>
* Copyright (C) 2013 Canonical, Ltd.
*
* Originally dbus-send.c from the dbus package. It has been heavily modified
* to work within the regression test framework.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "dbus_common.h"
DBusConnection *connection = NULL;
DBusError error;
DBusBusType type = DBUS_BUS_SESSION;
const char *address = NULL;
int session_or_system = FALSE;
static void usage(void)
{
fprintf(stderr, "Usage: dbus_eavesdrop [ADDRESS]\n\n"
" ADDRESS\t\t--system, --session (default), or --address=ADDR\n");
}
static int do_eavesdrop(void)
{
dbus_bus_add_match(connection, "eavesdrop=true,type='method_call'",
&error);
if (dbus_error_is_set(&error)) {
fprintf(stderr, "FAIL: %s: %s\n", error.name, error.message);
dbus_error_free(&error);
return 1;
}
return 0;
}
int main(int argc, char *argv[])
{
int i, rc;
if (argc < 2) {
usage();
rc = 1;
goto out;
}
for (i = 1; i < argc; i++) {
char *arg = argv[i];
if (strcmp(arg, "--system") == 0) {
type = DBUS_BUS_SYSTEM;
session_or_system = TRUE;
} else if (strcmp(arg, "--session") == 0) {
type = DBUS_BUS_SESSION;
session_or_system = TRUE;
} else if (strstr(arg, "--address") == arg) {
address = strchr(arg, '=');
if (address == NULL) {
fprintf(stderr,
"FAIL: \"--address=\" requires an ADDRESS\n");
usage();
rc = 1;
goto out;
} else {
address = address + 1;
}
} else if (!strcmp(arg, "--help")) {
usage();
rc = 0;
goto out;
} else {
usage();
rc = 1;
goto out;
}
}
if ((session_or_system == FALSE && address == NULL) || i < argc) {
usage();
rc = 1;
goto out;
}
if (session_or_system && (address != NULL)) {
fprintf(stderr,
"FAIL: \"--address\" may not be used with \"--system\" or \"--session\"\n");
usage();
rc = 1;
goto out;
}
dbus_error_init(&error);
if (address != NULL)
connection = dbus_connection_open(address, &error);
else
connection = dbus_bus_get(type, &error);
if (connection == NULL) {
fprintf(stderr,
"FAIL: Failed to open connection to \"%s\" message bus: %s\n",
address ? address :
((type == DBUS_BUS_SYSTEM) ? "system" : "session"),
error.message);
dbus_error_free(&error);
rc = 1;
goto out;
} else if (address != NULL)
dbus_bus_register(connection, &error);
rc = do_eavesdrop();
out:
if (connection)
dbus_connection_unref(connection);
if (rc == 0)
printf("PASS\n");
exit(rc);
}

View File

@@ -0,0 +1,77 @@
#! /bin/bash
# Copyright (C) 2013 Canonical, Ltd.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, version 2 of the
# License.
#=NAME dbus_eavesdrop
#=DESCRIPTION
# This test verifies that dbus eavesdropping is restricted for confined
# processes.
#=END
pwd=`dirname $0`
pwd=`cd $pwd ; /bin/pwd`
bin=$pwd
. $bin/prologue.inc
required_features dbus
. $bin/dbus.inc
args="--session"
start_bus
# Make sure we can eavesdrop unconfined
settest dbus_eavesdrop
runchecktest "eavesdrop (unconfined)" pass $args
# Make sure we get denials when confined but not allowed
genprofile
runchecktest "eavesdrop (confined w/o dbus perms)" fail $args
gendbusprofile "dbus send,"
runchecktest "eavesdrop (confined w/ only send allowed)" fail $args
gendbusprofile "dbus eavesdrop,"
runchecktest "eavesdrop (confined w/ only eavesdrop allowed)" fail $args
# Make sure we're okay when confined with appropriate permissions
gendbusprofile "dbus,"
runchecktest "eavesdrop (dbus allowed)" pass $args
gendbusprofile "dbus (send eavesdrop),"
runchecktest "eavesdrop (send, eavesdrop allowed)" pass $args
gendbusprofile "dbus (send eavesdrop) bus=session,"
runchecktest "eavesdrop (send, eavesdrop allowed w/ bus conditional)" pass $args
gendbusprofile "dbus send bus=session path=/org/freedesktop/DBus \
interface=org.freedesktop.DBus \
member=Hello, \
dbus send bus=session path=/org/freedesktop/DBus \
interface=org.freedesktop.DBus \
member=AddMatch, \
dbus eavesdrop bus=session,"
runchecktest "eavesdrop (send, eavesdrop allowed w/ bus and send member conditionals)" pass $args
gendbusprofile "dbus send, \
audit dbus eavesdrop,"
runchecktest "eavesdrop (send allowed, eavesdrop audited)" pass $args
# Make sure we're denied when confined without appropriate conditionals
gendbusprofile "dbus send bus=session, \
dbus eavesdrop bus=system,"
runchecktest "eavesdrop (wrong bus)" fail $args
gendbusprofile "dbus send, \
deny dbus eavesdrop,"
runchecktest "eavesdrop (send allowed, eavesdrop denied)" fail $args

View File

@@ -1,5 +1,6 @@
/*
* Copyright (C) 2002-2005 Novell/SUSE
* Copyright (C) 2013 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -69,14 +70,14 @@ int main(int argc, char *argv[])
if (rc != strlen(profile) + strlen(mode) + 4) {
/* rc includes mode. + 2 null term + 1 ( + 1 space */
fprintf(stderr,
"FAIL: expected return len %d != actual %d\n",
"FAIL: expected return len %zd != actual %d\n",
strlen(profile) + strlen(mode) + 4, rc);
exit(1);
}
} else if (rc != strlen(profile) + 1) {
/* rc includes null termination */
fprintf(stderr,
"FAIL: expected return len %d != actual %d\n",
"FAIL: expected return len %zd != actual %d\n",
strlen(profile) + 1, rc);
exit(1);
}