mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-09-04 16:25:10 +00:00
Compare commits
84 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
b37bd8a1aa | ||
|
ead71a306a | ||
|
aa45be1c10 | ||
|
c1c5192532 | ||
|
2b9260f27a | ||
|
4063647a5f | ||
|
b4048cf3de | ||
|
8a475341e8 | ||
|
5ca6986b43 | ||
|
4b58cf3bc4 | ||
|
a373b4ee93 | ||
|
8d5569f20b | ||
|
5390777e45 | ||
|
79240e7ddd | ||
|
494daee246 | ||
|
194cbfa94c | ||
|
9452e1e2af | ||
|
1556f782e3 | ||
|
7d1ff607fe | ||
|
242ece320a | ||
|
dc1d8e5253 | ||
|
0ac23ee34a | ||
|
5bc15cda41 | ||
|
9ebb1913bd | ||
|
720f6624e6 | ||
|
387de4458f | ||
|
38a69f5ebc | ||
|
7d84c61b6c | ||
|
f836ebd42b | ||
|
52b6aeb04c | ||
|
475a9bc691 | ||
|
0f7bf53afb | ||
|
8dcd54e365 | ||
|
097eb4258f | ||
|
9bc15eb6b8 | ||
|
9d6f7f53cb | ||
|
c1ae887576 | ||
|
0ec6ce96d2 | ||
|
8c19eb5521 | ||
|
21a41deabe | ||
|
576e8fe33b | ||
|
3c928c04e1 | ||
|
37b872b155 | ||
|
5ab8b7a483 | ||
|
b813f4ba53 | ||
|
05ab11fec4 | ||
|
2d7ba0871f | ||
|
c98b26069a | ||
|
70dc81c4fd | ||
|
1b68baf7a3 | ||
|
6af7faa2b7 | ||
|
a1529a16bd | ||
|
321a2c1dcb | ||
|
735ef5d32b | ||
|
9428498d90 | ||
|
3ea1e541c7 | ||
|
29b0634f34 | ||
|
586222c94e | ||
|
232b51504c | ||
|
df099620dd | ||
|
22d647ecb1 | ||
|
07b0886796 | ||
|
9da31bf281 | ||
|
c5ff27a91b | ||
|
cf4afcb860 | ||
|
75a186fa9f | ||
|
05bef291d7 | ||
|
76f71f7d84 | ||
|
34f2c1c6ea | ||
|
67dae2f1cf | ||
|
bbaaa00249 | ||
|
9ed8789918 | ||
|
f45628d749 | ||
|
602decfbfc | ||
|
9aa1efd744 | ||
|
c51a68eaaf | ||
|
49b739b184 | ||
|
53d071adf5 | ||
|
70cda06789 | ||
|
e8ffc1c4e8 | ||
|
09c93be47c | ||
|
ac8d886645 | ||
|
ec1dda24d0 | ||
|
e7e9053598 |
29
.bzrignore
29
.bzrignore
@@ -18,13 +18,13 @@ parser/*.8
|
||||
parser/*.7.html
|
||||
parser/*.5.html
|
||||
parser/*.8.html
|
||||
parser/common
|
||||
parser/apparmor_parser
|
||||
parser/libapparmor_re/regexp.cc
|
||||
parser/techdoc.aux
|
||||
parser/techdoc.log
|
||||
parser/techdoc.pdf
|
||||
parser/techdoc.toc
|
||||
profiles/apparmor.d/local/*.*
|
||||
libraries/libapparmor/Makefile
|
||||
libraries/libapparmor/Makefile.in
|
||||
libraries/libapparmor/aclocal.m4
|
||||
@@ -41,32 +41,20 @@ libraries/libapparmor/install-sh
|
||||
libraries/libapparmor/libtool
|
||||
libraries/libapparmor/ltmain.sh
|
||||
libraries/libapparmor/missing
|
||||
libraries/libapparmor/test-driver
|
||||
libraries/libapparmor/ylwrap
|
||||
libraries/libapparmor/doc/Makefile
|
||||
libraries/libapparmor/doc/Makefile.in
|
||||
libraries/libapparmor/doc/*.2
|
||||
libraries/libapparmor/doc/aa_*.3
|
||||
libraries/libapparmor/include/Makefile
|
||||
libraries/libapparmor/include/Makefile.in
|
||||
libraries/libapparmor/include/sys/Makefile
|
||||
libraries/libapparmor/include/sys/Makefile.in
|
||||
libraries/libapparmor/src/.deps
|
||||
libraries/libapparmor/src/.libs
|
||||
libraries/libapparmor/src/Makefile
|
||||
libraries/libapparmor/src/Makefile.in
|
||||
libraries/libapparmor/src/af_protos.h
|
||||
libraries/libapparmor/src/change_hat.lo
|
||||
libraries/libapparmor/src/features.lo
|
||||
libraries/libapparmor/src/grammar.lo
|
||||
libraries/libapparmor/src/kernel.lo
|
||||
libraries/libapparmor/src/kernel_interface.lo
|
||||
libraries/libapparmor/src/libaalogparse.lo
|
||||
libraries/libapparmor/src/libimmunix_warning.lo
|
||||
libraries/libapparmor/src/policy_cache.lo
|
||||
libraries/libapparmor/src/private.lo
|
||||
libraries/libapparmor/src/scanner.lo
|
||||
libraries/libapparmor/src/libapparmor.pc
|
||||
libraries/libapparmor/src/libapparmor.la
|
||||
libraries/libapparmor/src/libimmunix.la
|
||||
libraries/libapparmor/src/grammar.c
|
||||
@@ -82,20 +70,12 @@ libraries/libapparmor/swig/perl/Makefile
|
||||
libraries/libapparmor/swig/perl/Makefile.PL
|
||||
libraries/libapparmor/swig/perl/Makefile.in
|
||||
libraries/libapparmor/swig/perl/Makefile.perl
|
||||
libraries/libapparmor/swig/perl/Makefile.perle
|
||||
libraries/libapparmor/swig/perl/MYMETA.json
|
||||
libraries/libapparmor/swig/perl/MYMETA.yml
|
||||
libraries/libapparmor/swig/perl/blib
|
||||
libraries/libapparmor/swig/perl/libapparmor_wrap.c
|
||||
libraries/libapparmor/swig/perl/pm_to_blib
|
||||
libraries/libapparmor/swig/python/__init__.py
|
||||
libraries/libapparmor/swig/python/build/
|
||||
libraries/libapparmor/swig/python/libapparmor_wrap.c
|
||||
libraries/libapparmor/swig/python/Makefile
|
||||
libraries/libapparmor/swig/python/Makefile.in
|
||||
libraries/libapparmor/swig/python/setup.py
|
||||
libraries/libapparmor/swig/python/test/Makefile
|
||||
libraries/libapparmor/swig/python/test/Makefile.in
|
||||
libraries/libapparmor/swig/ruby/Makefile
|
||||
libraries/libapparmor/swig/ruby/Makefile.in
|
||||
libraries/libapparmor/testsuite/.deps
|
||||
@@ -114,6 +94,10 @@ libraries/libapparmor/testsuite/libaalogparse.test/Makefile
|
||||
libraries/libapparmor/testsuite/libaalogparse.test/Makefile.in
|
||||
libraries/libapparmor/testsuite/test_multi/out
|
||||
changehat/mod_apparmor/.libs
|
||||
changehat/mod_apparmor/common
|
||||
changehat/pam_apparmor/common
|
||||
changehat/tomcat_apparmor/common
|
||||
utils/common
|
||||
utils/*.8
|
||||
utils/*.8.html
|
||||
utils/*.5
|
||||
@@ -181,5 +165,4 @@ tests/regression/apparmor/unix_fd_server
|
||||
tests/regression/apparmor/unlink
|
||||
tests/regression/apparmor/xattrs
|
||||
tests/regression/apparmor/coredump
|
||||
**/__pycache__/
|
||||
*.orig
|
||||
./utils/apparmor/__pycache__
|
||||
|
10
Makefile
10
Makefile
@@ -1,12 +1,8 @@
|
||||
#
|
||||
#
|
||||
.PHONY: all
|
||||
all:
|
||||
@echo "*** See README for information how to build AppArmor ***"
|
||||
exit 1
|
||||
OVERRIDE_TARBALL=yes
|
||||
|
||||
COMMONDIR=common
|
||||
include ${COMMONDIR}/Make.rules
|
||||
include common/Make.rules
|
||||
|
||||
DIRS=parser \
|
||||
profiles \
|
||||
@@ -18,7 +14,7 @@ DIRS=parser \
|
||||
|
||||
#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/2.10
|
||||
REPO_URL?=https://code.launchpad.net/~apparmor-dev/apparmor/2.9
|
||||
# alternate possibilities to export from
|
||||
#REPO_URL=.
|
||||
#REPO_URL="bzr+ssh://bazaar.launchpad.net/~sbeattie/+junk/apparmor-dev/"
|
||||
|
@@ -17,7 +17,13 @@ NAME:=apache2-mod_apparmor
|
||||
all:
|
||||
COMMONDIR=../../common/
|
||||
|
||||
include $(COMMONDIR)/Make.rules
|
||||
include common/Make.rules
|
||||
|
||||
COMMONDIR_EXISTS=$(strip $(shell [ -d ${COMMONDIR} ] && echo true))
|
||||
ifeq ($(COMMONDIR_EXISTS), true)
|
||||
common/Make.rules: $(COMMONDIR)/Make.rules
|
||||
ln -sf $(COMMONDIR) .
|
||||
endif
|
||||
|
||||
TARGET:=mod_apparmor.so
|
||||
MANPAGES=mod_apparmor.8
|
||||
@@ -86,9 +92,9 @@ install: ${TARGET} ${MANPAGES}
|
||||
make install_manpages DESTDIR=${DESTDIR}
|
||||
|
||||
.PHONY: clean
|
||||
clean: pod_clean
|
||||
clean: _clean
|
||||
rm -rf .libs
|
||||
rm -f *.la *.lo *.so *.o *.slo
|
||||
rm -f *.la *.lo *.so *.o *.slo Make.rules
|
||||
|
||||
.PHONY: check
|
||||
check: check_pod_files
|
||||
|
@@ -137,7 +137,7 @@ aa_enter_hat(request_rec *r)
|
||||
ap_get_module_config(r->server->module_config, &apparmor_module);
|
||||
const char *aa_hat_array[6] = { NULL, NULL, NULL, NULL, NULL, NULL };
|
||||
int i = 0;
|
||||
char *aa_label, *aa_mode, *aa_hat;
|
||||
char *aa_con, *aa_mode, *aa_hat;
|
||||
const char *vhost_uri;
|
||||
|
||||
debug_dump_uri(r);
|
||||
@@ -201,14 +201,14 @@ aa_enter_hat(request_rec *r)
|
||||
/* Check to see if a defined AAHatName or AADefaultHatName would
|
||||
* apply, but wasn't the hat we landed up in; report a warning if
|
||||
* that's the case. */
|
||||
aa_ret = aa_getcon(&aa_label, &aa_mode);
|
||||
aa_ret = aa_getcon(&aa_con, &aa_mode);
|
||||
if (aa_ret < 0) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_WARNING, errno, r, "aa_getcon call failed");
|
||||
} else {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
|
||||
"AA checks: aa_getcon result is '%s', mode '%s'", aa_label, aa_mode);
|
||||
"AA checks: aa_getcon result is '%s', mode '%s'", aa_con, aa_mode);
|
||||
/* TODO: use libapparmor get hat_name fn here once it is implemented */
|
||||
aa_hat = strstr(aa_label, "//");
|
||||
aa_hat = strstr(aa_con, "//");
|
||||
if (aa_hat != NULL && strcmp(aa_mode, "enforce") == 0) {
|
||||
aa_hat += 2; /* skip "//" */
|
||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
|
||||
@@ -226,7 +226,7 @@ aa_enter_hat(request_rec *r)
|
||||
scfg->hat_name);
|
||||
}
|
||||
}
|
||||
free(aa_label);
|
||||
free(aa_con);
|
||||
}
|
||||
|
||||
return OK;
|
||||
|
@@ -16,8 +16,15 @@
|
||||
NAME=pam_apparmor
|
||||
all:
|
||||
COMMONDIR=../../common/
|
||||
MAKE_RULES=common/Make.rules
|
||||
|
||||
include $(COMMONDIR)/Make.rules
|
||||
include ${MAKE_RULES}
|
||||
|
||||
COMMONDIR_EXISTS=$(strip $(shell [ -d ${COMMONDIR} ] && echo true))
|
||||
ifeq ($(COMMONDIR_EXISTS), true)
|
||||
common/Make.rules: $(COMMONDIR)/Make.rules
|
||||
ln -sf $(COMMONDIR) .
|
||||
endif
|
||||
|
||||
ifdef USE_SYSTEM
|
||||
LIBAPPARMOR = $(shell if pkg-config --exists libapparmor ; then \
|
||||
@@ -53,7 +60,7 @@ libapparmor by adding USE_SYSTEM=1 to your make command.${nl}\
|
||||
AA_LINK_FLAGS = -L$(LIBAPPARMOR_PATH)
|
||||
AA_LDLIBS = -lapparmor
|
||||
endif
|
||||
EXTRA_CFLAGS=$(CFLAGS) $(CPPFLAGS) -fPIC -shared -Wall $(LIBAPPARMOR_INCLUDE)
|
||||
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
|
||||
@@ -80,5 +87,7 @@ install: $(NAME).so
|
||||
install -m 555 $(NAME).so $(SECDIR)/
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
clean: ${MAKE_RULES}
|
||||
rm -f core core.* *.so *.o *.s *.a *~
|
||||
rm -f ${NAME}-*.tar.gz Make.rules
|
||||
|
||||
|
@@ -111,7 +111,6 @@ int pam_sm_open_session(pam_handle_t *pamh, int flags,
|
||||
sizeof(magic_token));
|
||||
if (retval < 0) {
|
||||
pam_syslog(pamh, LOG_ERR, "Can't read from /dev/urandom\n");
|
||||
close(fd);
|
||||
return PAM_PERM_DENIED;
|
||||
}
|
||||
} while ((magic_token == 0) || (retval != sizeof(magic_token)));
|
||||
|
@@ -17,7 +17,13 @@ NAME = tomcat_apparmor
|
||||
all:
|
||||
COMMONDIR=../../../common/
|
||||
|
||||
include $(COMMONDIR)/Make.rules
|
||||
include common/Make.rules
|
||||
|
||||
COMMONDIR_EXISTS=$(strip $(shell [ -d ${COMMONDIR} ] && echo true))
|
||||
ifeq ($(COMMONDIR_EXISTS), true)
|
||||
common/Make.rules: $(COMMONDIR)/Make.rules
|
||||
ln -sf $(COMMONDIR) .
|
||||
endif
|
||||
|
||||
LIB = lib
|
||||
CATALINA_HOME = /usr/share/tomcat5
|
||||
@@ -28,6 +34,7 @@ all:
|
||||
|
||||
clean:
|
||||
ant clean
|
||||
rm -f tomcat_apparmor.spec ${NAME}-*.tar.gz Make.rules
|
||||
|
||||
install:
|
||||
install: $(SPECFILE)
|
||||
ant -Dversion=$(VERSION) -Drelease=$(MAN_RELEASE) -Dcatalina_home=${CATALINA_HOME} -Dinstall_lib=${LIB} install_jar install_jni
|
||||
|
@@ -17,7 +17,13 @@ NAME = tomcat_apparmor
|
||||
all:
|
||||
COMMONDIR=../../../common/
|
||||
|
||||
include $(COMMONDIR)/Make.rules
|
||||
include common/Make.rules
|
||||
|
||||
COMMONDIR_EXISTS=$(strip $(shell [ -d ${COMMONDIR} ] && echo true))
|
||||
ifeq ($(COMMONDIR_EXISTS), true)
|
||||
common/Make.rules: $(COMMONDIR)/Make.rules
|
||||
ln -sf $(COMMONDIR) .
|
||||
endif
|
||||
|
||||
LIB = lib
|
||||
CATALINA_HOME = /usr/share/tomcat55
|
||||
@@ -28,6 +34,7 @@ all:
|
||||
|
||||
clean:
|
||||
ant clean
|
||||
rm -f tomcat_apparmor.spec ${NAME}-*.tar.gz Make.rules
|
||||
|
||||
install:
|
||||
install: $(SPECFILE)
|
||||
ant -Dversion=$(VERSION) -Drelease=$(MAN_RELEASE) -Dcatalina_home=${CATALINA_HOME} -Dinstall_lib=${LIB} install_jar install_jni
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# ------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (C) 2002-2005 Novell/SUSE
|
||||
# Copyright (C) 2010-2015 Canonical, Ltd.
|
||||
# Copyright (C) 2010 Canonical, Ltd.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2 of the GNU General Public
|
||||
@@ -15,21 +15,17 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, contact Novell, Inc.
|
||||
# ------------------------------------------------------------------
|
||||
# Make.rules - common make targets and variables for building AppArmor
|
||||
# Make.rules - common make targets and variables for building the SHASS
|
||||
# product.
|
||||
#
|
||||
# NOTES:
|
||||
# Before including this file in your Makefile, you should
|
||||
# - define COMMONDIR (the location of the common/ directory)
|
||||
# - define the default rule (usually 'all:'). (Note: you can redefine
|
||||
# it later in your Makefile)
|
||||
|
||||
.PHONY: common_Make.rules_is_a_bad_target
|
||||
common_Make.rules_is_a_bad_target:
|
||||
@echo "*** default target in common/Make.rules hit - either you did something strange, or something is broken... ***"
|
||||
exit 1
|
||||
# - must define the package NAME before including this file.
|
||||
# - After checking in to cvs, you'll need to delele the hardlinked
|
||||
# Make.rules files that already exist in the individual application
|
||||
# directories
|
||||
|
||||
DISTRIBUTION=AppArmor
|
||||
VERSION=$(shell cat $(COMMONDIR)/Version)
|
||||
VERSION=$(shell cat common/Version)
|
||||
|
||||
# Convenience functions
|
||||
pathsearch = $(firstword $(wildcard $(addsuffix /$(1),$(subst :, ,$(PATH)))))
|
||||
@@ -45,8 +41,106 @@ define nl
|
||||
|
||||
endef
|
||||
|
||||
# OVERRIDABLE variables
|
||||
# Set these variables before including Make.rules to change its behavior
|
||||
# SPECFILE - for packages that have a non-standard specfile name
|
||||
# EXTERNAL_PACKAGE - for packages that have upstream versions that
|
||||
# we're locally modifying (e.g. imnxcerttool/gnutls).
|
||||
#
|
||||
# use 'make BUILDIR=/some/where/else/' to override the /usr/src/redhat
|
||||
# location -- it *should* pick out the right thing to do based on the
|
||||
# .rpmmacros file, but you can still use BUILDDIR to override that.
|
||||
TESTBUILDDIR=$(shell [ -f ${HOME}/.rpmmacros ] && awk '/^%_topdir/ {print $$2}' ${HOME}/.rpmmacros)
|
||||
ifndef BUILDDIR
|
||||
BUILDDIR=$(shell if [ -d "${TESTBUILDDIR}" ] ; then \
|
||||
echo ${TESTBUILDDIR} | sed "s^/$$^^" ; \
|
||||
elif [ -d "/usr/src/redhat" ] ; then \
|
||||
echo "/usr/src/redhat" ; \
|
||||
elif [ -d "/usr/src/packages" ] ; then \
|
||||
echo "/usr/src/packages" ; \
|
||||
else \
|
||||
echo "/tmp/${NAME}" ; \
|
||||
fi ;)
|
||||
endif
|
||||
ifndef DISTRO
|
||||
DISTRO=$(shell if [ -f /etc/slackware-version ] ; then \
|
||||
echo slackware ; \
|
||||
elif [ -f /etc/debian_version ] ; then \
|
||||
echo debian ;\
|
||||
elif which rpm > /dev/null ; then \
|
||||
if [ "$(rpm --eval '0%{?suse_version}')" != "0" ] ; then \
|
||||
echo suse ;\
|
||||
elif [ "$(rpm --eval '%{_host_vendor}')" = redhat ] ; then \
|
||||
echo rhel4 ;\
|
||||
elif [ "$(rpm --eval '0%{?fedora}')" != "0" ] ; then \
|
||||
echo rhel4 ;\
|
||||
else \
|
||||
echo unknown ;\
|
||||
fi ;\
|
||||
else \
|
||||
echo unknown ;\
|
||||
fi)
|
||||
endif
|
||||
RPMARG=--define "_topdir $(BUILDDIR:/=)" \
|
||||
--define "vendor NOVELL, Inc." \
|
||||
--define "distribution ${DISTRIBUTION}" \
|
||||
--define "debug_package %{nil}" \
|
||||
--define "immunix_version ${VERSION}" \
|
||||
$(shell [ -d ${BUILDDIR}/BUILDROOT ] && echo --define \"buildroot $(BUILDDIR:/=)/BUILDROOT\") \
|
||||
$(shell [ -n "${DISTRO}" ] && echo --define \"distro ${DISTRO}\")
|
||||
|
||||
REPO_VERSION_CMD=([ -x /usr/bin/bzr ] && /usr/bin/bzr version-info . 2> /dev/null || awk '{ print "revno: "$2 }' common/.stamp_rev) | awk '/^revno:/ { print $2 }'
|
||||
|
||||
ifdef EXTERNAL_PACKAGE
|
||||
RPMARG+=--define "_sourcedir $(shell pwd)"
|
||||
endif
|
||||
|
||||
ifndef SPECFILE
|
||||
SPECFILE = $(NAME).spec
|
||||
endif
|
||||
RELEASE_DIR = $(NAME)-$(VERSION)
|
||||
TAR = /bin/tar czvp -h --exclude .svn --exclude .bzr --exclude .bzrignore --exclude ${RELEASE_DIR}/${RELEASE_DIR} $(shell test -f ${NAME}.exclude && echo "-X ${NAME}.exclude")
|
||||
LDCONFIG = /sbin/ldconfig
|
||||
|
||||
RPMSUBDIRS=SOURCES SPECS BUILD BUILDROOT SRPMS RPMS/i386 RPMS/i586 \
|
||||
RPMS/i686 RPMS/athlon RPMS/noarch RPMS/x86_64
|
||||
BUILDRPMSUBDIRS=$(foreach subdir, $(RPMSUBDIRS), $(BUILDDIR:/=)/$(subdir))
|
||||
|
||||
ifdef EXTERNAL_PACKAGE
|
||||
.PHONY: rpm
|
||||
rpm: clean $(BUILDRPMSUBDIRS)
|
||||
rpmbuild -ba ${RPMARG} ${SPECFILE}
|
||||
|
||||
else
|
||||
.PHONY: rpm
|
||||
rpm: clean $(BUILDRPMSUBDIRS)
|
||||
__REPO_VERSION=`$(value REPO_VERSION_CMD)` ; \
|
||||
__TARBALL=$(NAME)-$(VERSION)-$${__REPO_VERSION}.tar.gz ; \
|
||||
make $${__TARBALL} ; \
|
||||
cp $${__TARBALL} $(BUILDDIR)/SOURCES/
|
||||
cp ${SPECFILE} $(BUILDDIR)/SPECS/
|
||||
rpmbuild -ba ${RPMARG} ${SPECFILE}
|
||||
|
||||
.PHONY: ${SPECFILE}
|
||||
${SPECFILE}: ${SPECFILE}.in
|
||||
__REPO_VERSION=`$(value REPO_VERSION_CMD)` ; \
|
||||
sed -e "s/@@immunix_version@@/${VERSION}/g" \
|
||||
-e "s/@@repo_version@@/$${__REPO_VERSION}/g" $< > $@
|
||||
|
||||
%.tar.gz: clean ${SPECFILE}
|
||||
-rm -rf $(RELEASE_DIR)
|
||||
mkdir $(RELEASE_DIR)
|
||||
$(TAR) --exclude $@ . | tar xz -C $(RELEASE_DIR)
|
||||
$(TAR) --exclude $@ -f $@ $(RELEASE_DIR)
|
||||
rm -rf $(RELEASE_DIR)
|
||||
|
||||
ifndef OVERRIDE_TARBALL
|
||||
.PHONY: tarball
|
||||
tarball: clean $(TARBALL)
|
||||
endif
|
||||
|
||||
endif
|
||||
|
||||
ifndef PYTHON_VERSIONS
|
||||
PYTHON_VERSIONS = $(call map, pathsearch, python2 python3)
|
||||
endif
|
||||
@@ -61,18 +155,24 @@ pyalldo=set -e; $(foreach py, $(PYTHON_VERSIONS), $(py) $(1);)
|
||||
.PHONY: version
|
||||
.SILENT: version
|
||||
version:
|
||||
echo $(VERSION)
|
||||
rpm -q --define "_sourcedir ." ${RPMARG} --specfile ${SPECFILE}
|
||||
|
||||
.PHONY: repo_version
|
||||
.SILENT: repo_version
|
||||
repo_version:
|
||||
$(value REPO_VERSION_CMD)
|
||||
|
||||
.PHONY: pod_clean
|
||||
ifndef VERBOSE
|
||||
.SILENT: pod_clean
|
||||
endif
|
||||
pod_clean:
|
||||
|
||||
.PHONY: build_dir
|
||||
build_dir: $(BUILDRPMSUBDIRS)
|
||||
|
||||
$(BUILDRPMSUBDIRS):
|
||||
mkdir -p $(BUILDRPMSUBDIRS)
|
||||
|
||||
.PHONY: _clean
|
||||
.SILENT: _clean
|
||||
_clean:
|
||||
-[ -z "${NAME}" ] || rm -f ${NAME}-${VERSION}-*.tar.gz
|
||||
-rm -f ${MANPAGES} *.[0-9].gz ${HTMLMANPAGES} pod2htm*.tmp
|
||||
|
||||
# =====================
|
||||
@@ -82,7 +182,7 @@ pod_clean:
|
||||
# =====================
|
||||
|
||||
# emits defined capabilities in a simple list, e.g. "CAP_NAME CAP_NAME2"
|
||||
CAPABILITIES=$(shell echo "\#include <linux/capability.h>" | cpp -dM | LC_ALL=C sed -n -e '/CAP_EMPTY_SET/d' -e 's/^\#define[ \t]\+CAP_\([A-Z0-9_]\+\)[ \t]\+\([0-9xa-f]\+\)\(.*\)$$/CAP_\1/p' | LC_ALL=C sort)
|
||||
CAPABILITIES=$(shell echo "\#include <linux/capability.h>" | cpp -dM | LC_ALL=C sed -n -e '/CAP_EMPTY_SET/d' -e 's/^\#define[ \t]\+CAP_\([A-Z0-9_]\+\)[ \t]\+\([0-9xa-f]\+\)\(.*\)$$/CAP_\1/p' | sort)
|
||||
|
||||
.PHONY: list_capabilities
|
||||
list_capabilities: /usr/include/linux/capability.h
|
||||
@@ -98,7 +198,7 @@ list_capabilities: /usr/include/linux/capability.h
|
||||
# to mediate. We use PF_ here since that is what is required in
|
||||
# bits/socket.h, but we will rewrite these as AF_.
|
||||
|
||||
FILTER_FAMILIES=PF_UNIX
|
||||
FILTER_FAMILIES=PF_UNSPEC PF_UNIX
|
||||
|
||||
__FILTER=$(shell echo $(strip $(FILTER_FAMILIES)) | sed -e 's/ /\\\|/g')
|
||||
|
||||
|
@@ -1 +1 @@
|
||||
2.10.1
|
||||
2.9.2
|
||||
|
@@ -24,7 +24,13 @@ NAME = apparmor-utils
|
||||
all:
|
||||
COMMONDIR=../../common/
|
||||
|
||||
include $(COMMONDIR)/Make.rules
|
||||
include common/Make.rules
|
||||
|
||||
COMMONDIR_EXISTS=$(strip $(shell [ -d ${COMMONDIR} ] && echo true))
|
||||
ifeq ($(COMMONDIR_EXISTS), true)
|
||||
common/Make.rules: $(COMMONDIR)/Make.rules
|
||||
ln -sf $(COMMONDIR) .
|
||||
endif
|
||||
|
||||
MODDIR = Immunix
|
||||
PERLTOOLS = aa-genprof aa-logprof aa-autodep aa-audit aa-complain aa-enforce \
|
||||
@@ -50,8 +56,9 @@ install:
|
||||
ifndef VERBOSE
|
||||
.SILENT: clean
|
||||
endif
|
||||
clean: pod_clean
|
||||
clean: _clean
|
||||
rm -f core core.* *.o *.s *.a *~
|
||||
rm -f Make.rules
|
||||
rm -rf staging/ build/
|
||||
|
||||
.PHONY: check
|
||||
|
Binary file not shown.
@@ -4,4 +4,22 @@ SRCDIR = src
|
||||
|
||||
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: //" ; \
|
||||
fi)
|
||||
|
||||
REPO_URL=$(shell if [ -x /usr/bin/svn ] ; then \
|
||||
/usr/bin/svn info . 2> /dev/null | grep "^URL:" | sed "s/^URL: //" ; \
|
||||
fi)
|
||||
RELEASE_DIR = $(NAME)-$(VERSION)-${REPO_VERSION}
|
||||
|
||||
SVNTARBALL = $(NAME)-$(VERSION)-${REPO_VERSION}.tar.gz
|
||||
SVNTAR = /bin/tar czvp -h --exclude .svn --exclude CVS --exclude .cvsignore --exclude ${SVNTARBALL} --exclude ${RELEASE_DIR}/${RELEASE_DIR} $(shell test -f ${NAME}.exclude && echo "-X ${NAME}.exclude")
|
||||
|
||||
distball: clean
|
||||
rm -rf $(RELEASE_DIR)
|
||||
svn export -r $(REPO_VERSION) $(REPO_URL) $(RELEASE_DIR)
|
||||
$(SVNTAR) -f $(SVNTARBALL) $(RELEASE_DIR)
|
||||
rm -rf $(RELEASE_DIR)
|
||||
|
||||
EXTRA_DIST = AUTHORS ChangeLog COPYING.LGPL INSTALL NEWS README
|
||||
|
@@ -14,14 +14,6 @@ PKG_PROG_PKG_CONFIG
|
||||
|
||||
AC_PATH_PROG([SWIG], [swig])
|
||||
|
||||
AC_MSG_CHECKING([whether the libapparmor debug output should be enabled])
|
||||
AC_ARG_ENABLE([debug_output],
|
||||
[AS_HELP_STRING([--enable-debug-output], [generate the libapparmor debug output [[default=no]]])],
|
||||
[AC_MSG_RESULT([$enableval])],
|
||||
[enable_debug_output=no]
|
||||
[AC_MSG_RESULT([$enable_debug_output])])
|
||||
AS_IF([test "$enable_debug_output" = "yes"], [AC_DEFINE([ENABLE_DEBUG_OUTPUT], [1], [debug output])])
|
||||
|
||||
AC_MSG_CHECKING([whether the libapparmor man pages should be generated])
|
||||
AC_ARG_ENABLE(man_pages,
|
||||
[AS_HELP_STRING([--enable-man-pages], [generate the libapparmor man pages [[default=yes]]])],
|
||||
@@ -79,19 +71,14 @@ AM_CONDITIONAL(HAVE_PERL, test x$with_perl = xyes)
|
||||
AM_CONDITIONAL(HAVE_RUBY, test x$with_ruby = xyes)
|
||||
|
||||
AC_HEADER_STDC
|
||||
AC_CHECK_HEADERS(unistd.h stdint.h syslog.h)
|
||||
AC_CHECK_HEADERS(unistd.h stdint.h)
|
||||
|
||||
AC_CHECK_FUNCS([asprintf __secure_getenv secure_getenv])
|
||||
AC_CHECK_FUNCS(asprintf)
|
||||
|
||||
AM_PROG_CC_C_O
|
||||
AC_C_CONST
|
||||
AM_PROG_LIBTOOL
|
||||
|
||||
AC_PROG_CC_C99
|
||||
if test "$ac_cv_prog_cc_c99" = "no"; then
|
||||
AC_MSG_ERROR([C99 mode is required to build libapparmor])
|
||||
fi
|
||||
|
||||
AC_OUTPUT(
|
||||
Makefile
|
||||
doc/Makefile
|
||||
|
@@ -5,14 +5,14 @@ PODCHECKER = podchecker
|
||||
|
||||
if ENABLE_MAN_PAGES
|
||||
|
||||
man_MANS = aa_change_hat.2 aa_change_profile.2 aa_getcon.2 aa_find_mountpoint.2 aa_splitcon.3 aa_query_label.2 aa_features.3 aa_kernel_interface.3 aa_policy_cache.3
|
||||
man_MANS = aa_change_hat.2 aa_change_profile.2 aa_getcon.2 aa_find_mountpoint.2
|
||||
|
||||
PODS = $(subst .2,.pod,$(man_MANS)) $(subst .3,.pod,$(man_MANS))
|
||||
PODS = $(subst .2,.pod,$(man_MANS))
|
||||
|
||||
EXTRA_DIST = $(man_MANS) $(PODS)
|
||||
|
||||
## delete man pages at make clean
|
||||
CLEANFILES = $(man_MANS)
|
||||
## delete man pages at maintainer-clean
|
||||
BUILT_SOURCES = $(man_MANS)
|
||||
|
||||
%.2: %.pod
|
||||
$(PODCHECKER) -warnings -warnings $<
|
||||
@@ -23,13 +23,4 @@ CLEANFILES = $(man_MANS)
|
||||
--stderr \
|
||||
$< > $@
|
||||
|
||||
%.3: %.pod
|
||||
$(PODCHECKER) -warnings -warnings $<
|
||||
$(POD2MAN) \
|
||||
--section=3 \
|
||||
--release="AppArmor $(VERSION)" \
|
||||
--center="AppArmor" \
|
||||
--stderr \
|
||||
$< > $@
|
||||
|
||||
endif
|
||||
|
@@ -40,15 +40,16 @@ An AppArmor profile applies to an executable program; if a portion of
|
||||
the program needs different access permissions than other portions,
|
||||
the program can "change profile" to a different profile. To change into a
|
||||
new profile, it can use the aa_change_profile() function to do so. It passes
|
||||
in a pointer to the I<profile> to transition to. Confined programs wanting to
|
||||
use aa_change_profile() need to have rules permitting changing to the named
|
||||
profile. See apparmor.d(8) for details.
|
||||
in a pointer to the I<profile> to transition to. Transitioning to another
|
||||
profile via aa_change_profile() is permanent and the process is not
|
||||
permitted to transition back to the original profile. Confined programs
|
||||
wanting to use aa_change_profile() need to have rules permitting changing
|
||||
to the named profile. See apparmor.d(8) for details.
|
||||
|
||||
If a program wants to return out of the current profile to the
|
||||
original profile, it may use aa_change_hat(2). Otherwise, the two profiles must
|
||||
have rules permitting changing between the two profiles.
|
||||
original profile, it should use aa_change_hat(2) instead.
|
||||
|
||||
Open file descriptors may not be remediated after a call to aa_change_profile()
|
||||
Open file descriptors are not remediated after a call to aa_change_profile()
|
||||
so the calling program must close(2) open file descriptors to ensure they
|
||||
are not available after calling aa_change_profile(). As aa_change_profile()
|
||||
is typically used just before execve(2), you may want to use open(2) or
|
||||
@@ -83,8 +84,8 @@ Insufficient kernel memory was available.
|
||||
|
||||
=item B<EPERM>
|
||||
|
||||
The calling application is confined by apparmor and the no_new_privs bit is
|
||||
set.
|
||||
The calling application is not confined by apparmor, or the no_new_privs
|
||||
bit is set.
|
||||
|
||||
=item B<EACCES>
|
||||
|
||||
|
@@ -1,148 +0,0 @@
|
||||
# This publication is intellectual property of Canonical Ltd. Its contents
|
||||
# can be duplicated, either in part or in whole, provided that a copyright
|
||||
# label is visibly located on each copy.
|
||||
#
|
||||
# All information found in this book has been compiled with utmost
|
||||
# attention to detail. However, this does not guarantee complete accuracy.
|
||||
# Neither Canonical Ltd, the authors, nor the translators shall be held
|
||||
# liable for possible errors or the consequences thereof.
|
||||
#
|
||||
# Many of the software and hardware descriptions cited in this book
|
||||
# are registered trademarks. All trade names are subject to copyright
|
||||
# restrictions and may be registered trade marks. Canonical Ltd.
|
||||
# essentially adhere to the manufacturer's spelling.
|
||||
#
|
||||
# Names of products and trademarks appearing in this book (with or without
|
||||
# specific notation) are likewise subject to trademark and trade protection
|
||||
# laws and may thus fall under copyright restrictions.
|
||||
#
|
||||
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
aa_features - an opaque object representing a set of AppArmor kernel features
|
||||
|
||||
aa_features_new - create a new aa_features object based on a path
|
||||
|
||||
aa_features_new_from_string - create a new aa_features object based on a string
|
||||
|
||||
aa_features_new_from_kernel - create a new aa_features object based on the current kernel
|
||||
|
||||
aa_features_ref - increments the ref count of an aa_features object
|
||||
|
||||
aa_features_unref - decrements the ref count and frees the aa_features object when 0
|
||||
|
||||
aa_features_write_to_file - write a string representation of an aa_features object to a file
|
||||
|
||||
aa_features_is_equal - equality test for two aa_features objects
|
||||
|
||||
aa_features_supports - provides aa_features object support status
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
B<#include E<lt>sys/apparmor.hE<gt>>
|
||||
|
||||
B<typedef struct aa_features aa_features;>
|
||||
|
||||
B<int aa_features_new(aa_features **features, int dirfd, const char *path);>
|
||||
|
||||
B<int aa_features_new_from_string(aa_features **features, const char *string, size_t size);>
|
||||
|
||||
B<int aa_features_new_from_kernel(aa_features **features);>
|
||||
|
||||
B<aa_features *aa_features_ref(aa_features *features);>
|
||||
|
||||
B<void aa_features_unref(aa_features *features);>
|
||||
|
||||
B<int aa_features_write_to_file(aa_features *features, int dirfd, const char *path);>
|
||||
|
||||
B<bool aa_features_is_equal(aa_features *features1, aa_features *features2);>
|
||||
|
||||
B<bool aa_features_supports(aa_features *features, const char *str);>
|
||||
|
||||
Link with B<-lapparmor> when compiling.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The I<aa_features> object contains information about the AppArmor features
|
||||
supported by a kernel. The feature support information is based upon the files
|
||||
AppArmor represents in securityfs, which is typically found at
|
||||
/sys/kernel/security/apparmor/features/. That information may be parsed and
|
||||
turned into a string or flat file in order to represent a set of features of a
|
||||
kernel that is not currently running.
|
||||
|
||||
The aa_features_new() function creates an I<aa_features> object based upon a
|
||||
directory file descriptor and path. The I<path> can point to a file or
|
||||
directory. See the openat(2) man page for examples of I<dirfd> and I<path>. The
|
||||
allocated I<features> object must be freed using aa_features_unref().
|
||||
|
||||
The aa_features_new_from_string() function is similar except that it accepts a
|
||||
NUL-terminated string representation of the AppArmor features as the I<string>
|
||||
argument. The length of the features string, not counting the NUL-terminator,
|
||||
must be specified as the I<size> argument. The allocated I<features> object
|
||||
must be freed using aa_features_unref().
|
||||
|
||||
The aa_features_new_from_kernel() function creates an I<aa_features> object
|
||||
from the current running kernel. The allocated I<features> object must be freed
|
||||
using aa_features_unref().
|
||||
|
||||
aa_features_ref() increments the reference count on the I<features> object.
|
||||
|
||||
aa_features_unref() decrements the reference count on the I<features> object
|
||||
and releases all corresponding resources when the reference count reaches zero.
|
||||
|
||||
The aa_features_write_to_file() function writes a string representation of the
|
||||
I<features> object to the file specified by the I<dirfd> and I<path>
|
||||
combination.
|
||||
|
||||
aa_features_is_equal() can be used to detect if the I<features1> and
|
||||
I<features2> objects are equal. The definition of equality is private to
|
||||
libapparmor and may be changed in ways that do not break backward
|
||||
compatibility.
|
||||
|
||||
The aa_features_supports() function can be used to query the I<features> object
|
||||
to determine if a feature is supported. The I<str> argument should be equal to
|
||||
the path, relative to the "apparmor/features/" directory of securityfs, of the
|
||||
feature to query. For example, to test if policy version 6 is supported, I<str>
|
||||
would be "policy/versions/v6".
|
||||
|
||||
=head1 RETURN VALUE
|
||||
|
||||
The aa_features_new() family of functions return 0 on success and I<*features>
|
||||
will point to an I<aa_features> object that must be freed by
|
||||
aa_features_unref(). -1 is returned on error, with errno set appropriately, and
|
||||
I<*features> will be set to NULL.
|
||||
|
||||
aa_features_ref() returns the value of I<features>.
|
||||
|
||||
aa_features_write_to_file() returns 0 on success. -1 is returned on error, with
|
||||
errno set appropriately.
|
||||
|
||||
aa_features_is_equal() returns true if I<features1> and I<features2> are equal
|
||||
and false if they are not equal.
|
||||
|
||||
aa_features_supports() returns true if the feature represented by I<str> is
|
||||
supported and false if it is not supported.
|
||||
|
||||
=head1 ERRORS
|
||||
|
||||
The errno value will be set according to the underlying error in the
|
||||
I<aa_features> family of functions that return -1 on error.
|
||||
|
||||
=head1 NOTES
|
||||
|
||||
All aa_features functions described above are present in libapparmor version
|
||||
2.10 and newer.
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
None known. If you find any, please report them at
|
||||
L<https://bugs.launchpad.net/apparmor/+filebug>.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
openat(2) and L<http://wiki.apparmor.net>.
|
||||
|
||||
=cut
|
@@ -34,38 +34,27 @@ B<#include E<lt>sys/apparmor.hE<gt>>
|
||||
|
||||
B<int aa_getprocattr_raw(pid_t tid, const char *attr, char *buf, int len, char **mode);>
|
||||
|
||||
B<int aa_getprocattr(pid_t tid, const char *attr, char **label, char **mode);>
|
||||
B<int aa_getprocattr(pid_t tid, const char *attr, char **con, char **mode);>
|
||||
|
||||
B<int aa_gettaskcon(pid_t target, char **label, char **mode);>
|
||||
B<int aa_gettaskcon(pid_t target, char **con, char **mode);>
|
||||
|
||||
B<int aa_getcon(char **label, char **mode);>
|
||||
B<int aa_getcon(char **con, char **mode);>
|
||||
|
||||
B<int aa_getpeercon_raw(int fd, char *buf, int *len, char **mode);>
|
||||
|
||||
B<int aa_getpeercon(int fd, char **label, char **mode);>
|
||||
B<int aa_getpeercon(int fd, char **con, char **mode);>
|
||||
|
||||
Link with B<-lapparmor> when compiling.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The aa_getcon function gets the current AppArmor confinement context for the
|
||||
current task. The confinement context consists of a label and a mode. The label
|
||||
is usually just the name of the AppArmor profile restricting the task, but it
|
||||
may include the profile namespace or in some cases a set of profile names
|
||||
(known as a stack of profiles). The mode is a string that describes how the
|
||||
kernel is enforcing the policy defined in the profile. Profiles loaded in
|
||||
"enforce" mode will result in enforcement of the policy defined in the profile
|
||||
as well as reporting policy violation attempts. Profiles in "complain" mode
|
||||
will not enforce policy but instead report policy violation attempts.
|
||||
|
||||
Some examples of possible returned *label strings are "unconfined", "/sbin/dhclient",
|
||||
and "Firefox". The string can consist of any non-NUL characters but it will be
|
||||
NUL-terminated. The *label string must be freed using free().
|
||||
|
||||
The possible *mode strings are "enforce" and "complain". Additionally, *mode may
|
||||
be NULL when *label is "unconfined". B<The *mode string must not be freed>. The
|
||||
*label and *mode strings come from a single buffer allocation and are separated
|
||||
by a NUL character.
|
||||
current task. The confinement context is usually just the name of the AppArmor
|
||||
profile restricting the task, but it may include the profile namespace or in
|
||||
some cases a set of profile names (known as a stack of profiles). The returned
|
||||
string *con should be freed using free(), but the returned string *mode should
|
||||
not be freed. The *con and *mode strings come from a single buffer allocation
|
||||
and are separated by a NUL character.
|
||||
|
||||
The aa_gettaskcon function is like the aa_getcon function except it will work
|
||||
for any arbitrary task in the system.
|
||||
@@ -131,7 +120,7 @@ L<https://bugs.launchpad.net/apparmor/+filebug>.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
apparmor(7), apparmor.d(5), apparmor_parser(8), aa_change_profile(2),
|
||||
aa_splitcon(3) and L<http://wiki.apparmor.net>.
|
||||
apparmor(7), apparmor.d(5), apparmor_parser(8), aa_change_profile(2) and
|
||||
L<http://wiki.apparmor.net>.
|
||||
|
||||
=cut
|
||||
|
@@ -1,162 +0,0 @@
|
||||
# This publication is intellectual property of Canonical Ltd. Its contents
|
||||
# can be duplicated, either in part or in whole, provided that a copyright
|
||||
# label is visibly located on each copy.
|
||||
#
|
||||
# All information found in this book has been compiled with utmost
|
||||
# attention to detail. However, this does not guarantee complete accuracy.
|
||||
# Neither Canonical Ltd, the authors, nor the translators shall be held
|
||||
# liable for possible errors or the consequences thereof.
|
||||
#
|
||||
# Many of the software and hardware descriptions cited in this book
|
||||
# are registered trademarks. All trade names are subject to copyright
|
||||
# restrictions and may be registered trade marks. Canonical Ltd.
|
||||
# essentially adhere to the manufacturer's spelling.
|
||||
#
|
||||
# Names of products and trademarks appearing in this book (with or without
|
||||
# specific notation) are likewise subject to trademark and trade protection
|
||||
# laws and may thus fall under copyright restrictions.
|
||||
#
|
||||
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
aa_kernel_interface - an opaque object representing the AppArmor kernel interface for policy loading, replacing, and removing
|
||||
|
||||
aa_kernel_interface_new - create a new aa_kernel_interface object from an optional path
|
||||
|
||||
aa_kernel_interface_ref - increments the ref count of an aa_kernel_interface object
|
||||
|
||||
aa_kernel_interface_unref - decrements the ref count and frees the aa_kernel_interface object when 0
|
||||
|
||||
aa_kernel_interface_load_policy - load a policy from a buffer into the kernel
|
||||
|
||||
aa_kernel_interface_load_policy_from_file - load a policy from a file into the kernel
|
||||
|
||||
aa_kernel_interface_load_policy_from_fd - load a policy from a file descriptor into the kernel
|
||||
|
||||
aa_kernel_interface_replace_policy - replace a policy in the kernel with a policy from a buffer
|
||||
|
||||
aa_kernel_interface_replace_policy_from_file - replace a policy in the kernel with a policy from a file
|
||||
|
||||
aa_kernel_interface_replace_policy_from_fd - replace a policy in the kernel with a policy from a file descriptor
|
||||
|
||||
aa_kernel_interface_remove_policy - remove a policy from the kernel
|
||||
|
||||
aa_kernel_interface_write_policy - write a policy to a file descriptor
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
B<#include E<lt>sys/apparmor.hE<gt>>
|
||||
|
||||
B<typedef struct aa_kernel_interface aa_kernel_interface;>
|
||||
|
||||
B<int aa_kernel_interface_new(aa_kernel_interface **kernel_interface, aa_features *kernel_features, const char *apparmorfs);>
|
||||
|
||||
B<aa_kernel_interface *aa_kernel_interface_ref(aa_kernel_interface *kernel_interface);>
|
||||
|
||||
B<void aa_kernel_interface_unref(aa_kernel_interface *kernel_interface);>
|
||||
|
||||
B<int aa_kernel_interface_load_policy(aa_kernel_interface *kernel_interface, const char *buffer, size_t size);>
|
||||
|
||||
B<int aa_kernel_interface_load_policy_from_file(aa_kernel_interface *kernel_interface, int dirfd, const char *path);>
|
||||
|
||||
B<int aa_kernel_interface_load_policy_from_fd(aa_kernel_interface *kernel_interface, int fd);>
|
||||
|
||||
B<int aa_kernel_interface_replace_policy(aa_kernel_interface *kernel_interface, const char *buffer, size_t size);>
|
||||
|
||||
B<int aa_kernel_interface_replace_policy_from_file(aa_kernel_interface *kernel_interface, int dirfd, const char *path);>
|
||||
|
||||
B<int aa_kernel_interface_replace_policy_from_fd(aa_kernel_interface *kernel_interface, int fd);>
|
||||
|
||||
B<int aa_kernel_interface_remove_policy(aa_kernel_interface *kernel_interface, const char *fqname);>
|
||||
|
||||
B<int aa_kernel_interface_write_policy(int fd, const char *buffer, size_t size);>
|
||||
|
||||
Link with B<-lapparmor> when compiling.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The I<aa_kernel_interface> object contains information about the AppArmor
|
||||
kernel interface for policy loading, replacing, and removing.
|
||||
|
||||
The aa_kernel_interface_new() function creates an I<aa_kernel_interface> object
|
||||
based on an optional I<aa_features> object and an optional path to the apparmor
|
||||
directory of securityfs, which is typically found at
|
||||
"/sys/kernel/security/apparmor/". If I<kernel_features> is NULL, then the
|
||||
features of the current kernel are used. When specifying a valid
|
||||
I<kernel_features> object, it must be compatible with the features of the
|
||||
currently running kernel. If I<apparmorfs> is NULL, then the default location
|
||||
is used. The allocated I<kernel_interface> object must be freed using
|
||||
aa_kernel_interface_unref().
|
||||
|
||||
aa_kernel_interface_ref() increments the reference count on the
|
||||
I<kernel_interface> object.
|
||||
|
||||
aa_kernel_interface_unref() decrements the reference count on the
|
||||
I<kernel_interface> object and releases all corresponding resources when the
|
||||
reference count reaches zero.
|
||||
|
||||
The aa_kernel_interface_load() family of functions load a policy into the
|
||||
kernel. The operation will fail if a policy of the same name is already loaded.
|
||||
Use the aa_kernel_interface_replace() family of functions if you wish to
|
||||
replace a previously loaded policy with a new policy of the same name. The
|
||||
aa_kernel_interface_replace() functions can also be used to load a policy that
|
||||
does not correspond to a previously loaded policy.
|
||||
|
||||
When loading or replacing from a buffer, the I<buffer> will contain binary
|
||||
data. The I<size> argument must specify the size of the I<buffer> argument.
|
||||
|
||||
When loading or replacing from a file, the I<dirfd> and I<path> combination are
|
||||
used to specify the location of the file. See the openat(2) man page for
|
||||
examples of I<dirfd> and I<path>.
|
||||
|
||||
It is also possible to load or replace from a file descriptor specified by the
|
||||
I<fd> argument. The file must be open for reading and the file offset must be
|
||||
set appropriately.
|
||||
|
||||
The aa_kernel_interface_remove_policy() function can be used to unload a
|
||||
previously loaded policy. The fully qualified policy name must be specified
|
||||
with the I<fqname> argument. The operation will fail if a policy matching
|
||||
I<fqname> is not found.
|
||||
|
||||
The aa_kernel_interface_write_policy() function allows for a policy, which is
|
||||
stored in I<buffer> and consists of I<size> bytes, to be written to a file
|
||||
descriptor. The I<fd> must be open for writing and the file offset must be set
|
||||
appropriately.
|
||||
|
||||
=head1 RETURN VALUE
|
||||
|
||||
The aa_kernel_interface_new() function returns 0 on success and
|
||||
I<*kernel_interface> will point to an I<aa_kernel_interface> object that must
|
||||
be freed by aa_kernel_interface_unref(). -1 is returned on error, with errno
|
||||
set appropriately, and I<*kernel_interface> will be set to NULL.
|
||||
|
||||
aa_kernel_features_ref() returns the value of I<kernel_features>.
|
||||
|
||||
The aa_kernel_interface_load() family of functions, the
|
||||
aa_kernel_interface_replace() family of functions,
|
||||
aa_kernel_interface_remove(), and aa_kernel_interface_write_policy()
|
||||
return 0 on success. -1 is returned on error, with errno set appropriately.
|
||||
|
||||
=head1 ERRORS
|
||||
|
||||
The errno value will be set according to the underlying error in the
|
||||
I<aa_kernel_interface> family of functions that return -1 on error.
|
||||
|
||||
=head1 NOTES
|
||||
|
||||
All aa_kernel_interface functions described above are present in libapparmor
|
||||
version 2.10 and newer.
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
None known. If you find any, please report them at
|
||||
L<https://bugs.launchpad.net/apparmor/+filebug>.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
aa_features(3), openat(2) and L<http://wiki.apparmor.net>.
|
||||
|
||||
=cut
|
@@ -1,125 +0,0 @@
|
||||
# This publication is intellectual property of Canonical Ltd. Its contents
|
||||
# can be duplicated, either in part or in whole, provided that a copyright
|
||||
# label is visibly located on each copy.
|
||||
#
|
||||
# All information found in this book has been compiled with utmost
|
||||
# attention to detail. However, this does not guarantee complete accuracy.
|
||||
# Neither Canonical Ltd, the authors, nor the translators shall be held
|
||||
# liable for possible errors or the consequences thereof.
|
||||
#
|
||||
# Many of the software and hardware descriptions cited in this book
|
||||
# are registered trademarks. All trade names are subject to copyright
|
||||
# restrictions and may be registered trade marks. Canonical Ltd.
|
||||
# essentially adhere to the manufacturer's spelling.
|
||||
#
|
||||
# Names of products and trademarks appearing in this book (with or without
|
||||
# specific notation) are likewise subject to trademark and trade protection
|
||||
# laws and may thus fall under copyright restrictions.
|
||||
#
|
||||
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
aa_policy_cache - an opaque object representing an AppArmor policy cache
|
||||
|
||||
aa_policy_cache_new - create a new aa_policy_cache object from a path
|
||||
|
||||
aa_policy_cache_ref - increments the ref count of an aa_policy_cache object
|
||||
|
||||
aa_policy_cache_unref - decrements the ref count and frees the aa_policy_cache object when 0
|
||||
|
||||
aa_policy_cache_remove - removes all policy cache files under a path
|
||||
|
||||
aa_policy_cache_replace_all - performs a kernel policy replacement of all cached policies
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
B<#include E<lt>sys/apparmor.hE<gt>>
|
||||
|
||||
B<typedef struct aa_policy_cache aa_policy_cache;>
|
||||
|
||||
B<int aa_policy_cache_new(aa_policy_cache **policy_cache, aa_features *kernel_features, int dirfd, const char *path, uint16_t max_caches);>
|
||||
|
||||
B<aa_policy_cache *aa_policy_cache_ref(aa_policy_cache *policy_cache);>
|
||||
|
||||
B<void aa_policy_cache_unref(aa_policy_cache *policy_cache);>
|
||||
|
||||
B<int aa_policy_cache_remove(int dirfd, const char *path);>
|
||||
|
||||
B<int aa_policy_cache_replace_all(aa_policy_cache *policy_cache, aa_kernel_interface *kernel_interface);>
|
||||
|
||||
Link with B<-lapparmor> when compiling.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The I<aa_policy_cache> object contains information about a set of AppArmor
|
||||
policy cache files. The policy cache files are the binary representation of a
|
||||
human-readable AppArmor profile. The binary representation is the form that is
|
||||
loaded into the kernel.
|
||||
|
||||
The aa_policy_cache_new() function creates an I<aa_policy_cache> object based
|
||||
upon a directory file descriptor and path. The I<path> must point to a
|
||||
directory. See the openat(2) man page for examples of I<dirfd> and I<path>. If
|
||||
I<kernel_features> is NULL, then the features of the current kernel are used.
|
||||
When specifying a valid I<kernel_features> object, it must be the compatible
|
||||
with the features of the kernel of interest. The value of I<max_caches> should
|
||||
be equal to the number of caches that should be allowed before old caches are
|
||||
automatically reaped. The definition of what is considered to be an old cache
|
||||
is private to libapparmor. Specifying 0 means that no new caches should be
|
||||
created and only existing, valid caches may be used. Specifying UINT16_MAX
|
||||
means that a new cache may be created and that the reaping of old caches is
|
||||
disabled. The allocated I<aa_policy_cache> object must be freed using
|
||||
aa_policy_cache_unref().
|
||||
|
||||
aa_policy_cache_ref() increments the reference count on the I<policy_cache>
|
||||
object.
|
||||
|
||||
aa_policy_cache_unref() decrements the reference count on the I<policy_cache>
|
||||
object and releases all corresponding resources when the reference count
|
||||
reaches zero.
|
||||
|
||||
The aa_policy_cache_remove() function deletes all of the policy cache files
|
||||
based upon a directory file descriptor and path. The I<path> must point to a
|
||||
directory. See the openat(2) man page for examples of I<dirfd> and I<path>.
|
||||
|
||||
The aa_policy_cache_replace_all() function can be used to perform a policy
|
||||
replacement of all of the cache policies in the cache directory represented by
|
||||
the I<policy_cache> object. If I<kernel_interface> is NULL, then the current
|
||||
kernel interface is used. When specifying a valid I<kernel_interface> object,
|
||||
it must be the interface of the currently running kernel.
|
||||
|
||||
=head1 RETURN VALUE
|
||||
|
||||
The aa_policy_cache_new() function returns 0 on success and I<*policy_cache>
|
||||
will point to an I<aa_policy_cache> object that must be freed by
|
||||
aa_policy_cache_unref(). -1 is returned on error, with errno set appropriately,
|
||||
and I<*policy_cache> will be set to NULL.
|
||||
|
||||
aa_policy_cache_ref() returns the value of I<policy_cache>.
|
||||
|
||||
aa_policy_cache_remove() and aa_policy_cache_replace_all() return 0 on success.
|
||||
-1 is returned on error, with errno set appropriately.
|
||||
|
||||
=head1 ERRORS
|
||||
|
||||
The errno value will be set according to the underlying error in the
|
||||
I<aa_policy_cache> family of functions that return -1 on error.
|
||||
|
||||
=head1 NOTES
|
||||
|
||||
All aa_policy_cache functions described above are present in libapparmor
|
||||
version 2.10 and newer.
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
None known. If you find any, please report them at
|
||||
L<https://bugs.launchpad.net/apparmor/+filebug>.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
aa_features(3), aa_kernel_interface(3), openat(2) and
|
||||
L<http://wiki.apparmor.net>.
|
||||
|
||||
=cut
|
@@ -1,137 +0,0 @@
|
||||
# This publication is intellectual property of Canonical Ltd. Its contents
|
||||
# can be duplicated, either in part or in whole, provided that a copyright
|
||||
# label is visibly located on each copy.
|
||||
#
|
||||
# All information found in this book has been compiled with utmost
|
||||
# attention to detail. However, this does not guarantee complete accuracy.
|
||||
# Neither Canonical Ltd, the authors, nor the translators shall be held
|
||||
# liable for possible errors or the consequences thereof.
|
||||
#
|
||||
# Many of the software and hardware descriptions cited in this book
|
||||
# are registered trademarks. All trade names are subject to copyright
|
||||
# restrictions and may be registered trade marks. Canonical Ltd.
|
||||
# essentially adhere to the manufacturer's spelling.
|
||||
#
|
||||
# Names of products and trademarks appearing in this book (with or without
|
||||
# specific notation) are likewise subject to trademark and trade protection
|
||||
# laws and may thus fall under copyright restrictions.
|
||||
#
|
||||
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
aa_query_label - query access permission associated with a label
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
B<#include E<lt>sys/apparmor.hE<gt>>
|
||||
|
||||
B<int aa_query_label((uint32_t mask, char *query, size_t size,
|
||||
int *allowed, int *audited);>
|
||||
|
||||
B<int aa_query_file_path((uint32_t mask, const char *label, size_t label_len,
|
||||
const char *path, int *allowed, int *audited);>
|
||||
|
||||
B<int aa_query_file_path_len((uint32_t mask, const char *label,
|
||||
size_t label_len, const char *path, size_t path_len,
|
||||
int *allowed, int *audited);>
|
||||
|
||||
B<int aa_query_link_path_len(const char *label, size_t label_len,
|
||||
const char *target, size_t target_len,
|
||||
const char *link, size_t link_len,
|
||||
int *allowed, int *audited);>
|
||||
|
||||
B<int aa_query_link_path(const char *label, const char *target,
|
||||
const char *link, int *allowed, int *audited);>
|
||||
|
||||
|
||||
Link with B<-lapparmor> when compiling.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The aa_query_label function fetches the current permissions granted by the
|
||||
specified I<label> in the I<query> string.
|
||||
|
||||
The query is a raw binary formatted query, containing the label and
|
||||
permission query to make. The returned I<allowed> and I<audited> values are
|
||||
interpreted boolean values, simple stating whether the query is allowed and
|
||||
if it is audited.
|
||||
|
||||
The mask of the query string is a bit mask of permissions to query and is
|
||||
class type dependent (see AA_CLASS_xxx) entries in I<sys/apparmor.h>.
|
||||
|
||||
The format of the query string is also dependent on the B<AA_CLASS> and as
|
||||
such the the aa_query_xxx helper functions should usually be used instead
|
||||
of directly using I<aa_query_label>. If directly using the interface the
|
||||
I<query> string is required to have a header of B<AA_QUERY_CMD_LABEL_SIZE>
|
||||
that will be used by I<aa_query_label>.
|
||||
|
||||
The B<aa_query_file_path> and B<aa_query_file_path_len> functions are helper
|
||||
function that assemble a properly formatted file path query for the
|
||||
B<aa_query_label> function. The I<label> is a valid apparmor label as
|
||||
returned by I<aa_splitcon> with I<label_len> being the length of the I<label>.
|
||||
The I<path> is any valid filesystem path to query permissions for. For the
|
||||
B<aa_query_file_path_len> variant the I<path_len> parameter specifies the
|
||||
number of bytes in the I<path> to use as part of the query.
|
||||
|
||||
The B<aa_query_link_path> and B<aa_query_link_path_len> functions are helper
|
||||
functions that assemble a properly formatted link path query for the
|
||||
B<aa_query_label> function. The I<link_len> and I<target_len> parameters
|
||||
specify the number of bytes in the I<link> and I<target> to use as part of
|
||||
the query.
|
||||
|
||||
=head1 RETURN VALUE
|
||||
|
||||
On success 0 is returned, and the I<allowed> and I<audited> parameters
|
||||
contain a boolean value of 0 not allowed/audited or 1 allowed/audited. On
|
||||
error, -1 is returned, and errno(3) is set appropriately.
|
||||
|
||||
=head1 ERRORS
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<EINVAL>
|
||||
|
||||
The requested I<mask> is empty.
|
||||
|
||||
The I<size> of the query is E<lt> the query B<AA_QUER?Y_CMD_LABEL_SIZE>
|
||||
|
||||
The apparmor kernel module is not loaded or the he kernel interface access
|
||||
interface is not available
|
||||
|
||||
=item B<ENOMEM>
|
||||
|
||||
Insufficient memory was available.
|
||||
|
||||
=item B<EACCES>
|
||||
|
||||
Access to the specified I<label> or query interface was denied.
|
||||
|
||||
=item B<ENOENT>
|
||||
|
||||
The specified I<label> does not exist or is not visible.
|
||||
|
||||
=item B<ERANGE>
|
||||
|
||||
The confinement data is too large to fit in the supplied buffer.
|
||||
|
||||
=back
|
||||
|
||||
=head1 NOTES
|
||||
|
||||
The label permissions returned are only valid for the time of the
|
||||
query and can change at any point in the future.
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
None known. If you find any, please report them at
|
||||
L<https://bugs.launchpad.net/apparmor/+filebug>.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
apparmor(7), apparmor.d(5), apparmor_parser(8), aa_getcon(2), aa_splitcon(3)
|
||||
and L<http://wiki.apparmor.net>.
|
||||
|
||||
=cut
|
@@ -1,72 +0,0 @@
|
||||
# This publication is intellectual property of Canonical Ltd. Its contents
|
||||
# can be duplicated, either in part or in whole, provided that a copyright
|
||||
# label is visibly located on each copy.
|
||||
#
|
||||
# All information found in this book has been compiled with utmost
|
||||
# attention to detail. However, this does not guarantee complete accuracy.
|
||||
# Neither Canonical Ltd, the authors, nor the translators shall be held
|
||||
# liable for possible errors or the consequences thereof.
|
||||
#
|
||||
# Many of the software and hardware descriptions cited in this book
|
||||
# are registered trademarks. All trade names are subject to copyright
|
||||
# restrictions and may be registered trade marks. Canonical Ltd.
|
||||
# essentially adhere to the manufacturer's spelling.
|
||||
#
|
||||
# Names of products and trademarks appearing in this book (with or without
|
||||
# specific notation) are likewise subject to trademark and trade protection
|
||||
# laws and may thus fall under copyright restrictions.
|
||||
#
|
||||
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
aa_splitcon - split the confinement context into a label and mode
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
B<#include E<lt>sys/apparmor.hE<gt>>
|
||||
|
||||
B<char *aa_splitcon(char *con, char **mode);>
|
||||
|
||||
Link with B<-lapparmor> when compiling.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The aa_splitcon() function splits a confinement context into separate label
|
||||
and mode strings. The @con string is modified so that the label portion is NUL
|
||||
terminated. The enforcement mode is also NUL terminated and the parenthesis
|
||||
surrounding the mode are removed. If @mode is non-NULL, it will point to the
|
||||
first character in the enforcement mode string on success.
|
||||
|
||||
The Linux kernel's /proc/E<lt>PIDE<gt>/attr/current interface appends a
|
||||
trailing newline character to AppArmor contexts that are read from that file.
|
||||
If @con contains a single trailing newline character, it will be stripped by
|
||||
aa_splitcon() prior to all other processing.
|
||||
|
||||
=head1 RETURN VALUE
|
||||
|
||||
Returns a pointer to the first character in the label string. NULL is returned
|
||||
on error.
|
||||
|
||||
=head1 EXAMPLE
|
||||
|
||||
Context Label Mode
|
||||
----------------------------- ------------------ -------
|
||||
unconfined unconfined NULL
|
||||
unconfined\n unconfined NULL
|
||||
/bin/ping (enforce) /bin/ping enforce
|
||||
/bin/ping (enforce)\n /bin/ping enforce
|
||||
/usr/sbin/rsyslogd (complain) /usr/sbin/rsyslogd complain
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
None known. If you find any, please report them at
|
||||
L<https://bugs.launchpad.net/apparmor/+filebug>.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
aa_getcon(2) and L<http://wiki.apparmor.net>.
|
||||
|
||||
=cut
|
@@ -1,3 +1,3 @@
|
||||
|
||||
apparmor_hdrdir = $(includedir)/sys
|
||||
apparmor_hdr_HEADERS = apparmor.h apparmor_private.h
|
||||
apparmor_hdr_HEADERS = apparmor.h
|
||||
|
@@ -18,7 +18,6 @@
|
||||
#ifndef _SYS_APPARMOR_H
|
||||
#define _SYS_APPARMOR_H 1
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
@@ -27,31 +26,10 @@ __BEGIN_DECLS
|
||||
/*
|
||||
* Class of public mediation types in the AppArmor policy db
|
||||
*/
|
||||
#define AA_CLASS_FILE 2
|
||||
|
||||
#define AA_CLASS_DBUS 32
|
||||
|
||||
|
||||
/* Permission flags for the AA_CLASS_FILE mediation class */
|
||||
#define AA_MAY_EXEC (1 << 0)
|
||||
#define AA_MAY_WRITE (1 << 1)
|
||||
#define AA_MAY_READ (1 << 2)
|
||||
#define AA_MAY_APPEND (1 << 3)
|
||||
#define AA_MAY_CREATE (1 << 4)
|
||||
#define AA_MAY_DELETE (1 << 5)
|
||||
#define AA_MAY_OPEN (1 << 6)
|
||||
#define AA_MAY_RENAME (1 << 7)
|
||||
#define AA_MAY_SETATTR (1 << 8)
|
||||
#define AA_MAY_GETATTR (1 << 9)
|
||||
#define AA_MAY_SETCRED (1 << 10)
|
||||
#define AA_MAY_GETCRED (1 << 11)
|
||||
#define AA_MAY_CHMOD (1 << 12)
|
||||
#define AA_MAY_CHOWN (1 << 13)
|
||||
#define AA_MAY_LOCK 0x8000
|
||||
#define AA_EXEC_MMAP 0x10000
|
||||
#define AA_MAY_LINK 0x40000
|
||||
#define AA_MAY_ONEXEC 0x20000000
|
||||
#define AA_MAY_CHANGE_PROFILE 0x40000000
|
||||
|
||||
/* Permission flags for the AA_CLASS_DBUS mediation class */
|
||||
#define AA_DBUS_SEND (1 << 1)
|
||||
#define AA_DBUS_RECEIVE (1 << 2)
|
||||
@@ -79,18 +57,16 @@ extern int aa_change_onexec(const char *profile);
|
||||
extern int aa_change_hatv(const char *subprofiles[], unsigned long token);
|
||||
extern int (aa_change_hat_vargs)(unsigned long token, int count, ...);
|
||||
|
||||
extern char *aa_splitcon(char *con, char **mode);
|
||||
/* Protypes for introspecting task confinement
|
||||
* Please see the aa_getcon(2) manpage for information
|
||||
*/
|
||||
extern int aa_getprocattr_raw(pid_t tid, const char *attr, char *buf, int len,
|
||||
char **mode);
|
||||
extern int aa_getprocattr(pid_t tid, const char *attr, char **label,
|
||||
char **mode);
|
||||
extern int aa_gettaskcon(pid_t target, char **label, char **mode);
|
||||
extern int aa_getcon(char **label, char **mode);
|
||||
extern int aa_getprocattr(pid_t tid, const char *attr, char **con, char **mode);
|
||||
extern int aa_gettaskcon(pid_t target, char **con, char **mode);
|
||||
extern int aa_getcon(char **con, char **mode);
|
||||
extern int aa_getpeercon_raw(int fd, char *buf, int *len, char **mode);
|
||||
extern int aa_getpeercon(int fd, char **label, char **mode);
|
||||
extern int aa_getpeercon(int fd, char **con, char **mode);
|
||||
|
||||
/* A NUL character is used to separate the query command prefix string from the
|
||||
* rest of the query string. The query command sizes intentionally include the
|
||||
@@ -101,17 +77,6 @@ extern int aa_getpeercon(int fd, char **label, char **mode);
|
||||
|
||||
extern int aa_query_label(uint32_t mask, char *query, size_t size, int *allow,
|
||||
int *audit);
|
||||
extern int aa_query_file_path_len(uint32_t mask, const char *label,
|
||||
size_t label_len, const char *path,
|
||||
size_t path_len, int *allowed, int *audited);
|
||||
extern int aa_query_file_path(uint32_t mask, const char *label,
|
||||
const char *path, int *allowed, int *audited);
|
||||
extern int aa_query_link_path_len(const char *label, size_t label_len,
|
||||
const char *target, size_t target_len,
|
||||
const char *link, size_t link_len,
|
||||
int *allowed, int *audited);
|
||||
extern int aa_query_link_path(const char *label, const char *target,
|
||||
const char *link, int *allowed, int *audited);
|
||||
|
||||
#define __macroarg_counter(Y...) __macroarg_count1 ( , ##Y)
|
||||
#define __macroarg_count1(Y...) __macroarg_count2 (Y, 16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)
|
||||
@@ -137,58 +102,6 @@ extern int aa_query_link_path(const char *label, const char *target,
|
||||
#define aa_change_hat_vargs(T, X...) \
|
||||
(aa_change_hat_vargs)(T, __macroarg_counter(X), X)
|
||||
|
||||
typedef struct aa_features aa_features;
|
||||
extern int aa_features_new(aa_features **features, int dirfd, const char *path);
|
||||
extern int aa_features_new_from_string(aa_features **features,
|
||||
const char *string, size_t size);
|
||||
extern int aa_features_new_from_kernel(aa_features **features);
|
||||
extern aa_features *aa_features_ref(aa_features *features);
|
||||
extern void aa_features_unref(aa_features *features);
|
||||
|
||||
extern int aa_features_write_to_file(aa_features *features,
|
||||
int dirfd, const char *path);
|
||||
extern bool aa_features_is_equal(aa_features *features1,
|
||||
aa_features *features2);
|
||||
extern bool aa_features_supports(aa_features *features, const char *str);
|
||||
|
||||
typedef struct aa_kernel_interface aa_kernel_interface;
|
||||
extern int aa_kernel_interface_new(aa_kernel_interface **kernel_interface,
|
||||
aa_features *kernel_features,
|
||||
const char *apparmorfs);
|
||||
extern aa_kernel_interface *aa_kernel_interface_ref(aa_kernel_interface *kernel_interface);
|
||||
extern void aa_kernel_interface_unref(aa_kernel_interface *kernel_interface);
|
||||
|
||||
extern int aa_kernel_interface_load_policy(aa_kernel_interface *kernel_interface,
|
||||
const char *buffer, size_t size);
|
||||
extern int aa_kernel_interface_load_policy_from_file(aa_kernel_interface *kernel_interface,
|
||||
int dirfd,
|
||||
const char *path);
|
||||
extern int aa_kernel_interface_load_policy_from_fd(aa_kernel_interface *kernel_interface,
|
||||
int fd);
|
||||
extern int aa_kernel_interface_replace_policy(aa_kernel_interface *kernel_interface,
|
||||
const char *buffer, size_t size);
|
||||
extern int aa_kernel_interface_replace_policy_from_file(aa_kernel_interface *kernel_interface,
|
||||
int dirfd,
|
||||
const char *path);
|
||||
extern int aa_kernel_interface_replace_policy_from_fd(aa_kernel_interface *kernel_interface,
|
||||
int fd);
|
||||
extern int aa_kernel_interface_remove_policy(aa_kernel_interface *kernel_interface,
|
||||
const char *fqname);
|
||||
extern int aa_kernel_interface_write_policy(int fd, const char *buffer,
|
||||
size_t size);
|
||||
|
||||
typedef struct aa_policy_cache aa_policy_cache;
|
||||
extern int aa_policy_cache_new(aa_policy_cache **policy_cache,
|
||||
aa_features *kernel_features,
|
||||
int dirfd, const char *path,
|
||||
uint16_t max_caches);
|
||||
extern aa_policy_cache *aa_policy_cache_ref(aa_policy_cache *policy_cache);
|
||||
extern void aa_policy_cache_unref(aa_policy_cache *policy_cache);
|
||||
|
||||
extern int aa_policy_cache_remove(int dirfd, const char *path);
|
||||
extern int aa_policy_cache_replace_all(aa_policy_cache *policy_cache,
|
||||
aa_kernel_interface *kernel_interface);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /* sys/apparmor.h */
|
||||
|
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014 Canonical Ltd.
|
||||
*
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the file
|
||||
* COPYING.LGPL.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _SYS_APPARMOR_PRIVATE_H
|
||||
#define _SYS_APPARMOR_PRIVATE_H 1
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
int _aa_is_blacklisted(const char *name);
|
||||
|
||||
void _aa_autofree(void *p);
|
||||
void _aa_autoclose(int *fd);
|
||||
void _aa_autofclose(FILE **f);
|
||||
|
||||
int _aa_asprintf(char **strp, const char *fmt, ...);
|
||||
|
||||
int _aa_dirat_for_each(int dirfd, const char *name, void *data,
|
||||
int (* cb)(int, const char *, struct stat *, void *));
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /* sys/apparmor_private.h */
|
@@ -26,9 +26,9 @@ INCLUDES = $(all_includes)
|
||||
# For more information, see:
|
||||
# http://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html
|
||||
#
|
||||
AA_LIB_CURRENT = 4
|
||||
AA_LIB_REVISION = 0
|
||||
AA_LIB_AGE = 3
|
||||
AA_LIB_CURRENT = 3
|
||||
AA_LIB_REVISION = 1
|
||||
AA_LIB_AGE = 2
|
||||
|
||||
SUFFIXES = .pc.in .pc
|
||||
|
||||
@@ -46,9 +46,9 @@ 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" $< > $@
|
||||
|
||||
lib_LTLIBRARIES = libapparmor.la
|
||||
noinst_HEADERS = grammar.h parser.h scanner.h af_protos.h private.h
|
||||
noinst_HEADERS = grammar.h parser.h scanner.h af_protos.h
|
||||
|
||||
libapparmor_la_SOURCES = grammar.y libaalogparse.c kernel.c scanner.c private.c features.c kernel_interface.c policy_cache.c
|
||||
libapparmor_la_SOURCES = grammar.y libaalogparse.c kernel_interface.c scanner.c
|
||||
libapparmor_la_LDFLAGS = -version-info $(AA_LIB_CURRENT):$(AA_LIB_REVISION):$(AA_LIB_AGE) -XCClinker -dynamic -pthread \
|
||||
-Wl,--version-script=$(top_srcdir)/src/libapparmor.map
|
||||
|
||||
@@ -63,15 +63,7 @@ CLEANFILES = libapparmor.pc
|
||||
|
||||
tst_aalogmisc_SOURCES = tst_aalogmisc.c
|
||||
tst_aalogmisc_LDADD = .libs/libapparmor.a
|
||||
|
||||
tst_features_SOURCES = tst_features.c
|
||||
tst_features_LDADD = .libs/libapparmor.a
|
||||
|
||||
tst_kernel_SOURCES = tst_kernel.c
|
||||
tst_kernel_LDADD = .libs/libapparmor.a
|
||||
tst_kernel_LDFLAGS = -pthread
|
||||
|
||||
check_PROGRAMS = tst_aalogmisc tst_features tst_kernel
|
||||
check_PROGRAMS = tst_aalogmisc
|
||||
TESTS = $(check_PROGRAMS)
|
||||
|
||||
EXTRA_DIST = grammar.y scanner.l libapparmor.map libapparmor.pc
|
||||
|
@@ -1,561 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014
|
||||
* Canonical, Ltd. (All rights reserved)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, contact Novell, Inc. or Canonical
|
||||
* Ltd.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/apparmor.h>
|
||||
|
||||
#include "private.h"
|
||||
|
||||
#define FEATURES_FILE "/sys/kernel/security/apparmor/features"
|
||||
|
||||
#define STRING_SIZE 8192
|
||||
|
||||
struct aa_features {
|
||||
unsigned int ref_count;
|
||||
char string[STRING_SIZE];
|
||||
};
|
||||
|
||||
struct features_struct {
|
||||
char *buffer;
|
||||
int size;
|
||||
char *pos;
|
||||
};
|
||||
|
||||
struct component {
|
||||
const char *str;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
static int features_snprintf(struct features_struct *fst, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int i, remaining = fst->size - (fst->pos - fst->buffer);
|
||||
|
||||
if (remaining < 0) {
|
||||
errno = EINVAL;
|
||||
PERROR("Invalid features buffer offset\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
va_start(args, fmt);
|
||||
i = vsnprintf(fst->pos, remaining, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (i < 0) {
|
||||
errno = EIO;
|
||||
PERROR("Failed to write to features buffer\n");
|
||||
return -1;
|
||||
} else if (i >= remaining) {
|
||||
errno = ENOBUFS;
|
||||
PERROR("Feature buffer full.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fst->pos += i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* load_features_file - opens and reads a file into @buffer and then NUL-terminates @buffer
|
||||
* @dirfd: a directory file descriptory or AT_FDCWD (see openat(2))
|
||||
* @path: name of the file
|
||||
* @buffer: the buffer to read the features file into (will be NUL-terminated on success)
|
||||
* @size: the size of @buffer
|
||||
*
|
||||
* Returns: The number of bytes copied into @buffer on success (not counting
|
||||
* the NUL-terminator), else -1 and errno is set. Note that @size must be
|
||||
* larger than the size of the file or -1 will be returned with errno set to
|
||||
* ENOBUFS indicating that @buffer was not large enough to contain all of the
|
||||
* file contents.
|
||||
*/
|
||||
static int load_features_file(int dirfd, const char *path,
|
||||
char *buffer, size_t size)
|
||||
{
|
||||
autoclose int file = -1;
|
||||
char *pos = buffer;
|
||||
ssize_t len;
|
||||
|
||||
file = openat(dirfd, path, O_RDONLY);
|
||||
if (file < 0) {
|
||||
PDEBUG("Could not open '%s'\n", path);
|
||||
return -1;
|
||||
}
|
||||
PDEBUG("Opened features \"%s\"\n", path);
|
||||
|
||||
if (!size) {
|
||||
errno = ENOBUFS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Save room for a NUL-terminator at the end of @buffer */
|
||||
size--;
|
||||
|
||||
do {
|
||||
len = read(file, pos, size);
|
||||
if (len > 0) {
|
||||
size -= len;
|
||||
pos += len;
|
||||
}
|
||||
} while (len > 0 && size);
|
||||
|
||||
/**
|
||||
* Possible error conditions:
|
||||
* - len == -1: read failed and errno is already set so return -1
|
||||
* - len > 0: read() never returned 0 (EOF) meaning that @buffer was
|
||||
* too small to contain all of the file contents so set
|
||||
* errno to ENOBUFS and return -1
|
||||
*/
|
||||
if (len) {
|
||||
if (len > 0)
|
||||
errno = ENOBUFS;
|
||||
|
||||
PDEBUG("Error reading features file '%s': %m\n", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* NUL-terminate @buffer */
|
||||
*pos = 0;
|
||||
|
||||
return pos - buffer;
|
||||
}
|
||||
|
||||
static int features_dir_cb(int dirfd, const char *name, struct stat *st,
|
||||
void *data)
|
||||
{
|
||||
struct features_struct *fst = (struct features_struct *) data;
|
||||
|
||||
/* skip dot files and files with no name */
|
||||
if (*name == '.' || !strlen(name))
|
||||
return 0;
|
||||
|
||||
if (features_snprintf(fst, "%s {", name) == -1)
|
||||
return -1;
|
||||
|
||||
if (S_ISREG(st->st_mode)) {
|
||||
int len;
|
||||
int remaining = fst->size - (fst->pos - fst->buffer);
|
||||
|
||||
len = load_features_file(dirfd, name, fst->pos, remaining);
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
fst->pos += len;
|
||||
} else if (S_ISDIR(st->st_mode)) {
|
||||
if (_aa_dirat_for_each(dirfd, name, fst, features_dir_cb))
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (features_snprintf(fst, "}\n") == -1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_features_dir(int dirfd, const char *path,
|
||||
char *buffer, int size)
|
||||
{
|
||||
struct features_struct fst = { buffer, size, buffer };
|
||||
|
||||
if (_aa_dirat_for_each(dirfd, path, &fst, features_dir_cb)) {
|
||||
PDEBUG("Failed evaluating %s\n", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool islbrace(int c)
|
||||
{
|
||||
return c == '{';
|
||||
}
|
||||
|
||||
static bool isrbrace(int c)
|
||||
{
|
||||
return c == '}';
|
||||
}
|
||||
|
||||
static bool isnul(int c)
|
||||
{
|
||||
return c == '\0';
|
||||
}
|
||||
|
||||
static bool isbrace(int c)
|
||||
{
|
||||
return islbrace(c) || isrbrace(c);
|
||||
}
|
||||
|
||||
static bool isbrace_or_nul(int c)
|
||||
{
|
||||
return isbrace(c) || isnul(c);
|
||||
}
|
||||
|
||||
static bool isbrace_space_or_nul(int c)
|
||||
{
|
||||
return isbrace(c) || isspace(c) || isnul(c);
|
||||
}
|
||||
|
||||
static size_t tokenize_path_components(const char *str,
|
||||
struct component *components,
|
||||
size_t max_components)
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
memset(components, 0, sizeof(*components) * max_components);
|
||||
|
||||
if (!str)
|
||||
return 0;
|
||||
|
||||
while (*str && i < max_components) {
|
||||
const char *fwdslash = strchrnul(str, '/');
|
||||
|
||||
/* Save the token if it is not "/" */
|
||||
if (fwdslash != str) {
|
||||
components[i].str = str;
|
||||
components[i].len = fwdslash - str;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (isnul(*fwdslash))
|
||||
break;
|
||||
|
||||
str = fwdslash + 1;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* walk_one - walk one component of a features string
|
||||
* @str: a pointer to the current position in a features string
|
||||
* @component: the component to walk
|
||||
* @is_top_level: true if component is a top-level component
|
||||
*
|
||||
* Imagine a features string such as:
|
||||
*
|
||||
* "feat1 { subfeat1.1 subfeat1.2 } feat2 { subfeat2.1 { subfeat2.1.1 } }"
|
||||
*
|
||||
* You want to know if "feat2/subfeat2.1/subfeat2.8" is valid. It will take 3
|
||||
* invocations of this function to determine if that string is valid. On the
|
||||
* first call, *@str will point to the beginning of the features string,
|
||||
* component->str will be "feat2", and is_top_level will be true since feat2 is
|
||||
* a top level feature. The function will return true and *@str will now point
|
||||
* at the the left brace after "feat2" in the features string. You can call
|
||||
* this function again with component->str being equal to "subfeat2.1" and it
|
||||
* will return true and *@str will now point at the left brace after
|
||||
* "subfeat2.1" in the features string. A third call to the function, with
|
||||
* component->str equal to "subfeat2.8", will return false and *@str will not
|
||||
* be changed.
|
||||
*
|
||||
* Returns true if the walk was successful and false otherwise. If the walk was
|
||||
* successful, *@str will point to the first encountered brace after the walk.
|
||||
* If the walk was unsuccessful, *@str is not updated.
|
||||
*/
|
||||
static bool walk_one(const char **str, const struct component *component,
|
||||
bool is_top_level)
|
||||
{
|
||||
const char *cur;
|
||||
uint32_t brace_count = 0;
|
||||
size_t i = 0;
|
||||
|
||||
/* NULL pointers and empty strings are not accepted */
|
||||
if (!str || !*str || !component || !component->str || !component->len)
|
||||
return false;
|
||||
|
||||
cur = *str;
|
||||
|
||||
/**
|
||||
* If @component is not top-level, the first character in the string to
|
||||
* search MUST be '{'
|
||||
*/
|
||||
if (!is_top_level) {
|
||||
if (!islbrace(*cur))
|
||||
return false;
|
||||
|
||||
cur++;
|
||||
}
|
||||
|
||||
/**
|
||||
* This loop tries to find the @component in *@str. When this loops
|
||||
* completes, cur will either point one character past the end of the
|
||||
* matched @component or to the NUL terminator of *@str.
|
||||
*/
|
||||
while(!isnul(*cur) && i < component->len) {
|
||||
if (!isascii(*cur)) {
|
||||
/* Only ASCII is expected */
|
||||
return false;
|
||||
} else if (islbrace(*cur)) {
|
||||
/* There's a limit to the number of left braces */
|
||||
if (brace_count == UINT32_MAX)
|
||||
return false;
|
||||
|
||||
brace_count++;
|
||||
} else if (isrbrace(*cur)) {
|
||||
/* Check for unexpected right braces */
|
||||
if (brace_count == 0)
|
||||
return false;
|
||||
|
||||
brace_count--;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move to the next character in @component if we're not inside
|
||||
* of braces and we have a character match
|
||||
*/
|
||||
if (brace_count == 0 && *cur == component->str[i])
|
||||
i++;
|
||||
else
|
||||
i = 0;
|
||||
|
||||
cur++;
|
||||
}
|
||||
|
||||
/* Return false if a full match was not found */
|
||||
if (i != component->len) {
|
||||
return false;
|
||||
} else if (!isbrace_space_or_nul(*cur))
|
||||
return false;
|
||||
|
||||
/**
|
||||
* This loop eats up valid (ASCII) characters until a brace or NUL
|
||||
* character is found so that *@str is properly set to call back into
|
||||
* this function
|
||||
*/
|
||||
while (!isbrace_or_nul(*cur)) {
|
||||
if (!isascii(*cur))
|
||||
return false;
|
||||
|
||||
cur++;
|
||||
}
|
||||
|
||||
*str = cur;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_features_new - create a new aa_features object based on a path
|
||||
* @features: will point to the address of an allocated and initialized
|
||||
* aa_features object upon success
|
||||
* @dirfd: directory file descriptor or AT_FDCWD (see openat(2))
|
||||
* @path: path to a features file or directory
|
||||
*
|
||||
* Returns: 0 on success, -1 on error with errno set and *@features pointing to
|
||||
* NULL
|
||||
*/
|
||||
int aa_features_new(aa_features **features, int dirfd, const char *path)
|
||||
{
|
||||
struct stat stat_file;
|
||||
aa_features *f;
|
||||
int retval;
|
||||
|
||||
*features = NULL;
|
||||
|
||||
if (fstatat(dirfd, path, &stat_file, 0) == -1)
|
||||
return -1;
|
||||
|
||||
f = calloc(1, sizeof(*f));
|
||||
if (!f) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
aa_features_ref(f);
|
||||
|
||||
retval = S_ISDIR(stat_file.st_mode) ?
|
||||
load_features_dir(dirfd, path, f->string, STRING_SIZE) :
|
||||
load_features_file(dirfd, path, f->string, STRING_SIZE);
|
||||
if (retval == -1) {
|
||||
int save = errno;
|
||||
|
||||
aa_features_unref(f);
|
||||
errno = save;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*features = f;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_features_new_from_string - create a new aa_features object based on a string
|
||||
* @features: will point to the address of an allocated and initialized
|
||||
* aa_features object upon success
|
||||
* @string: a NUL-terminated string representation of features
|
||||
* @size: the size of @string, not counting the NUL-terminator
|
||||
*
|
||||
* Returns: 0 on success, -1 on error with errno set and *@features pointing to
|
||||
* NULL
|
||||
*/
|
||||
int aa_features_new_from_string(aa_features **features,
|
||||
const char *string, size_t size)
|
||||
{
|
||||
aa_features *f;
|
||||
|
||||
*features = NULL;
|
||||
|
||||
/* Require size to be less than STRING_SIZE so there's room for a NUL */
|
||||
if (size >= STRING_SIZE)
|
||||
return ENOBUFS;
|
||||
|
||||
f = calloc(1, sizeof(*f));
|
||||
if (!f) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
aa_features_ref(f);
|
||||
|
||||
memcpy(f->string, string, size);
|
||||
f->string[size] = '\0';
|
||||
*features = f;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_features_new_from_kernel - create a new aa_features object based on the current kernel
|
||||
* @features: will point to the address of an allocated and initialized
|
||||
* aa_features object upon success
|
||||
*
|
||||
* Returns: 0 on success, -1 on error with errno set and *@features pointing to
|
||||
* NULL
|
||||
*/
|
||||
int aa_features_new_from_kernel(aa_features **features)
|
||||
{
|
||||
return aa_features_new(features, -1, FEATURES_FILE);
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_features_ref - increments the ref count of an aa_features object
|
||||
* @features: the features
|
||||
*
|
||||
* Returns: the features
|
||||
*/
|
||||
aa_features *aa_features_ref(aa_features *features)
|
||||
{
|
||||
atomic_inc(&features->ref_count);
|
||||
return features;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_features_unref - decrements the ref count and frees the aa_features object when 0
|
||||
* @features: the features (can be NULL)
|
||||
*/
|
||||
void aa_features_unref(aa_features *features)
|
||||
{
|
||||
if (features && atomic_dec_and_test(&features->ref_count))
|
||||
free(features);
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_features_write_to_file - write a string representation of an aa_features object to a file
|
||||
* @features: the features
|
||||
* @dirfd: directory file descriptor or AT_FDCWD (see openat(2))
|
||||
* @path: the path to write to
|
||||
*
|
||||
* Returns: 0 on success, -1 on error with errno set
|
||||
*/
|
||||
int aa_features_write_to_file(aa_features *features,
|
||||
int dirfd, const char *path)
|
||||
{
|
||||
autoclose int fd = -1;
|
||||
size_t size;
|
||||
ssize_t retval;
|
||||
char *string;
|
||||
|
||||
fd = openat(dirfd, path,
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_SYNC | O_CLOEXEC,
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
string = features->string;
|
||||
size = strlen(string);
|
||||
do {
|
||||
retval = write(fd, string, size);
|
||||
if (retval == -1)
|
||||
return -1;
|
||||
|
||||
size -= retval;
|
||||
string += retval;
|
||||
} while (size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_features_is_equal - equality test for two aa_features objects
|
||||
* @features1: the first features (can be NULL)
|
||||
* @features2: the second features (can be NULL)
|
||||
*
|
||||
* Returns: true if they're equal, false if they're not or either are NULL
|
||||
*/
|
||||
bool aa_features_is_equal(aa_features *features1, aa_features *features2)
|
||||
{
|
||||
return features1 && features2 &&
|
||||
strcmp(features1->string, features2->string) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_features_supports - provides aa_features object support status
|
||||
* @features: the features
|
||||
* @str: the string representation of a feature to check
|
||||
*
|
||||
* Example @str values are "dbus/mask/send", "caps/mask/audit_read", and
|
||||
* "policy/versions/v7".
|
||||
*
|
||||
* Returns: a bool specifying the support status of @str feature
|
||||
*/
|
||||
bool aa_features_supports(aa_features *features, const char *str)
|
||||
{
|
||||
const char *features_string = features->string;
|
||||
struct component components[32];
|
||||
size_t i, num_components;
|
||||
|
||||
/* Empty strings are not accepted. Neither are leading '/' chars. */
|
||||
if (!str || str[0] == '/')
|
||||
return false;
|
||||
|
||||
/**
|
||||
* Break @str into an array of components. For example,
|
||||
* "mount/mask/mount" would turn into { "mount", 5 } as the first
|
||||
* component, { "mask", 4 } as the second, and { "mount", 5 } as the
|
||||
* third
|
||||
*/
|
||||
num_components = tokenize_path_components(str, components,
|
||||
sizeof(components) / sizeof(*components));
|
||||
|
||||
/* At least one valid token is required */
|
||||
if (!num_components)
|
||||
return false;
|
||||
|
||||
/* Ensure that all components are valid and found */
|
||||
for (i = 0; i < num_components; i++) {
|
||||
if (!walk_one(&features_string, &components[i], i == 0))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
@@ -166,15 +166,9 @@ aa_record_event_type lookup_aa_event(unsigned int type)
|
||||
%token TOK_SYSLOG_KERNEL
|
||||
%token TOK_SYSLOG_USER
|
||||
|
||||
%destructor { free($$); } TOK_QUOTED_STRING TOK_ID TOK_MODE TOK_DMESG_STAMP
|
||||
%destructor { free($$); } TOK_AUDIT_DIGITS TOK_DATE_MONTH TOK_DATE TOK_TIME
|
||||
%destructor { free($$); } TOK_HEXSTRING TOK_TYPE_OTHER TOK_MSG_REST
|
||||
%destructor { free($$); } TOK_IP_ADDR
|
||||
|
||||
%%
|
||||
|
||||
log_message: audit_type
|
||||
| dmesg_type
|
||||
| syslog_type
|
||||
| audit_dispatch
|
||||
;
|
||||
@@ -205,10 +199,6 @@ other_audit: TOK_TYPE_OTHER audit_msg TOK_MSG_REST
|
||||
}
|
||||
;
|
||||
|
||||
dmesg_type: TOK_DMESG_STAMP TOK_AUDIT TOK_COLON key_type audit_id key_list
|
||||
{ ret_record->version = AA_RECORD_SYNTAX_V2; free($1); }
|
||||
;
|
||||
|
||||
syslog_type:
|
||||
syslog_date TOK_ID TOK_SYSLOG_KERNEL audit_id key_list
|
||||
{ ret_record->version = AA_RECORD_SYNTAX_V2; free($2); }
|
||||
@@ -428,7 +418,7 @@ _parse_yacc(char *str)
|
||||
int parser_return;
|
||||
|
||||
ret_record = NULL;
|
||||
ret_record = malloc(sizeof(aa_log_record));
|
||||
ret_record = (aa_log_record *) malloc(sizeof(aa_log_record));
|
||||
|
||||
_init_log_record(ret_record);
|
||||
|
||||
|
@@ -1,982 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
|
||||
* Copyright 2009-2010 Canonical Ltd.
|
||||
*
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the file
|
||||
* COPYING.LGPL.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/socket.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <mntent.h>
|
||||
#include <inttypes.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <sys/apparmor.h>
|
||||
#include "private.h"
|
||||
|
||||
/* some non-Linux systems do not define a static value */
|
||||
#ifndef PATH_MAX
|
||||
# define PATH_MAX 4096
|
||||
#endif
|
||||
|
||||
#define symbol_version(real, name, version) \
|
||||
__asm__ (".symver " #real "," #name "@" #version)
|
||||
#define default_symbol_version(real, name, version) \
|
||||
__asm__ (".symver " #real "," #name "@@" #version)
|
||||
|
||||
#define UNCONFINED "unconfined"
|
||||
#define UNCONFINED_SIZE strlen(UNCONFINED)
|
||||
|
||||
/**
|
||||
* aa_find_mountpoint - find where the apparmor interface filesystem is mounted
|
||||
* @mnt: returns buffer with the mountpoint string
|
||||
*
|
||||
* Returns: 0 on success else -1 on error
|
||||
*
|
||||
* NOTE: this function only supports versions of apparmor using securityfs
|
||||
*/
|
||||
int aa_find_mountpoint(char **mnt)
|
||||
{
|
||||
struct stat statbuf;
|
||||
struct mntent *mntpt;
|
||||
FILE *mntfile;
|
||||
int rc = -1;
|
||||
|
||||
if (!mnt) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
mntfile = setmntent("/proc/mounts", "r");
|
||||
if (!mntfile)
|
||||
return -1;
|
||||
|
||||
while ((mntpt = getmntent(mntfile))) {
|
||||
char *proposed = NULL;
|
||||
if (strcmp(mntpt->mnt_type, "securityfs") != 0)
|
||||
continue;
|
||||
|
||||
if (asprintf(&proposed, "%s/apparmor", mntpt->mnt_dir) < 0)
|
||||
/* ENOMEM */
|
||||
break;
|
||||
|
||||
if (stat(proposed, &statbuf) == 0) {
|
||||
*mnt = proposed;
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
free(proposed);
|
||||
}
|
||||
endmntent(mntfile);
|
||||
if (rc == -1)
|
||||
errno = ENOENT;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_is_enabled - determine if apparmor is enabled
|
||||
*
|
||||
* Returns: 1 if enabled else reason it is not, or 0 on error
|
||||
*
|
||||
* ENOSYS - no indication apparmor is present in the system
|
||||
* ENOENT - enabled but interface could not be found
|
||||
* ECANCELED - disabled at boot
|
||||
* ENOMEM - out of memory
|
||||
*/
|
||||
int aa_is_enabled(void)
|
||||
{
|
||||
int serrno, fd, rc, size;
|
||||
char buffer[2];
|
||||
char *mnt;
|
||||
|
||||
/* if the interface mountpoint is available apparmor is enabled */
|
||||
rc = aa_find_mountpoint(&mnt);
|
||||
if (rc == 0) {
|
||||
free(mnt);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* determine why the interface mountpoint isn't available */
|
||||
fd = open("/sys/module/apparmor/parameters/enabled", O_RDONLY);
|
||||
if (fd == -1) {
|
||||
if (errno == ENOENT)
|
||||
errno = ENOSYS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size = read(fd, &buffer, 2);
|
||||
serrno = errno;
|
||||
close(fd);
|
||||
errno = serrno;
|
||||
|
||||
if (size > 0) {
|
||||
if (buffer[0] == 'Y')
|
||||
errno = ENOENT;
|
||||
else
|
||||
errno = ECANCELED;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline pid_t aa_gettid(void)
|
||||
{
|
||||
#ifdef SYS_gettid
|
||||
return syscall(SYS_gettid);
|
||||
#else
|
||||
return getpid();
|
||||
#endif
|
||||
}
|
||||
|
||||
static char *procattr_path(pid_t pid, const char *attr)
|
||||
{
|
||||
char *path = NULL;
|
||||
if (asprintf(&path, "/proc/%d/attr/%s", pid, attr) > 0)
|
||||
return path;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* parse_unconfined - check for the unconfined label
|
||||
* @con: the confinement context
|
||||
* @size: size of the confinement context (not including the NUL terminator)
|
||||
*
|
||||
* Returns: True if the con is the unconfined label or false otherwise
|
||||
*/
|
||||
static bool parse_unconfined(char *con, int size)
|
||||
{
|
||||
return size == UNCONFINED_SIZE &&
|
||||
strncmp(con, UNCONFINED, UNCONFINED_SIZE) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* splitcon - split the confinement context into a label and mode
|
||||
* @con: the confinement context
|
||||
* @size: size of the confinement context (not including the NUL terminator)
|
||||
* @strip_newline: true if a trailing newline character should be stripped
|
||||
* @mode: if non-NULL and a mode is present, will point to mode string in @con
|
||||
* on success
|
||||
*
|
||||
* Modifies the @con string to split it into separate label and mode strings.
|
||||
* If @strip_newline is true and @con contains a single trailing newline, it
|
||||
* will be stripped on success (it will not be stripped on error). The @mode
|
||||
* argument is optional. If @mode is NULL, @con will still be split between the
|
||||
* label and mode (if present) but @mode will not be set.
|
||||
*
|
||||
* Returns: a pointer to the label string or NULL on error
|
||||
*/
|
||||
static char *splitcon(char *con, int size, bool strip_newline, char **mode)
|
||||
{
|
||||
char *label = NULL;
|
||||
char *mode_str = NULL;
|
||||
char *newline = NULL;
|
||||
|
||||
if (size == 0)
|
||||
goto out;
|
||||
|
||||
if (strip_newline && con[size - 1] == '\n') {
|
||||
newline = &con[size - 1];
|
||||
size--;
|
||||
}
|
||||
|
||||
if (parse_unconfined(con, size)) {
|
||||
label = con;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (size > 3 && con[size - 1] == ')') {
|
||||
int pos = size - 2;
|
||||
|
||||
while (pos > 0 && !(con[pos] == ' ' && con[pos + 1] == '('))
|
||||
pos--;
|
||||
if (pos > 0) {
|
||||
con[pos] = 0; /* overwrite ' ' */
|
||||
con[size - 1] = 0; /* overwrite trailing ) */
|
||||
mode_str = &con[pos + 2]; /* skip '(' */
|
||||
label = con;
|
||||
}
|
||||
}
|
||||
out:
|
||||
if (label && strip_newline && newline)
|
||||
*newline = 0; /* overwrite '\n', if requested, on success */
|
||||
if (mode)
|
||||
*mode = mode_str;
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_splitcon - split the confinement context into a label and mode
|
||||
* @con: the confinement context
|
||||
* @mode: if non-NULL and a mode is present, will point to mode string in @con
|
||||
* on success
|
||||
*
|
||||
* Modifies the @con string to split it into separate label and mode strings. A
|
||||
* single trailing newline character will be stripped from @con, if found. The
|
||||
* @mode argument is optional. If @mode is NULL, @con will still be split
|
||||
* between the label and mode (if present) but @mode will not be set.
|
||||
*
|
||||
* Returns: a pointer to the label string or NULL on error
|
||||
*/
|
||||
char *aa_splitcon(char *con, char **mode)
|
||||
{
|
||||
return splitcon(con, strlen(con), true, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_getprocattr_raw - get the contents of @attr for @tid into @buf
|
||||
* @tid: tid of task to query
|
||||
* @attr: which /proc/<tid>/attr/<attr> to query
|
||||
* @buf: buffer to store the result in
|
||||
* @len: size of the buffer
|
||||
* @mode: if non-NULL and a mode is present, will point to mode string in @buf
|
||||
*
|
||||
* Returns: size of data read or -1 on error, and sets errno
|
||||
*/
|
||||
int aa_getprocattr_raw(pid_t tid, const char *attr, char *buf, int len,
|
||||
char **mode)
|
||||
{
|
||||
int rc = -1;
|
||||
int fd, ret;
|
||||
char *tmp = NULL;
|
||||
int size = 0;
|
||||
|
||||
if (!buf || len <= 0) {
|
||||
errno = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
tmp = procattr_path(tid, attr);
|
||||
if (!tmp)
|
||||
goto out;
|
||||
|
||||
fd = open(tmp, O_RDONLY);
|
||||
free(tmp);
|
||||
if (fd == -1) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
tmp = buf;
|
||||
do {
|
||||
ret = read(fd, tmp, len);
|
||||
if (ret <= 0)
|
||||
break;
|
||||
tmp += ret;
|
||||
size += ret;
|
||||
len -= ret;
|
||||
if (len < 0) {
|
||||
errno = ERANGE;
|
||||
goto out2;
|
||||
}
|
||||
} while (ret > 0);
|
||||
|
||||
if (ret < 0) {
|
||||
int saved;
|
||||
if (ret != -1) {
|
||||
errno = EPROTO;
|
||||
}
|
||||
saved = errno;
|
||||
(void)close(fd);
|
||||
errno = saved;
|
||||
goto out;
|
||||
} else if (size > 0 && buf[size - 1] != 0) {
|
||||
/* check for null termination */
|
||||
if (buf[size - 1] != '\n') {
|
||||
if (len == 0) {
|
||||
errno = ERANGE;
|
||||
goto out2;
|
||||
} else {
|
||||
buf[size] = 0;
|
||||
size++;
|
||||
}
|
||||
}
|
||||
|
||||
if (splitcon(buf, size, true, mode) != buf) {
|
||||
errno = EINVAL;
|
||||
goto out2;
|
||||
}
|
||||
}
|
||||
rc = size;
|
||||
|
||||
out2:
|
||||
(void)close(fd);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
#define INITIAL_GUESS_SIZE 128
|
||||
|
||||
/**
|
||||
* aa_getprocattr - get the contents of @attr for @tid into @label and @mode
|
||||
* @tid: tid of task to query
|
||||
* @attr: which /proc/<tid>/attr/<attr> to query
|
||||
* @label: allocated buffer the label is stored in
|
||||
* @mode: if non-NULL and a mode is present, will point to mode string in @label
|
||||
*
|
||||
* Returns: size of data read or -1 on error, and sets errno
|
||||
*
|
||||
* Guarantees that @label and @mode are null terminated. The length returned
|
||||
* is for all data including both @label and @mode, and maybe > than
|
||||
* strlen(@label) even if @mode is NULL
|
||||
*
|
||||
* Caller is responsible for freeing the buffer returned in @label. @mode is
|
||||
* always contained within @label's buffer and so NEVER do free(@mode)
|
||||
*/
|
||||
int aa_getprocattr(pid_t tid, const char *attr, char **label, char **mode)
|
||||
{
|
||||
int rc, size = INITIAL_GUESS_SIZE/2;
|
||||
char *buffer = NULL;
|
||||
|
||||
if (!label) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
do {
|
||||
char *tmp;
|
||||
|
||||
size <<= 1;
|
||||
tmp = realloc(buffer, size);
|
||||
if (!tmp) {
|
||||
free(buffer);
|
||||
return -1;
|
||||
}
|
||||
buffer = tmp;
|
||||
memset(buffer, 0, size);
|
||||
|
||||
rc = aa_getprocattr_raw(tid, attr, buffer, size, mode);
|
||||
} while (rc == -1 && errno == ERANGE);
|
||||
|
||||
if (rc == -1) {
|
||||
free(buffer);
|
||||
*label = NULL;
|
||||
if (mode)
|
||||
*mode = NULL;
|
||||
} else
|
||||
*label = buffer;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int setprocattr(pid_t tid, const char *attr, const char *buf, int len)
|
||||
{
|
||||
int rc = -1;
|
||||
int fd, ret;
|
||||
char *ctl = NULL;
|
||||
|
||||
if (!buf) {
|
||||
errno = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctl = procattr_path(tid, attr);
|
||||
if (!ctl)
|
||||
goto out;
|
||||
|
||||
fd = open(ctl, O_WRONLY);
|
||||
if (fd == -1) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = write(fd, buf, len);
|
||||
if (ret != len) {
|
||||
int saved;
|
||||
if (ret != -1) {
|
||||
errno = EPROTO;
|
||||
}
|
||||
saved = errno;
|
||||
(void)close(fd);
|
||||
errno = saved;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
(void)close(fd);
|
||||
|
||||
out:
|
||||
if (ctl) {
|
||||
free(ctl);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int aa_change_hat(const char *subprofile, unsigned long token)
|
||||
{
|
||||
int rc = -1;
|
||||
int len = 0;
|
||||
char *buf = NULL;
|
||||
const char *fmt = "changehat %016lx^%s";
|
||||
|
||||
/* both may not be null */
|
||||
if (!(token || subprofile)) {
|
||||
errno = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (subprofile && strnlen(subprofile, PATH_MAX + 1) > PATH_MAX) {
|
||||
errno = EPROTO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
len = asprintf(&buf, fmt, token, subprofile ? subprofile : "");
|
||||
if (len < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = setprocattr(aa_gettid(), "current", buf, len);
|
||||
out:
|
||||
if (buf) {
|
||||
/* clear local copy of magic token before freeing */
|
||||
memset(buf, '\0', len);
|
||||
free(buf);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* original change_hat interface */
|
||||
int __change_hat(char *subprofile, unsigned int token)
|
||||
{
|
||||
return aa_change_hat(subprofile, (unsigned long) token);
|
||||
}
|
||||
|
||||
int aa_change_profile(const char *profile)
|
||||
{
|
||||
char *buf = NULL;
|
||||
int len;
|
||||
int rc;
|
||||
|
||||
if (!profile) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = asprintf(&buf, "changeprofile %s", profile);
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
rc = setprocattr(aa_gettid(), "current", buf, len);
|
||||
|
||||
free(buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int aa_change_onexec(const char *profile)
|
||||
{
|
||||
char *buf = NULL;
|
||||
int len;
|
||||
int rc;
|
||||
|
||||
if (!profile) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = asprintf(&buf, "exec %s", profile);
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
rc = setprocattr(aa_gettid(), "exec", buf, len);
|
||||
|
||||
free(buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* create an alias for the old change_hat@IMMUNIX_1.0 symbol */
|
||||
extern typeof((__change_hat)) __old_change_hat __attribute__((alias ("__change_hat")));
|
||||
symbol_version(__old_change_hat, change_hat, IMMUNIX_1.0);
|
||||
default_symbol_version(__change_hat, change_hat, APPARMOR_1.0);
|
||||
|
||||
|
||||
int aa_change_hatv(const char *subprofiles[], unsigned long token)
|
||||
{
|
||||
int size, totallen = 0, hatcount = 0;
|
||||
int rc = -1;
|
||||
const char **hats;
|
||||
char *pos, *buf = NULL;
|
||||
const char *cmd = "changehat";
|
||||
|
||||
/* both may not be null */
|
||||
if (!token && !(subprofiles && *subprofiles)) {
|
||||
errno = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* validate hat lengths and while we are at it count how many and
|
||||
* mem required */
|
||||
if (subprofiles) {
|
||||
for (hats = subprofiles; *hats; hats++) {
|
||||
int len = strnlen(*hats, PATH_MAX + 1);
|
||||
if (len > PATH_MAX) {
|
||||
errno = EPROTO;
|
||||
goto out;
|
||||
}
|
||||
totallen += len + 1;
|
||||
hatcount++;
|
||||
}
|
||||
}
|
||||
|
||||
/* allocate size of cmd + space + token + ^ + vector of hats */
|
||||
size = strlen(cmd) + 18 + totallen + 1;
|
||||
buf = malloc(size);
|
||||
if (!buf) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* setup command string which is of the form
|
||||
* changehat <token>^hat1\0hat2\0hat3\0..\0
|
||||
*/
|
||||
sprintf(buf, "%s %016lx^", cmd, token);
|
||||
pos = buf + strlen(buf);
|
||||
if (subprofiles) {
|
||||
for (hats = subprofiles; *hats; hats++) {
|
||||
strcpy(pos, *hats);
|
||||
pos += strlen(*hats) + 1;
|
||||
}
|
||||
} else
|
||||
/* step pos past trailing \0 */
|
||||
pos++;
|
||||
|
||||
rc = setprocattr(aa_gettid(), "current", buf, pos - buf);
|
||||
|
||||
out:
|
||||
if (buf) {
|
||||
/* clear local copy of magic token before freeing */
|
||||
memset(buf, '\0', size);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* change_hat_vargs - change_hatv but passing the hats as fn arguments
|
||||
* @token: the magic token
|
||||
* @nhat: the number of hats being passed in the arguments
|
||||
* ...: a argument list of const char * being passed
|
||||
*
|
||||
* change_hat_vargs can be called directly but it is meant to be called
|
||||
* through its macro wrapper of the same name. Which automatically
|
||||
* fills in the nhats arguments based on the number of parameters
|
||||
* passed.
|
||||
* to call change_hat_vargs direction do
|
||||
* (change_hat_vargs)(token, nhats, hat1, hat2...)
|
||||
*/
|
||||
int (aa_change_hat_vargs)(unsigned long token, int nhats, ...)
|
||||
{
|
||||
va_list ap;
|
||||
const char *argv[nhats+1];
|
||||
int i;
|
||||
|
||||
va_start(ap, nhats);
|
||||
for (i = 0; i < nhats ; i++) {
|
||||
argv[i] = va_arg(ap, char *);
|
||||
}
|
||||
argv[nhats] = NULL;
|
||||
va_end(ap);
|
||||
return aa_change_hatv(argv, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_gettaskcon - get the confinement context for task @target in an allocated buffer
|
||||
* @target: task to query
|
||||
* @label: pointer to returned buffer with the label
|
||||
* @mode: if non-NULL and a mode is present, will point to mode string in @label
|
||||
*
|
||||
* Returns: length of confinement context or -1 on error and sets errno
|
||||
*
|
||||
* Guarantees that @label and @mode are null terminated. The length returned
|
||||
* is for all data including both @label and @mode, and maybe > than
|
||||
* strlen(@label) even if @mode is NULL
|
||||
*
|
||||
* Caller is responsible for freeing the buffer returned in @label. @mode is
|
||||
* always contained within @label's buffer and so NEVER do free(@mode)
|
||||
*/
|
||||
int aa_gettaskcon(pid_t target, char **label, char **mode)
|
||||
{
|
||||
return aa_getprocattr(target, "current", label, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_getcon - get the confinement context for current task in an allocated buffer
|
||||
* @label: pointer to return buffer with the label if successful
|
||||
* @mode: if non-NULL and a mode is present, will point to mode string in @label
|
||||
*
|
||||
* Returns: length of confinement context or -1 on error and sets errno
|
||||
*
|
||||
* Guarantees that @label and @mode are null terminated. The length returned
|
||||
* is for all data including both @label and @mode, and may > than
|
||||
* strlen(@label) even if @mode is NULL
|
||||
*
|
||||
* Caller is responsible for freeing the buffer returned in @label. @mode is
|
||||
* always contained within @label's buffer and so NEVER do free(@mode)
|
||||
*/
|
||||
int aa_getcon(char **label, char **mode)
|
||||
{
|
||||
return aa_gettaskcon(aa_gettid(), label, mode);
|
||||
}
|
||||
|
||||
|
||||
#ifndef SO_PEERSEC
|
||||
#define SO_PEERSEC 31
|
||||
#endif
|
||||
|
||||
/**
|
||||
* aa_getpeercon_raw - get the confinement context of the socket's peer (other end)
|
||||
* @fd: socket to get peer confinement context for
|
||||
* @buf: buffer to store the result in
|
||||
* @len: initially contains size of the buffer, returns size of data read
|
||||
* @mode: if non-NULL and a mode is present, will point to mode string in @buf
|
||||
*
|
||||
* Returns: length of confinement context including null termination or -1 on
|
||||
* error if errno == ERANGE then @len will hold the size needed
|
||||
*/
|
||||
int aa_getpeercon_raw(int fd, char *buf, int *len, char **mode)
|
||||
{
|
||||
socklen_t optlen = *len;
|
||||
int rc;
|
||||
|
||||
if (optlen <= 0 || buf == NULL) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, buf, &optlen);
|
||||
if (rc == -1 || optlen <= 0)
|
||||
goto out;
|
||||
|
||||
/* check for null termination */
|
||||
if (buf[optlen - 1] != 0) {
|
||||
if (optlen < *len) {
|
||||
buf[optlen] = 0;
|
||||
optlen++;
|
||||
} else {
|
||||
/* buf needs to be bigger by 1 */
|
||||
rc = -1;
|
||||
errno = ERANGE;
|
||||
optlen++;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (splitcon(buf, optlen - 1, false, mode) != buf) {
|
||||
rc = -1;
|
||||
errno = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = optlen;
|
||||
out:
|
||||
*len = optlen;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_getpeercon - get the confinement context of the socket's peer (other end)
|
||||
* @fd: socket to get peer confinement context for
|
||||
* @label: pointer to allocated buffer with the label
|
||||
* @mode: if non-NULL and a mode is present, will point to mode string in @label
|
||||
*
|
||||
* Returns: length of confinement context including null termination or -1 on error
|
||||
*
|
||||
* Guarantees that @label and @mode are null terminated. The length returned
|
||||
* is for all data including both @label and @mode, and maybe > than
|
||||
* strlen(@label) even if @mode is NULL
|
||||
*
|
||||
* Caller is responsible for freeing the buffer returned in @label. @mode is
|
||||
* always contained within @label's buffer and so NEVER do free(@mode)
|
||||
*/
|
||||
int aa_getpeercon(int fd, char **label, char **mode)
|
||||
{
|
||||
int rc, last_size, size = INITIAL_GUESS_SIZE;
|
||||
char *buffer = NULL;
|
||||
|
||||
if (!label) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
do {
|
||||
char *tmp;
|
||||
|
||||
last_size = size;
|
||||
tmp = realloc(buffer, size);
|
||||
if (!tmp) {
|
||||
free(buffer);
|
||||
return -1;
|
||||
}
|
||||
buffer = tmp;
|
||||
memset(buffer, 0, size);
|
||||
|
||||
rc = aa_getpeercon_raw(fd, buffer, &size, mode);
|
||||
/* size should contain actual size needed if errno == ERANGE */
|
||||
} while (rc == -1 && errno == ERANGE && size > last_size);
|
||||
|
||||
if (rc == -1) {
|
||||
free(buffer);
|
||||
*label = NULL;
|
||||
if (mode)
|
||||
*mode = NULL;
|
||||
size = -1;
|
||||
} else
|
||||
*label = buffer;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static pthread_once_t aafs_access_control = PTHREAD_ONCE_INIT;
|
||||
static char *aafs_access = NULL;
|
||||
|
||||
static void aafs_access_init_once(void)
|
||||
{
|
||||
char *aafs;
|
||||
int ret;
|
||||
|
||||
ret = aa_find_mountpoint(&aafs);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
ret = asprintf(&aafs_access, "%s/.access", aafs);
|
||||
if (ret < 0)
|
||||
aafs_access = NULL;
|
||||
|
||||
free(aafs);
|
||||
}
|
||||
|
||||
/* "allow 0x00000000\ndeny 0x00000000\naudit 0x00000000\nquiet 0x00000000\n" */
|
||||
#define QUERY_LABEL_REPLY_LEN 67
|
||||
|
||||
/**
|
||||
* aa_query_label - query the access(es) of a label
|
||||
* @mask: permission bits to query
|
||||
* @query: binary query string, must be offset by AA_QUERY_CMD_LABEL_SIZE
|
||||
* @size: size of the query string must include AA_QUERY_CMD_LABEL_SIZE
|
||||
* @allowed: upon successful return, will be 1 if query is allowed and 0 if not
|
||||
* @audited: upon successful return, will be 1 if query should be audited and 0
|
||||
* if not
|
||||
*
|
||||
* Returns: 0 on success else -1 and sets errno. If -1 is returned and errno is
|
||||
* ENOENT, the subject label in the query string is unknown to the
|
||||
* kernel.
|
||||
*/
|
||||
int query_label(uint32_t mask, char *query, size_t size, int *allowed,
|
||||
int *audited)
|
||||
{
|
||||
char buf[QUERY_LABEL_REPLY_LEN];
|
||||
uint32_t allow, deny, audit, quiet;
|
||||
int fd, ret, saved;
|
||||
|
||||
if (!mask || size <= AA_QUERY_CMD_LABEL_SIZE) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = pthread_once(&aafs_access_control, aafs_access_init_once);
|
||||
if (ret) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
} else if (!aafs_access) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = open(aafs_access, O_RDWR);
|
||||
if (fd == -1) {
|
||||
if (errno == ENOENT)
|
||||
errno = EPROTONOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(query, AA_QUERY_CMD_LABEL, AA_QUERY_CMD_LABEL_SIZE);
|
||||
errno = 0;
|
||||
ret = write(fd, query, size);
|
||||
if (ret != size) {
|
||||
if (ret >= 0)
|
||||
errno = EPROTO;
|
||||
/* IMPORTANT: This is the only valid error path that can have
|
||||
* errno set to ENOENT. It indicates that the subject label
|
||||
* could not be found by the kernel.
|
||||
*/
|
||||
(void)close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = read(fd, buf, QUERY_LABEL_REPLY_LEN);
|
||||
saved = errno;
|
||||
(void)close(fd);
|
||||
errno = saved;
|
||||
if (ret != QUERY_LABEL_REPLY_LEN) {
|
||||
errno = EPROTO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = sscanf(buf, "allow 0x%8" SCNx32 "\n"
|
||||
"deny 0x%8" SCNx32 "\n"
|
||||
"audit 0x%8" SCNx32 "\n"
|
||||
"quiet 0x%8" SCNx32 "\n",
|
||||
&allow, &deny, &audit, &quiet);
|
||||
if (ret != 4) {
|
||||
errno = EPROTONOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*allowed = mask & ~(allow & ~deny) ? 0 : 1;
|
||||
if (!(*allowed))
|
||||
audit = 0xFFFFFFFF;
|
||||
*audited = mask & ~(audit & ~quiet) ? 0 : 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* export multiple aa_query_label symbols to compensate for downstream
|
||||
* releases with differing symbol versions. */
|
||||
extern typeof((query_label)) __aa_query_label __attribute__((alias ("query_label")));
|
||||
symbol_version(__aa_query_label, aa_query_label, APPARMOR_1.1);
|
||||
default_symbol_version(query_label, aa_query_label, APPARMOR_2.9);
|
||||
|
||||
|
||||
/**
|
||||
* aa_query_file_path_len - query access permissions for a file @path
|
||||
* @mask: permission bits to query
|
||||
* @label: apparmor label
|
||||
* @label_len: length of @label (does not include any terminating nul byte)
|
||||
* @path: file path to query permissions for
|
||||
* @path_len: length of @path (does not include any terminating nul byte)
|
||||
* @allowed: upon successful return, will be 1 if query is allowed and 0 if not
|
||||
* @audited: upon successful return, will be 1 if query should be audited and 0
|
||||
* if not
|
||||
*
|
||||
* Returns: 0 on success else -1 and sets errno. If -1 is returned and errno is
|
||||
* ENOENT, the subject label in the query string is unknown to the
|
||||
* kernel.
|
||||
*/
|
||||
int aa_query_file_path_len(uint32_t mask, const char *label, size_t label_len,
|
||||
const char *path, size_t path_len, int *allowed,
|
||||
int *audited)
|
||||
{
|
||||
autofree char *query = NULL;
|
||||
|
||||
/* + 1 for null separator */
|
||||
size_t size = AA_QUERY_CMD_LABEL_SIZE + label_len + 1 + path_len;
|
||||
query = malloc(size + 1);
|
||||
if (!query)
|
||||
return -1;
|
||||
memcpy(query + AA_QUERY_CMD_LABEL_SIZE, label, label_len);
|
||||
/* null separator */
|
||||
query[AA_QUERY_CMD_LABEL_SIZE + label_len] = 0;
|
||||
query[AA_QUERY_CMD_LABEL_SIZE + label_len + 1] = AA_CLASS_FILE;
|
||||
memcpy(query + AA_QUERY_CMD_LABEL_SIZE + label_len + 2, path, path_len);
|
||||
return aa_query_label(mask, query, size , allowed, audited);
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_query_file_path - query access permissions for a file @path
|
||||
* @mask: permission bits to query
|
||||
* @label: apparmor label
|
||||
* @path: file path to query permissions for
|
||||
* @allowed: upon successful return, will be 1 if query is allowed and 0 if not
|
||||
* @audited: upon successful return, will be 1 if query should be audited and 0
|
||||
* if not
|
||||
*
|
||||
* Returns: 0 on success else -1 and sets errno. If -1 is returned and errno is
|
||||
* ENOENT, the subject label in the query string is unknown to the
|
||||
* kernel.
|
||||
*/
|
||||
int aa_query_file_path(uint32_t mask, const char *label, const char *path,
|
||||
int *allowed, int *audited)
|
||||
{
|
||||
return aa_query_file_path_len(mask, label, strlen(label), path,
|
||||
strlen(path), allowed, audited);
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_query_link_path_len - query access permissions for a hard link @link
|
||||
* @label: apparmor label
|
||||
* @label_len: length of @label (does not include any terminating nul byte)
|
||||
* @target: file path that hard link will point to
|
||||
* @target_len: length of @target (does not include any terminating nul byte)
|
||||
* @link: file path of hard link
|
||||
* @link_len: length of @link (does not include any terminating nul byte)
|
||||
* @allowed: upon successful return, will be 1 if query is allowed and 0 if not
|
||||
* @audited: upon successful return, will be 1 if query should be audited and 0
|
||||
* if not
|
||||
*
|
||||
* Returns: 0 on success else -1 and sets errno. If -1 is returned and errno is
|
||||
* ENOENT, the subject label in the query string is unknown to the
|
||||
* kernel.
|
||||
*/
|
||||
int aa_query_link_path_len(const char *label, size_t label_len,
|
||||
const char *target, size_t target_len,
|
||||
const char *link, size_t link_len,
|
||||
int *allowed, int *audited)
|
||||
{
|
||||
autofree char *query = NULL;
|
||||
int rc;
|
||||
|
||||
/* + 1 for null separators */
|
||||
size_t size = AA_QUERY_CMD_LABEL_SIZE + label_len + 1 + target_len +
|
||||
1 + link_len;
|
||||
size_t pos = AA_QUERY_CMD_LABEL_SIZE;
|
||||
|
||||
query = malloc(size);
|
||||
if (!query)
|
||||
return -1;
|
||||
memcpy(query + pos, label, label_len);
|
||||
/* null separator */
|
||||
pos += label_len;
|
||||
query[pos] = 0;
|
||||
query[++pos] = AA_CLASS_FILE;
|
||||
memcpy(query + pos + 1, link, link_len);
|
||||
/* The kernel does the query in two parts we could similate this
|
||||
* doing the following, however as long as policy is compiled
|
||||
* correctly this isn't requied, and it requires and extra round
|
||||
* trip to the kernel and adds a race on policy replacement between
|
||||
* the two queries.
|
||||
*
|
||||
rc = aa_query_label(AA_MAY_LINK, query, size, allowed, audited);
|
||||
if (rc || !*allowed)
|
||||
return rc;
|
||||
*/
|
||||
pos += 1 + link_len;
|
||||
query[pos] = 0;
|
||||
memcpy(query + pos + 1, target, target_len);
|
||||
return aa_query_label(AA_MAY_LINK, query, size, allowed, audited);
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_query_link_path - query access permissions for a hard link @link
|
||||
* @label: apparmor label
|
||||
* @target: file path that hard link will point to
|
||||
* @link: file path of hard link
|
||||
* @allowed: upon successful return, will be 1 if query is allowed and 0 if not
|
||||
* @audited: upon successful return, will be 1 if query should be audited and 0
|
||||
* if not
|
||||
*
|
||||
* Returns: 0 on success else -1 and sets errno. If -1 is returned and errno is
|
||||
* ENOENT, the subject label in the query string is unknown to the
|
||||
* kernel.
|
||||
*/
|
||||
int aa_query_link_path(const char *label, const char *target, const char *link,
|
||||
int *allowed, int *audited)
|
||||
{
|
||||
return aa_query_link_path_len(label, strlen(label), target,
|
||||
strlen(target), link, strlen(link),
|
||||
allowed, audited);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -51,49 +51,3 @@ APPARMOR_2.9 {
|
||||
local:
|
||||
*;
|
||||
} APPARMOR_1.1;
|
||||
|
||||
APPARMOR_2.10 {
|
||||
global:
|
||||
aa_query_file_path;
|
||||
aa_query_file_path_len;
|
||||
aa_query_link_path;
|
||||
aa_query_link_path_len;
|
||||
aa_features_new;
|
||||
aa_features_new_from_string;
|
||||
aa_features_new_from_kernel;
|
||||
aa_features_ref;
|
||||
aa_features_unref;
|
||||
aa_features_write_to_file;
|
||||
aa_features_is_equal;
|
||||
aa_features_supports;
|
||||
aa_kernel_interface_new;
|
||||
aa_kernel_interface_ref;
|
||||
aa_kernel_interface_unref;
|
||||
aa_kernel_interface_load_policy;
|
||||
aa_kernel_interface_load_policy_from_file;
|
||||
aa_kernel_interface_load_policy_from_fd;
|
||||
aa_kernel_interface_replace_policy;
|
||||
aa_kernel_interface_replace_policy_from_file;
|
||||
aa_kernel_interface_replace_policy_from_fd;
|
||||
aa_kernel_interface_remove_policy;
|
||||
aa_kernel_interface_write_policy;
|
||||
aa_policy_cache_new;
|
||||
aa_policy_cache_ref;
|
||||
aa_policy_cache_unref;
|
||||
aa_policy_cache_remove;
|
||||
aa_policy_cache_replace_all;
|
||||
aa_splitcon;
|
||||
local:
|
||||
*;
|
||||
} APPARMOR_2.9;
|
||||
|
||||
PRIVATE {
|
||||
global:
|
||||
_aa_is_blacklisted;
|
||||
_aa_autofree;
|
||||
_aa_autoclose;
|
||||
_aa_autofclose;
|
||||
_aa_dirat_for_each;
|
||||
local:
|
||||
*;
|
||||
};
|
||||
|
@@ -1,277 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014
|
||||
* Canonical, Ltd. (All rights reserved)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, contact Novell, Inc. or Canonical
|
||||
* Ltd.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/apparmor.h>
|
||||
|
||||
#include "private.h"
|
||||
|
||||
#define CACHE_FEATURES_FILE ".features"
|
||||
|
||||
struct aa_policy_cache {
|
||||
unsigned int ref_count;
|
||||
aa_features *features;
|
||||
aa_features *kernel_features;
|
||||
int dirfd;
|
||||
};
|
||||
|
||||
static int clear_cache_cb(int dirfd, const char *path, struct stat *st,
|
||||
void *data unused)
|
||||
{
|
||||
/* remove regular files */
|
||||
if (S_ISREG(st->st_mode))
|
||||
return unlinkat(dirfd, path, 0);
|
||||
|
||||
/* do nothing with other file types */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_cache(aa_policy_cache *policy_cache, aa_features *features)
|
||||
{
|
||||
if (aa_policy_cache_remove(policy_cache->dirfd, "."))
|
||||
return -1;
|
||||
|
||||
if (aa_features_write_to_file(features, policy_cache->dirfd,
|
||||
CACHE_FEATURES_FILE) == -1)
|
||||
return -1;
|
||||
|
||||
aa_features_unref(policy_cache->features);
|
||||
policy_cache->features = aa_features_ref(features);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int init_cache_features(aa_policy_cache *policy_cache,
|
||||
aa_features *kernel_features, bool create)
|
||||
{
|
||||
bool call_create_cache = false;
|
||||
|
||||
if (aa_features_new(&policy_cache->features, policy_cache->dirfd,
|
||||
CACHE_FEATURES_FILE)) {
|
||||
policy_cache->features = NULL;
|
||||
if (!create || errno != ENOENT)
|
||||
return -1;
|
||||
|
||||
/* The cache directory needs to be created */
|
||||
call_create_cache = true;
|
||||
} else if (!aa_features_is_equal(policy_cache->features,
|
||||
kernel_features)) {
|
||||
if (!create) {
|
||||
errno = EEXIST;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* The cache directory needs to be refreshed */
|
||||
call_create_cache = true;
|
||||
}
|
||||
|
||||
return call_create_cache ?
|
||||
create_cache(policy_cache, kernel_features) : 0;
|
||||
}
|
||||
|
||||
struct replace_all_cb_data {
|
||||
aa_policy_cache *policy_cache;
|
||||
aa_kernel_interface *kernel_interface;
|
||||
};
|
||||
|
||||
static int replace_all_cb(int dirfd unused, const char *name, struct stat *st,
|
||||
void *cb_data)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
if (!S_ISDIR(st->st_mode) && !_aa_is_blacklisted(name)) {
|
||||
struct replace_all_cb_data *data;
|
||||
|
||||
data = (struct replace_all_cb_data *) cb_data;
|
||||
retval = aa_kernel_interface_replace_policy_from_file(data->kernel_interface,
|
||||
data->policy_cache->dirfd,
|
||||
name);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_policy_cache_new - create a new aa_policy_cache object from a path
|
||||
* @policy_cache: will point to the address of an allocated and initialized
|
||||
* aa_policy_cache_new object upon success
|
||||
* @kernel_features: features representing a kernel (may be NULL if you want to
|
||||
* use the features of the currently running kernel)
|
||||
* @dirfd: directory file descriptor or AT_FDCWD (see openat(2))
|
||||
* @path: path to the policy cache
|
||||
* @max_caches: The maximum number of policy caches, one for each unique set of
|
||||
* kernel features, before older caches are auto-reaped. 0 means
|
||||
* that no new caches should be created (existing, valid caches
|
||||
* will be used) and auto-reaping is disabled. UINT16_MAX means
|
||||
* that a cache can be created and auto-reaping is disabled.
|
||||
*
|
||||
* Returns: 0 on success, -1 on error with errno set and *@policy_cache
|
||||
* pointing to NULL
|
||||
*/
|
||||
int aa_policy_cache_new(aa_policy_cache **policy_cache,
|
||||
aa_features *kernel_features,
|
||||
int dirfd, const char *path, uint16_t max_caches)
|
||||
{
|
||||
aa_policy_cache *pc;
|
||||
bool create = max_caches > 0;
|
||||
|
||||
*policy_cache = NULL;
|
||||
|
||||
if (!path) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (max_caches > 1) {
|
||||
errno = ENOTSUP;
|
||||
return -1;
|
||||
}
|
||||
|
||||
pc = calloc(1, sizeof(*pc));
|
||||
if (!pc) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
pc->dirfd = -1;
|
||||
aa_policy_cache_ref(pc);
|
||||
|
||||
open:
|
||||
pc->dirfd = openat(dirfd, path, O_RDONLY | O_CLOEXEC | O_DIRECTORY);
|
||||
if (pc->dirfd < 0) {
|
||||
int save;
|
||||
|
||||
/* does the dir exist? */
|
||||
if (create && errno == ENOENT) {
|
||||
if (mkdirat(dirfd, path, 0700) == 0)
|
||||
goto open;
|
||||
PERROR("Can't create cache directory '%s': %m\n", path);
|
||||
} else if (create) {
|
||||
PERROR("Can't update cache directory '%s': %m\n", path);
|
||||
} else {
|
||||
PDEBUG("Cache directory '%s' does not exist\n", path);
|
||||
}
|
||||
|
||||
save = errno;
|
||||
aa_policy_cache_unref(pc);
|
||||
errno = save;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (kernel_features) {
|
||||
aa_features_ref(kernel_features);
|
||||
} else if (aa_features_new_from_kernel(&kernel_features) == -1) {
|
||||
int save = errno;
|
||||
|
||||
aa_policy_cache_unref(pc);
|
||||
errno = save;
|
||||
return -1;
|
||||
}
|
||||
pc->kernel_features = kernel_features;
|
||||
|
||||
if (init_cache_features(pc, kernel_features, create)) {
|
||||
int save = errno;
|
||||
|
||||
aa_policy_cache_unref(pc);
|
||||
errno = save;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*policy_cache = pc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_policy_cache_ref - increments the ref count of an aa_policy_cache object
|
||||
* @policy_cache: the policy_cache
|
||||
*
|
||||
* Returns: the policy_cache
|
||||
*/
|
||||
aa_policy_cache *aa_policy_cache_ref(aa_policy_cache *policy_cache)
|
||||
{
|
||||
atomic_inc(&policy_cache->ref_count);
|
||||
return policy_cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_policy_cache_unref - decrements the ref count and frees the aa_policy_cache object when 0
|
||||
* @policy_cache: the policy_cache (can be NULL)
|
||||
*/
|
||||
void aa_policy_cache_unref(aa_policy_cache *policy_cache)
|
||||
{
|
||||
if (policy_cache && atomic_dec_and_test(&policy_cache->ref_count)) {
|
||||
aa_features_unref(policy_cache->features);
|
||||
aa_features_unref(policy_cache->kernel_features);
|
||||
if (policy_cache->dirfd != -1)
|
||||
close(policy_cache->dirfd);
|
||||
free(policy_cache);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_policy_cache_remove - removes all policy cache files under a path
|
||||
* @dirfd: directory file descriptor or AT_FDCWD (see openat(2))
|
||||
* @path: the path to a policy cache directory
|
||||
*
|
||||
* Returns: 0 on success, -1 on error with errno set
|
||||
*/
|
||||
int aa_policy_cache_remove(int dirfd, const char *path)
|
||||
{
|
||||
return _aa_dirat_for_each(dirfd, path, NULL, clear_cache_cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_policy_cache_replace_all - performs a kernel policy replacement of all cached policies
|
||||
* @policy_cache: the policy_cache
|
||||
* @kernel_interface: the kernel interface to use when doing the replacement
|
||||
* (may be NULL if the currently running kernel features
|
||||
* were used when calling aa_policy_cache_new())
|
||||
*
|
||||
* Returns: 0 on success, -1 on error with errno set and features pointing to
|
||||
* NULL
|
||||
*/
|
||||
int aa_policy_cache_replace_all(aa_policy_cache *policy_cache,
|
||||
aa_kernel_interface *kernel_interface)
|
||||
{
|
||||
struct replace_all_cb_data cb_data;
|
||||
int retval;
|
||||
|
||||
if (kernel_interface) {
|
||||
aa_kernel_interface_ref(kernel_interface);
|
||||
} else if (aa_kernel_interface_new(&kernel_interface,
|
||||
policy_cache->kernel_features,
|
||||
NULL) == -1) {
|
||||
kernel_interface = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
cb_data.policy_cache = policy_cache;
|
||||
cb_data.kernel_interface = kernel_interface;
|
||||
retval = _aa_dirat_for_each(policy_cache->dirfd, ".", &cb_data,
|
||||
replace_all_cb);
|
||||
|
||||
aa_kernel_interface_unref(kernel_interface);
|
||||
|
||||
return retval;
|
||||
}
|
@@ -1,252 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014 Canonical Ltd.
|
||||
*
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the file
|
||||
* COPYING.LGPL.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "private.h"
|
||||
|
||||
/**
|
||||
* Allow libapparmor to build on older systems where secure_getenv() is still
|
||||
* named __secure_getenv(). This snippet was taken from the glibc wiki
|
||||
* (https://sourceware.org/glibc/wiki/Tips_and_Tricks/secure_getenv).
|
||||
*/
|
||||
#ifndef HAVE_SECURE_GETENV
|
||||
#ifdef HAVE___SECURE_GETENV
|
||||
#define secure_getenv __secure_getenv
|
||||
#else
|
||||
#error neither secure_getenv nor __secure_getenv is available
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct ignored_suffix_t {
|
||||
const char * text;
|
||||
int len;
|
||||
int silent;
|
||||
};
|
||||
|
||||
static struct ignored_suffix_t ignored_suffixes[] = {
|
||||
/* Debian packging files, which are in flux during install
|
||||
should be silently ignored. */
|
||||
{ ".dpkg-new", 9, 1 },
|
||||
{ ".dpkg-old", 9, 1 },
|
||||
{ ".dpkg-dist", 10, 1 },
|
||||
{ ".dpkg-bak", 9, 1 },
|
||||
/* RPM packaging files have traditionally not been silently
|
||||
ignored */
|
||||
{ ".rpmnew", 7, 0 },
|
||||
{ ".rpmsave", 8, 0 },
|
||||
/* patch file backups/conflicts */
|
||||
{ ".orig", 5, 0 },
|
||||
{ ".rej", 4, 0 },
|
||||
/* Backup files should be mentioned */
|
||||
{ "~", 1, 0 },
|
||||
{ NULL, 0, 0 }
|
||||
};
|
||||
|
||||
#define DEBUG_ENV_VAR "LIBAPPARMOR_DEBUG"
|
||||
|
||||
void print_error(bool honor_env_var, const char *ident, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int openlog_options = 0;
|
||||
|
||||
if (honor_env_var && secure_getenv(DEBUG_ENV_VAR))
|
||||
openlog_options |= LOG_PERROR;
|
||||
|
||||
openlog(ident, openlog_options, LOG_ERR);
|
||||
va_start(args, fmt);
|
||||
vsyslog(LOG_ERR, fmt, args);
|
||||
va_end(args);
|
||||
closelog();
|
||||
}
|
||||
|
||||
void print_debug(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
if (!secure_getenv(DEBUG_ENV_VAR))
|
||||
return;
|
||||
|
||||
va_start(args, fmt);
|
||||
vfprintf(stderr, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void atomic_inc(unsigned int *v)
|
||||
{
|
||||
__sync_add_and_fetch(v, 1);
|
||||
}
|
||||
|
||||
bool atomic_dec_and_test(unsigned int *v)
|
||||
{
|
||||
return __sync_sub_and_fetch(v, 1) == 0;
|
||||
}
|
||||
|
||||
int _aa_is_blacklisted(const char *name)
|
||||
{
|
||||
size_t name_len = strlen(name);
|
||||
struct ignored_suffix_t *suffix;
|
||||
|
||||
/* skip dot files and files with no name */
|
||||
if (!name_len || *name == '.' || !strcmp(name, "README"))
|
||||
return 1;
|
||||
|
||||
/* skip blacklisted suffixes */
|
||||
for (suffix = ignored_suffixes; suffix->text; suffix++) {
|
||||
char *found;
|
||||
if ( (found = strstr((char *) name, suffix->text)) &&
|
||||
found - name + suffix->len == name_len ) {
|
||||
if (!suffix->silent)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* automaticly free allocated variables tagged with autofree on fn exit */
|
||||
void _aa_autofree(void *p)
|
||||
{
|
||||
void **_p = (void**)p;
|
||||
free(*_p);
|
||||
}
|
||||
|
||||
void _aa_autoclose(int *fd)
|
||||
{
|
||||
if (*fd != -1) {
|
||||
/* if close was interrupted retry */
|
||||
while(close(*fd) == -1 && errno == EINTR);
|
||||
*fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void _aa_autofclose(FILE **f)
|
||||
{
|
||||
if (*f) {
|
||||
fclose(*f);
|
||||
*f = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int _aa_asprintf(char **strp, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int rc;
|
||||
|
||||
va_start(args, fmt);
|
||||
rc = vasprintf(strp, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (rc == -1)
|
||||
*strp = NULL;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dot_or_dot_dot_filter(const struct dirent *ent)
|
||||
{
|
||||
if (strcmp(ent->d_name, ".") == 0 ||
|
||||
strcmp(ent->d_name, "..") == 0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* _aa_dirat_for_each: iterate over a directory calling cb for each entry
|
||||
* @dirfd: already opened directory
|
||||
* @name: name of the directory (NOT NULL)
|
||||
* @data: data pointer to pass to the callback fn (MAY BE NULL)
|
||||
* @cb: the callback to pass entry too (NOT NULL)
|
||||
*
|
||||
* Iterate over the entries in a directory calling cb for each entry.
|
||||
* The directory to iterate is determined by a combination of @dirfd and
|
||||
* @name.
|
||||
*
|
||||
* See the openat section of the open(2) man page for details on valid @dirfd
|
||||
* and @name combinations. This function does accept AT_FDCWD as @dirfd if
|
||||
* @name should be considered relative to the current working directory.
|
||||
*
|
||||
* Pass "." for @name if @dirfd is the directory to iterate over.
|
||||
*
|
||||
* The cb function is called with the DIR in use and the name of the
|
||||
* file in that directory. If the file is to be opened it should
|
||||
* use the openat, fstatat, and related fns.
|
||||
*
|
||||
* Returns: 0 on success, else -1 and errno is set to the error code
|
||||
*/
|
||||
int _aa_dirat_for_each(int dirfd, const char *name, void *data,
|
||||
int (* cb)(int, const char *, struct stat *, void *))
|
||||
{
|
||||
autofree struct dirent **namelist = NULL;
|
||||
autoclose int cb_dirfd = -1;
|
||||
int i, num_dirs, rc;
|
||||
|
||||
if (!cb || !name) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
cb_dirfd = openat(dirfd, name, O_RDONLY | O_CLOEXEC | O_DIRECTORY);
|
||||
if (cb_dirfd == -1) {
|
||||
PDEBUG("could not open directory '%s': %m\n", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
num_dirs = scandirat(cb_dirfd, ".", &namelist,
|
||||
dot_or_dot_dot_filter, NULL);
|
||||
if (num_dirs == -1) {
|
||||
PDEBUG("scandirat of directory '%s' failed: %m\n", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (rc = 0, i = 0; i < num_dirs; i++) {
|
||||
/* Must cycle through all dirs so that each one is autofreed */
|
||||
autofree struct dirent *dir = namelist[i];
|
||||
struct stat my_stat;
|
||||
|
||||
if (rc)
|
||||
continue;
|
||||
|
||||
if (fstatat(cb_dirfd, dir->d_name, &my_stat, 0)) {
|
||||
PDEBUG("stat failed for '%s': %m\n", dir->d_name);
|
||||
rc = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cb(cb_dirfd, dir->d_name, &my_stat, data)) {
|
||||
PDEBUG("dir_for_each callback failed for '%s'\n",
|
||||
dir->d_name);
|
||||
rc = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014 Canonical Ltd.
|
||||
*
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the file
|
||||
* COPYING.LGPL.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _AA_PRIVATE_H
|
||||
#define _AA_PRIVATE_H 1
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <sys/apparmor_private.h>
|
||||
|
||||
#define autofree __attribute((cleanup(_aa_autofree)))
|
||||
#define autoclose __attribute((cleanup(_aa_autoclose)))
|
||||
#define autofclose __attribute((cleanup(_aa_autofclose)))
|
||||
#define unused __attribute__ ((unused))
|
||||
|
||||
#define asprintf _aa_asprintf
|
||||
|
||||
#if ENABLE_DEBUG_OUTPUT
|
||||
|
||||
#define PERROR(fmt, args...) print_error(true, "libapparmor", fmt, ## args)
|
||||
#define PDEBUG(fmt, args...) print_debug("libapparmor: " fmt, ## args)
|
||||
|
||||
#else /* ENABLE_DEBUG_OUTPUT */
|
||||
|
||||
#define PERROR(fmt, args...) print_error(false, "libapparmor", fmt, ## args)
|
||||
#define PDEBUG(fmt, args...) /* do nothing */
|
||||
|
||||
#endif /* ENABLE_DEBUG_OUTPUT */
|
||||
|
||||
#define MY_TEST(statement, error) \
|
||||
if (!(statement)) { \
|
||||
fprintf(stderr, "FAIL: %s\n", error); \
|
||||
rc = 1; \
|
||||
}
|
||||
|
||||
void print_error(bool honor_env_var, const char *ident, const char *fmt, ...);
|
||||
void print_debug(const char *fmt, ...);
|
||||
|
||||
void atomic_inc(unsigned int *v);
|
||||
bool atomic_dec_and_test(unsigned int *v);
|
||||
|
||||
#endif /* _AA_PRIVATE_H */
|
@@ -355,7 +355,6 @@ yy_flex_debug = 0;
|
||||
{syslog_time} { yylval->t_str = strdup(yytext); BEGIN(hostname); return(TOK_TIME); }
|
||||
|
||||
{audit} { yy_push_state(audit_id, yyscanner); return(TOK_AUDIT); }
|
||||
{dmesg_timestamp} { yylval->t_str = strdup(yytext); return(TOK_DMESG_STAMP); }
|
||||
|
||||
. { /* ignore any non-matched input */ BEGIN(unknown_message); yyless(0); }
|
||||
|
||||
|
@@ -20,7 +20,13 @@
|
||||
#include <stdio.h>
|
||||
#include <aalogparse.h>
|
||||
#include "parser.h"
|
||||
#include "private.h"
|
||||
|
||||
|
||||
#define MY_TEST(statement, error) \
|
||||
if (!(statement)) { \
|
||||
fprintf(stderr, "FAIL: %s\n", error); \
|
||||
rc = 1; \
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
@@ -34,25 +40,19 @@ int main(void)
|
||||
retstr = hex_to_string("2F746D702F646F6573206E6F74206578697374");
|
||||
MY_TEST(retstr, "basic allocation");
|
||||
MY_TEST(strcmp(retstr, "/tmp/does not exist") == 0, "basic dehex 1");
|
||||
free(retstr);
|
||||
|
||||
retstr = hex_to_string("61");
|
||||
MY_TEST(strcmp(retstr, "a") == 0, "basic dehex 2");
|
||||
free(retstr);
|
||||
|
||||
retstr = hex_to_string("");
|
||||
MY_TEST(strcmp(retstr, "") == 0, "empty string");
|
||||
free(retstr);
|
||||
|
||||
/* ipproto_to_string() tests */
|
||||
retstr = ipproto_to_string((unsigned) 99999);
|
||||
MY_TEST(strcmp(retstr, "unknown(99999)") == 0, "invalid protocol test");
|
||||
free(retstr);
|
||||
|
||||
retstr = ipproto_to_string((unsigned) 6);
|
||||
MY_TEST(strcmp(retstr, "tcp") == 0, "protocol=tcp");
|
||||
free(retstr);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@@ -1,247 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015
|
||||
* Canonical, Ltd. (All rights reserved)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, contact Novell, Inc. or Canonical
|
||||
* Ltd.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "private.h"
|
||||
|
||||
#include "features.c"
|
||||
|
||||
static int test_tokenize_path_components(void)
|
||||
{
|
||||
struct component components[32];
|
||||
size_t max = sizeof(components) / sizeof(*components);
|
||||
size_t num;
|
||||
int rc = 0;
|
||||
|
||||
num = tokenize_path_components(NULL, components, max);
|
||||
MY_TEST(num == 0, "basic NULL test");
|
||||
|
||||
num = tokenize_path_components("", components, max);
|
||||
MY_TEST(num == 0, "basic empty string test");
|
||||
|
||||
num = tokenize_path_components("a", components, 0);
|
||||
MY_TEST(num == 0, "basic empty array test");
|
||||
|
||||
num = tokenize_path_components("a", components, 1);
|
||||
MY_TEST(num == 1, "one component full test (num)");
|
||||
MY_TEST(!strncmp(components[0].str, "a", components[0].len),
|
||||
"one component full test (components[0])");
|
||||
|
||||
num = tokenize_path_components("a/b", components, 2);
|
||||
MY_TEST(num == 2, "two component full test (num)");
|
||||
MY_TEST(!strncmp(components[0].str, "a", components[0].len),
|
||||
"two component full test (components[0])");
|
||||
MY_TEST(!strncmp(components[1].str, "b", components[0].len),
|
||||
"two component full test (components[1])");
|
||||
|
||||
num = tokenize_path_components("a/b/c", components, 1);
|
||||
MY_TEST(num == 1, "not enough components full test (num)");
|
||||
MY_TEST(!strncmp(components[0].str, "a/b/c", components[0].len),
|
||||
"not enough components full test (components[0])");
|
||||
|
||||
num = tokenize_path_components("/", components, max);
|
||||
MY_TEST(num == 0, "no valid components #1 (num)");
|
||||
|
||||
num = tokenize_path_components("////////", components, max);
|
||||
MY_TEST(num == 0, "no valid components #2 (num)");
|
||||
|
||||
num = tokenize_path_components("////////////foo////", components, max);
|
||||
MY_TEST(num == 1, "many invalid components (num)");
|
||||
MY_TEST(!strncmp(components[0].str, "foo", components[0].len),
|
||||
"many invalid components (components[0])");
|
||||
|
||||
num = tokenize_path_components("file", components, max);
|
||||
MY_TEST(num == 1, "file (num)");
|
||||
MY_TEST(!strncmp(components[0].str, "file", components[0].len),
|
||||
"file (components[0])");
|
||||
|
||||
num = tokenize_path_components("/policy///versions//v7/", components, max);
|
||||
MY_TEST(num == 3, "v7 (num)");
|
||||
MY_TEST(!strncmp(components[0].str, "policy", components[0].len),
|
||||
"v7 (components[0])");
|
||||
MY_TEST(!strncmp(components[1].str, "versions", components[1].len),
|
||||
"v7 (components[1])");
|
||||
MY_TEST(!strncmp(components[2].str, "v7", components[2].len),
|
||||
"v7 (components[2])");
|
||||
|
||||
num = tokenize_path_components("dbus/mask/send", components, max);
|
||||
MY_TEST(num == 3, "dbus send (num)");
|
||||
MY_TEST(!strncmp(components[0].str, "dbus", components[0].len),
|
||||
"dbus send (components[0])");
|
||||
MY_TEST(!strncmp(components[1].str, "mask", components[1].len),
|
||||
"dbus send (components[1])");
|
||||
MY_TEST(!strncmp(components[2].str, "send", components[2].len),
|
||||
"dbus send (components[2])");
|
||||
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int do_test_walk_one(const char **str, const struct component *component,
|
||||
bool is_top_level, bool expect_walk, const char *e1,
|
||||
const char *e2, const char *e3)
|
||||
{
|
||||
const char *save = str ? *str : NULL;
|
||||
bool walked = walk_one(str, component, is_top_level);
|
||||
int rc = 0;
|
||||
|
||||
/* Check if the result of the walk matches the expected result*/
|
||||
MY_TEST(expect_walk == walked, e1);
|
||||
if (save) {
|
||||
/**
|
||||
* If a walk was expected, @*str should have changed. It
|
||||
* shouldn't change if a walk was unexpected.
|
||||
*/
|
||||
if (expect_walk) {
|
||||
MY_TEST(*str != save, e2);
|
||||
} else {
|
||||
MY_TEST(*str == save, e3);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#define MY_WALK_TEST(str, component, is_top_level, expect_walk, error) \
|
||||
if (do_test_walk_one(str, component, is_top_level, \
|
||||
expect_walk, \
|
||||
error " (walk check)", \
|
||||
error " (str didn't change)", \
|
||||
error " (str changed)")) { \
|
||||
rc = 1; \
|
||||
}
|
||||
|
||||
#define MY_GOOD_WALK_TEST(str, component, is_top_level, error) \
|
||||
MY_WALK_TEST(str, component, is_top_level, true, error)
|
||||
#define MY_BAD_WALK_TEST(str, component, is_top_level, error) \
|
||||
MY_WALK_TEST(str, component, is_top_level, false, error)
|
||||
|
||||
static int test_walk_one(void)
|
||||
{
|
||||
struct component c;
|
||||
const char *str;
|
||||
int rc = 0;
|
||||
|
||||
MY_BAD_WALK_TEST(NULL, &c, true, "basic NULL str test");
|
||||
|
||||
str = NULL;
|
||||
MY_BAD_WALK_TEST(&str, &c, true, "basic NULL *str test");
|
||||
|
||||
str = "test { a b }";
|
||||
MY_BAD_WALK_TEST(&str, NULL, true, "basic NULL component test");
|
||||
|
||||
str = "test { a b }";
|
||||
c = (struct component) { NULL, 8 };
|
||||
MY_BAD_WALK_TEST(&str, &c, true, "basic NULL c.str test");
|
||||
|
||||
str = "test { a b }";
|
||||
c = (struct component) { "", 0 };
|
||||
MY_BAD_WALK_TEST(&str, &c, true, "basic empty c.str test");
|
||||
|
||||
str = "test";
|
||||
c = (struct component) { "test", 4 };
|
||||
MY_GOOD_WALK_TEST(&str, &c, true, "single component");
|
||||
|
||||
str = "testX";
|
||||
c = (struct component) { "test", 4 };
|
||||
MY_BAD_WALK_TEST(&str, &c, true, "single component bad str");
|
||||
|
||||
str = "test";
|
||||
c = (struct component) { "testX", 5 };
|
||||
MY_BAD_WALK_TEST(&str, &c, true, "single component bad c.str");
|
||||
|
||||
str = "test { }";
|
||||
c = (struct component) { "test", 4 };
|
||||
MY_GOOD_WALK_TEST(&str, &c, true, "single component empty braces #1");
|
||||
|
||||
str = "test {\n\t}";
|
||||
c = (struct component) { "test", 4 };
|
||||
MY_GOOD_WALK_TEST(&str, &c, true, "single component empty braces #2");
|
||||
|
||||
str = "test{}";
|
||||
c = (struct component) { "test", 4 };
|
||||
MY_GOOD_WALK_TEST(&str, &c, true, "single component empty braces #3");
|
||||
|
||||
str = "test\t{}\n ";
|
||||
c = (struct component) { "test", 4 };
|
||||
MY_GOOD_WALK_TEST(&str, &c, true, "single component empty braces #4");
|
||||
|
||||
str = "test {}";
|
||||
c = (struct component) { "test", 4 };
|
||||
MY_BAD_WALK_TEST(&str, &c, false, "single component bad is_top_level");
|
||||
|
||||
str = "front{back";
|
||||
c = (struct component) { "frontback", 9};
|
||||
MY_BAD_WALK_TEST(&str, &c, true, "brace in the middle #1");
|
||||
MY_BAD_WALK_TEST(&str, &c, false, "brace in the middle #2");
|
||||
|
||||
str = "ardvark { bear cat { deer } }";
|
||||
c = (struct component) { "ardvark", 7 };
|
||||
MY_GOOD_WALK_TEST(&str, &c, true, "animal walk good ardvark");
|
||||
c = (struct component) { "deer", 4 };
|
||||
MY_BAD_WALK_TEST(&str, &c, false, "animal walk bad deer");
|
||||
MY_BAD_WALK_TEST(&str, &c, true, "animal walk bad top-level deer");
|
||||
c = (struct component) { "bear", 4 };
|
||||
MY_BAD_WALK_TEST(&str, &c, true, "animal walk bad bear");
|
||||
c = (struct component) { "cat", 3 };
|
||||
MY_GOOD_WALK_TEST(&str, &c, false, "animal walk good cat");
|
||||
c = (struct component) { "ardvark", 7 };
|
||||
MY_BAD_WALK_TEST(&str, &c, true, "animal walk bad ardvark");
|
||||
c = (struct component) { "deer", 4 };
|
||||
MY_GOOD_WALK_TEST(&str, &c, false, "animal walk good deer");
|
||||
|
||||
str = "dbus {mask {acquire send receive\n}\n}\nsignal {mask {hup int\n}\n}";
|
||||
c = (struct component) { "hup", 3 };
|
||||
MY_BAD_WALK_TEST(&str, &c, true, "dbus/signal bad hup #1");
|
||||
MY_BAD_WALK_TEST(&str, &c, false, "dbus/signal bad hup #2");
|
||||
c = (struct component) { "signal", 6 };
|
||||
MY_BAD_WALK_TEST(&str, &c, false, "dbus/signal bad signal");
|
||||
MY_GOOD_WALK_TEST(&str, &c, true, "dbus/signal good signal");
|
||||
c = (struct component) { "mask", 4 };
|
||||
MY_BAD_WALK_TEST(&str, &c, true, "dbus/signal bad mask");
|
||||
MY_GOOD_WALK_TEST(&str, &c, false, "dbus/signal good mask");
|
||||
c = (struct component) { "hup", 3 };
|
||||
MY_GOOD_WALK_TEST(&str, &c, false, "dbus/signal good hup");
|
||||
|
||||
str = "policy {set_load {yes\n}\nversions {v7 {yes\n}\nv6 {yes\n}";
|
||||
c = (struct component) { "policy", 6 };
|
||||
MY_GOOD_WALK_TEST(&str, &c, true, "policy good");
|
||||
c = (struct component) { "versions", 8 };
|
||||
MY_GOOD_WALK_TEST(&str, &c, false, "versions good");
|
||||
c = (struct component) { "v7", 2 };
|
||||
MY_GOOD_WALK_TEST(&str, &c, false, "v7 good");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int retval, rc = 0;
|
||||
|
||||
retval = test_tokenize_path_components();
|
||||
if (retval)
|
||||
rc = retval;
|
||||
|
||||
retval = test_walk_one();
|
||||
if (retval)
|
||||
rc = retval;
|
||||
|
||||
return rc;
|
||||
}
|
@@ -1,228 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015
|
||||
* Canonical, Ltd. (All rights reserved)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, contact Novell, Inc. or Canonical
|
||||
* Ltd.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "kernel.c"
|
||||
|
||||
static int nullcmp_and_strcmp(const void *s1, const void *s2)
|
||||
{
|
||||
/* Return 0 if both pointers are NULL & non-zero if only one is NULL */
|
||||
if (!s1 || !s2)
|
||||
return s1 != s2;
|
||||
|
||||
return strcmp(s1, s2);
|
||||
}
|
||||
|
||||
static int do_test_splitcon(char *con, int size, bool strip_nl, char **mode,
|
||||
const char *expected_label,
|
||||
const char *expected_mode, const char *error)
|
||||
{
|
||||
char *label;
|
||||
int rc = 0;
|
||||
|
||||
label = splitcon(con, size, strip_nl, mode);
|
||||
|
||||
if (nullcmp_and_strcmp(label, expected_label)) {
|
||||
fprintf(stderr, "FAIL: %s: label \"%s\" != \"%s\"\n",
|
||||
error, label, expected_label);
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
if (mode && nullcmp_and_strcmp(*mode, expected_mode)) {
|
||||
fprintf(stderr, "FAIL: %s: mode \"%s\" != \"%s\"\n",
|
||||
error, *mode, expected_mode);
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int do_test_aa_splitcon(char *con, char **mode,
|
||||
const char *expected_label,
|
||||
const char *expected_mode, const char *error)
|
||||
{
|
||||
char *label;
|
||||
int rc = 0;
|
||||
|
||||
label = aa_splitcon(con, mode);
|
||||
|
||||
if (nullcmp_and_strcmp(label, expected_label)) {
|
||||
fprintf(stderr, "FAIL: %s: label \"%s\" != \"%s\"\n",
|
||||
error, label, expected_label);
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
if (mode && nullcmp_and_strcmp(*mode, expected_mode)) {
|
||||
fprintf(stderr, "FAIL: %s: mode \"%s\" != \"%s\"\n",
|
||||
error, *mode, expected_mode);
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#define TEST_SPLITCON(con, size, strip_nl, expected_label, \
|
||||
expected_mode, error) \
|
||||
do { \
|
||||
char c1[] = con; \
|
||||
char c2[] = con; \
|
||||
size_t sz = size < 0 ? strlen(con) : size; \
|
||||
char *mode; \
|
||||
\
|
||||
if (do_test_splitcon(c1, sz, strip_nl, &mode, \
|
||||
expected_label, expected_mode, \
|
||||
"splitcon: " error)) { \
|
||||
rc = 1; \
|
||||
} else if (do_test_splitcon(c2, sz, strip_nl, NULL, \
|
||||
expected_label, NULL, \
|
||||
"splitcon: " error " (NULL mode)")) { \
|
||||
rc = 1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TEST_AA_SPLITCON(con, expected_label, expected_mode, error) \
|
||||
do { \
|
||||
char c1[] = con; \
|
||||
char c2[] = con; \
|
||||
char c3[] = con "\n"; \
|
||||
char *mode; \
|
||||
\
|
||||
if (do_test_aa_splitcon(c1, &mode, expected_label, \
|
||||
expected_mode, "aa_splitcon: " error)) {\
|
||||
rc = 1; \
|
||||
} else if (do_test_aa_splitcon(c2, NULL, expected_label,\
|
||||
NULL, \
|
||||
"aa_splitcon: " error " (NULL mode)")) {\
|
||||
rc = 1; \
|
||||
} else if (do_test_aa_splitcon(c3, &mode, \
|
||||
expected_label, expected_mode, \
|
||||
"aa_splitcon: " error " (newline)")) { \
|
||||
rc = 1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static int test_splitcon(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
/**
|
||||
* NOTE: the TEST_SPLITCON() macro automatically generates
|
||||
* corresponding tests with a NULL mode pointer.
|
||||
*/
|
||||
|
||||
TEST_SPLITCON("", 0, true, NULL, NULL, "empty string test #1");
|
||||
TEST_SPLITCON("", 0, false, NULL, NULL, "empty string test #2");
|
||||
|
||||
TEST_SPLITCON("unconfined", -1, true, "unconfined", NULL,
|
||||
"unconfined #1");
|
||||
TEST_SPLITCON("unconfined", -1, false, "unconfined", NULL,
|
||||
"unconfined #2");
|
||||
TEST_SPLITCON("unconfined\n", -1, true, "unconfined", NULL,
|
||||
"unconfined #3");
|
||||
TEST_SPLITCON("unconfined\n", -1, false, NULL, NULL,
|
||||
"unconfined #4");
|
||||
|
||||
TEST_SPLITCON("label (mode)", -1, true, "label", "mode",
|
||||
"basic split #1");
|
||||
TEST_SPLITCON("label (mode)", -1, false, "label", "mode",
|
||||
"basic split #2");
|
||||
TEST_SPLITCON("label (mode)\n", -1, true, "label", "mode",
|
||||
"basic split #3");
|
||||
TEST_SPLITCON("label (mode)\n", -1, false, NULL, NULL,
|
||||
"basic split #4");
|
||||
|
||||
TEST_SPLITCON("/a/b/c (enforce)", -1, true, "/a/b/c", "enforce",
|
||||
"path enforce split #1");
|
||||
TEST_SPLITCON("/a/b/c (enforce)", -1, false, "/a/b/c", "enforce",
|
||||
"path enforce split #2");
|
||||
TEST_SPLITCON("/a/b/c (enforce)\n", -1, true, "/a/b/c", "enforce",
|
||||
"path enforce split #3");
|
||||
TEST_SPLITCON("/a/b/c (enforce)\n", -1, false, NULL, NULL,
|
||||
"path enforce split #4");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int test_aa_splitcon(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
/**
|
||||
* NOTE: the TEST_AA_SPLITCON() macro automatically generates
|
||||
* corresponding tests with a NULL mode pointer and contexts with
|
||||
* trailing newline characters.
|
||||
*/
|
||||
|
||||
TEST_AA_SPLITCON("label (mode)", "label", "mode", "basic split");
|
||||
|
||||
TEST_AA_SPLITCON("/a/b/c (enforce)", "/a/b/c", "enforce",
|
||||
"path enforce split");
|
||||
|
||||
TEST_AA_SPLITCON("/a/b/c (complain)", "/a/b/c", "complain",
|
||||
"path complain split");
|
||||
|
||||
TEST_AA_SPLITCON("profile_name (enforce)", "profile_name", "enforce",
|
||||
"name enforce split");
|
||||
|
||||
TEST_AA_SPLITCON("profile_name (complain)", "profile_name", "complain",
|
||||
"name complain split");
|
||||
|
||||
TEST_AA_SPLITCON("unconfined", "unconfined", NULL, "unconfined");
|
||||
|
||||
TEST_AA_SPLITCON("(odd) (enforce)", "(odd)", "enforce",
|
||||
"parenthesized label #1");
|
||||
|
||||
TEST_AA_SPLITCON("(odd) (enforce) (enforce)", "(odd) (enforce)",
|
||||
"enforce", "parenthesized label #2");
|
||||
|
||||
TEST_AA_SPLITCON("/usr/bin/😺 (enforce)", "/usr/bin/😺", "enforce",
|
||||
"non-ASCII path");
|
||||
|
||||
TEST_AA_SPLITCON("👍 (enforce)", "👍", "enforce",
|
||||
"non-ASCII profile name");
|
||||
|
||||
/* Negative tests */
|
||||
|
||||
TEST_AA_SPLITCON("", NULL, NULL, "empty string test");
|
||||
|
||||
TEST_AA_SPLITCON("profile\t(enforce)", NULL, NULL,
|
||||
"invalid tab separator");
|
||||
|
||||
TEST_AA_SPLITCON("profile(enforce)", NULL, NULL,
|
||||
"invalid missing separator");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int retval, rc = 0;
|
||||
|
||||
retval = test_splitcon();
|
||||
if (retval)
|
||||
rc = retval;
|
||||
|
||||
retval = test_aa_splitcon();
|
||||
if (retval)
|
||||
rc = retval;
|
||||
|
||||
return rc;
|
||||
}
|
@@ -3,30 +3,12 @@
|
||||
%{
|
||||
#include <aalogparse.h>
|
||||
#include <sys/apparmor.h>
|
||||
#include <sys/apparmor_private.h>
|
||||
|
||||
%}
|
||||
|
||||
%include "typemaps.i"
|
||||
%include <aalogparse.h>
|
||||
|
||||
/**
|
||||
* swig doesn't like the macro magic we do in apparmor.h and apparmor_private.h
|
||||
* so the function prototypes must be manually inserted.
|
||||
*
|
||||
* Functions that return a negative int and set errno upon error use a special
|
||||
* %exception directive and must be listed after the %exception below. All
|
||||
* other functions go here.
|
||||
*/
|
||||
|
||||
/* apparmor.h */
|
||||
|
||||
extern char *aa_splitcon(char *con, char **mode);
|
||||
|
||||
/* apparmor_private.h */
|
||||
|
||||
extern int _aa_is_blacklisted(const char *name);
|
||||
|
||||
#ifdef SWIGPYTHON
|
||||
%exception {
|
||||
$action
|
||||
@@ -37,9 +19,9 @@ extern int _aa_is_blacklisted(const char *name);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Functions that return a negative int and set errno upon error go here. */
|
||||
|
||||
/* apparmor.h */
|
||||
/* swig doesn't like the macro magic we do in apparmor.h so the fn prototypes
|
||||
* are manually inserted here
|
||||
*/
|
||||
|
||||
extern int aa_is_enabled(void);
|
||||
extern int aa_find_mountpoint(char **mnt);
|
||||
@@ -51,22 +33,11 @@ extern int aa_change_hat_vargs(unsigned long token, int count, ...);
|
||||
extern int aa_getprocattr_raw(pid_t tid, const char *attr, char *buf, int len,
|
||||
char **mode);
|
||||
extern int aa_getprocattr(pid_t tid, const char *attr, char **buf, char **mode);
|
||||
extern int aa_gettaskcon(pid_t target, char **label, char **mode);
|
||||
extern int aa_getcon(char **label, char **mode);
|
||||
extern int aa_gettaskcon(pid_t target, char **con, char **mode);
|
||||
extern int aa_getcon(char **con, char **mode);
|
||||
extern int aa_getpeercon_raw(int fd, char *buf, int *len, char **mode);
|
||||
extern int aa_getpeercon(int fd, char **label, char **mode);
|
||||
extern int aa_getpeercon(int fd, char **con, char **mode);
|
||||
extern int aa_query_label(uint32_t mask, char *query, size_t size, int *allow,
|
||||
int *audit);
|
||||
extern int aa_query_file_path_len(uint32_t mask, const char *label,
|
||||
size_t label_len, const char *path,
|
||||
size_t path_len, int *allowed, int *audited);
|
||||
extern int aa_query_file_path(uint32_t mask, const char *label,
|
||||
const char *path, int *allowed, int *audited);
|
||||
extern int aa_query_link_path_len(const char *label, size_t label_len,
|
||||
const char *target, size_t target_len,
|
||||
const char *link, size_t link_len,
|
||||
int *allowed, int *audited);
|
||||
extern int aa_query_link_path(const char *label, const char *target,
|
||||
const char *link, int *allowed, int *audited);
|
||||
|
||||
%exception;
|
||||
|
@@ -1 +0,0 @@
|
||||
type=AVC msg=audit(1449442292.901:961): apparmor="ALLOWED" operation="change_hat" profile="/usr/sbin/httpd{,2}-prefork" pid=8527 comm="httpd-prefork" target="/usr/sbin/httpd{,2}-prefork//HANDLING_UNTRUSTED_INPUT"
|
@@ -1,11 +0,0 @@
|
||||
START
|
||||
File: testcase_changehat_01.in
|
||||
Event type: AA_RECORD_ALLOWED
|
||||
Audit ID: 1449442292.901:961
|
||||
Operation: change_hat
|
||||
Profile: /usr/sbin/httpd{,2}-prefork
|
||||
Command: httpd-prefork
|
||||
Name2: /usr/sbin/httpd{,2}-prefork//HANDLING_UNTRUSTED_INPUT
|
||||
PID: 8527
|
||||
Epoch: 1449442292
|
||||
Audit subid: 961
|
@@ -1 +0,0 @@
|
||||
Sep 9 12:51:36 ubuntu-desktop kernel: [ 97.492562] audit: type=1400 audit(1431116353.523:77): apparmor="DENIED" operation="change_profile" profile="/tests/regression/apparmor/changeprofile" pid=3459 comm="changeprofile" target="/tests/regression/apparmor/rename"
|
@@ -1,11 +0,0 @@
|
||||
START
|
||||
File: testcase_changeprofile_01.in
|
||||
Event type: AA_RECORD_DENIED
|
||||
Audit ID: 1431116353.523:77
|
||||
Operation: change_profile
|
||||
Profile: /tests/regression/apparmor/changeprofile
|
||||
Command: changeprofile
|
||||
Name2: /tests/regression/apparmor/rename
|
||||
PID: 3459
|
||||
Epoch: 1431116353
|
||||
Audit subid: 77
|
@@ -1 +0,0 @@
|
||||
[ 1612.746129] audit: type=1400 audit(1284061910.975:672): apparmor="DENIED" operation="capable" parent=2663 profile="/home/ubuntu/bzr/apparmor/tests/regression/apparmor/syscall_setpriority" pid=7292 comm="syscall_setprio" capability=23 capname="sys_nice"
|
@@ -1,12 +0,0 @@
|
||||
START
|
||||
File: testcase_dmesg_capability.in
|
||||
Event type: AA_RECORD_DENIED
|
||||
Audit ID: 1284061910.975:672
|
||||
Operation: capable
|
||||
Profile: /home/ubuntu/bzr/apparmor/tests/regression/apparmor/syscall_setpriority
|
||||
Name: sys_nice
|
||||
Command: syscall_setprio
|
||||
Parent: 2663
|
||||
PID: 7292
|
||||
Epoch: 1284061910
|
||||
Audit subid: 672
|
@@ -1 +0,0 @@
|
||||
[ 1597.774866] audit: type=1400 audit(1284061896.005:28): apparmor="DENIED" operation="change_hat" info="unconfined" error=-1 pid=2698 comm="syscall_ptrace"
|
@@ -1,11 +0,0 @@
|
||||
START
|
||||
File: testcase_dmesg_changehat_negative_error.in
|
||||
Event type: AA_RECORD_DENIED
|
||||
Audit ID: 1284061896.005:28
|
||||
Operation: change_hat
|
||||
Command: syscall_ptrace
|
||||
Info: unconfined
|
||||
ErrorCode: 1
|
||||
PID: 2698
|
||||
Epoch: 1284061896
|
||||
Audit subid: 28
|
@@ -1 +0,0 @@
|
||||
[ 97.492562] audit: type=1400 audit(1431116353.523:77): apparmor="DENIED" operation="change_profile" profile="/tests/regression/apparmor/changeprofile" pid=3459 comm="changeprofile" target="/tests/regression/apparmor/rename"
|
@@ -1,11 +0,0 @@
|
||||
START
|
||||
File: testcase_dmesg_changeprofile_01.in
|
||||
Event type: AA_RECORD_DENIED
|
||||
Audit ID: 1431116353.523:77
|
||||
Operation: change_profile
|
||||
Profile: /tests/regression/apparmor/changeprofile
|
||||
Command: changeprofile
|
||||
Name2: /tests/regression/apparmor/rename
|
||||
PID: 3459
|
||||
Epoch: 1431116353
|
||||
Audit subid: 77
|
@@ -1 +0,0 @@
|
||||
[ 2010.738449] audit: type=1400 audit(1284062308.965:276251): apparmor="DENIED" operation="link" parent=19088 profile="/home/ubuntu/bzr/apparmor/tests/regression/apparmor/link" name="/tmp/sdtest.19088-12382-HWH57d/linkfile" pid=19142 comm="link" requested_mask="l" denied_mask="l" fsuid=0 ouid=0 target="/tmp/sdtest.19088-12382-HWH57d/target"
|
@@ -1,17 +0,0 @@
|
||||
START
|
||||
File: testcase_dmesg_link_01.in
|
||||
Event type: AA_RECORD_DENIED
|
||||
Audit ID: 1284062308.965:276251
|
||||
Operation: link
|
||||
Mask: l
|
||||
Denied Mask: l
|
||||
fsuid: 0
|
||||
ouid: 0
|
||||
Profile: /home/ubuntu/bzr/apparmor/tests/regression/apparmor/link
|
||||
Name: /tmp/sdtest.19088-12382-HWH57d/linkfile
|
||||
Command: link
|
||||
Name2: /tmp/sdtest.19088-12382-HWH57d/target
|
||||
Parent: 19088
|
||||
PID: 19142
|
||||
Epoch: 1284062308
|
||||
Audit subid: 276251
|
@@ -1 +0,0 @@
|
||||
[45334.755142] audit: type=1503 audit(1282671283.411:2199): operation="mkdir" pid=4786 parent=4708 profile="/usr/sbin/sshd//ubuntu" requested_mask="c::" denied_mask="c::" fsuid=1000 ouid=1000 name="/tmp/ssh-gRozJw4786/"
|
@@ -1,15 +0,0 @@
|
||||
START
|
||||
File: testcase_dmesg_mkdir.in
|
||||
Event type: AA_RECORD_DENIED
|
||||
Audit ID: 1282671283.411:2199
|
||||
Operation: mkdir
|
||||
Mask: c::
|
||||
Denied Mask: c::
|
||||
fsuid: 1000
|
||||
ouid: 1000
|
||||
Profile: /usr/sbin/sshd//ubuntu
|
||||
Name: /tmp/ssh-gRozJw4786/
|
||||
Parent: 4708
|
||||
PID: 4786
|
||||
Epoch: 1282671283
|
||||
Audit subid: 2199
|
@@ -1 +0,0 @@
|
||||
[ 878.663418] audit: type=1502 audit(1282626827.320:413): operation="rename_dest" pid=1881 parent=650 profile="/usr/sbin/sshd" requested_mask="wc::" denied_mask="wc::" fsuid=0 ouid=0 name="/var/run/motd"
|
@@ -1,15 +0,0 @@
|
||||
START
|
||||
File: testcase_dmesg_rename_dest.in
|
||||
Event type: AA_RECORD_ALLOWED
|
||||
Audit ID: 1282626827.320:413
|
||||
Operation: rename_dest
|
||||
Mask: wc::
|
||||
Denied Mask: wc::
|
||||
fsuid: 0
|
||||
ouid: 0
|
||||
Profile: /usr/sbin/sshd
|
||||
Name: /var/run/motd
|
||||
Parent: 650
|
||||
PID: 1881
|
||||
Epoch: 1282626827
|
||||
Audit subid: 413
|
@@ -1 +0,0 @@
|
||||
[ 878.663410] audit: type=1502 audit(1282626827.320:412): operation="rename_src" pid=1881 parent=650 profile="/usr/sbin/sshd" requested_mask="r::" denied_mask="r::" fsuid=0 ouid=0 name="/var/run/motd.new"
|
@@ -1,15 +0,0 @@
|
||||
START
|
||||
File: testcase_dmesg_rename_src.in
|
||||
Event type: AA_RECORD_ALLOWED
|
||||
Audit ID: 1282626827.320:412
|
||||
Operation: rename_src
|
||||
Mask: r::
|
||||
Denied Mask: r::
|
||||
fsuid: 0
|
||||
ouid: 0
|
||||
Profile: /usr/sbin/sshd
|
||||
Name: /var/run/motd.new
|
||||
Parent: 650
|
||||
PID: 1881
|
||||
Epoch: 1282626827
|
||||
Audit subid: 412
|
@@ -1 +0,0 @@
|
||||
[ 2143.902340] audit: type=1400 audit(1283989336.064:272335): apparmor="STATUS" info="failed to unpack profile" error=-71 pid=4958 comm="apparmor_parser" name="/home/jj/master/tests/regression/apparmor/net_raw" offset=159
|
@@ -1,11 +0,0 @@
|
||||
START
|
||||
File: testcase_dmesg_status_offset.in
|
||||
Event type: AA_RECORD_STATUS
|
||||
Audit ID: 1283989336.064:272335
|
||||
Name: /home/jj/master/tests/regression/apparmor/net_raw
|
||||
Command: apparmor_parser
|
||||
Info: failed to unpack profile
|
||||
ErrorCode: 71
|
||||
PID: 4958
|
||||
Epoch: 1283989336
|
||||
Audit subid: 272335
|
@@ -1 +0,0 @@
|
||||
[ 878.662172] audit: type=1503 audit(1282626827.320:411): operation="truncate" pid=1957 parent=1 profile="/etc/update-motd.d/91-release-upgrade" requested_mask="w::" denied_mask="w::" fsuid=0 ouid=0 name="/var/lib/update-notifier/release-upgrade-available"
|
@@ -1,15 +0,0 @@
|
||||
START
|
||||
File: testcase_dmesg_truncate.in
|
||||
Event type: AA_RECORD_DENIED
|
||||
Audit ID: 1282626827.320:411
|
||||
Operation: truncate
|
||||
Mask: w::
|
||||
Denied Mask: w::
|
||||
fsuid: 0
|
||||
ouid: 0
|
||||
Profile: /etc/update-motd.d/91-release-upgrade
|
||||
Name: /var/lib/update-notifier/release-upgrade-available
|
||||
Parent: 1
|
||||
PID: 1957
|
||||
Epoch: 1282626827
|
||||
Audit subid: 411
|
@@ -1 +0,0 @@
|
||||
Jul 25 15:02:00 redacted kernel: [ 296.524447] audit: type=1400 audit(1437850920.403:64): apparmor="ALLOWED" operation="open" profile="/usr/sbin/vsftpd" name="/home/bane/foo" pid=1811 comm="vsftpd" requested_mask="r" denied_mask="r" fsuid=1000 ouid=1000
|
@@ -1,15 +0,0 @@
|
||||
START
|
||||
File: testcase_syslog_read.in
|
||||
Event type: AA_RECORD_ALLOWED
|
||||
Audit ID: 1437850920.403:64
|
||||
Operation: open
|
||||
Mask: r
|
||||
Denied Mask: r
|
||||
fsuid: 1000
|
||||
ouid: 1000
|
||||
Profile: /usr/sbin/vsftpd
|
||||
Name: /home/bane/foo
|
||||
Command: vsftpd
|
||||
PID: 1811
|
||||
Epoch: 1437850920
|
||||
Audit subid: 64
|
@@ -18,7 +18,13 @@ NAME=apparmor-parser
|
||||
all:
|
||||
COMMONDIR=../common/
|
||||
|
||||
include $(COMMONDIR)/Make.rules
|
||||
include common/Make.rules
|
||||
|
||||
COMMONDIR_EXISTS=$(strip $(shell [ -d ${COMMONDIR} ] && echo true))
|
||||
ifeq ($(COMMONDIR_EXISTS), true)
|
||||
common/Make.rules: $(COMMONDIR)/Make.rules
|
||||
ln -sf $(COMMONDIR) .
|
||||
endif
|
||||
|
||||
DESTDIR=/
|
||||
APPARMOR_BIN_PREFIX=${DESTDIR}/lib/apparmor
|
||||
@@ -50,7 +56,7 @@ CFLAGS = -g -pg -fprofile-arcs -ftest-coverage
|
||||
endif
|
||||
endif #CFLAGS
|
||||
|
||||
EXTRA_CXXFLAGS = ${CFLAGS} ${CPPFLAGS} ${CXX_WARNINGS} -std=gnu++0x -D_GNU_SOURCE
|
||||
EXTRA_CXXFLAGS = ${CFLAGS} ${CXX_WARNINGS} -std=gnu++0x -D_GNU_SOURCE
|
||||
EXTRA_CFLAGS = ${EXTRA_CXXFLAGS} ${CPP_WARNINGS}
|
||||
|
||||
#LEXLIB := -lfl
|
||||
@@ -75,10 +81,9 @@ SRCS = parser_common.c parser_include.c parser_interface.c parser_lex.c \
|
||||
parser_yacc.c parser_regex.c parser_variable.c parser_policy.c \
|
||||
parser_alias.c common_optarg.c lib.c network.c \
|
||||
mount.cc dbus.cc profile.cc rule.cc signal.cc ptrace.cc \
|
||||
af_rule.cc af_unix.cc policy_cache.c
|
||||
af_rule.cc af_unix.cc
|
||||
HDRS = parser.h parser_include.h immunix.h mount.h dbus.h lib.h profile.h \
|
||||
rule.h common_optarg.h signal.h ptrace.h network.h af_rule.h af_unix.h \
|
||||
policy_cache.h
|
||||
rule.h common_optarg.h signal.h ptrace.h network.h af_rule.h af_unix.h
|
||||
TOOLS = apparmor_parser
|
||||
|
||||
OBJECTS = $(patsubst %.cc, %.o, $(SRCS:.c=.o))
|
||||
@@ -115,11 +120,9 @@ TEST_OBJECTS = $(filter-out \
|
||||
parser_lex.o \
|
||||
parser_yacc.o \
|
||||
common_optarg.o \
|
||||
parser_main.o \
|
||||
policy_cache.o, ${OBJECTS}) \
|
||||
parser_main.o, ${OBJECTS}) \
|
||||
$(AAREOBJECTS)
|
||||
TEST_LDFLAGS = $(AARE_LDFLAGS)
|
||||
TEST_LDLIBS = $(AALIB)
|
||||
|
||||
ifdef V
|
||||
VERBOSE = 1
|
||||
@@ -140,7 +143,7 @@ po/${NAME}.pot: ${SRCS} ${HDRS}
|
||||
$(MAKE) -C po ${NAME}.pot NAME=${NAME} SOURCES="${SRCS} ${HDRS}"
|
||||
|
||||
techdoc.pdf: techdoc.tex
|
||||
timestamp=$(shell date --utc "+%Y%m%d%H%M%S%z" -r $< );\
|
||||
timestamp=$(shell date "+%Y%m%d%H%M%S+02'00'" -r $< );\
|
||||
while pdflatex "\def\fixedpdfdate{$$timestamp}\input $<" ${BUILD_OUTPUT} || exit 1 ; \
|
||||
grep -q "Label(s) may have changed" techdoc.log; \
|
||||
do :; done
|
||||
@@ -189,7 +192,7 @@ apparmor_parser: $(OBJECTS) $(AAREOBJECTS) $(LIBAPPARMOR_A)
|
||||
parser_yacc.c parser_yacc.h: parser_yacc.y parser.h profile.h
|
||||
$(YACC) $(YFLAGS) -o parser_yacc.c parser_yacc.y
|
||||
|
||||
parser_lex.c: parser_lex.l parser_yacc.h parser.h profile.h mount.h dbus.h policy_cache.h
|
||||
parser_lex.c: parser_lex.l parser_yacc.h parser.h profile.h mount.h dbus.h
|
||||
$(LEX) ${LEXFLAGS} -o$@ $<
|
||||
|
||||
parser_lex.o: parser_lex.c parser.h parser_yacc.h
|
||||
@@ -201,7 +204,7 @@ parser_misc.o: parser_misc.c parser.h parser_yacc.h profile.h cap_names.h $(APPA
|
||||
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 policy_cache.h libapparmor_re/apparmor_re.h $(APPARMOR_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
|
||||
@@ -213,7 +216,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 libapparmor_re/aare_rules.h $(APPARMOR_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
|
||||
@@ -237,9 +240,6 @@ mount.o: mount.cc mount.h parser.h immunix.h rule.h
|
||||
common_optarg.o: common_optarg.c common_optarg.h parser.h libapparmor_re/apparmor_re.h
|
||||
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
|
||||
|
||||
policy_cache.o: policy_cache.c policy_cache.h parser.h lib.h
|
||||
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
|
||||
|
||||
lib.o: lib.c lib.h parser.h
|
||||
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
|
||||
|
||||
@@ -285,9 +285,9 @@ cap_names.h: /usr/include/linux/capability.h
|
||||
echo "$(CAPABILITIES)" | LC_ALL=C sed -n -e "s/[ \\t]\\?CAP_\\([A-Z0-9_]\\+\\)/\{\"\\L\\1\", \\UCAP_\\1\},\\n/pg" > $@
|
||||
|
||||
tst_lib: lib.c parser.h $(filter-out lib.o, ${TEST_OBJECTS})
|
||||
$(CXX) $(TEST_CFLAGS) -o $@ $< $(filter-out $(<:.c=.o), ${TEST_OBJECTS}) $(TEST_LDFLAGS) $(TEST_LDLIBS)
|
||||
$(CXX) $(TEST_CFLAGS) -o $@ $< $(filter-out $(<:.c=.o), ${TEST_OBJECTS}) $(TEST_LDFLAGS)
|
||||
tst_%: parser_%.c parser.h $(filter-out parser_%.o, ${TEST_OBJECTS})
|
||||
$(CXX) $(TEST_CFLAGS) -o $@ $< $(filter-out $(<:.c=.o), ${TEST_OBJECTS}) $(TEST_LDFLAGS) $(TEST_LDLIBS)
|
||||
$(CXX) $(TEST_CFLAGS) -o $@ $< $(filter-out $(<:.c=.o), ${TEST_OBJECTS}) $(TEST_LDFLAGS)
|
||||
|
||||
.SILENT: check
|
||||
.PHONY: check
|
||||
@@ -334,27 +334,6 @@ install-debian:
|
||||
install-unknown:
|
||||
|
||||
INSTALLDEPS=arch
|
||||
|
||||
ifndef DISTRO
|
||||
DISTRO=$(shell if [ -f /etc/slackware-version ] ; then \
|
||||
echo slackware ; \
|
||||
elif [ -f /etc/debian_version ] ; then \
|
||||
echo debian ;\
|
||||
elif which rpm > /dev/null ; then \
|
||||
if [ "$(rpm --eval '0%{?suse_version}')" != "0" ] ; then \
|
||||
echo suse ;\
|
||||
elif [ "$(rpm --eval '%{_host_vendor}')" = redhat ] ; then \
|
||||
echo rhel4 ;\
|
||||
elif [ "$(rpm --eval '0%{?fedora}')" != "0" ] ; then \
|
||||
echo rhel4 ;\
|
||||
else \
|
||||
echo unknown ;\
|
||||
fi ;\
|
||||
else \
|
||||
echo unknown ;\
|
||||
fi)
|
||||
endif
|
||||
|
||||
ifdef DISTRO
|
||||
INSTALLDEPS+=install-$(DISTRO)
|
||||
endif
|
||||
@@ -378,11 +357,9 @@ install-indep:
|
||||
$(MAKE) -C po install NAME=${NAME} DESTDIR=${DESTDIR}
|
||||
$(MAKE) install_manpages DESTDIR=${DESTDIR}
|
||||
|
||||
ifndef VERBOSE
|
||||
.SILENT: clean
|
||||
endif
|
||||
.PHONY: clean
|
||||
clean: pod_clean
|
||||
clean: _clean
|
||||
rm -f core core.* *.o *.s *.a *~ *.gcda *.gcno
|
||||
rm -f gmon.out
|
||||
rm -f $(TOOLS) $(TESTS)
|
||||
@@ -396,4 +373,9 @@ clean: pod_clean
|
||||
$(MAKE) -s -C $(AAREDIR) clean
|
||||
$(MAKE) -s -C po clean
|
||||
$(MAKE) -s -C tst clean
|
||||
rm -f common
|
||||
|
||||
.SILENT: dist_clean
|
||||
dist_clean:
|
||||
@$(MAKE) clean
|
||||
@rm -f $(LEX_C_FILES) $(YACC_C_FILES)
|
||||
|
@@ -243,7 +243,7 @@ bool unix_rule::write_addr(std::ostringstream &buffer, const char *addr)
|
||||
buffer << "\\x01";
|
||||
} else {
|
||||
/* skip leading @ */
|
||||
ptype = convert_aaregex_to_pcre(addr + 1, 0, glob_null, buf, &pos);
|
||||
ptype = convert_aaregex_to_pcre(addr + 1, 0, buf, &pos);
|
||||
if (ptype == ePatternInvalid)
|
||||
return false;
|
||||
/* kernel starts abstract with \0 */
|
||||
@@ -267,7 +267,7 @@ bool unix_rule::write_label(std::ostringstream &buffer, const char *label)
|
||||
|
||||
if (label) {
|
||||
int pos;
|
||||
ptype = convert_aaregex_to_pcre(label, 0, glob_default, buf, &pos);
|
||||
ptype = convert_aaregex_to_pcre(label, 0, buf, &pos);
|
||||
if (ptype == ePatternInvalid)
|
||||
return false;
|
||||
/* kernel starts abstract with \0 */
|
||||
|
@@ -44,78 +44,44 @@ to the policy; this behaviour is modelled after cpp(1).
|
||||
|
||||
=over 4
|
||||
|
||||
B<PROFILE FILE> = ( [ I<PREAMBLE> ] [ I<PROFILE> ] )*
|
||||
|
||||
B<PREAMBLE> = ( I<COMMENT> | I<VARIABLE ASSIGNMENT> | I<INCLUDE> )* (variable assignment must come before the profile)
|
||||
|
||||
B<INCLUDE> = '#include' ( I<ABS PATH> | I<MAGIC PATH> )
|
||||
|
||||
B<ABS PATH> = '"' path '"' (the path is passed to open(2))
|
||||
|
||||
B<MAGIC PATH> = 'E<lt>' relative path 'E<gt>' (the path is relative to F</etc/apparmor.d/>)
|
||||
|
||||
B<COMMENT> = '#' I<TEXT> [ '\r' ] '\n'
|
||||
B<COMMENT> = '#' I<TEXT>
|
||||
|
||||
B<TEXT> = any characters
|
||||
|
||||
B<PROFILE> = ( I<PROFILE HEAD> ) [ I<ATTACHMENT SPECIFICATION> ] [ I<PROFILE FLAG CONDS> ] '{' ( I<RULES> )* '}'
|
||||
B<PROFILE> = [ I<COMMENT> ... ] [ I<VARIABLE ASSIGNMENT> ... ] ( '"' I<PROGRAM> '"' | I<PROGRAM> ) [ 'flags=(complain)' ]'{' [ ( I<RESOURCE RULE> | I<COMMENT> | I<INCLUDE> | I<SUBPROFILE> | 'capability ' I<CAPABILITY> | I<NETWORK RULE> | I<MOUNT RULE> | I<PIVOT ROOT RULE> | I<DBUS RULE> | I<UNIX RULE> I<FILE RULE> | 'change_profile -E<gt> ' I<PROGRAMCHILD> ) ... ] '}'
|
||||
|
||||
B<PROFILE HEAD> = [ 'profile' ] I<FILEGLOB> | 'profile' I<PROFILE NAME>
|
||||
|
||||
B<PROFILE NAME> ( I<UNQUOTED PROFILE NAME> | I<QUOTED PROFILE NAME> )
|
||||
|
||||
B<QUOTED PROFILE NAME> = '"' I<UNQUOTED PROFILE NAME> '"'
|
||||
|
||||
B<UNQUOTED PROFILE NAME> = (must start with alphanumeric character (after variable expansion), or '/' B<AARE> have special meanings; see below. May include I<VARIABLE>. Rules with embedded spaces or tabs must be quoted.)
|
||||
|
||||
B<ATTACHMENT SPECIFICATION> = I<FILEGLOB>
|
||||
|
||||
B<PROFILE FLAG CONDS> = [ 'flags=' ] '(' comma or white space separated list of I<PROFILE FLAGS> ')'
|
||||
|
||||
B<PROFILE FLAGS> = 'complain' | 'audit' | 'enforce' | 'mediate_deleted' | 'attach_disconnected' | 'chroot_relative'
|
||||
|
||||
B<RULES> = [ ( I<LINE RULES> | I<COMMA RULES> ',' | I<BLOCK RULES> )
|
||||
|
||||
B<LINE RULES> = ( I<COMMENT> | I<INCLUDE> ) [ '\r' ] '\n'
|
||||
|
||||
B<COMMA RULES> = ( I<CAPABILITY RULE> | I<NETWORK RULE> | I<MOUNT RULE> | I<PIVOT ROOT RULE> | I<UNIX RULE> | I<FILE RULE> | I<LINK RULE> | I<CHANGE_PROFILE RULE> | I<RLIMIT RULE> | I<ALIAS RULE> | I<DBUS RULE> )
|
||||
|
||||
B<BLOCK RULES> = ( I<SUBPROFILE> | I<HAT> | I<QUALIFIER BLOCK> )
|
||||
|
||||
B<SUBPROFILE> = 'profile' I<PROFILE NAME> [ I<ATTACHMENT SPECIFICATION> ] [ I<PROFILE FLAG CONDS> ] '{' ( I<RULES> )* '}'
|
||||
|
||||
B<HAT> = ('hat' | '^') I<HATNAME> [ I<PROFILE FLAG CONDS> ] '{' ( I<RULES> )* '}'
|
||||
|
||||
B<HATNAME> = ( must start with alphanumeric character. see aa_change_hat(2) for a description of how this "hat" is used. IF '^' is used to start a hat then there is no space between the '^' and I<HATNAME>)
|
||||
|
||||
B<QUALIFIER BLOCK> = I<QUALIFIERS> I<BLOCK>
|
||||
|
||||
B<ACCESS TYPE> = ( 'allow' | 'deny' )
|
||||
|
||||
B<QUALIFIERS> = [ 'audit' ] [ I<ACCESS TYPE> ]
|
||||
|
||||
B<CAPABILITY RULE> = [ I<QUALIFIERS> ] 'capability' [ I<CAPABILITY LIST> ]
|
||||
|
||||
B<CAPABILITY LIST> = ( I<CAPABILITY> )+
|
||||
B<SUBPROFILE> = [ I<COMMENT> ... ] ( I<PROGRAMHAT> | 'profile ' I<PROGRAMCHILD> ) '{' [ ( I<FILE RULE> | I<COMMENT> | I<INCLUDE> ) ... ] '}'
|
||||
|
||||
B<CAPABILITY> = (lowercase capability name without 'CAP_' prefix; see
|
||||
capabilities(7))
|
||||
|
||||
B<NETWORK RULE> = [ I<QUALIFIERS> ] 'network' [ I<DOMAIN> ] [ I<TYPE> | I<PROTOCOL> ]
|
||||
B<NETWORK RULE> = 'network' [ [ I<DOMAIN> [ I<TYPE> | I<PROTOCOL> ] ] | [ I<PROTOCOL> ] ] ','
|
||||
|
||||
B<DOMAIN> = ( 'inet' | 'ax25' | 'ipx' | 'appletalk' | 'netrom' | 'bridge' | 'atmpvc' | 'x25' | 'inet6' | 'rose' | 'netbeui' | 'security' | 'key' | 'packet' | 'ash' | 'econet' | 'atmsvc' | 'sna' | 'irda' | 'pppox' | 'wanpipe' | 'bluetooth' | 'netlink' | 'unix' | 'rds' | 'llc' | 'can' | 'tipc' | 'iucv' | 'rxrpc' | 'isdn' | 'phonet' | 'ieee802154' | 'caif' | 'alg' | 'nfc' | 'vsock' | 'mpls' | 'ib' ) ','
|
||||
B<DOMAIN> = ( 'inet' | 'ax25' | 'ipx' | 'appletalk' | 'netrom' | 'bridge' | 'atmpvc' | 'x25' | 'inet6' | 'rose' | 'netbeui' | 'security' | 'key' | 'packet' | 'ash' | 'econet' | 'atmsvc' | 'sna' | 'irda' | 'pppox' | 'wanpipe' | 'bluetooth' | 'netlink' ) ','
|
||||
|
||||
B<TYPE> = ( 'stream' | 'dgram' | 'seqpacket' | 'rdm' | 'raw' | 'packet' )
|
||||
|
||||
B<PROTOCOL> = ( 'tcp' | 'udp' | 'icmp' )
|
||||
|
||||
B<PROGRAM> = (non-whitespace characters except for '^', must start with '/'. Embedded spaces or tabs must be quoted.)
|
||||
|
||||
B<PROGRAMHAT> = '^' (non-whitespace characters; see aa_change_hat(2) for a description of how this "hat" is used.)
|
||||
|
||||
B<PROGRAMCHILD> = I<SUBPROFILE> name
|
||||
|
||||
B<MOUNT RULE> = ( I<MOUNT> | I<REMOUNT> | I<UMOUNT> )
|
||||
|
||||
B<MOUNT> = [ I<QUALIFIERS> ] 'mount' [ I<MOUNT CONDITIONS> ] [ I<SOURCE FILEGLOB> ] [ '-E<gt>' [ I<MOUNTPOINT FILEGLOB> ]
|
||||
B<MOUNT> = [ 'audit' ] [ 'deny' ] 'mount' [ I<MOUNT CONDITIONS> ] [ I<SOURCE FILEGLOB> ] [ -E<gt> [ I<MOUNTPOINT FILEGLOB> ]
|
||||
|
||||
B<REMOUNT> = [ I<QUALIFIERS> ] 'remount' [ I<MOUNT CONDITIONS> ] I<MOUNTPOINT FILEGLOB>
|
||||
B<REMOUNT> = [ 'audit' ] [ 'deny' ] 'remount' [ I<MOUNT CONDITIONS> ] I<MOUNTPOINT FILEGLOB>
|
||||
|
||||
B<UMOUNT> = [ I<QUALIFIERS> ] 'umount' [ I<MOUNT CONDITIONS> ] I<MOUNTPOINT FILEGLOB>
|
||||
B<UMOUNT> = [ 'audit' ] [ 'deny' ] 'umount' [ I<MOUNT CONDITIONS> ] I<MOUNTPOINT FILEGLOB>
|
||||
|
||||
B<MOUNT CONDITIONS> = [ ( 'fstype' | 'vfstype' ) ( '=' | 'in' ) I<MOUNT FSTYPE EXPRESSION> ] [ 'options' ( '=' | 'in' ) I<MOUNT FLAGS EXPRESSION> ]
|
||||
|
||||
@@ -131,15 +97,9 @@ B<MOUNT FLAGS> = ( 'ro' | 'rw' | 'nosuid' | 'suid' | 'nodev' | 'dev' | 'noexec'
|
||||
|
||||
B<MOUNT EXPRESSION> = ( I<ALPHANUMERIC> | I<AARE> ) ...
|
||||
|
||||
B<PIVOT ROOT RULE> = [ I<QUALIFIERS> ] pivot_root [ oldroot=I<OLD PUT FILEGLOB> ] [ I<NEW ROOT FILEGLOB> ] [ '-E<gt>' I<PROFILE NAME> ]
|
||||
B<PIVOT ROOT RULE> = [ 'audit' ] [ 'deny' ] pivot_root [ oldroot=I<OLD PUT FILEGLOB> ] [ I<NEW ROOT FILEGLOB> ] [ -E<gt> I<PROGRAMCHILD> ]
|
||||
|
||||
B<SOURCE FILEGLOB> = I<FILEGLOB>
|
||||
|
||||
B<MOUNTPOINT FILEGLOB> = I<FILEGLOB>
|
||||
|
||||
B<OLD PUT FILEGLOB> = I<FILEGLOB>
|
||||
|
||||
B<PTRACE_RULE> = [ I<QUALIFIERS> ] 'ptrace' [ I<PTRACE ACCESS PERMISSIONS> ] [ I<PTRACE PEER> ]
|
||||
B<PTRACE_RULE> = [ 'audit' ] [ 'deny' ] 'ptrace' [ I<PTRACE ACCESS PERMISSIONS> ] [ I<PTRACE PEER> ]
|
||||
|
||||
B<PTRACE ACCESS PERMISSIONS> = I<PTRACE ACCESS> | I<PTRACE ACCESS LIST>
|
||||
|
||||
@@ -149,7 +109,7 @@ B<PTRACE ACCESS> = ( 'r' | 'w' | 'rw' | 'read' | 'readby' | 'trace' | 'tracedby'
|
||||
|
||||
B<PTRACE PEER> = 'peer' '=' I<AARE>
|
||||
|
||||
B<SIGNAL_RULE> = [ I<QUALIFIERS> ] 'signal' [ I<SIGNAL ACCESS PERMISSIONS> ] [ I<SIGNAL SET> ] [ I<SIGNAL PEER> ]
|
||||
B<SIGNAL_RULE> = [ 'audit' ] [ 'deny' ] 'signal' [ I<SIGNAL ACCESS PERMISSIONS> ] [ I<SIGNAL SET> ] [ I<SIGNAL PEER> ]
|
||||
|
||||
B<SIGNAL ACCESS PERMISSIONS> = I<SIGNAL ACCESS> | I<SIGNAL ACCESS LIST>
|
||||
|
||||
@@ -161,19 +121,19 @@ B<SIGNAL SET> = 'set' '=' '(' I<SIGNAL LIST> ')'
|
||||
|
||||
B<SIGNAL LIST> = Comma or space separated list of I<SIGNALS>
|
||||
|
||||
B<SIGNALS> = ( 'hup' | 'int' | 'quit' | 'ill' | 'trap' | 'abrt' | 'bus' | 'fpe' | 'kill' | 'usr1' | 'segv' | 'usr2' | 'pipe' | 'alrm' | 'term' | 'stkflt' | 'chld' | 'cont' | 'stop' | 'stp' | 'ttin' | 'ttou' | 'urg' | 'xcpu' | 'xfsz' | 'vtalrm' | 'prof' | 'winch' | 'io' | 'pwr' | 'sys' | 'emt' | 'exists' | 'rtmin+0' ... 'rtmin+32' )
|
||||
B<SIGNALS> = ( 'hup' | 'int' | 'quit' | 'ill' | 'trap' | 'abrt' | 'bus' | 'fpe' | 'kill' | 'usr1' | 'segv' | 'usr2' | 'pipe' | 'alrm' | 'term' | 'stkflt' | 'chld' | 'cont' | 'stop' | 'stp' | 'ttin' | 'ttou' | 'urg' | 'xcpu' | 'xfsz' | 'vtalrm' | 'prof' | 'winch' | 'io' | 'pwr' | 'sys' | 'emt' | 'exists' )
|
||||
|
||||
B<SIGNAL PEER> = 'peer' '=' I<AARE>
|
||||
|
||||
B<DBUS RULE> = ( I<DBUS MESSAGE RULE> | I<DBUS SERVICE RULE> | I<DBUS EAVESDROP RULE> | I<DBUS COMBINED RULE> )
|
||||
|
||||
B<DBUS MESSAGE RULE> = [ I<QUALIFIERS> ] 'dbus' [ I<DBUS ACCESS EXPRESSION> ] [ I<DBUS BUS> ] [ I<DBUS PATH> ] [ I<DBUS INTERFACE> ] [ I<DBUS MEMBER> ] [ I<DBUS PEER> ]
|
||||
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> = [ I<QUALIFIERS> ] 'dbus' [ I<DBUS ACCESS EXPRESSION> ] [ I<DBUS BUS> ] [ I<DBUS NAME> ]
|
||||
B<DBUS SERVICE RULE> = [ 'audit' ] [ 'deny' ] 'dbus' [ I<DBUS ACCESS EXPRESSION> ] [ I<DBUS BUS> ] [ I<DBUS NAME> ]
|
||||
|
||||
B<DBUS EAVESDROP RULE> = [ I<QUALIFIERS> ] 'dbus' [ I<DBUS ACCESS EXPRESSION> ] [ I<DBUS BUS> ]
|
||||
B<DBUS EAVESDROP RULE> = [ 'audit' ] [ 'deny' ] 'dbus' [ I<DBUS ACCESS EXPRESSION> ] [ I<DBUS BUS> ]
|
||||
|
||||
B<DBUS COMBINED RULE> = [ I<QUALIFIERS> ] '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> ')' )
|
||||
|
||||
@@ -197,7 +157,9 @@ B<DBUS ACCESS> = ( 'send' | 'receive' | 'bind' | 'eavesdrop' ) (some accesses a
|
||||
|
||||
B<AARE> = B<?*[]{}^> (see below for meanings)
|
||||
|
||||
B<UNIX RULE> = [ I<QUALIFIERS> ] 'unix' [ I<UNIX ACCESS EXPR> ] [ I<UNIX RULE CONDS> ] [ I<UNIX LOCAL EXPR> ] [ I<UNIX PEER EXPR> ]
|
||||
B<UNIX RILE> = [ I<UNIX QUALIFIERS> ] 'unix' [ I<UNIX ACCESS EXPR> ] [ I<UNIX RULE CONDS> ] [ I<UNIX LOCAL EXPR> ] [ I<UNIX PEER EXPR> ]
|
||||
|
||||
B<UNIX QUALIFIERS> = [ 'audit' ] [ 'allow' | 'deny' ]
|
||||
|
||||
B<UNIX ACCESS EXPR> = ( I<UNIX ACCESS> | I<UNIX ACCESS LIST> )
|
||||
|
||||
@@ -227,50 +189,24 @@ B<UNIX ATTR COND> 'attr' '=' ( I<AARE> | '(' '"' I<AARE> '"' | I<AARE> ')' )
|
||||
|
||||
B<UNIX OPT COND> 'opt' '=' ( I<AARE> | '(' '"' I<AARE> '"' | I<AARE> ')' )
|
||||
|
||||
B<RLIMIT RULE> = 'set' 'rlimit' [I<RLIMIT> 'E<lt>=' I<RLIMIT VALUE> ]
|
||||
B<FILE RULE> = I<RULE QUALIFIER> ( '"' I<FILEGLOB> '"' | I<FILEGLOB> ) I<ACCESS> ','
|
||||
|
||||
B<RLIMIT> = ( 'cpu' | 'fsize' | 'data' | 'stack' | 'core' | 'rss' | 'nofile' | 'ofile' | 'as' | 'nproc' | 'memlock' | 'locks' | 'sigpending' | 'msgqueue' | 'nice' | 'rtprio' | 'rttime' )
|
||||
B<RULE QUALIFIER> = [ 'audit' ] [ 'deny' ] [ 'owner' ]
|
||||
|
||||
B<RLIMIT VALUE> = ( I<RLIMIT SIZE> | I<RLIMIT NUMBER> | I<RLIMIT TIME> | I<RLIMIT NICE> )
|
||||
B<FILEGLOB> = (must start with '/' (after variable expansion), B<AARE> have special meanings; see below. May include I<VARIABLE>. Rules with embedded spaces or tabs must be quoted. Rules must end with '/' to apply to directories.)
|
||||
|
||||
B<RLIMIT SIZE> = I<NUMBER> ( 'K' | 'M' | 'G' ) Only applies to RLIMIT of 'fsize', 'data', 'stack', 'core', 'rss', 'as', 'memlock', 'msgqueue'.
|
||||
|
||||
B<RLIMIT NUMBER> = number from 0 to max rlimit value. Only applies ot RLIMIT of 'ofile', 'nofile', 'locks', 'sigpending', 'nproc', 'rtprio'
|
||||
|
||||
B<RLIMIT TIME> = I<NUMBER> ( 'us' | 'microsecond' | 'microseconds' | 'ms' | 'millisecond' | 'milliseconds' | 's' | 'sec' | 'second' | 'seconds' | 'min' | 'minute' | 'minutes' | 'h' | 'hour' | 'hours' | 'd' | 'day' | 'days' | 'week' | 'weeks' ) Only applies to RLIMIT of 'cpu', 'rttime'. RLIMIT 'cpu' only allows units >= 'seconds'.
|
||||
|
||||
B<RLIMIT NICE> = a number between -20 and 19. Only applies to RLIMIT of 'nice'
|
||||
|
||||
B<FILE RULE> = [ I<QUALIFIERS> ] [ 'owner' ] ( 'file' | [ 'file' ] ( I<FILEGLOB> I<ACCESS> | I<ACCESS> I<FILEGLOB> ) [ '-E<gt>' I<EXEC TARGET> ] )
|
||||
|
||||
B<FILEGLOB> = ( I<QUOTED FILEGLOB> | I<UNQUOTED FILEGLOB> )
|
||||
|
||||
B<QUOTED FILEGLOB> = '"' I<UNQUOTED FILEGLOB> '"'
|
||||
|
||||
B<UNQUOTED FILEGLOB> = (must start with '/' (after variable expansion), B<AARE> have special meanings; see below. May include I<VARIABLE>. Rules with embedded spaces or tabs must be quoted. Rules must end with '/' to apply to directories.)
|
||||
|
||||
B<ACCESS> = ( 'r' | 'w' | 'a' | 'l' | 'k' | 'm' | I<EXEC TRANSITION> )+ (not all combinations are allowed; see below.)
|
||||
|
||||
B<EXEC TRANSITION> = ( 'ix' | 'ux' | 'Ux' | 'px' | 'Px' | 'cx' | 'Cx' | 'pix' | 'Pix' | 'cix' | 'Cix' | 'pux' | 'PUx' | 'cux' | 'CUx' | 'x' ) ('x' is only allowed in rules with the deny qualifier, everything else only without the deny qualifier)
|
||||
|
||||
B<EXEC TARGET> = name (requires I<EXEC TRANSITION> specified)
|
||||
|
||||
B<LINK RULE> = I<QUALIFIERS> [ 'owner' ] 'link' [ 'subset' ] I<FILEGLOB> ( 'to' | '-E<gt>' ) I<FILEGLOB>
|
||||
B<ACCESS> = ( 'r' | 'w' | 'l' | 'ix' | 'ux' | 'Ux' | 'px' | 'Px' | 'cx -E<gt> ' I<PROGRAMCHILD> | 'Cx -E<gt> ' I<PROGRAMCHILD> | 'm' ) [ I<ACCESS> ... ] (not all combinations are allowed; see below.)
|
||||
|
||||
B<VARIABLE> = '@{' I<ALPHA> [ ( I<ALPHANUMERIC> | '_' ) ... ] '}'
|
||||
|
||||
B<VARIABLE ASSIGNMENT> = I<VARIABLE> ('=' | '+=') (space separated values)
|
||||
|
||||
B<ALIAS RULE> = I<ABS PATH> '-E<gt>' I<REWRITTEN ABS PATH>
|
||||
B<ALIAS RULE> = I<ABS PATH> '-E<gt>' I<REWRITTEN ABS PATH> ','
|
||||
|
||||
B<ALPHA> = ('a', 'b', 'c', ... 'z', 'A', 'B', ... 'Z')
|
||||
|
||||
B<ALPHANUMERIC> = ('0', '1', '2', ... '9', 'a', 'b', 'c', ... 'z', 'A', 'B', ... 'Z')
|
||||
|
||||
B<CHANGE_PROFILE RULE> = 'change_profile' [ I<EXEC COND> ] [ '-E<gt>' I<PROFILE NAME> ]
|
||||
|
||||
B<EXEC COND> = I<FILEGLOB>
|
||||
|
||||
=back
|
||||
|
||||
All resources and programs need a full path. There may be any number of
|
||||
@@ -334,42 +270,6 @@ modes:
|
||||
|
||||
- inherit execute
|
||||
|
||||
=item B<pix>
|
||||
|
||||
- discrete profile execute with inherit fallback
|
||||
|
||||
=item B<Pix>
|
||||
|
||||
- discrete profile execute with inherit fallback -- scrub the environment
|
||||
|
||||
=item B<cix>
|
||||
|
||||
- transition to subprofile on execute with inherit fallback
|
||||
|
||||
=item B<Cix>
|
||||
|
||||
- transition to subprofile on execute with inherit fallback -- scrub the environment
|
||||
|
||||
=item B<pux>
|
||||
|
||||
- discrete profile execute with fallback to unconfined
|
||||
|
||||
=item B<PUx>
|
||||
|
||||
- discrete profile execute with fallback to unconfined -- scrub the environment
|
||||
|
||||
=item B<cux>
|
||||
|
||||
- transition to subprofile on execute with fallback to unconfined
|
||||
|
||||
=item B<CUx>
|
||||
|
||||
- transition to subprofile on execute with fallback to unconfined -- scrub the environment
|
||||
|
||||
=item B<deny x>
|
||||
|
||||
- disallow execute (in rules with the deny qualifier)
|
||||
|
||||
=item B<m>
|
||||
|
||||
- allow PROT_EXEC with mmap(2) calls
|
||||
@@ -429,7 +329,7 @@ over the callee. Use this mode only if the child absolutely must be
|
||||
run unconfined and LD_PRELOAD must be used. Any profile using this mode
|
||||
provides negligible security. Use at your own risk.
|
||||
|
||||
Incompatible with other exec transition modes and the deny qualifier.
|
||||
Incompatible with 'Ux', 'px', 'Px', 'cx', 'Cx', 'ix'.
|
||||
|
||||
=item B<Ux - unconfined execute -- scrub the environment>
|
||||
|
||||
@@ -443,7 +343,7 @@ designated child processes to be run without any AppArmor protection.
|
||||
Use this mode only if the child absolutely must be run unconfined. Use
|
||||
at your own risk.
|
||||
|
||||
Incompatible with other exec transition modes and the deny qualifier.
|
||||
Incompatible with 'ux', 'px', 'Px', 'cx', 'Cx', 'ix'.
|
||||
|
||||
=item B<px - Discrete Profile execute mode>
|
||||
|
||||
@@ -455,7 +355,7 @@ B<WARNING> 'px' does not scrub the environment of variables such as
|
||||
LD_PRELOAD; as a result, the calling domain may have an undue amount of
|
||||
influence over the callee.
|
||||
|
||||
Incompatible with other exec transition modes and the deny qualifier.
|
||||
Incompatible with 'Ux', 'ux', 'Px', 'cx', 'Cx', 'ix'.
|
||||
|
||||
=item B<Px - Discrete Profile execute mode -- scrub the environment>
|
||||
|
||||
@@ -464,7 +364,7 @@ will invoke the Linux Kernel's B<unsafe_exec> routines to scrub
|
||||
the environment, similar to setuid programs. (See ld.so(8) for some
|
||||
information on setuid/setgid environment scrubbing.)
|
||||
|
||||
Incompatible with other exec transition modes and the deny qualifier.
|
||||
Incompatible with 'Ux', 'ux', 'px', 'cx', 'Cx', 'ix'.
|
||||
|
||||
=item B<cx - Transition to Subprofile execute mode>
|
||||
|
||||
@@ -476,7 +376,7 @@ B<WARNING> 'cx' does not scrub the environment of variables such as
|
||||
LD_PRELOAD; as a result, the calling domain may have an undue amount of
|
||||
influence over the callee.
|
||||
|
||||
Incompatible with other exec transition modes and the deny qualifier.
|
||||
Incompatible with 'Ux', 'ux', 'px', 'Px', 'Cx', 'ix'.
|
||||
|
||||
=item B<Cx - Transition to Subprofile execute mode -- scrub the environment>
|
||||
|
||||
@@ -485,7 +385,7 @@ will invoke the Linux Kernel's B<unsafe_exec> routines to scrub
|
||||
the environment, similar to setuid programs. (See ld.so(8) for some
|
||||
information on setuid/setgid environment scrubbing.)
|
||||
|
||||
Incompatible with other exec transition modes and the deny qualifier.
|
||||
Incompatible with 'Ux', 'ux', 'px', 'Px', 'cx', 'ix'.
|
||||
|
||||
=item B<ix - Inherit execute mode>
|
||||
|
||||
@@ -499,58 +399,7 @@ profile, or losing the permissions of the current profile. There is no
|
||||
version to scrub the environment because 'ix' executions don't change
|
||||
privileges.
|
||||
|
||||
Incompatible with other exec transition modes and the deny qualifier.
|
||||
|
||||
=item B<Profile transition with inheritance fallback execute mode>
|
||||
|
||||
These modes attempt to perform a domain transition as specified by
|
||||
the matching permission (shown below) and if that transition fails
|
||||
to find the matching profile the domain transition proceeds using
|
||||
the 'ix' transition mode.
|
||||
|
||||
'Pix' == 'Px' with fallback to 'ix'
|
||||
'pix' == 'px' with fallback to 'ix'
|
||||
'Cix' == 'Cx' with fallback to 'ix'
|
||||
'cix' == 'cx' with fallback to 'ix'
|
||||
|
||||
Incompatible with other exec transition modes and the deny qualifier.
|
||||
|
||||
=item B<Profile transition with unconfined fallback execute mode>
|
||||
|
||||
These modes attempt to perform a domain transition as specified by
|
||||
the matching permission (shown below) and if that transition fails
|
||||
to find the matching profile the domain transition proceeds using
|
||||
the 'ux' transition mode if 'pux', 'cux' or the 'Ux' transition mode
|
||||
if 'PUx', 'CUx' is used.
|
||||
|
||||
'PUx' == 'Px' with fallback to 'Ux'
|
||||
'pux' == 'px' with fallback to 'ux'
|
||||
'CUx' == 'Cx' with fallback to 'Ux'
|
||||
'cux' == 'cx' with fallback to 'ux'
|
||||
|
||||
Incompatible with other exec transition modes and the deny qualifier.
|
||||
|
||||
=item B<deny x - Deny execute>
|
||||
|
||||
For rules including the deny modifier, only 'x' is allowed to deny execute.
|
||||
|
||||
The 'ix', 'Px', 'px', 'Cx', 'cx' and the fallback modes conflict with the deny
|
||||
modifier.
|
||||
|
||||
=item B<Directed profile transitions>
|
||||
|
||||
The directed ('px', 'Px', 'pix', 'Pix', 'pux', 'PUx') profile and
|
||||
subprofile ('cx', 'Cx', 'cix', 'Cix', 'cux', 'CUx') transitions normally
|
||||
determine the profile to transition to from the executable name. It
|
||||
is however possible to specify the name of the profile that the transition
|
||||
should use.
|
||||
|
||||
The name of the profile to transition to is specified using the '-E<gt>'
|
||||
followed by the name of the profile to transition to. Eg.
|
||||
|
||||
/bin/** px -> profile,
|
||||
|
||||
Incompatible with other exec transition modes.
|
||||
Incompatible with 'Ux', 'ux', 'Px', 'px', 'cx', 'Cx'. Implies 'm'.
|
||||
|
||||
=item B<m - Allow executable mapping>
|
||||
|
||||
@@ -567,71 +416,17 @@ B<LD_LIBRARY_PATH>, given to ld.so(8).
|
||||
|
||||
Allows the program to be able to create a link with this name. When a
|
||||
link is created, the new link B<MUST> have a subset of permissions as
|
||||
the original file (with the exception that the destination does not have
|
||||
to have link access.) If there is an 'x' rule on the new link, it must
|
||||
match the original file exactly.
|
||||
the original file (with the exception that
|
||||
the destination does not have to have link access.) If there is an 'x' rule
|
||||
on the new link, it must match the original file exactly.
|
||||
|
||||
=item B<k - lock mode>
|
||||
|
||||
Allows the program to be able lock a file with this name. This permission
|
||||
covers both advisory and mandatory locking.
|
||||
|
||||
=item B<leading OR trailing access permissions>
|
||||
|
||||
File rules can be specified with the access permission either leading
|
||||
or trailing the file glob. Eg.
|
||||
|
||||
rw /**, # leading permissions
|
||||
|
||||
/** rw, # trailing permissions
|
||||
|
||||
When leading permissions are used further rule options and context
|
||||
may be allowed, Eg.
|
||||
|
||||
l /foo -> /bar, # lead 'l' link permission is equivalent to link rules
|
||||
|
||||
=back
|
||||
|
||||
=head2 Link rules
|
||||
|
||||
Link rules allow specifying permission to form a hard link as a link
|
||||
target pair. If the subset condition is specified then the permissions
|
||||
to access the link file must be a subset of the profiles permissions
|
||||
to access the target file. If there is an 'x' rule on the new link, it
|
||||
must match the original file exactly.
|
||||
|
||||
Eg.
|
||||
|
||||
/file1 r,
|
||||
/file2 rwk,
|
||||
/link* rw,
|
||||
link subset /link* -> /**,
|
||||
|
||||
The link rule allows linking of /link to both /file1 or /file2 by
|
||||
name however because the /link file has 'rw' permissions it is not
|
||||
allowed to link to /file1 because that would grant an access path
|
||||
to /file1 with more permissions than the 'r' permissions the profile
|
||||
specifies.
|
||||
|
||||
A link of /link to /file2 would be allowed because the 'rw' permissions
|
||||
of /link are a subset of the 'rwk' permissions for /file1.
|
||||
|
||||
The link rule is equivalent to specifying the 'l' link permission as
|
||||
a leading permission with no other file access permissions. When this
|
||||
is done the link rule options can be specified.
|
||||
|
||||
The following link rule is equivalent to the 'l' permission file rule
|
||||
|
||||
link /foo -> bar,
|
||||
l /foo -> /bar,
|
||||
|
||||
File rules that specify the 'l' permission and don't specify the extend
|
||||
link permissions map to link rules as follows.
|
||||
|
||||
/foo l,
|
||||
l /foo,
|
||||
link subset /foo -> /**,
|
||||
|
||||
=head2 Comments
|
||||
|
||||
Comments start with # and may begin at any place within a line. The
|
||||
@@ -975,9 +770,6 @@ Example AppArmor signal rules:
|
||||
# Allow us to signal ourselves using the built-in @{profile_name} variable
|
||||
signal peer=@{profile_name},
|
||||
|
||||
# Allow two real-time signals
|
||||
signal set=(rtmin+0 rtmin+32),
|
||||
|
||||
=head2 DBus rules
|
||||
|
||||
AppArmor supports DBus mediation. The mediation is performed in conjunction
|
||||
@@ -1162,69 +954,6 @@ the much wider permission rule of
|
||||
|
||||
network unix,
|
||||
|
||||
=head2 change_profile rules
|
||||
|
||||
AppArmor supports self directed profile transitions via the change_profile
|
||||
api. Change_profile rules control which permissions for which profiles
|
||||
a confined task can transition to. The profile name can contain apparmor
|
||||
pattern matching to specify different profiles.
|
||||
|
||||
change_profile -> **,
|
||||
|
||||
The change_profile api allows the transition to be delayed until when
|
||||
a task executes another application. If an exec rule transition is
|
||||
specified for the application and the change_profile api is used to
|
||||
make a transition at exec time, the transition specified by the
|
||||
change_profile api takes precedence.
|
||||
|
||||
The Change_profile permission can restrict which profiles can be transitioned
|
||||
to based off of the executable name by specifying the exec condition.
|
||||
|
||||
change_profile /bin/bash -> new_profile,
|
||||
|
||||
The restricting of the transition profile to a given executable at exec
|
||||
time is only useful when then current task is allowed to make dynamic
|
||||
decisions about what confinement should be, but the decision set needs
|
||||
to be controlled. A list of profiles or multiple rules can be used to
|
||||
specify the profiles in the set. Eg.
|
||||
|
||||
change_profile /bin/bash -> {new_profile1,new_profile2,new_profile3},
|
||||
|
||||
An exec rule can be used to specify a transition for the executable, if
|
||||
the transition should be allowed even if the change_profile api has not
|
||||
been used to select a transition for those available in the change_profile
|
||||
rule set. Eg.
|
||||
|
||||
/bin/bash Px -> new_profile1,
|
||||
change_profile /bin/bash -> {new_profile1,new_profile2,new_profile3},
|
||||
|
||||
=head2 rlimit rules
|
||||
|
||||
AppArmor can set and control the resource limits associated with a
|
||||
profile as described in the setrlimit(2) man page.
|
||||
|
||||
The AppArmor rlimit controls allow setting of limits and restricting
|
||||
changes of them and these actions can be audited. Enforcement of the
|
||||
set limits is handled by the standard kernel enforcement mechanism
|
||||
for rlimits and will not result in an audited apparmor message if
|
||||
the limit is enforced.
|
||||
|
||||
If a profile does not have an rlimit rule associated with a given
|
||||
rlimit then the rlimit is left alone and regular access, including
|
||||
changing the limit, is allowed. However if the profile sets an rlimit
|
||||
then the current limit is checked and if greater than the limit specified
|
||||
in the rule it will be changed to the specified limit.
|
||||
|
||||
AppArmor rlimit rules control the hard limit of an application and
|
||||
ensure that if the hard limit is lowered that the soft limit does not
|
||||
exceed the hard limit value.
|
||||
|
||||
Eg.
|
||||
|
||||
set rlimit data <= 100M,
|
||||
set rlimit nproc <= 10,
|
||||
set rlimit nice <= 5,
|
||||
|
||||
=head2 Variables
|
||||
|
||||
AppArmor's policy language allows embedding variables into file rules
|
||||
@@ -1243,10 +972,8 @@ provided AppArmor policy:
|
||||
@{HOMEDIRS}
|
||||
@{multiarch}
|
||||
@{pid}
|
||||
@{pids}
|
||||
@{PROC}
|
||||
@{securityfs}
|
||||
@{apparmorfs}
|
||||
@{sys}
|
||||
@{tid}
|
||||
@{XDG_DESKTOP_DIR}
|
||||
@@ -1348,12 +1075,6 @@ Rule qualifiers can modify the rule and/or permissions within the rule.
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<allow>
|
||||
|
||||
Specifies that permissions requests that match the rule are allowed. This
|
||||
is the default value for rules and does not need to be specified. Conflicts
|
||||
with the I<deny> qualifier.
|
||||
|
||||
=item B<audit>
|
||||
|
||||
Specifies that permissions requests that match the rule should be recorded
|
||||
@@ -1362,8 +1083,7 @@ to the audit log.
|
||||
=item B<deny>
|
||||
|
||||
Specifies that permissions requests that match the rule should be denied
|
||||
without logging. Can be combined with 'audit' to enable logging. Conflicts
|
||||
with the I<allow> qualifier.
|
||||
without logging. Can be combined with 'audit' to enable logging.
|
||||
|
||||
=item B<owner>
|
||||
|
||||
@@ -1372,16 +1092,6 @@ referenced by the permission check.
|
||||
|
||||
=back
|
||||
|
||||
=head3 Qualifier Blocks
|
||||
|
||||
Rule Qualifiers can be applied to multiple rules at a time by grouping the
|
||||
rules into a rule block.
|
||||
|
||||
audit {
|
||||
/foo r,
|
||||
network,
|
||||
}
|
||||
|
||||
=head2 #include mechanism
|
||||
|
||||
AppArmor provides an easy abstraction mechanism to group common file
|
||||
|
@@ -183,12 +183,6 @@ defined as an absolute paths.
|
||||
Set the location of the apparmor security filesystem (default is
|
||||
"/sys/kernel/security/apparmor").
|
||||
|
||||
=item -M n, --features-file n
|
||||
|
||||
Use the features file located at path "n" (default is
|
||||
/etc/apparmor.d/cache/.features). If the --cache-loc option is present, the
|
||||
".features" file in the specified cache directory is used.
|
||||
|
||||
=item -m n, --match-string n
|
||||
|
||||
Only use match features "n".
|
||||
@@ -280,7 +274,7 @@ Use --help=dump to see a full list of which dump flags are supported
|
||||
|
||||
=item -O n, --optimize=n
|
||||
|
||||
Set the optimization flags used by policy compilation. A single optimization
|
||||
Set the optimization flags used by policy compilation. A sinlge optimization
|
||||
flag can be toggled per -O option, but the optimize flag can be passed
|
||||
multiple times. Turning off some phases of the optimization can make
|
||||
it so that policy can't complete compilation due to size constraints
|
||||
|
@@ -98,7 +98,7 @@ dbus_rule::dbus_rule(int mode_p, struct cond_entry *conds,
|
||||
if (mode_p) {
|
||||
mode = mode_p;
|
||||
if (mode & ~AA_VALID_DBUS_PERMS)
|
||||
yyerror("mode contains unknown dbus access\n");
|
||||
yyerror("mode contains unknown dbus accesss\n");
|
||||
else if (message_rule && (mode & AA_DBUS_BIND))
|
||||
yyerror("dbus \"bind\" access cannot be used with message rule conditionals\n");
|
||||
else if (service_rule && (mode & (AA_DBUS_SEND | AA_DBUS_RECEIVE)))
|
||||
@@ -228,7 +228,7 @@ int dbus_rule::gen_policy_re(Profile &prof)
|
||||
busbuf.append(buffer.str());
|
||||
|
||||
if (bus) {
|
||||
ptype = convert_aaregex_to_pcre(bus, 0, glob_default, busbuf, &pos);
|
||||
ptype = convert_aaregex_to_pcre(bus, 0, busbuf, &pos);
|
||||
if (ptype == ePatternInvalid)
|
||||
goto fail;
|
||||
} else {
|
||||
@@ -238,7 +238,7 @@ int dbus_rule::gen_policy_re(Profile &prof)
|
||||
vec[0] = busbuf.c_str();
|
||||
|
||||
if (name) {
|
||||
ptype = convert_aaregex_to_pcre(name, 0, glob_default, namebuf, &pos);
|
||||
ptype = convert_aaregex_to_pcre(name, 0, namebuf, &pos);
|
||||
if (ptype == ePatternInvalid)
|
||||
goto fail;
|
||||
vec[1] = namebuf.c_str();
|
||||
@@ -248,7 +248,7 @@ int dbus_rule::gen_policy_re(Profile &prof)
|
||||
}
|
||||
|
||||
if (peer_label) {
|
||||
ptype = convert_aaregex_to_pcre(peer_label, 0, glob_default,
|
||||
ptype = convert_aaregex_to_pcre(peer_label, 0,
|
||||
peer_labelbuf, &pos);
|
||||
if (ptype == ePatternInvalid)
|
||||
goto fail;
|
||||
@@ -259,7 +259,7 @@ int dbus_rule::gen_policy_re(Profile &prof)
|
||||
}
|
||||
|
||||
if (path) {
|
||||
ptype = convert_aaregex_to_pcre(path, 0, glob_default, pathbuf, &pos);
|
||||
ptype = convert_aaregex_to_pcre(path, 0, pathbuf, &pos);
|
||||
if (ptype == ePatternInvalid)
|
||||
goto fail;
|
||||
vec[3] = pathbuf.c_str();
|
||||
@@ -269,7 +269,7 @@ int dbus_rule::gen_policy_re(Profile &prof)
|
||||
}
|
||||
|
||||
if (interface) {
|
||||
ptype = convert_aaregex_to_pcre(interface, 0, glob_default, ifacebuf, &pos);
|
||||
ptype = convert_aaregex_to_pcre(interface, 0, ifacebuf, &pos);
|
||||
if (ptype == ePatternInvalid)
|
||||
goto fail;
|
||||
vec[4] = ifacebuf.c_str();
|
||||
@@ -279,7 +279,7 @@ int dbus_rule::gen_policy_re(Profile &prof)
|
||||
}
|
||||
|
||||
if (member) {
|
||||
ptype = convert_aaregex_to_pcre(member, 0, glob_default, memberbuf, &pos);
|
||||
ptype = convert_aaregex_to_pcre(member, 0, memberbuf, &pos);
|
||||
if (ptype == ePatternInvalid)
|
||||
goto fail;
|
||||
vec[5] = memberbuf.c_str();
|
||||
|
@@ -29,9 +29,9 @@
|
||||
#define AA_MAY_WRITE (1 << 1)
|
||||
#define AA_MAY_READ (1 << 2)
|
||||
#define AA_MAY_APPEND (1 << 3)
|
||||
#define AA_OLD_MAY_LINK (1 << 4)
|
||||
#define AA_OLD_MAY_LOCK (1 << 5)
|
||||
#define AA_OLD_EXEC_MMAP (1 << 6)
|
||||
#define AA_MAY_LINK (1 << 4)
|
||||
#define AA_MAY_LOCK (1 << 5)
|
||||
#define AA_EXEC_MMAP (1 << 6)
|
||||
#define AA_EXEC_PUX (1 << 7)
|
||||
#define AA_EXEC_UNSAFE (1 << 8)
|
||||
#define AA_EXEC_INHERIT (1 << 9)
|
||||
@@ -42,8 +42,8 @@
|
||||
|
||||
#define AA_BASE_PERMS (AA_MAY_EXEC | AA_MAY_WRITE | \
|
||||
AA_MAY_READ | AA_MAY_APPEND | \
|
||||
AA_OLD_MAY_LINK | AA_OLD_MAY_LOCK | \
|
||||
AA_EXEC_PUX | AA_OLD_EXEC_MMAP | \
|
||||
AA_MAY_LINK | AA_MAY_LOCK | \
|
||||
AA_EXEC_PUX | AA_EXEC_MMAP | \
|
||||
AA_EXEC_UNSAFE | AA_EXEC_INHERIT | \
|
||||
AA_EXEC_MOD_0 | AA_EXEC_MOD_1 | \
|
||||
AA_EXEC_MOD_2 | AA_EXEC_MOD_3)
|
||||
@@ -95,8 +95,8 @@
|
||||
#define ALL_USER_EXEC (AA_USER_EXEC | AA_USER_EXEC_TYPE)
|
||||
#define ALL_OTHER_EXEC (AA_OTHER_EXEC | AA_OTHER_EXEC_TYPE)
|
||||
|
||||
#define AA_LINK_BITS ((AA_OLD_MAY_LINK << AA_USER_SHIFT) | \
|
||||
(AA_OLD_MAY_LINK << AA_OTHER_SHIFT))
|
||||
#define AA_LINK_BITS ((AA_MAY_LINK << AA_USER_SHIFT) | \
|
||||
(AA_MAY_LINK << AA_OTHER_SHIFT))
|
||||
|
||||
#define SHIFT_MODE(MODE, SHIFT) ((((MODE) & AA_BASE_PERMS) << (SHIFT))\
|
||||
| ((MODE) & ~AA_FILE_PERMS))
|
||||
@@ -104,7 +104,7 @@
|
||||
| ((MODE) & ~AA_FILE_PERMS))
|
||||
|
||||
|
||||
#define AA_LINK_SUBSET_TEST (AA_OLD_MAY_LINK << 1)
|
||||
#define AA_LINK_SUBSET_TEST (AA_MAY_LINK << 1)
|
||||
#define LINK_SUBSET_BITS ((AA_LINK_SUBSET_TEST << AA_USER_SHIFT) | \
|
||||
(AA_LINK_SUBSET_TEST << AA_OTHER_SHIFT))
|
||||
#define LINK_TO_LINK_SUBSET(X) (((X) << 1) & AA_LINK_SUBSET_TEST)
|
||||
@@ -137,9 +137,9 @@ enum pattern_t {
|
||||
#define HAS_MAY_WRITE(mode) ((mode) & AA_MAY_WRITE)
|
||||
#define HAS_MAY_APPEND(mode) ((mode) & AA_MAY_APPEND)
|
||||
#define HAS_MAY_EXEC(mode) ((mode) & AA_MAY_EXEC)
|
||||
#define HAS_MAY_LINK(mode) ((mode) & AA_OLD_MAY_LINK)
|
||||
#define HAS_MAY_LOCK(mode) ((mode) & AA_OLD_MAY_LOCK)
|
||||
#define HAS_EXEC_MMAP(mode) ((mode) & AA_OLD_EXEC_MMAP)
|
||||
#define HAS_MAY_LINK(mode) ((mode) & AA_MAY_LINK)
|
||||
#define HAS_MAY_LOCK(mode) ((mode) & AA_MAY_LOCK)
|
||||
#define HAS_EXEC_MMAP(mode) ((mode) & AA_EXEC_MMAP)
|
||||
|
||||
#define HAS_EXEC_UNSAFE(mode) ((mode) & AA_EXEC_UNSAFE)
|
||||
#define HAS_CHANGE_PROFILE(mode) ((mode) & AA_CHANGE_PROFILE)
|
||||
@@ -161,6 +161,3 @@ static inline int is_merged_x_consistent(int a, int b)
|
||||
}
|
||||
|
||||
#endif /* ! _IMMUNIX_H */
|
||||
|
||||
/* LocalWords: MMAP
|
||||
*/
|
||||
|
125
parser/lib.c
125
parser/lib.c
@@ -16,27 +16,134 @@
|
||||
* Ltd.
|
||||
*/
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <sys/apparmor_private.h>
|
||||
|
||||
#include "lib.h"
|
||||
#include "parser.h"
|
||||
|
||||
int dirat_for_each(int dirfd, const char *name, void *data,
|
||||
int (* cb)(int, const char *, struct stat *, void *))
|
||||
/**
|
||||
* dirat_for_each: iterate over a directory calling cb for each entry
|
||||
* @dir: already opened directory (MAY BE NULL)
|
||||
* @name: name of the directory (MAY BE NULL)
|
||||
* @data: data pointer to pass to the callback fn (MAY BE NULL)
|
||||
* @cb: the callback to pass entry too (NOT NULL)
|
||||
*
|
||||
* Iterate over the entries in a directory calling cb for each entry.
|
||||
* The directory to iterate is determined by a combination of @dir and
|
||||
* @name.
|
||||
*
|
||||
* IF @name is a relative path it is determine relative to at @dir if it
|
||||
* is specified, else it the lookup is done relative to the current
|
||||
* working directory.
|
||||
*
|
||||
* If @name is not specified then @dir is used as the directory to iterate
|
||||
* over.
|
||||
*
|
||||
* It is an error if both @name and @dir are null
|
||||
*
|
||||
* The cb function is called with the DIR in use and the name of the
|
||||
* file in that directory. If the file is to be opened it should
|
||||
* use the openat, fstatat, and related fns.
|
||||
*
|
||||
* Returns: 0 on success, else -1 and errno is set to the error code
|
||||
*/
|
||||
int dirat_for_each(DIR *dir, const char *name, void *data,
|
||||
int (* cb)(DIR *, const char *, struct stat *, void *))
|
||||
{
|
||||
int retval = _aa_dirat_for_each(dirfd, name, data, cb);
|
||||
struct dirent *dirent = NULL;
|
||||
DIR *d = NULL;
|
||||
int error;
|
||||
|
||||
if (retval)
|
||||
PDEBUG("dirat_for_each failed: %m\n");
|
||||
if (!cb || (!dir && !name)) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return retval;
|
||||
if (dir && (!name || *name != '/')) {
|
||||
dirent = (struct dirent *)
|
||||
malloc(offsetof(struct dirent, d_name) +
|
||||
fpathconf(dirfd(dir), _PC_NAME_MAX) + 1);
|
||||
} else {
|
||||
dirent = (struct dirent *)
|
||||
malloc(offsetof(struct dirent, d_name) +
|
||||
pathconf(name, _PC_NAME_MAX) + 1);
|
||||
}
|
||||
if (!dirent) {
|
||||
PDEBUG("could not alloc dirent");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (name) {
|
||||
if (dir && *name != '/') {
|
||||
int fd = openat(dirfd(dir), name, O_RDONLY);
|
||||
if (fd == -1)
|
||||
goto fail;
|
||||
d = fdopendir(fd);
|
||||
} else {
|
||||
d = opendir(name);
|
||||
}
|
||||
PDEBUG("Open dir '%s': %s\n", name, d ? "succeeded" : "failed");
|
||||
if (!(d))
|
||||
goto fail;
|
||||
} else { /* dir && !name */
|
||||
PDEBUG("Recieved cache directory\n");
|
||||
d = dir;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
struct dirent *ent;
|
||||
struct stat my_stat;
|
||||
|
||||
error = readdir_r(d, dirent, &ent);
|
||||
if (error) {
|
||||
PDEBUG("readdir_r failed");
|
||||
errno = error; /* readdir_r directly returns an errno */
|
||||
goto fail;
|
||||
} else if (!ent) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (strcmp(ent->d_name, ".") == 0 ||
|
||||
strcmp(ent->d_name, "..") == 0)
|
||||
continue;
|
||||
|
||||
if (fstatat(dirfd(d), ent->d_name, &my_stat, 0)) {
|
||||
PDEBUG("stat failed for '%s'", name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (cb(d, ent->d_name, &my_stat, data)) {
|
||||
PDEBUG("dir_for_each callback failed\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (d != dir)
|
||||
closedir(d);
|
||||
free(dirent);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
error = errno;
|
||||
if (d && d != dir)
|
||||
closedir(d);
|
||||
free(dirent);
|
||||
errno = error;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,7 +152,7 @@ int dirat_for_each(int dirfd, const char *name, void *data,
|
||||
*
|
||||
* Returns: true if an octal digit, else false
|
||||
*/
|
||||
int isodigit(char c)
|
||||
bool isodigit(char c)
|
||||
{
|
||||
return (c >= '0' && c <= '7') ? true : false;
|
||||
}
|
||||
|
14
parser/lib.h
14
parser/lib.h
@@ -1,18 +1,12 @@
|
||||
#ifndef __AA_LIB_H_
|
||||
#define __AA_LIB_H_
|
||||
|
||||
#include <sys/apparmor_private.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#define autofree __attribute((cleanup(_aa_autofree)))
|
||||
#define autoclose __attribute((cleanup(_aa_autoclose)))
|
||||
#define autofclose __attribute((cleanup(_aa_autofclose)))
|
||||
int dirat_for_each(DIR *dir, const char *name, void *data,
|
||||
int (* cb)(DIR *, const char *, struct stat *, void *));
|
||||
|
||||
#define asprintf _aa_asprintf
|
||||
|
||||
int dirat_for_each(int dirfd, const char *name, void *data,
|
||||
int (* cb)(int, const char *, struct stat *, void *));
|
||||
|
||||
int isodigit(char c);
|
||||
bool isodigit(char c);
|
||||
long strntol(const char *str, const char **endptr, int base, long maxval,
|
||||
size_t n);
|
||||
int strn_escseq(const char **pos, const char *chrs, size_t n);
|
||||
|
@@ -1,17 +1,10 @@
|
||||
# Profiling:
|
||||
#EXTRA_CFLAGS = -pg
|
||||
|
||||
ifdef USE_SYSTEM
|
||||
# Using the system libapparmor
|
||||
INCLUDE_APPARMOR =
|
||||
else
|
||||
INCLUDE_APPARMOR = -I../../libraries/libapparmor/include
|
||||
endif
|
||||
|
||||
TARGET=libapparmor_re.a
|
||||
|
||||
CFLAGS ?= -g -Wall -O2 ${EXTRA_CFLAGS} -std=gnu++0x
|
||||
CXXFLAGS := ${CFLAGS} ${INCLUDE_APPARMOR}
|
||||
CXXFLAGS := ${CFLAGS}
|
||||
|
||||
ARFLAGS=-rcs
|
||||
|
||||
|
@@ -35,13 +35,13 @@
|
||||
#include "../immunix.h"
|
||||
|
||||
|
||||
|
||||
aare_rules::~aare_rules(void)
|
||||
{
|
||||
if (root)
|
||||
root->release();
|
||||
|
||||
unique_perms.clear();
|
||||
expr_map.clear();
|
||||
aare_reset_matchflags();
|
||||
}
|
||||
|
||||
bool aare_rules::add_rule(const char *rule, int deny, uint32_t perms,
|
||||
@@ -50,15 +50,40 @@ bool aare_rules::add_rule(const char *rule, int deny, uint32_t perms,
|
||||
return add_rule_vec(deny, perms, audit, 1, &rule, flags);
|
||||
}
|
||||
|
||||
#define FLAGS_WIDTH 2
|
||||
#define MATCH_FLAGS_SIZE (sizeof(uint32_t) * 8 - 1)
|
||||
MatchFlag *match_flags[FLAGS_WIDTH][MATCH_FLAGS_SIZE];
|
||||
DenyMatchFlag *deny_flags[FLAGS_WIDTH][MATCH_FLAGS_SIZE];
|
||||
#define EXEC_MATCH_FLAGS_SIZE (AA_EXEC_COUNT *2 * 2 * 2) /* double for each of ix pux, unsafe x bits * u::o */
|
||||
MatchFlag *exec_match_flags[FLAGS_WIDTH][EXEC_MATCH_FLAGS_SIZE]; /* mods + unsafe + ix + pux * u::o */
|
||||
ExactMatchFlag *exact_match_flags[FLAGS_WIDTH][EXEC_MATCH_FLAGS_SIZE]; /* mods + unsafe + ix + pux *u::o */
|
||||
|
||||
void aare_reset_matchflags(void)
|
||||
{
|
||||
uint32_t i, j;
|
||||
#define RESET_FLAGS(group, size) { \
|
||||
for (i = 0; i < FLAGS_WIDTH; i++) { \
|
||||
for (j = 0; j < size; j++) { \
|
||||
if ((group)[i][j]) delete (group)[i][j]; \
|
||||
(group)[i][j] = NULL; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
RESET_FLAGS(match_flags, MATCH_FLAGS_SIZE);
|
||||
RESET_FLAGS(deny_flags, MATCH_FLAGS_SIZE);
|
||||
RESET_FLAGS(exec_match_flags, EXEC_MATCH_FLAGS_SIZE);
|
||||
RESET_FLAGS(exact_match_flags, EXEC_MATCH_FLAGS_SIZE);
|
||||
#undef RESET_FLAGS
|
||||
}
|
||||
|
||||
void aare_rules::add_to_rules(Node *tree, Node *perms)
|
||||
{
|
||||
if (reverse)
|
||||
flip_tree(tree);
|
||||
Node *base = expr_map[perms];
|
||||
if (base)
|
||||
expr_map[perms] = new AltNode(base, tree);
|
||||
if (root)
|
||||
root = new AltNode(root, new CatNode(tree, perms));
|
||||
else
|
||||
expr_map[perms] = tree;
|
||||
root = new CatNode(tree, perms);
|
||||
}
|
||||
|
||||
static Node *cat_with_null_seperator(Node *l, Node *r)
|
||||
@@ -66,6 +91,84 @@ static Node *cat_with_null_seperator(Node *l, Node *r)
|
||||
return new CatNode(new CatNode(l, new CharNode(0)), r);
|
||||
}
|
||||
|
||||
static Node *convert_file_perms(int deny, uint32_t perms, uint32_t audit,
|
||||
bool exact_match)
|
||||
{
|
||||
Node *accept;
|
||||
|
||||
assert(perms != 0);
|
||||
|
||||
/* 0x7f == 4 bits x mods + 1 bit unsafe mask + 1 bit ix, + 1 pux after shift */
|
||||
#define EXTRACT_X_INDEX(perm, shift) (((perm) >> (shift + 7)) & 0x7f)
|
||||
|
||||
|
||||
/* the permissions set is assumed to be non-empty if any audit
|
||||
* bits are specified */
|
||||
accept = NULL;
|
||||
for (unsigned int n = 0; perms && n < (sizeof(perms) * 8); n++) {
|
||||
uint32_t mask = 1 << n;
|
||||
|
||||
if (!(perms & mask))
|
||||
continue;
|
||||
|
||||
int ai = audit & mask ? 1 : 0;
|
||||
perms &= ~mask;
|
||||
|
||||
Node *flag;
|
||||
if (mask & ALL_AA_EXEC_TYPE)
|
||||
/* these cases are covered by EXEC_BITS */
|
||||
continue;
|
||||
if (deny) {
|
||||
if (deny_flags[ai][n]) {
|
||||
flag = deny_flags[ai][n];
|
||||
} else {
|
||||
//fprintf(stderr, "Adding deny ai %d mask 0x%x audit 0x%x\n", ai, mask, audit & mask);
|
||||
deny_flags[ai][n] = new DenyMatchFlag(mask, audit & mask);
|
||||
flag = deny_flags[ai][n];
|
||||
}
|
||||
} else if (mask & AA_EXEC_BITS) {
|
||||
uint32_t eperm = 0;
|
||||
uint32_t index = 0;
|
||||
if (mask & AA_USER_EXEC) {
|
||||
eperm = mask | (perms & AA_USER_EXEC_TYPE);
|
||||
index = EXTRACT_X_INDEX(eperm, AA_USER_SHIFT);
|
||||
} else {
|
||||
eperm = mask | (perms & AA_OTHER_EXEC_TYPE);
|
||||
index = EXTRACT_X_INDEX(eperm, AA_OTHER_SHIFT) + (AA_EXEC_COUNT << 2);
|
||||
}
|
||||
//fprintf(stderr, "index %d eperm 0x%x\n", index, eperm);
|
||||
if (exact_match) {
|
||||
if (exact_match_flags[ai][index]) {
|
||||
flag = exact_match_flags[ai][index];
|
||||
} else {
|
||||
exact_match_flags[ai][index] = new ExactMatchFlag(eperm, audit & mask);
|
||||
flag = exact_match_flags[ai][index];
|
||||
}
|
||||
} else {
|
||||
if (exec_match_flags[ai][index]) {
|
||||
flag = exec_match_flags[ai][index];
|
||||
} else {
|
||||
exec_match_flags[ai][index] = new MatchFlag(eperm, audit & mask);
|
||||
flag = exec_match_flags[ai][index];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (match_flags[ai][n]) {
|
||||
flag = match_flags[ai][n];
|
||||
} else {
|
||||
match_flags[ai][n] = new MatchFlag(mask, audit & mask);
|
||||
flag = match_flags[ai][n];
|
||||
}
|
||||
}
|
||||
if (accept)
|
||||
accept = new AltNode(accept, flag);
|
||||
else
|
||||
accept = flag;
|
||||
} /* for ... */
|
||||
|
||||
return accept;
|
||||
}
|
||||
|
||||
bool aare_rules::add_rule_vec(int deny, uint32_t perms, uint32_t audit,
|
||||
int count, const char **rulev, dfaflags_t flags)
|
||||
{
|
||||
@@ -99,7 +202,7 @@ bool aare_rules::add_rule_vec(int deny, uint32_t perms, uint32_t audit,
|
||||
if (reverse)
|
||||
flip_tree(tree);
|
||||
|
||||
accept = unique_perms.insert(deny, perms, audit, exact_match);
|
||||
accept = convert_file_perms(deny, perms, audit, exact_match);
|
||||
|
||||
if (flags & DFA_DUMP_RULE_EXPR) {
|
||||
cerr << "rule: ";
|
||||
@@ -132,30 +235,6 @@ void *aare_rules::create_dfa(size_t *size, dfaflags_t flags)
|
||||
{
|
||||
char *buffer = NULL;
|
||||
|
||||
/* finish constructing the expr tree from the different permission
|
||||
* set nodes */
|
||||
PermExprMap::iterator i = expr_map.begin();
|
||||
if (i != expr_map.end()) {
|
||||
if (flags & DFA_CONTROL_TREE_SIMPLE) {
|
||||
Node *tmp = simplify_tree(i->second, flags);
|
||||
root = new CatNode(tmp, i->first);
|
||||
} else
|
||||
root = new CatNode(i->second, i->first);
|
||||
for (i++; i != expr_map.end(); i++) {
|
||||
Node *tmp;
|
||||
if (flags & DFA_CONTROL_TREE_SIMPLE) {
|
||||
tmp = simplify_tree(i->second, flags);
|
||||
} else
|
||||
tmp = i->second;
|
||||
root = new AltNode(root, new CatNode(tmp, i->first));
|
||||
}
|
||||
}
|
||||
|
||||
/* dumping of the none simplified tree without -O no-expr-simplify
|
||||
* is broken because we need to build the tree above first, and
|
||||
* simplification is woven into the build. Reevaluate how to fix
|
||||
* this debug dump.
|
||||
*/
|
||||
label_nodes(root);
|
||||
if (flags & DFA_DUMP_TREE) {
|
||||
cerr << "\nDFA: Expression Tree\n";
|
||||
@@ -164,13 +243,7 @@ void *aare_rules::create_dfa(size_t *size, dfaflags_t flags)
|
||||
}
|
||||
|
||||
if (flags & DFA_CONTROL_TREE_SIMPLE) {
|
||||
/* This is old total tree, simplification point
|
||||
* For now just do simplification up front. It gets most
|
||||
* of the benefit running on the smaller chains, and is
|
||||
* overall faster because there are less nodes. Reevaluate
|
||||
* once tree simplification is rewritten
|
||||
*/
|
||||
//root = simplify_tree(root, flags);
|
||||
root = simplify_tree(root, flags);
|
||||
|
||||
if (flags & DFA_DUMP_SIMPLE_TREE) {
|
||||
cerr << "\nDFA: Simplified Expression Tree\n";
|
||||
|
@@ -26,78 +26,14 @@
|
||||
#include "apparmor_re.h"
|
||||
#include "expr-tree.h"
|
||||
|
||||
class UniquePerm {
|
||||
public:
|
||||
bool deny;
|
||||
bool exact_match;
|
||||
uint32_t perms;
|
||||
uint32_t audit;
|
||||
|
||||
bool operator<(UniquePerm const &rhs)const
|
||||
{
|
||||
if (deny == rhs.deny) {
|
||||
if (exact_match == rhs.exact_match) {
|
||||
if (perms == rhs.perms)
|
||||
return audit < rhs.audit;
|
||||
return perms < rhs.perms;
|
||||
}
|
||||
return exact_match;
|
||||
}
|
||||
return deny;
|
||||
}
|
||||
};
|
||||
|
||||
class UniquePermsCache {
|
||||
public:
|
||||
typedef map<UniquePerm, Node*> UniquePermMap;
|
||||
typedef UniquePermMap::iterator iterator;
|
||||
UniquePermMap nodes;
|
||||
|
||||
UniquePermsCache(void) { };
|
||||
~UniquePermsCache() { clear(); }
|
||||
|
||||
void clear()
|
||||
{
|
||||
for (iterator i = nodes.begin(); i != nodes.end(); i++) {
|
||||
delete i->second;
|
||||
}
|
||||
nodes.clear();
|
||||
}
|
||||
|
||||
Node *insert(bool deny, uint32_t perms, uint32_t audit,
|
||||
bool exact_match)
|
||||
{
|
||||
UniquePerm tmp = { deny, exact_match, perms, audit };
|
||||
iterator res = nodes.find(tmp);
|
||||
if (res == nodes.end()) {
|
||||
Node *node;
|
||||
if (deny)
|
||||
node = new DenyMatchFlag(perms, audit);
|
||||
else if (exact_match)
|
||||
node = new ExactMatchFlag(perms, audit);
|
||||
else
|
||||
node = new MatchFlag(perms, audit);
|
||||
pair<iterator, bool> val = nodes.insert(make_pair(tmp, node));
|
||||
if (val.second == false)
|
||||
return val.first->second;
|
||||
return node;
|
||||
}
|
||||
return res->second;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<Node *, Node *> PermExprMap;
|
||||
|
||||
class aare_rules {
|
||||
Node *root;
|
||||
void add_to_rules(Node *tree, Node *perms);
|
||||
UniquePermsCache unique_perms;
|
||||
PermExprMap expr_map;
|
||||
public:
|
||||
public:
|
||||
int reverse;
|
||||
int rule_count;
|
||||
aare_rules(void): root(NULL), unique_perms(), expr_map(), reverse(0), rule_count(0) { };
|
||||
aare_rules(int reverse): root(NULL), unique_perms(), expr_map(), reverse(reverse), rule_count(0) { };
|
||||
aare_rules(): root(NULL), reverse(0), rule_count(0) { };
|
||||
aare_rules(int reverse): root(NULL), reverse(reverse), rule_count(0) { };
|
||||
~aare_rules();
|
||||
|
||||
bool add_rule(const char *rule, int deny, uint32_t perms,
|
||||
@@ -107,4 +43,6 @@ class aare_rules {
|
||||
void *create_dfa(size_t *size, dfaflags_t flags);
|
||||
};
|
||||
|
||||
void aare_reset_matchflags(void);
|
||||
|
||||
#endif /* __LIBAA_RE_RULES_H */
|
||||
|
@@ -554,7 +554,7 @@ static int build_mnt_opts(std::string& buffer, struct value_list *opts)
|
||||
}
|
||||
|
||||
list_for_each(opts, ent) {
|
||||
ptype = convert_aaregex_to_pcre(ent->value, 0, glob_default, buffer, &pos);
|
||||
ptype = convert_aaregex_to_pcre(ent->value, 0, buffer, &pos);
|
||||
if (ptype == ePatternInvalid)
|
||||
return FALSE;
|
||||
|
||||
|
@@ -103,7 +103,7 @@
|
||||
#define MS_CMDS (MS_MOVE | MS_REMOUNT | MS_BIND | MS_RBIND | \
|
||||
MS_UNBINDABLE | MS_RUNBINDABLE | MS_PRIVATE | MS_RPRIVATE | \
|
||||
MS_SLAVE | MS_RSLAVE | MS_SHARED | MS_RSHARED)
|
||||
#define MS_REMOUNT_FLAGS (MS_ALL_FLAGS & ~(MS_CMDS & ~MS_REMOUNT & ~MS_BIND & ~MS_RBIND))
|
||||
#define MS_REMOUNT_FLAGS (MS_ALL_FLAGS & ~(MS_CMDS & ~MS_REMOUNT))
|
||||
|
||||
#define MNT_SRC_OPT 1
|
||||
#define MNT_DST_OPT 2
|
||||
|
@@ -25,7 +25,6 @@
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
#include "lib.h"
|
||||
#include "parser.h"
|
||||
#include "profile.h"
|
||||
#include "parser_yacc.h"
|
||||
@@ -155,8 +154,7 @@ static struct network_tuple network_mappings[] = {
|
||||
static size_t kernel_af_max(void) {
|
||||
char buffer[32];
|
||||
int major;
|
||||
autoclose int fd = -1;
|
||||
int res;
|
||||
int fd, res;
|
||||
|
||||
if (!net_af_max_override) {
|
||||
return 0;
|
||||
@@ -170,6 +168,7 @@ static size_t kernel_af_max(void) {
|
||||
/* fall back to default provided during build */
|
||||
return 0;
|
||||
res = read(fd, &buffer, sizeof(buffer) - 1);
|
||||
close(fd);
|
||||
if (res <= 0)
|
||||
return 0;
|
||||
buffer[res] = '\0';
|
||||
|
@@ -17,11 +17,8 @@
|
||||
## Be verbose
|
||||
#verbose
|
||||
|
||||
## Set additional include path
|
||||
#Include /etc/apparmor.d/
|
||||
# or
|
||||
#Include /usr/share/apparmor
|
||||
|
||||
## Set include path
|
||||
#Include /etc/apparmor.d/abstractions
|
||||
|
||||
## Set location of apparmor filesystem
|
||||
#subdomainfs /sys/kernel/security/apparmor
|
||||
|
@@ -30,8 +30,6 @@
|
||||
#include <libintl.h>
|
||||
#define _(s) gettext(s)
|
||||
|
||||
#include <sys/apparmor.h>
|
||||
|
||||
#include "immunix.h"
|
||||
#include "libapparmor_re/apparmor_re.h"
|
||||
#include "libapparmor_re/aare_rules.h"
|
||||
@@ -44,8 +42,6 @@ using namespace std;
|
||||
class Profile;
|
||||
class rule_t;
|
||||
|
||||
#define MODULE_NAME "apparmor"
|
||||
|
||||
/* Global variable to pass token to lexer. Will be replaced by parameter
|
||||
* when lexer and parser are made reentrant
|
||||
*/
|
||||
@@ -100,10 +96,7 @@ struct cond_entry_list {
|
||||
struct cod_entry {
|
||||
char *ns;
|
||||
char *name;
|
||||
union {
|
||||
char *link_name;
|
||||
char *onexec;
|
||||
};
|
||||
char *link_name;
|
||||
char *nt_name;
|
||||
Profile *prof; /* Special profile defined
|
||||
* just for this executable */
|
||||
@@ -313,6 +306,7 @@ extern int option;
|
||||
extern int current_lineno;
|
||||
extern dfaflags_t dfaflags;
|
||||
extern const char *progname;
|
||||
extern char *subdomainbase;
|
||||
extern char *profilename;
|
||||
extern char *profile_ns;
|
||||
extern char *current_filename;
|
||||
@@ -322,20 +316,9 @@ extern void pwarn(const char *fmt, ...) __attribute__((__format__(__printf__, 1,
|
||||
|
||||
/* from parser_main (cannot be used in tst builds) */
|
||||
extern int force_complain;
|
||||
extern struct timespec mru_tstamp;
|
||||
extern void update_mru_tstamp(FILE *file);
|
||||
extern void display_version(void);
|
||||
extern int show_cache;
|
||||
extern int skip_cache;
|
||||
extern int skip_read_cache;
|
||||
extern int write_cache;
|
||||
extern int cond_clear_cache;
|
||||
extern int force_clear_cache;
|
||||
extern int create_cache_dir;
|
||||
extern int preprocess_only;
|
||||
extern int skip_mode_force;
|
||||
extern int abort_on_error;
|
||||
extern int skip_bad_cache_rebuild;
|
||||
extern int mru_skip_cache;
|
||||
extern int debug_cache;
|
||||
|
||||
/* provided by parser_lex.l (cannot be used in tst builds) */
|
||||
extern FILE *yyin;
|
||||
@@ -351,9 +334,7 @@ extern const char *basedir;
|
||||
#define default_match_pattern "[^\\000]*"
|
||||
#define anyone_match_pattern "[^\\000]+"
|
||||
|
||||
#define glob_default 0
|
||||
#define glob_null 1
|
||||
extern pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, int glob,
|
||||
extern pattern_t convert_aaregex_to_pcre(const char *aare, int anchor,
|
||||
std::string& pcre, int *first_re_pos);
|
||||
extern int build_list_val_expr(std::string& buffer, struct value_list *list);
|
||||
extern int convert_entry(std::string& buffer, char *entry);
|
||||
@@ -361,6 +342,8 @@ extern int clear_and_convert_entry(std::string& buffer, char *entry);
|
||||
extern int process_regex(Profile *prof);
|
||||
extern int post_process_entry(struct cod_entry *entry);
|
||||
|
||||
extern void reset_regex(void);
|
||||
|
||||
extern int process_policydb(Profile *prof);
|
||||
|
||||
extern int process_policy_ents(Profile *prof);
|
||||
@@ -393,9 +376,6 @@ extern int get_rlimit(const char *name);
|
||||
extern char *process_var(const char *var);
|
||||
extern int parse_mode(const char *mode);
|
||||
extern int parse_X_mode(const char *X, int valid, const char *str_mode, int *mode, int fail);
|
||||
void parse_label(char **ns, char **name, const char *label);
|
||||
void parse_named_transition_target(struct named_transition *nt,
|
||||
const char *target);
|
||||
extern struct cod_entry *new_entry(char *ns, char *id, int mode, char *link_id);
|
||||
|
||||
/* returns -1 if value != true or false, otherwise 0 == false, 1 == true */
|
||||
@@ -405,9 +385,6 @@ extern void free_cod_entries(struct cod_entry *list);
|
||||
extern void __debug_capabilities(uint64_t capset, const char *name);
|
||||
void debug_cod_entries(struct cod_entry *list);
|
||||
|
||||
#define SECONDS_P_MS (1000LL * 1000LL)
|
||||
long long convert_time_units(long long value, long long base, const char *units);
|
||||
|
||||
|
||||
/* parser_symtab.c */
|
||||
struct set_value {
|
||||
@@ -434,8 +411,7 @@ extern void free_aliases(void);
|
||||
extern int profile_merge_rules(Profile *prof);
|
||||
|
||||
/* parser_interface.c */
|
||||
extern int load_profile(int option, aa_kernel_interface *kernel_interface,
|
||||
Profile *prof, int cache_fd);
|
||||
extern int load_profile(int option, Profile *prof);
|
||||
extern void sd_serialize_profile(std::ostringstream &buf, Profile *prof,
|
||||
int flatten);
|
||||
extern int sd_load_buffer(int option, char *buffer, int size);
|
||||
@@ -456,12 +432,9 @@ extern int process_profile_policydb(Profile *prof);
|
||||
extern int post_merge_rules(void);
|
||||
extern int merge_hat_rules(Profile *prof);
|
||||
extern Profile *merge_policy(Profile *a, Profile *b);
|
||||
extern int load_policy(int option, aa_kernel_interface *kernel_interface,
|
||||
int cache_fd);
|
||||
extern int load_policy(int option);
|
||||
extern int load_hats(std::ostringstream &buf, Profile *prof);
|
||||
extern int load_flattened_hats(Profile *prof, int option,
|
||||
aa_kernel_interface *kernel_interface,
|
||||
int cache_fd);
|
||||
extern int load_flattened_hats(Profile *prof, int option);
|
||||
extern void dump_policy_hats(Profile *prof);
|
||||
extern void dump_policy_names(void);
|
||||
void dump_policy(void);
|
||||
|
@@ -57,7 +57,7 @@
|
||||
* numbers where supported.
|
||||
*/
|
||||
uint32_t policy_version = 2;
|
||||
uint32_t parser_abi_version = 2;
|
||||
uint32_t parser_abi_version = 1;
|
||||
uint32_t kernel_abi_version = 5;
|
||||
|
||||
int force_complain = 0;
|
||||
@@ -82,6 +82,7 @@ int option = OPTION_ADD;
|
||||
dfaflags_t dfaflags = (dfaflags_t)(DFA_CONTROL_TREE_NORMAL | DFA_CONTROL_TREE_SIMPLE | DFA_CONTROL_MINIMIZE | DFA_CONTROL_DIFF_ENCODE);
|
||||
dfaflags_t warnflags = 0;
|
||||
|
||||
char *subdomainbase = NULL;
|
||||
const char *progname = __FILE__;
|
||||
char *profile_ns = NULL;
|
||||
char *profilename = NULL;
|
||||
|
@@ -45,8 +45,6 @@
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include "lib.h"
|
||||
#include "parser.h"
|
||||
#include "parser_include.h"
|
||||
|
||||
@@ -178,7 +176,7 @@ int add_search_dir(const char *dir)
|
||||
SUBDOMAIN_PATH=/etc/subdomain.d/include */
|
||||
void parse_default_paths(void)
|
||||
{
|
||||
autofclose FILE *f;
|
||||
FILE *f;
|
||||
char buf[1024];
|
||||
char *t, *s;
|
||||
int saved_npath = npath;
|
||||
@@ -204,6 +202,7 @@ void parse_default_paths(void)
|
||||
} while (s != NULL);
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
/* if subdomain.conf doesn't set a base search dir set it to this */
|
||||
out:
|
||||
|
@@ -27,9 +27,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <sys/apparmor.h>
|
||||
|
||||
#include "lib.h"
|
||||
#include "parser.h"
|
||||
#include "profile.h"
|
||||
#include "libapparmor_re/apparmor_re.h"
|
||||
@@ -42,8 +40,7 @@
|
||||
#define SD_STR_LEN (sizeof(u16))
|
||||
|
||||
|
||||
int __sd_serialize_profile(int option, aa_kernel_interface *kernel_interface,
|
||||
Profile *prof, int cache_fd);
|
||||
int __sd_serialize_profile(int option, Profile *prof);
|
||||
|
||||
static void print_error(int error)
|
||||
{
|
||||
@@ -84,14 +81,13 @@ static void print_error(int error)
|
||||
}
|
||||
}
|
||||
|
||||
int load_profile(int option, aa_kernel_interface *kernel_interface,
|
||||
Profile *prof, int cache_fd)
|
||||
int load_profile(int option, Profile *prof)
|
||||
{
|
||||
int retval = 0;
|
||||
int error = 0;
|
||||
|
||||
PDEBUG("Serializing policy for %s.\n", prof->name);
|
||||
retval = __sd_serialize_profile(option, kernel_interface, prof, cache_fd);
|
||||
retval = __sd_serialize_profile(option, prof);
|
||||
|
||||
if (retval < 0) {
|
||||
error = retval; /* yeah, we'll just report the last error */
|
||||
@@ -378,11 +374,13 @@ void sd_serialize_profile(std::ostringstream &buf, Profile *profile,
|
||||
sd_write_struct(buf, "profile");
|
||||
if (flattened) {
|
||||
assert(profile->parent);
|
||||
autofree char *name = (char *) malloc(3 + strlen(profile->name) + strlen(profile->parent->name));
|
||||
char *name = (char *) malloc(3 + strlen(profile->name) +
|
||||
strlen(profile->parent->name));
|
||||
if (!name)
|
||||
return;
|
||||
sprintf(name, "%s//%s", profile->parent->name, profile->name);
|
||||
sd_write_string(buf, name, NULL);
|
||||
free(name);
|
||||
} else {
|
||||
sd_write_string(buf, profile->name, NULL);
|
||||
}
|
||||
@@ -470,42 +468,45 @@ void sd_serialize_top_profile(std::ostringstream &buf, Profile *profile)
|
||||
sd_write_name(buf, "version");
|
||||
sd_write_uint32(buf, version);
|
||||
|
||||
if (profile->ns) {
|
||||
if (profile_ns) {
|
||||
sd_write_string(buf, profile_ns, "namespace");
|
||||
} else if (profile->ns) {
|
||||
sd_write_string(buf, profile->ns, "namespace");
|
||||
}
|
||||
|
||||
sd_serialize_profile(buf, profile, profile->parent ? 1 : 0);
|
||||
}
|
||||
|
||||
int __sd_serialize_profile(int option, aa_kernel_interface *kernel_interface,
|
||||
Profile *prof, int cache_fd)
|
||||
int cache_fd = -1;
|
||||
int __sd_serialize_profile(int option, Profile *prof)
|
||||
{
|
||||
autoclose int fd = -1;
|
||||
int error, size, wsize;
|
||||
int fd = -1;
|
||||
int error = -ENOMEM, size, wsize;
|
||||
std::ostringstream work_area;
|
||||
char *filename = NULL;
|
||||
|
||||
switch (option) {
|
||||
case OPTION_ADD:
|
||||
if (asprintf(&filename, "%s/.load", subdomainbase) == -1)
|
||||
goto exit;
|
||||
if (kernel_load) fd = open(filename, O_WRONLY);
|
||||
break;
|
||||
case OPTION_REPLACE:
|
||||
if (asprintf(&filename, "%s/.replace", subdomainbase) == -1)
|
||||
goto exit;
|
||||
if (kernel_load) fd = open(filename, O_WRONLY);
|
||||
break;
|
||||
case OPTION_REMOVE:
|
||||
if (asprintf(&filename, "%s/.remove", subdomainbase) == -1)
|
||||
goto exit;
|
||||
if (kernel_load) fd = open(filename, O_WRONLY);
|
||||
break;
|
||||
case OPTION_STDOUT:
|
||||
filename = strdup("stdout");
|
||||
fd = dup(1);
|
||||
if (fd < 0) {
|
||||
error = -errno;
|
||||
PERROR(_("Unable to open stdout - %s\n"),
|
||||
strerror(errno));
|
||||
goto exit;
|
||||
}
|
||||
break;
|
||||
case OPTION_OFILE:
|
||||
fd = dup(fileno(ofile));
|
||||
if (fd < 0) {
|
||||
error = -errno;
|
||||
PERROR(_("Unable to open output file - %s\n"),
|
||||
strerror(errno));
|
||||
goto exit;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error = -EINVAL;
|
||||
@@ -513,37 +514,78 @@ int __sd_serialize_profile(int option, aa_kernel_interface *kernel_interface,
|
||||
break;
|
||||
}
|
||||
|
||||
if (fd < 0 && (kernel_load || option == OPTION_OFILE || option == OPTION_STDOUT)) {
|
||||
PERROR(_("Unable to open %s - %s\n"), filename,
|
||||
strerror(errno));
|
||||
error = -errno;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
error = 0;
|
||||
|
||||
free(filename);
|
||||
|
||||
if (option == OPTION_REMOVE) {
|
||||
char *name, *ns = NULL;
|
||||
int len = 0;
|
||||
|
||||
if (profile_ns) {
|
||||
len += strlen(profile_ns) + 2;
|
||||
ns = profile_ns;
|
||||
} else if (prof->ns) {
|
||||
len += strlen(prof->ns) + 2;
|
||||
ns = prof->ns;
|
||||
}
|
||||
if (prof->parent) {
|
||||
name = (char *) malloc(strlen(prof->name) + 3 +
|
||||
strlen(prof->parent->name) + len);
|
||||
if (!name) {
|
||||
PERROR(_("Memory Allocation Error: Unable to remove ^%s\n"), prof->name);
|
||||
error = -errno;
|
||||
goto exit;
|
||||
}
|
||||
if (ns)
|
||||
sprintf(name, ":%s:%s//%s", ns,
|
||||
prof->parent->name, prof->name);
|
||||
else
|
||||
sprintf(name, "%s//%s", prof->parent->name,
|
||||
prof->name);
|
||||
} else if (ns) {
|
||||
name = (char *) malloc(len + strlen(prof->name) + 1);
|
||||
if (!name) {
|
||||
PERROR(_("Memory Allocation Error: Unable to remove %s:%s."), ns, prof->name);
|
||||
error = -errno;
|
||||
goto exit;
|
||||
}
|
||||
sprintf(name, ":%s:%s", ns, prof->name);
|
||||
} else {
|
||||
name = prof->name;
|
||||
}
|
||||
size = strlen(name) + 1;
|
||||
if (kernel_load) {
|
||||
if (aa_kernel_interface_remove_policy(kernel_interface,
|
||||
prof->fqname().c_str()) == -1)
|
||||
wsize = write(fd, name, size);
|
||||
if (wsize < 0)
|
||||
error = -errno;
|
||||
}
|
||||
if (prof->parent || ns)
|
||||
free(name);
|
||||
} else {
|
||||
std::string tmp;
|
||||
|
||||
sd_serialize_top_profile(work_area, prof);
|
||||
|
||||
tmp = work_area.str();
|
||||
size = (long) work_area.tellp();
|
||||
if (kernel_load) {
|
||||
if (option == OPTION_ADD &&
|
||||
aa_kernel_interface_load_policy(kernel_interface,
|
||||
tmp.c_str(), size) == -1) {
|
||||
error = -errno;
|
||||
} else if (option == OPTION_REPLACE &&
|
||||
aa_kernel_interface_replace_policy(kernel_interface,
|
||||
tmp.c_str(), size) == -1) {
|
||||
if (kernel_load || option == OPTION_STDOUT || option == OPTION_OFILE) {
|
||||
std::string tmp = work_area.str();
|
||||
wsize = write(fd, tmp.c_str(), size);
|
||||
if (wsize < 0) {
|
||||
error = -errno;
|
||||
} else if (wsize < size) {
|
||||
PERROR(_("%s: Unable to write entire profile entry\n"),
|
||||
progname);
|
||||
error = -EIO;
|
||||
}
|
||||
} else if ((option == OPTION_STDOUT || option == OPTION_OFILE) &&
|
||||
aa_kernel_interface_write_policy(fd, tmp.c_str(), size) == -1) {
|
||||
error = -errno;
|
||||
}
|
||||
|
||||
if (cache_fd != -1) {
|
||||
std::string tmp = work_area.str();
|
||||
wsize = write(cache_fd, tmp.c_str(), size);
|
||||
if (wsize < 0) {
|
||||
error = -errno;
|
||||
@@ -555,8 +597,11 @@ int __sd_serialize_profile(int option, aa_kernel_interface *kernel_interface,
|
||||
}
|
||||
}
|
||||
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
|
||||
if (!prof->hat_table.empty() && option != OPTION_REMOVE) {
|
||||
if (load_flattened_hats(prof, option, kernel_interface, cache_fd) == 0)
|
||||
if (load_flattened_hats(prof, option) == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -565,3 +610,91 @@ exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
/* bleah the kernel should just loop and do multiple load, but to support
|
||||
* older systems we need to do this
|
||||
*/
|
||||
#define PROFILE_HEADER_SIZE
|
||||
static char header_version[] = "\x04\x08\x00version";
|
||||
|
||||
static char *next_profile_buffer(char *buffer, int size)
|
||||
{
|
||||
char *b = buffer;
|
||||
|
||||
for (; size - sizeof(header_version); b++, size--) {
|
||||
if (memcmp(b, header_version, sizeof(header_version)) == 0) {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int write_buffer(int fd, char *buffer, int size, bool set)
|
||||
{
|
||||
const char *err_str = set ? "profile set" : "profile";
|
||||
int wsize = write(fd, buffer, size);
|
||||
if (wsize < 0) {
|
||||
PERROR(_("%s: Unable to write %s\n"), progname, err_str);
|
||||
return -errno;
|
||||
} else if (wsize < size) {
|
||||
PERROR(_("%s: Unable to write %s\n"), progname, err_str);
|
||||
return -EPROTO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_load_buffer(int option, char *buffer, int size)
|
||||
{
|
||||
int fd = -1;
|
||||
int error, bsize;
|
||||
char *filename = NULL;
|
||||
|
||||
/* TODO: push backup into caller */
|
||||
if (!kernel_load)
|
||||
return 0;
|
||||
|
||||
switch (option) {
|
||||
case OPTION_ADD:
|
||||
if (asprintf(&filename, "%s/.load", subdomainbase) == -1)
|
||||
return -ENOMEM;
|
||||
break;
|
||||
case OPTION_REPLACE:
|
||||
if (asprintf(&filename, "%s/.replace", subdomainbase) == -1)
|
||||
return -ENOMEM;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fd = open(filename, O_WRONLY);
|
||||
if (fd < 0) {
|
||||
PERROR(_("Unable to open %s - %s\n"), filename,
|
||||
strerror(errno));
|
||||
error = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (kernel_supports_setload) {
|
||||
error = write_buffer(fd, buffer, size, true);
|
||||
} else {
|
||||
char *b, *next;
|
||||
|
||||
error = 0; /* in case there are no profiles */
|
||||
for (b = buffer; b; b = next, size -= bsize) {
|
||||
next = next_profile_buffer(b + sizeof(header_version),
|
||||
size);
|
||||
if (next)
|
||||
bsize = next - b;
|
||||
else
|
||||
bsize = size;
|
||||
error = write_buffer(fd, b, bsize, false);
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
|
||||
out:
|
||||
free(filename);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
@@ -32,6 +32,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
@@ -41,7 +42,6 @@
|
||||
#include "parser_include.h"
|
||||
#include "parser_yacc.h"
|
||||
#include "lib.h"
|
||||
#include "policy_cache.h"
|
||||
|
||||
#ifdef PDEBUG
|
||||
#undef PDEBUG
|
||||
@@ -119,28 +119,32 @@ struct cb_struct {
|
||||
const char *filename;
|
||||
};
|
||||
|
||||
static int include_dir_cb(int dirfd unused, const char *name, struct stat *st,
|
||||
static int include_dir_cb(DIR *dir unused, const char *name, struct stat *st,
|
||||
void *data)
|
||||
{
|
||||
struct cb_struct *d = (struct cb_struct *) data;
|
||||
|
||||
autofree char *path = NULL;
|
||||
char *path;
|
||||
|
||||
if (asprintf(&path, "%s/%s", d->fullpath, name) < 0)
|
||||
yyerror("Out of memory");
|
||||
|
||||
if (is_blacklisted(name, path))
|
||||
if (is_blacklisted(name, path)) {
|
||||
free(path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (S_ISREG(st->st_mode)) {
|
||||
if (!(yyin = fopen(path,"r")))
|
||||
yyerror(_("Could not open '%s' in '%s'"), path, d->filename);
|
||||
PDEBUG("Opened include \"%s\" in \"%s\"\n", path, d->filename);
|
||||
update_mru_tstamp(yyin, path);
|
||||
update_mru_tstamp(yyin);
|
||||
push_include_stack(path);
|
||||
yypush_buffer_state(yy_create_buffer(yyin, YY_BUF_SIZE));
|
||||
}
|
||||
|
||||
free(path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -148,7 +152,7 @@ void include_filename(char *filename, int search)
|
||||
{
|
||||
FILE *include_file = NULL;
|
||||
struct stat my_stat;
|
||||
autofree char *fullpath = NULL;
|
||||
char *fullpath = NULL;
|
||||
|
||||
if (search) {
|
||||
if (preprocess_only)
|
||||
@@ -170,7 +174,7 @@ void include_filename(char *filename, int search)
|
||||
|
||||
if (S_ISREG(my_stat.st_mode)) {
|
||||
yyin = include_file;
|
||||
update_mru_tstamp(include_file, fullpath);
|
||||
update_mru_tstamp(include_file);
|
||||
PDEBUG("Opened include \"%s\"\n", fullpath);
|
||||
push_include_stack(fullpath);
|
||||
yypush_buffer_state(yy_create_buffer( yyin, YY_BUF_SIZE ));
|
||||
@@ -178,11 +182,14 @@ void include_filename(char *filename, int search)
|
||||
struct cb_struct data = { fullpath, filename };
|
||||
fclose(include_file);
|
||||
include_file = NULL;
|
||||
if (dirat_for_each(AT_FDCWD, fullpath, &data, include_dir_cb)) {
|
||||
if (dirat_for_each(NULL, fullpath, &data, include_dir_cb)) {
|
||||
yyerror(_("Could not process include directory"
|
||||
" '%s' in '%s'"), fullpath, filename);;
|
||||
}
|
||||
}
|
||||
|
||||
if (fullpath)
|
||||
free(fullpath);
|
||||
}
|
||||
|
||||
%}
|
||||
@@ -225,8 +232,8 @@ SET_VAR_PREFIX @
|
||||
SET_VARIABLE {SET_VAR_PREFIX}(\{{VARIABLE_NAME}\}|{VARIABLE_NAME})
|
||||
BOOL_VARIABLE $(\{{VARIABLE_NAME}\}|{VARIABLE_NAME})
|
||||
|
||||
LABEL (\/|{SET_VARIABLE}{POST_VAR_ID}|{COLON}){ID}*
|
||||
QUOTED_LABEL \"(\/|{SET_VAR_PREFIX}|{COLON})([^\0"]|\\\")*\"
|
||||
PATHNAME (\/|{SET_VARIABLE}{POST_VAR_ID}){ID}*
|
||||
QPATHNAME \"(\/|{SET_VAR_PREFIX})([^\0"]|\\\")*\"
|
||||
|
||||
OPEN_PAREN \(
|
||||
CLOSE_PAREN \)
|
||||
@@ -273,8 +280,9 @@ LT_EQUAL <=
|
||||
|
||||
<INCLUDE>{
|
||||
(\<([^\> \t\n]+)\>|\"([^\" \t\n]+)\") { /* <filename> */
|
||||
autofree char *filename = strndup(yytext, yyleng - 1);
|
||||
char *filename = strndup(yytext, yyleng - 1);
|
||||
include_filename(filename + 1, *filename == '<');
|
||||
free(filename);
|
||||
POP_NODUMP();
|
||||
}
|
||||
|
||||
@@ -442,12 +450,12 @@ LT_EQUAL <=
|
||||
|
||||
({IDS}|{QUOTED_ID}) {
|
||||
yylval.id = processid(yytext, yyleng);
|
||||
RETURN_TOKEN(TOK_ID);
|
||||
POP_AND_RETURN(TOK_ID);
|
||||
}
|
||||
}
|
||||
|
||||
<RLIMIT_MODE>{
|
||||
-?{NUMBER} {
|
||||
-?{NUMBER}[[:alpha:]]* {
|
||||
yylval.var_val = strdup(yytext);
|
||||
RETURN_TOKEN(TOK_VALUE);
|
||||
}
|
||||
@@ -510,7 +518,7 @@ LT_EQUAL <=
|
||||
}
|
||||
|
||||
<MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
|
||||
({IDS_NOEQ}|{LABEL}|{QUOTED_ID}) {
|
||||
({IDS_NOEQ}|{PATHNAME}|{QUOTED_ID}) {
|
||||
yylval.id = processid(yytext, yyleng);
|
||||
RETURN_TOKEN(TOK_ID);
|
||||
}
|
||||
@@ -519,14 +527,7 @@ LT_EQUAL <=
|
||||
#include/.*\r?\n {
|
||||
/* Don't use PUSH() macro here as we don't want #include echoed out.
|
||||
* It needs to be handled specially
|
||||
*/
|
||||
yy_push_state(INCLUDE);
|
||||
}
|
||||
|
||||
include/{WS} {
|
||||
/* Don't use PUSH() macro here as we don't want #include echoed out.
|
||||
* It needs to be handled specially
|
||||
*/
|
||||
*/
|
||||
yy_push_state(INCLUDE);
|
||||
}
|
||||
|
||||
@@ -557,7 +558,7 @@ include/{WS} {
|
||||
|
||||
{CLOSE_BRACE} { RETURN_TOKEN(TOK_CLOSE); }
|
||||
|
||||
({LABEL}|{QUOTED_LABEL}) {
|
||||
({PATHNAME}|{QPATHNAME}) {
|
||||
yylval.id = processid(yytext, yyleng);
|
||||
RETURN_TOKEN(TOK_ID);
|
||||
}
|
||||
@@ -618,7 +619,7 @@ include/{WS} {
|
||||
PUSH_AND_RETURN(state, token);
|
||||
}
|
||||
|
||||
<INITIAL,NETWORK_MODE,RLIMIT_MODE,CHANGE_PROFILE_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
|
||||
<INITIAL,NETWORK_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
|
||||
{END_OF_RULE} {
|
||||
if (YY_START != INITIAL)
|
||||
POP_NODUMP();
|
||||
|
@@ -28,6 +28,7 @@
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
|
||||
/* enable the following line to get voluminous debug info */
|
||||
/* #define DEBUG */
|
||||
@@ -39,17 +40,18 @@
|
||||
#include <sys/apparmor.h>
|
||||
|
||||
#include "lib.h"
|
||||
#include "features.h"
|
||||
#include "parser.h"
|
||||
#include "parser_version.h"
|
||||
#include "parser_include.h"
|
||||
#include "common_optarg.h"
|
||||
#include "policy_cache.h"
|
||||
#include "libapparmor_re/apparmor_re.h"
|
||||
|
||||
#define MODULE_NAME "apparmor"
|
||||
#define OLD_MODULE_NAME "subdomain"
|
||||
#define PROC_MODULES "/proc/modules"
|
||||
#define DEFAULT_APPARMORFS "/sys/kernel/security/" MODULE_NAME
|
||||
#define MATCH_FILE "/sys/kernel/security/" MODULE_NAME "/matching"
|
||||
#define FEATURES_FILE "/sys/kernel/security/" MODULE_NAME "/features"
|
||||
#define MOUNTED_FS "/proc/mounts"
|
||||
#define AADFA "pattern=aadfa"
|
||||
|
||||
@@ -69,19 +71,20 @@ int skip_read_cache = 0;
|
||||
int write_cache = 0;
|
||||
int cond_clear_cache = 1; /* only applies if write is set */
|
||||
int force_clear_cache = 0; /* force clearing regargless of state */
|
||||
int create_cache_dir = 0; /* DEPRECATED in favor of write_cache */
|
||||
int create_cache_dir = 0; /* create the cache dir if missing? */
|
||||
int preprocess_only = 0;
|
||||
int skip_mode_force = 0;
|
||||
int abort_on_error = 0; /* stop processing profiles if error */
|
||||
int skip_bad_cache_rebuild = 0;
|
||||
int mru_skip_cache = 1;
|
||||
int debug_cache = 0;
|
||||
struct timespec cache_tstamp, mru_policy_tstamp;
|
||||
struct timespec mru_tstamp;
|
||||
|
||||
static char *apparmorfs = NULL;
|
||||
static char *cacheloc = NULL;
|
||||
#define FEATURES_STRING_SIZE 8192
|
||||
char *features_string = NULL;
|
||||
char *cacheloc = NULL;
|
||||
|
||||
static aa_features *features = NULL;
|
||||
/* per-profile settings */
|
||||
|
||||
static int load_features(const char *name);
|
||||
|
||||
/* Make sure to update BOTH the short and long_options */
|
||||
static const char *short_options = "adf:h::rRVvI:b:BCD:NSm:M:qQn:XKTWkL:O:po:";
|
||||
@@ -125,7 +128,6 @@ struct option long_options[] = {
|
||||
{"abort-on-error", 0, 0, 132}, /* no short option */
|
||||
{"skip-bad-cache-rebuild", 0, 0, 133}, /* no short option */
|
||||
{"warn", 1, 0, 134}, /* no short option */
|
||||
{"debug-cache", 0, 0, 135}, /* no short option */
|
||||
{NULL, 0, 0, 0},
|
||||
};
|
||||
|
||||
@@ -164,7 +166,7 @@ static void display_usage(const char *command)
|
||||
"-W, --write-cache Save cached profile (force with -T)\n"
|
||||
" --skip-bad-cache Don't clear cache if out of sync\n"
|
||||
" --purge-cache Clear cache regardless of its state\n"
|
||||
" --debug-cache Debug cache file checks\n"
|
||||
" --create-cache-dir Create the cache dir if missing\n"
|
||||
"-L, --cache-loc n Set the location of the profile cache\n"
|
||||
"-q, --quiet Don't emit warnings\n"
|
||||
"-v, --verbose Show profile names as they load\n"
|
||||
@@ -361,7 +363,7 @@ static int process_arg(int c, char *optarg)
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
apparmorfs = strndup(optarg, PATH_MAX);
|
||||
subdomainbase = strndup(optarg, PATH_MAX);
|
||||
break;
|
||||
case 'D':
|
||||
skip_read_cache = 1;
|
||||
@@ -389,17 +391,11 @@ static int process_arg(int c, char *optarg)
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
if (aa_features_new_from_string(&features,
|
||||
optarg, strlen(optarg))) {
|
||||
fprintf(stderr,
|
||||
"Failed to parse features string: %m\n");
|
||||
exit(1);
|
||||
}
|
||||
features_string = strdup(optarg);
|
||||
break;
|
||||
case 'M':
|
||||
if (aa_features_new(&features, AT_FDCWD, optarg)) {
|
||||
fprintf(stderr,
|
||||
"Failed to load features from '%s': %m\n",
|
||||
if (load_features(optarg) == -1) {
|
||||
fprintf(stderr, "Failed to load features from '%s'\n",
|
||||
optarg);
|
||||
exit(1);
|
||||
}
|
||||
@@ -467,9 +463,6 @@ static int process_arg(int c, char *optarg)
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 135:
|
||||
debug_cache = 1;
|
||||
break;
|
||||
default:
|
||||
display_usage(progname);
|
||||
exit(1);
|
||||
@@ -504,7 +497,7 @@ static int process_args(int argc, char *argv[])
|
||||
static int process_config_file(const char *name)
|
||||
{
|
||||
char *optarg;
|
||||
autofclose FILE *f = NULL;
|
||||
FILE *f;
|
||||
int c, o;
|
||||
|
||||
f = fopen(name, "r");
|
||||
@@ -513,9 +506,27 @@ static int process_config_file(const char *name)
|
||||
|
||||
while ((c = getopt_long_file(f, long_options, &optarg, &o)) != -1)
|
||||
process_arg(c, optarg);
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int find_subdomainfs_mountpoint(void)
|
||||
{
|
||||
if (aa_find_mountpoint(&subdomainbase) == -1) {
|
||||
struct stat buf;
|
||||
if (stat(DEFAULT_APPARMORFS, &buf) == -1) {
|
||||
PERROR(_("Warning: unable to find a suitable fs in %s, is it "
|
||||
"mounted?\nUse --subdomainfs to override.\n"),
|
||||
MOUNTED_FS);
|
||||
} else {
|
||||
subdomainbase = strdup(DEFAULT_APPARMORFS);
|
||||
}
|
||||
}
|
||||
|
||||
return (subdomainbase == NULL);
|
||||
}
|
||||
|
||||
int have_enough_privilege(void)
|
||||
{
|
||||
uid_t uid, euid;
|
||||
@@ -539,118 +550,275 @@ int have_enough_privilege(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *snprintf_buffer(char *buf, char *pos, ssize_t size, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int i, remaining = size - (pos - buf);
|
||||
|
||||
va_start(args, fmt);
|
||||
i = vsnprintf(pos, remaining, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (i >= size) {
|
||||
PERROR(_("Feature buffer full."));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return pos + i;
|
||||
}
|
||||
|
||||
struct features_struct {
|
||||
char **buffer;
|
||||
int size;
|
||||
char *pos;
|
||||
};
|
||||
|
||||
static int features_dir_cb(DIR *dir, const char *name, struct stat *st,
|
||||
void *data)
|
||||
{
|
||||
struct features_struct *fst = (struct features_struct *) data;
|
||||
|
||||
/* skip dot files and files with no name */
|
||||
if (*name == '.' || !strlen(name))
|
||||
return 0;
|
||||
|
||||
fst->pos = snprintf_buffer(*fst->buffer, fst->pos, fst->size, "%s {", name);
|
||||
|
||||
if (S_ISREG(st->st_mode)) {
|
||||
int len, file;
|
||||
int remaining = fst->size - (fst->pos - *fst->buffer);
|
||||
|
||||
file = openat(dirfd(dir), name, O_RDONLY);
|
||||
if (file == -1) {
|
||||
PDEBUG("Could not open '%s'", name);
|
||||
return -1;
|
||||
}
|
||||
PDEBUG("Opened features \"%s\"\n", name);
|
||||
if (st->st_size > remaining) {
|
||||
PDEBUG("Feature buffer full.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
do {
|
||||
len = read(file, fst->pos, remaining);
|
||||
if (len > 0) {
|
||||
remaining -= len;
|
||||
fst->pos += len;
|
||||
*fst->pos = 0;
|
||||
}
|
||||
} while (len > 0);
|
||||
if (len < 0) {
|
||||
PDEBUG("Error reading feature file '%s'\n", name);
|
||||
return -1;
|
||||
}
|
||||
close(file);
|
||||
} else if (S_ISDIR(st->st_mode)) {
|
||||
if (dirat_for_each(dir, name, fst, features_dir_cb))
|
||||
return -1;
|
||||
}
|
||||
|
||||
fst->pos = snprintf_buffer(*fst->buffer, fst->pos, fst->size, "}\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *handle_features_dir(const char *filename, char **buffer, int size,
|
||||
char *pos)
|
||||
{
|
||||
struct features_struct fst = { buffer, size, pos };
|
||||
|
||||
if (dirat_for_each(NULL, filename, &fst, features_dir_cb)) {
|
||||
PDEBUG("Failed evaluating %s\n", filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return fst.pos;
|
||||
}
|
||||
|
||||
static char *load_features_file(const char *name) {
|
||||
char *buffer;
|
||||
FILE *f = NULL;
|
||||
size_t size;
|
||||
|
||||
f = fopen(name, "r");
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
buffer = (char *) malloc(FEATURES_STRING_SIZE);
|
||||
if (!buffer)
|
||||
goto fail;
|
||||
|
||||
size = fread(buffer, 1, FEATURES_STRING_SIZE - 1, f);
|
||||
if (!size || ferror(f))
|
||||
goto fail;
|
||||
buffer[size] = 0;
|
||||
|
||||
fclose(f);
|
||||
return buffer;
|
||||
|
||||
fail:
|
||||
int save = errno;
|
||||
free(buffer);
|
||||
if (f)
|
||||
fclose(f);
|
||||
errno = save;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int load_features(const char *name)
|
||||
{
|
||||
struct stat stat_file;
|
||||
|
||||
if (stat(name, &stat_file) == -1)
|
||||
return -1;
|
||||
|
||||
if (S_ISDIR(stat_file.st_mode)) {
|
||||
/* if we have a features directory default to */
|
||||
features_string = (char *) malloc(FEATURES_STRING_SIZE);
|
||||
handle_features_dir(name, &features_string, FEATURES_STRING_SIZE, features_string);
|
||||
} else {
|
||||
features_string = load_features_file(name);
|
||||
if (!features_string)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_features_by_match_file(void)
|
||||
{
|
||||
autofclose FILE *ms = fopen(MATCH_FILE, "r");
|
||||
FILE *ms = fopen(MATCH_FILE, "r");
|
||||
if (ms) {
|
||||
autofree char *match_string = (char *) malloc(1000);
|
||||
char *match_string = (char *) malloc(1000);
|
||||
if (!match_string)
|
||||
goto no_match;
|
||||
if (!fgets(match_string, 1000, ms))
|
||||
if (!fgets(match_string, 1000, ms)) {
|
||||
free(match_string);
|
||||
goto no_match;
|
||||
}
|
||||
if (strstr(match_string, " perms=c"))
|
||||
perms_create = 1;
|
||||
free(match_string);
|
||||
kernel_supports_network = 1;
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
no_match:
|
||||
perms_create = 1;
|
||||
|
||||
out:
|
||||
if (ms)
|
||||
fclose(ms);
|
||||
}
|
||||
|
||||
static void set_supported_features(void)
|
||||
{
|
||||
static void set_supported_features(void) {
|
||||
|
||||
/* has process_args() already assigned a match string? */
|
||||
if (!features && aa_features_new_from_kernel(&features) == -1) {
|
||||
set_features_by_match_file();
|
||||
return;
|
||||
if (!features_string) {
|
||||
if (load_features(FEATURES_FILE) == -1) {
|
||||
set_features_by_match_file();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
perms_create = 1;
|
||||
kernel_supports_policydb = aa_features_supports(features, "file");
|
||||
kernel_supports_network = aa_features_supports(features, "network");
|
||||
kernel_supports_unix = aa_features_supports(features,
|
||||
"network/af_unix");
|
||||
kernel_supports_mount = aa_features_supports(features, "mount");
|
||||
kernel_supports_dbus = aa_features_supports(features, "dbus");
|
||||
kernel_supports_signal = aa_features_supports(features, "signal");
|
||||
kernel_supports_ptrace = aa_features_supports(features, "ptrace");
|
||||
kernel_supports_setload = aa_features_supports(features,
|
||||
"policy/set_load");
|
||||
kernel_supports_diff_encode = aa_features_supports(features,
|
||||
"policy/diff_encode");
|
||||
|
||||
if (aa_features_supports(features, "policy/versions/v7"))
|
||||
kernel_abi_version = 7;
|
||||
else if (aa_features_supports(features, "policy/versions/v6"))
|
||||
/* TODO: make this real parsing and config setting */
|
||||
if (strstr(features_string, "file {")) /* pre policydb is file= */
|
||||
kernel_supports_policydb = 1;
|
||||
if (strstr(features_string, "v6"))
|
||||
kernel_abi_version = 6;
|
||||
|
||||
if (!kernel_supports_diff_encode)
|
||||
if (strstr(features_string, "v7"))
|
||||
kernel_abi_version = 7;
|
||||
if (strstr(features_string, "set_load"))
|
||||
kernel_supports_setload = 1;
|
||||
if (strstr(features_string, "network"))
|
||||
kernel_supports_network = 1;
|
||||
if (strstr(features_string, "af_unix"))
|
||||
kernel_supports_unix = 1;
|
||||
if (strstr(features_string, "mount"))
|
||||
kernel_supports_mount = 1;
|
||||
if (strstr(features_string, "dbus"))
|
||||
kernel_supports_dbus = 1;
|
||||
if (strstr(features_string, "signal"))
|
||||
kernel_supports_signal = 1;
|
||||
if (strstr(features_string, "ptrace {"))
|
||||
kernel_supports_ptrace = 1;
|
||||
if (strstr(features_string, "diff_encode"))
|
||||
kernel_supports_diff_encode = 1;
|
||||
else if (dfaflags & DFA_CONTROL_DIFF_ENCODE)
|
||||
/* clear diff_encode because it is not supported */
|
||||
dfaflags &= ~DFA_CONTROL_DIFF_ENCODE;
|
||||
}
|
||||
|
||||
int process_binary(int option, aa_kernel_interface *kernel_interface,
|
||||
const char *profilename)
|
||||
int process_binary(int option, const char *profilename)
|
||||
{
|
||||
const char *printed_name;
|
||||
int retval;
|
||||
char *buffer = NULL;
|
||||
int retval = 0, size = 0, asize = 0, rsize;
|
||||
int chunksize = 1 << 14;
|
||||
int fd;
|
||||
|
||||
printed_name = profilename ? profilename : "stdin";
|
||||
|
||||
if (kernel_load) {
|
||||
if (option == OPTION_ADD) {
|
||||
retval = profilename ?
|
||||
aa_kernel_interface_load_policy_from_file(kernel_interface, AT_FDCWD, profilename) :
|
||||
aa_kernel_interface_load_policy_from_fd(kernel_interface, 0);
|
||||
if (retval == -1) {
|
||||
retval = errno;
|
||||
PERROR(_("Error: Could not load profile %s: %s\n"),
|
||||
printed_name, strerror(retval));
|
||||
return retval;
|
||||
}
|
||||
} else if (option == OPTION_REPLACE) {
|
||||
retval = profilename ?
|
||||
aa_kernel_interface_replace_policy_from_file(kernel_interface, AT_FDCWD, profilename) :
|
||||
aa_kernel_interface_replace_policy_from_fd(kernel_interface, 0);
|
||||
if (retval == -1) {
|
||||
retval = errno;
|
||||
PERROR(_("Error: Could not replace profile %s: %s\n"),
|
||||
printed_name, strerror(retval));
|
||||
return retval;
|
||||
}
|
||||
} else {
|
||||
PERROR(_("Error: Invalid load option specified: %d\n"),
|
||||
option);
|
||||
return EINVAL;
|
||||
if (profilename) {
|
||||
fd = open(profilename, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
retval = errno;
|
||||
PERROR(_("Error: Could not read binary profile or cache file %s: %s.\n"),
|
||||
profilename, strerror(errno));
|
||||
return retval;
|
||||
}
|
||||
} else {
|
||||
fd = dup(0);
|
||||
}
|
||||
|
||||
do {
|
||||
if (asize - size == 0) {
|
||||
buffer = (char *) realloc(buffer, chunksize);
|
||||
asize = chunksize;
|
||||
chunksize <<= 1;
|
||||
if (!buffer) {
|
||||
PERROR(_("Memory allocation error."));
|
||||
return ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
rsize = read(fd, buffer + size, asize - size);
|
||||
if (rsize)
|
||||
size += rsize;
|
||||
} while (rsize > 0);
|
||||
|
||||
close(fd);
|
||||
|
||||
if (rsize == 0)
|
||||
retval = sd_load_buffer(option, buffer, size);
|
||||
else
|
||||
retval = rsize;
|
||||
|
||||
free(buffer);
|
||||
|
||||
if (conf_verbose) {
|
||||
switch (option) {
|
||||
case OPTION_ADD:
|
||||
printf(_("Cached load succeeded for \"%s\".\n"),
|
||||
printed_name);
|
||||
profilename ? profilename : "stdin");
|
||||
break;
|
||||
case OPTION_REPLACE:
|
||||
printf(_("Cached reload succeeded for \"%s\".\n"),
|
||||
printed_name);
|
||||
profilename ? profilename : "stdin");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return retval;
|
||||
}
|
||||
|
||||
void reset_parser(const char *filename)
|
||||
{
|
||||
memset(&mru_policy_tstamp, 0, sizeof(mru_policy_tstamp));
|
||||
memset(&cache_tstamp, 0, sizeof(cache_tstamp));
|
||||
mru_skip_cache = 1;
|
||||
memset(&mru_tstamp, 0, sizeof(mru_tstamp));
|
||||
free_aliases();
|
||||
free_symtabs();
|
||||
free_policies();
|
||||
reset_regex();
|
||||
reset_include_stack(filename);
|
||||
}
|
||||
|
||||
@@ -659,7 +827,7 @@ int test_for_dir_mode(const char *basename, const char *linkdir)
|
||||
int rc = 0;
|
||||
|
||||
if (!skip_mode_force) {
|
||||
autofree char *target = NULL;
|
||||
char *target = NULL;
|
||||
if (asprintf(&target, "%s/%s/%s", basedir, linkdir, basename) < 0) {
|
||||
perror("asprintf");
|
||||
exit(1);
|
||||
@@ -667,18 +835,64 @@ int test_for_dir_mode(const char *basename, const char *linkdir)
|
||||
|
||||
if (access(target, R_OK) == 0)
|
||||
rc = 1;
|
||||
|
||||
free(target);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int process_profile(int option, aa_kernel_interface *kernel_interface,
|
||||
const char *profilename, const char *cachedir)
|
||||
#define le16_to_cpu(x) ((uint16_t)(le16toh (*(uint16_t *) x)))
|
||||
|
||||
const char header_string[] = "\004\010\000version\000\002";
|
||||
#define HEADER_STRING_SIZE 12
|
||||
static bool valid_cached_file_version(const char *cachename)
|
||||
{
|
||||
char buffer[16];
|
||||
FILE *f;
|
||||
if (!(f = fopen(cachename, "r"))) {
|
||||
PERROR(_("Error: Could not read cache file '%s', skipping...\n"), cachename);
|
||||
return false;
|
||||
}
|
||||
size_t res = fread(buffer, 1, 16, f);
|
||||
fclose(f);
|
||||
if (res < 16)
|
||||
return false;
|
||||
|
||||
/* 12 byte header that is always the same and then 4 byte version # */
|
||||
if (memcmp(buffer, header_string, HEADER_STRING_SIZE) != 0)
|
||||
return false;
|
||||
|
||||
uint32_t version = cpu_to_le32(ENCODE_VERSION(force_complain,
|
||||
policy_version,
|
||||
parser_abi_version,
|
||||
kernel_abi_version));
|
||||
if (memcmp(buffer + 12, &version, 4) != 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* returns true if time is more recent than mru_tstamp */
|
||||
#define mru_t_cmp(a) \
|
||||
(((a).tv_sec == (mru_tstamp).tv_sec) ? \
|
||||
(a).tv_nsec > (mru_tstamp).tv_nsec : (a).tv_sec > (mru_tstamp).tv_sec)
|
||||
|
||||
void update_mru_tstamp(FILE *file)
|
||||
{
|
||||
struct stat stat_file;
|
||||
if (fstat(fileno(file), &stat_file))
|
||||
return;
|
||||
if (mru_t_cmp(stat_file.st_mtim))
|
||||
mru_tstamp = stat_file.st_mtim;
|
||||
}
|
||||
|
||||
int process_profile(int option, const char *profilename)
|
||||
{
|
||||
struct stat stat_bin;
|
||||
int retval = 0;
|
||||
autofree const char *cachename = NULL;
|
||||
autofree const char *cachetmpname = NULL;
|
||||
autoclose int cachetmp = -1;
|
||||
char * cachename = NULL;
|
||||
char * cachetemp = NULL;
|
||||
const char *basename = NULL;
|
||||
|
||||
/* per-profile states */
|
||||
@@ -690,12 +904,11 @@ int process_profile(int option, aa_kernel_interface *kernel_interface,
|
||||
profilename, strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
pwarn("%s: cannot use or update cache, disable, or force-complain via stdin\n", progname);
|
||||
}
|
||||
|
||||
reset_parser(profilename);
|
||||
|
||||
if (profilename && option != OPTION_REMOVE) {
|
||||
/* make decisions about disabled or complain-mode profiles */
|
||||
basename = strrchr(profilename, '/');
|
||||
@@ -715,17 +928,17 @@ int process_profile(int option, aa_kernel_interface *kernel_interface,
|
||||
force_complain = 1;
|
||||
}
|
||||
|
||||
/* setup cachename and tstamp */
|
||||
if (!force_complain && !skip_cache) {
|
||||
cachename = cache_filename(cachedir, basename);
|
||||
valid_read_cache(cachename);
|
||||
}
|
||||
|
||||
/* TODO: add primary cache check.
|
||||
* If .file for cached binary exists get the list of profile
|
||||
* names and check their time stamps.
|
||||
*/
|
||||
/* TODO: primary cache miss/hit messages */
|
||||
}
|
||||
|
||||
reset_parser(profilename);
|
||||
if (yyin) {
|
||||
yyrestart(yyin);
|
||||
update_mru_tstamp(yyin, profilename ? profilename : "stdin");
|
||||
update_mru_tstamp(yyin);
|
||||
}
|
||||
|
||||
retval = yyparse();
|
||||
@@ -737,21 +950,44 @@ int process_profile(int option, aa_kernel_interface *kernel_interface,
|
||||
* TODO: Add support for caching profiles in an alternate namespace
|
||||
* TODO: Add support for embedded namespace defines if they aren't
|
||||
* removed from the language.
|
||||
* TODO: test profile->ns NOT profile_ns (must be after parse)
|
||||
*/
|
||||
if (profile_ns)
|
||||
skip_cache = 1;
|
||||
|
||||
if (cachename) {
|
||||
/* Load a binary cache if it exists and is newest */
|
||||
if (cache_hit(cachename)) {
|
||||
retval = process_binary(option, kernel_interface,
|
||||
cachename);
|
||||
if (!retval || skip_bad_cache_rebuild)
|
||||
return retval;
|
||||
/* Do secondary test to see if cached binary profile is good,
|
||||
* instead of checking against a presupplied list of files
|
||||
* use the timestamps from the files that were parsed.
|
||||
* Parsing the profile is slower that doing primary cache check
|
||||
* its still faster than doing full compilation
|
||||
*/
|
||||
if ((profilename && option != OPTION_REMOVE) && !force_complain &&
|
||||
!skip_cache) {
|
||||
if (asprintf(&cachename, "%s/%s", cacheloc, basename)<0) {
|
||||
PERROR(_("Memory allocation error."));
|
||||
return ENOMEM;
|
||||
}
|
||||
/* Load a binary cache if it exists and is newest */
|
||||
if (!skip_read_cache &&
|
||||
stat(cachename, &stat_bin) == 0 &&
|
||||
stat_bin.st_size > 0 && (mru_t_cmp(stat_bin.st_mtim)) &&
|
||||
valid_cached_file_version(cachename)) {
|
||||
if (show_cache)
|
||||
PERROR("Cache hit: %s\n", cachename);
|
||||
retval = process_binary(option, cachename);
|
||||
if (!retval || skip_bad_cache_rebuild)
|
||||
goto out;
|
||||
}
|
||||
if (write_cache) {
|
||||
/* Otherwise, set up to save a cached copy */
|
||||
if (asprintf(&cachetemp, "%s-XXXXXX", cachename)<0) {
|
||||
perror("asprintf");
|
||||
return ENOMEM;
|
||||
}
|
||||
if ( (cache_fd = mkstemp(cachetemp)) < 0) {
|
||||
perror("mkstemp");
|
||||
return ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
cachetmp = setup_cache_tmp(&cachetmpname, cachename);
|
||||
}
|
||||
|
||||
if (show_cache)
|
||||
@@ -787,80 +1023,186 @@ int process_profile(int option, aa_kernel_interface *kernel_interface,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* cache file generated by load_policy */
|
||||
retval = load_policy(option, kernel_interface, cachetmp);
|
||||
if (retval == 0 && write_cache) {
|
||||
if (cachetmp == -1) {
|
||||
unlink(cachetmpname);
|
||||
PERROR("Warning failed to create cache: %s\n",
|
||||
basename);
|
||||
} else {
|
||||
install_cache(cachetmpname, cachename);
|
||||
}
|
||||
}
|
||||
out:
|
||||
retval = load_policy(option);
|
||||
|
||||
out:
|
||||
if (cachetemp) {
|
||||
/* Only install the generate cache file if it parsed correctly
|
||||
and did not have write/close errors */
|
||||
int useable_cache = (cache_fd != -1 && retval == 0);
|
||||
if (cache_fd != -1) {
|
||||
if (close(cache_fd))
|
||||
useable_cache = 0;
|
||||
cache_fd = -1;
|
||||
}
|
||||
|
||||
if (useable_cache) {
|
||||
if (rename(cachetemp, cachename) < 0) {
|
||||
pwarn("Warning failed to write cache: %s\n", cachename);
|
||||
unlink(cachetemp);
|
||||
}
|
||||
else if (show_cache)
|
||||
PERROR("Wrote cache: %s\n", cachename);
|
||||
}
|
||||
else {
|
||||
unlink(cachetemp);
|
||||
PERROR("Warning failed to create cache: %s\n", basename);
|
||||
}
|
||||
free(cachetemp);
|
||||
}
|
||||
if (cachename)
|
||||
free(cachename);
|
||||
return retval;
|
||||
}
|
||||
|
||||
struct dir_cb_data {
|
||||
aa_kernel_interface *kernel_interface;
|
||||
const char *dirname; /* name of the parent dir */
|
||||
const char *cachedir; /* path to the cache sub directory */
|
||||
};
|
||||
|
||||
/* data - pointer to a dir_cb_data */
|
||||
static int profile_dir_cb(int dirfd unused, const char *name, struct stat *st,
|
||||
/* data - name of parent dir */
|
||||
static int profile_dir_cb(DIR *dir unused, const char *name, struct stat *st,
|
||||
void *data)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (!S_ISDIR(st->st_mode) && !is_blacklisted(name, NULL)) {
|
||||
struct dir_cb_data *cb_data = (struct dir_cb_data *)data;
|
||||
autofree char *path = NULL;
|
||||
if (asprintf(&path, "%s/%s", cb_data->dirname, name) < 0)
|
||||
const char *dirname = (const char *)data;
|
||||
char *path;
|
||||
if (asprintf(&path, "%s/%s", dirname, name) < 0)
|
||||
PERROR(_("Out of memory"));
|
||||
rc = process_profile(option, cb_data->kernel_interface, path,
|
||||
cb_data->cachedir);
|
||||
rc = process_profile(option, path);
|
||||
free(path);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* data - pointer to a dir_cb_data */
|
||||
static int binary_dir_cb(int dirfd unused, const char *name, struct stat *st,
|
||||
/* data - name of parent dir */
|
||||
static int binary_dir_cb(DIR *dir unused, const char *name, struct stat *st,
|
||||
void *data)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (!S_ISDIR(st->st_mode) && !is_blacklisted(name, NULL)) {
|
||||
struct dir_cb_data *cb_data = (struct dir_cb_data *)data;
|
||||
autofree char *path = NULL;
|
||||
if (asprintf(&path, "%s/%s", cb_data->dirname, name) < 0)
|
||||
const char *dirname = (const char *)data;
|
||||
char *path;
|
||||
if (asprintf(&path, "%s/%s", dirname, name) < 0)
|
||||
PERROR(_("Out of memory"));
|
||||
rc = process_binary(option, cb_data->kernel_interface, path);
|
||||
rc = process_binary(option, path);
|
||||
free(path);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int clear_cache_cb(DIR *dir, const char *path, struct stat *st,
|
||||
void *data unused)
|
||||
{
|
||||
/* remove regular files */
|
||||
if (S_ISREG(st->st_mode))
|
||||
return unlinkat(dirfd(dir), path, 0);
|
||||
|
||||
/* do nothing with other file types */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clear_cache_files(const char *path)
|
||||
{
|
||||
return dirat_for_each(NULL, path, NULL, clear_cache_cb);
|
||||
}
|
||||
|
||||
static int create_cache(const char *cachedir, const char *path,
|
||||
const char *features)
|
||||
{
|
||||
struct stat stat_file;
|
||||
FILE * f = NULL;
|
||||
|
||||
if (clear_cache_files(cacheloc) != 0)
|
||||
goto error;
|
||||
|
||||
create_file:
|
||||
f = fopen(path, "w");
|
||||
if (f) {
|
||||
if (fwrite(features, strlen(features), 1, f) != 1 )
|
||||
goto error;
|
||||
|
||||
fclose(f);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
error:
|
||||
/* does the dir exist? */
|
||||
if (stat(cachedir, &stat_file) == -1 && create_cache_dir) {
|
||||
if (mkdir(cachedir, 0700) == 0)
|
||||
goto create_file;
|
||||
if (show_cache)
|
||||
PERROR(_("Can't create cache directory: %s\n"), cachedir);
|
||||
} else if (!S_ISDIR(stat_file.st_mode)) {
|
||||
if (show_cache)
|
||||
PERROR(_("File in cache directory location: %s\n"), cachedir);
|
||||
} else {
|
||||
if (show_cache)
|
||||
PERROR(_("Can't update cache directory: %s\n"), cachedir);
|
||||
}
|
||||
|
||||
if (show_cache)
|
||||
PERROR("Cache write disabled: cannot create %s\n", path);
|
||||
write_cache = 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void setup_flags(void)
|
||||
{
|
||||
char *cache_features_path = NULL;
|
||||
char *cache_flags = NULL;
|
||||
|
||||
/* Get the match string to determine type of regex support needed */
|
||||
set_supported_features();
|
||||
|
||||
/* Gracefully handle AppArmor kernel without compatibility patch */
|
||||
if (!features) {
|
||||
PERROR("Cache read/write disabled: interface file missing. "
|
||||
"(Kernel needs AppArmor 2.4 compatibility patch.)\n");
|
||||
if (!features_string) {
|
||||
PERROR("Cache read/write disabled: %s interface file missing. "
|
||||
"(Kernel needs AppArmor 2.4 compatibility patch.)\n",
|
||||
FEATURES_FILE);
|
||||
write_cache = 0;
|
||||
skip_read_cache = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Deal with cache directory versioning:
|
||||
* - If cache/.features is missing, create it if --write-cache.
|
||||
* - If cache/.features exists, and does not match features_string,
|
||||
* force cache reading/writing off.
|
||||
*/
|
||||
if (asprintf(&cache_features_path, "%s/.features", cacheloc) == -1) {
|
||||
PERROR(_("Memory allocation error."));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
cache_flags = load_features_file(cache_features_path);
|
||||
if (cache_flags) {
|
||||
if (strcmp(features_string, cache_flags) != 0) {
|
||||
if (write_cache && cond_clear_cache) {
|
||||
if (create_cache(cacheloc, cache_features_path,
|
||||
features_string))
|
||||
skip_read_cache = 1;
|
||||
} else {
|
||||
if (show_cache)
|
||||
PERROR("Cache read/write disabled: %s does not match %s\n", FEATURES_FILE, cache_features_path);
|
||||
write_cache = 0;
|
||||
skip_read_cache = 1;
|
||||
}
|
||||
}
|
||||
free(cache_flags);
|
||||
cache_flags = NULL;
|
||||
} else if (write_cache) {
|
||||
create_cache(cacheloc, cache_features_path, features_string);
|
||||
}
|
||||
|
||||
free(cache_features_path);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
aa_kernel_interface *kernel_interface = NULL;
|
||||
aa_policy_cache *policy_cache = NULL;
|
||||
int retval, last_error;
|
||||
int i;
|
||||
int optind;
|
||||
@@ -883,62 +1225,27 @@ int main(int argc, char *argv[])
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* create the cacheloc once and use it everywhere */
|
||||
if (!cacheloc) {
|
||||
if (asprintf(&cacheloc, "%s/cache", basedir) == -1) {
|
||||
PERROR(_("Memory allocation error."));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (force_clear_cache)
|
||||
exit(clear_cache_files(cacheloc));
|
||||
|
||||
/* Check to make sure there is an interface to load policy */
|
||||
if (!(UNPRIVILEGED_OPS) && (subdomainbase == NULL) &&
|
||||
(retval = find_subdomainfs_mountpoint())) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (!binary_input) parse_default_paths();
|
||||
|
||||
setup_flags();
|
||||
|
||||
if (!(UNPRIVILEGED_OPS) &&
|
||||
aa_kernel_interface_new(&kernel_interface, features, apparmorfs) == -1) {
|
||||
PERROR(_("Warning: unable to find a suitable fs in %s, is it "
|
||||
"mounted?\nUse --subdomainfs to override.\n"),
|
||||
MOUNTED_FS);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((!skip_cache && (write_cache || !skip_read_cache)) ||
|
||||
force_clear_cache) {
|
||||
uint16_t max_caches = write_cache && cond_clear_cache ? 1 : 0;
|
||||
|
||||
if (!cacheloc && asprintf(&cacheloc, "%s/cache", basedir) == -1) {
|
||||
PERROR(_("Memory allocation error."));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (force_clear_cache) {
|
||||
if (aa_policy_cache_remove(AT_FDCWD, cacheloc)) {
|
||||
PERROR(_("Failed to clear cache files (%s): %s\n"),
|
||||
cacheloc, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (create_cache_dir)
|
||||
pwarn(_("The --create-cache-dir option is deprecated. Please use --write-cache.\n"));
|
||||
|
||||
retval = aa_policy_cache_new(&policy_cache, features,
|
||||
AT_FDCWD, cacheloc, max_caches);
|
||||
if (retval) {
|
||||
if (errno != ENOENT && errno != EEXIST) {
|
||||
PERROR(_("Failed setting up policy cache (%s): %s\n"),
|
||||
cacheloc, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (show_cache) {
|
||||
if (max_caches > 0)
|
||||
PERROR("Cache write disabled: Cannot create cache '%s': %m\n",
|
||||
cacheloc);
|
||||
else
|
||||
PERROR("Cache read/write disabled: Policy cache is invalid\n");
|
||||
}
|
||||
|
||||
write_cache = 0;
|
||||
skip_read_cache = 1;
|
||||
}
|
||||
}
|
||||
|
||||
retval = last_error = 0;
|
||||
for (i = optind; i <= argc; i++) {
|
||||
struct stat stat_file;
|
||||
@@ -960,26 +1267,17 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
if (profilename && S_ISDIR(stat_file.st_mode)) {
|
||||
int (*cb)(int dirfd, const char *name, struct stat *st,
|
||||
int (*cb)(DIR *dir, const char *name, struct stat *st,
|
||||
void *data);
|
||||
struct dir_cb_data cb_data;
|
||||
|
||||
memset(&cb_data, 0, sizeof(struct dir_cb_data));
|
||||
cb_data.dirname = profilename;
|
||||
cb_data.cachedir = cacheloc;
|
||||
cb_data.kernel_interface = kernel_interface;
|
||||
cb = binary_input ? binary_dir_cb : profile_dir_cb;
|
||||
if ((retval = dirat_for_each(AT_FDCWD, profilename,
|
||||
&cb_data, cb))) {
|
||||
if ((retval = dirat_for_each(NULL, profilename, profilename, cb))) {
|
||||
PDEBUG("Failed loading profiles from %s\n",
|
||||
profilename);
|
||||
}
|
||||
} else if (binary_input) {
|
||||
retval = process_binary(option, kernel_interface,
|
||||
profilename);
|
||||
retval = process_binary(option, profilename);
|
||||
} else {
|
||||
retval = process_profile(option, kernel_interface,
|
||||
profilename, cacheloc);
|
||||
retval = process_profile(option, profilename);
|
||||
}
|
||||
|
||||
if (profilename) free(profilename);
|
||||
@@ -994,7 +1292,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (ofile)
|
||||
fclose(ofile);
|
||||
aa_policy_cache_unref(policy_cache);
|
||||
|
||||
return last_error;
|
||||
}
|
||||
|
@@ -32,7 +32,6 @@
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/apparmor.h>
|
||||
#include <sys/apparmor_private.h>
|
||||
|
||||
#include "lib.h"
|
||||
#include "parser.h"
|
||||
@@ -51,14 +50,53 @@
|
||||
#endif
|
||||
#define NPDEBUG(fmt, args...) /* Do nothing */
|
||||
|
||||
struct ignored_suffix_t {
|
||||
const char * text;
|
||||
int len;
|
||||
int silent;
|
||||
};
|
||||
|
||||
static struct ignored_suffix_t ignored_suffixes[] = {
|
||||
/* Debian packging files, which are in flux during install
|
||||
should be silently ignored. */
|
||||
{ ".dpkg-new", 9, 1 },
|
||||
{ ".dpkg-old", 9, 1 },
|
||||
{ ".dpkg-dist", 10, 1 },
|
||||
{ ".dpkg-bak", 9, 1 },
|
||||
/* RPM packaging files have traditionally not been silently
|
||||
ignored */
|
||||
{ ".rpmnew", 7, 0 },
|
||||
{ ".rpmsave", 8, 0 },
|
||||
/* patch file backups/conflicts */
|
||||
{ ".orig", 5, 0 },
|
||||
{ ".rej", 4, 0 },
|
||||
/* Backup files should be mentioned */
|
||||
{ "~", 1, 0 },
|
||||
{ NULL, 0, 0 }
|
||||
};
|
||||
|
||||
int is_blacklisted(const char *name, const char *path)
|
||||
{
|
||||
int retval = _aa_is_blacklisted(name);
|
||||
int name_len;
|
||||
struct ignored_suffix_t *suffix;
|
||||
|
||||
if (retval == -1)
|
||||
PERROR("Ignoring: '%s'\n", path ? path : name);
|
||||
/* skip dot files and files with no name */
|
||||
if (*name == '.' || !strlen(name))
|
||||
return 1;
|
||||
|
||||
return !retval ? 0 : 1;
|
||||
name_len = strlen(name);
|
||||
/* skip blacklisted suffixes */
|
||||
for (suffix = ignored_suffixes; suffix->text; suffix++) {
|
||||
char *found;
|
||||
if ( (found = strstr((char *) name, suffix->text)) &&
|
||||
found - name + suffix->len == name_len ) {
|
||||
if (!suffix->silent)
|
||||
PERROR("Ignoring: '%s'\n", path ? path : name);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct keyword_table {
|
||||
@@ -205,10 +243,7 @@ char *processunquoted(const char *string, int len)
|
||||
* pass it through to be handled by the backend
|
||||
* pcre conversion
|
||||
*/
|
||||
if (c == 0) {
|
||||
strncpy(s, string, pos - string);
|
||||
s += pos - string;
|
||||
} else if (strchr("*?[]{}^,\\", c) != NULL) {
|
||||
if (strchr("*?[]{}^,\\", c) != NULL) {
|
||||
*s++ = '\\';
|
||||
*s++ = c;
|
||||
} else
|
||||
@@ -332,7 +367,7 @@ reeval:
|
||||
case COD_READ_CHAR:
|
||||
if (read_implies_exec) {
|
||||
PDEBUG("Parsing mode: found %s READ imply X\n", mode_desc);
|
||||
mode |= AA_MAY_READ | AA_OLD_EXEC_MMAP;
|
||||
mode |= AA_MAY_READ | AA_EXEC_MMAP;
|
||||
} else {
|
||||
PDEBUG("Parsing mode: found %s READ\n", mode_desc);
|
||||
mode |= AA_MAY_READ;
|
||||
@@ -355,12 +390,12 @@ reeval:
|
||||
|
||||
case COD_LINK_CHAR:
|
||||
PDEBUG("Parsing mode: found %s LINK\n", mode_desc);
|
||||
mode |= AA_OLD_MAY_LINK;
|
||||
mode |= AA_MAY_LINK;
|
||||
break;
|
||||
|
||||
case COD_LOCK_CHAR:
|
||||
PDEBUG("Parsing mode: found %s LOCK\n", mode_desc);
|
||||
mode |= AA_OLD_MAY_LOCK;
|
||||
mode |= AA_MAY_LOCK;
|
||||
break;
|
||||
|
||||
case COD_INHERIT_CHAR:
|
||||
@@ -439,7 +474,7 @@ reeval:
|
||||
|
||||
case COD_MMAP_CHAR:
|
||||
PDEBUG("Parsing mode: found %s MMAP\n", mode_desc);
|
||||
mode |= AA_OLD_EXEC_MMAP;
|
||||
mode |= AA_EXEC_MMAP;
|
||||
break;
|
||||
|
||||
case COD_EXEC_CHAR:
|
||||
@@ -569,65 +604,6 @@ int parse_X_mode(const char *X, int valid, const char *str_mode, int *mode, int
|
||||
return 1;
|
||||
}
|
||||
|
||||
void parse_label(char **ns, char **name, const char *label)
|
||||
{
|
||||
const char *name_start = NULL;
|
||||
char *_ns = NULL;
|
||||
char *_name = NULL;
|
||||
|
||||
if (label[0] != ':') {
|
||||
/* There is no namespace specified in the label */
|
||||
name_start = label;
|
||||
} else {
|
||||
/* A leading ':' indicates that a namespace is specified */
|
||||
const char *ns_start = label + 1;
|
||||
const char *ns_end = strstr(ns_start, ":");
|
||||
|
||||
if (!ns_end)
|
||||
yyerror(_("Namespace not terminated: %s\n"), label);
|
||||
else if (ns_end - ns_start == 0)
|
||||
yyerror(_("Empty namespace: %s\n"), label);
|
||||
|
||||
/**
|
||||
* Handle either of the two namespace formats:
|
||||
* 1) :ns:name
|
||||
* 2) :ns://name
|
||||
*/
|
||||
name_start = ns_end + 1;
|
||||
if (!strncmp(name_start, "//", 2))
|
||||
name_start += 2;
|
||||
|
||||
_ns = strndup(ns_start, ns_end - ns_start);
|
||||
if (!_ns)
|
||||
yyerror(_("Memory allocation error."));
|
||||
}
|
||||
|
||||
if (!strlen(name_start))
|
||||
yyerror(_("Empty named transition profile name: %s\n"), label);
|
||||
|
||||
_name = strdup(name_start);
|
||||
if (!_name) {
|
||||
free(_ns);
|
||||
yyerror(_("Memory allocation error."));
|
||||
}
|
||||
|
||||
*ns = _ns;
|
||||
*name = _name;
|
||||
}
|
||||
|
||||
void parse_named_transition_target(struct named_transition *nt,
|
||||
const char *target)
|
||||
{
|
||||
memset(nt, 0, sizeof(*nt));
|
||||
if (!target) {
|
||||
/* Return with nt->present set to 0 (thanks to the memset) */
|
||||
return;
|
||||
}
|
||||
|
||||
parse_label(&nt->ns, &nt->name, target);
|
||||
nt->present = 1;
|
||||
}
|
||||
|
||||
struct cod_entry *new_entry(char *ns, char *id, int mode, char *link_id)
|
||||
{
|
||||
struct cod_entry *entry = NULL;
|
||||
@@ -783,7 +759,7 @@ static const char *capnames[] = {
|
||||
"audit_write",
|
||||
"audit_control",
|
||||
"setfcap",
|
||||
"mac_override",
|
||||
"mac_override"
|
||||
"syslog",
|
||||
};
|
||||
|
||||
@@ -926,54 +902,6 @@ void print_cond_entry(struct cond_entry *ent)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct time_units {
|
||||
const char *str;
|
||||
long long value;
|
||||
};
|
||||
|
||||
static struct time_units time_units[] = {
|
||||
{ "us", 1LL },
|
||||
{ "microsecond", 1LL },
|
||||
{ "microseconds", 1LL },
|
||||
{ "ms", 1000LL },
|
||||
{ "millisecond", 1000LL },
|
||||
{ "milliseconds", 1000LL },
|
||||
{ "s", 1000LL * 1000LL },
|
||||
{ "sec", SECONDS_P_MS },
|
||||
{ "second", SECONDS_P_MS },
|
||||
{ "seconds", SECONDS_P_MS },
|
||||
{ "min" , 60LL * SECONDS_P_MS },
|
||||
{ "minute", 60LL * SECONDS_P_MS },
|
||||
{ "minutes", 60LL * SECONDS_P_MS },
|
||||
{ "h", 60LL * 60LL * SECONDS_P_MS },
|
||||
{ "hour", 60LL * 60LL * SECONDS_P_MS },
|
||||
{ "hours", 60LL * 60LL * SECONDS_P_MS },
|
||||
{ "d", 24LL * 60LL * 60LL * SECONDS_P_MS },
|
||||
{ "day", 24LL * 60LL * 60LL * SECONDS_P_MS },
|
||||
{ "days", 24LL * 60LL * 60LL * SECONDS_P_MS },
|
||||
{ "week", 7LL * 24LL * 60LL * 60LL * SECONDS_P_MS },
|
||||
{ "weeks", 7LL * 24LL * 60LL * 60LL * SECONDS_P_MS },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
long long convert_time_units(long long value, long long base, const char *units)
|
||||
{
|
||||
struct time_units *ent;
|
||||
if (!units)
|
||||
/* default to base if no units */
|
||||
return value;
|
||||
|
||||
for (ent = time_units; ent->str; ent++) {
|
||||
if (strcmp(ent->str, units) == 0) {
|
||||
if (value * ent->value < base)
|
||||
return -1LL;
|
||||
return value * ent->value / base;
|
||||
}
|
||||
}
|
||||
return -2LL;
|
||||
}
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
|
||||
#include "unit_test.h"
|
||||
@@ -1192,50 +1120,6 @@ int test_processquoted(void)
|
||||
return rc;
|
||||
}
|
||||
|
||||
#define TIME_TEST(V, B, U, R) \
|
||||
MY_TEST(convert_time_units((V), (B), U) == (R), \
|
||||
"convert " #V " with base of " #B ", " #U " units")
|
||||
|
||||
int test_convert_time_units()
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
TIME_TEST(1LL, 1LL, NULL, 1LL);
|
||||
TIME_TEST(12345LL, 1LL, NULL, 12345LL);
|
||||
TIME_TEST(10LL, 10LL, NULL, 10LL);
|
||||
TIME_TEST(123450LL, 10LL, NULL, 123450LL);
|
||||
|
||||
TIME_TEST(12345LL, 1LL, "us", 12345LL);
|
||||
TIME_TEST(12345LL, 1LL, "microsecond", 12345LL);
|
||||
TIME_TEST(12345LL, 1LL, "microseconds", 12345LL);
|
||||
|
||||
TIME_TEST(12345LL, 1LL, "ms", 12345LL*1000LL);
|
||||
TIME_TEST(12345LL, 1LL, "millisecond", 12345LL*1000LL);
|
||||
TIME_TEST(12345LL, 1LL, "milliseconds", 12345LL*1000LL);
|
||||
|
||||
TIME_TEST(12345LL, 1LL, "s", 12345LL*1000LL*1000LL);
|
||||
TIME_TEST(12345LL, 1LL, "sec", 12345LL*1000LL*1000LL);
|
||||
TIME_TEST(12345LL, 1LL, "second", 12345LL*1000LL*1000LL);
|
||||
TIME_TEST(12345LL, 1LL, "seconds", 12345LL*1000LL*1000LL);
|
||||
|
||||
TIME_TEST(12345LL, 1LL, "min", 12345LL*1000LL*1000LL*60LL);
|
||||
TIME_TEST(12345LL, 1LL, "minute", 12345LL*1000LL*1000LL*60LL);
|
||||
TIME_TEST(12345LL, 1LL, "minutes", 12345LL*1000LL*1000LL*60LL);
|
||||
|
||||
TIME_TEST(12345LL, 1LL, "h", 12345LL*1000LL*1000LL*60LL*60LL);
|
||||
TIME_TEST(12345LL, 1LL, "hour", 12345LL*1000LL*1000LL*60LL*60LL);
|
||||
TIME_TEST(12345LL, 1LL, "hours", 12345LL*1000LL*1000LL*60LL*60LL);
|
||||
|
||||
TIME_TEST(12345LL, 1LL, "d", 12345LL*1000LL*1000LL*60LL*60LL*24LL);
|
||||
TIME_TEST(12345LL, 1LL, "day", 12345LL*1000LL*1000LL*60LL*60LL*24LL);
|
||||
TIME_TEST(12345LL, 1LL, "days", 12345LL*1000LL*1000LL*60LL*60LL*24LL);
|
||||
|
||||
TIME_TEST(12345LL, 1LL, "week", 12345LL*1000LL*1000LL*60LL*60LL*24LL*7LL);
|
||||
TIME_TEST(12345LL, 1LL, "weeks", 12345LL*1000LL*1000LL*60LL*60LL*24LL*7LL);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int rc = 0;
|
||||
@@ -1253,10 +1137,6 @@ int main(void)
|
||||
if (retval != 0)
|
||||
rc = retval;
|
||||
|
||||
retval = test_convert_time_units();
|
||||
if (retval != 0)
|
||||
rc = retval;
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif /* UNIT_TEST */
|
||||
|
@@ -27,7 +27,6 @@
|
||||
#include <search.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/apparmor.h>
|
||||
|
||||
#include "parser.h"
|
||||
#include "profile.h"
|
||||
@@ -221,13 +220,12 @@ static int profile_add_hat_rules(Profile *prof)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int load_policy_list(ProfileList &list, int option,
|
||||
aa_kernel_interface *kernel_interface, int cache_fd)
|
||||
int load_policy_list(ProfileList &list, int option)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
for (ProfileList::iterator i = list.begin(); i != list.end(); i++) {
|
||||
res = load_profile(option, kernel_interface, *i, cache_fd);
|
||||
res = load_profile(option, *i);
|
||||
if (res != 0)
|
||||
break;
|
||||
}
|
||||
@@ -235,16 +233,14 @@ int load_policy_list(ProfileList &list, int option,
|
||||
return res;
|
||||
}
|
||||
|
||||
int load_flattened_hats(Profile *prof, int option,
|
||||
aa_kernel_interface *kernel_interface, int cache_fd)
|
||||
int load_flattened_hats(Profile *prof, int option)
|
||||
{
|
||||
return load_policy_list(prof->hat_table, option, kernel_interface,
|
||||
cache_fd);
|
||||
return load_policy_list(prof->hat_table, option);
|
||||
}
|
||||
|
||||
int load_policy(int option, aa_kernel_interface *kernel_interface, int cache_fd)
|
||||
int load_policy(int option)
|
||||
{
|
||||
return load_policy_list(policy_list, option, kernel_interface, cache_fd);
|
||||
return load_policy_list(policy_list, option);
|
||||
}
|
||||
|
||||
int load_hats(std::ostringstream &buf, Profile *prof)
|
||||
|
@@ -29,7 +29,6 @@
|
||||
|
||||
/* #define DEBUG */
|
||||
|
||||
#include "lib.h"
|
||||
#include "parser.h"
|
||||
#include "profile.h"
|
||||
#include "libapparmor_re/apparmor_re.h"
|
||||
@@ -84,27 +83,9 @@ static void filter_slashes(char *path)
|
||||
*dptr = 0;
|
||||
}
|
||||
|
||||
static error_type append_glob(std::string &pcre, int glob,
|
||||
const char *default_glob, const char *null_glob)
|
||||
{
|
||||
switch (glob) {
|
||||
case glob_default:
|
||||
pcre.append(default_glob);
|
||||
break;
|
||||
case glob_null:
|
||||
pcre.append(null_glob);
|
||||
break;
|
||||
default:
|
||||
PERROR(_("%s: Invalid glob type %d\n"), progname, glob);
|
||||
return e_parse_error;
|
||||
break;
|
||||
}
|
||||
return e_no_error;
|
||||
}
|
||||
|
||||
/* converts the apparmor regex in aare and appends pcre regex output
|
||||
* to pcre string */
|
||||
pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, int glob,
|
||||
pattern_t convert_aaregex_to_pcre(const char *aare, int anchor,
|
||||
std::string& pcre, int *first_re_pos)
|
||||
{
|
||||
#define update_re_pos(X) if (!(*first_re_pos)) { *first_re_pos = (X); }
|
||||
@@ -189,8 +170,9 @@ pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, int glob,
|
||||
const char *s = sptr;
|
||||
while (*s == '*')
|
||||
s++;
|
||||
if (*s == '/' || !*s)
|
||||
error = append_glob(pcre, glob, "[^/\\x00]", "[^/]");
|
||||
if (*s == '/' || !*s) {
|
||||
pcre.append("[^/\\x00]");
|
||||
}
|
||||
}
|
||||
if (*(sptr + 1) == '*') {
|
||||
/* is this the first regex form we
|
||||
@@ -206,12 +188,13 @@ pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, int glob,
|
||||
} else {
|
||||
ptype = ePatternRegex;
|
||||
}
|
||||
error = append_glob(pcre, glob, "[^\\x00]*", ".*");
|
||||
|
||||
pcre.append("[^\\x00]*");
|
||||
sptr++;
|
||||
} else {
|
||||
update_re_pos(sptr - aare);
|
||||
ptype = ePatternRegex;
|
||||
error = append_glob(pcre, glob, "[^/\\x00]*", "[^/]*");
|
||||
pcre.append("[^/\\x00]*");
|
||||
} /* *(sptr+1) == '*' */
|
||||
} /* bEscape */
|
||||
|
||||
@@ -359,26 +342,12 @@ pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, int glob,
|
||||
|
||||
default:
|
||||
if (bEscape) {
|
||||
const char *pos = sptr;
|
||||
int c;
|
||||
if ((c = str_escseq(&pos, "")) != -1) {
|
||||
/* valid escape we don't want to
|
||||
* interpret here */
|
||||
pcre.append("\\");
|
||||
pcre.append(sptr, pos - sptr);
|
||||
sptr += (pos - sptr) - 1;
|
||||
} else {
|
||||
/* quoting mark used for something that
|
||||
* does not need to be quoted; give a
|
||||
* warning */
|
||||
pwarn("Character %c was quoted "
|
||||
"unnecessarily, dropped preceding"
|
||||
" quote ('\\') character\n",
|
||||
*sptr);
|
||||
pcre.append(1, *sptr);
|
||||
}
|
||||
} else
|
||||
pcre.append(1, *sptr);
|
||||
/* quoting mark used for something that
|
||||
* does not need to be quoted; give a warning */
|
||||
pwarn("Character %c was quoted unnecessarily, "
|
||||
"dropped preceding quote ('\\') character\n", *sptr);
|
||||
}
|
||||
pcre.append(1, *sptr);
|
||||
break;
|
||||
} /* switch (*sptr) */
|
||||
|
||||
@@ -443,7 +412,7 @@ static int process_profile_name_xmatch(Profile *prof)
|
||||
name = prof->attachment;
|
||||
else
|
||||
name = local_name(prof->name);
|
||||
ptype = convert_aaregex_to_pcre(name, 0, glob_default, tbuf,
|
||||
ptype = convert_aaregex_to_pcre(name, 0, tbuf,
|
||||
&prof->xmatch_len);
|
||||
if (ptype == ePatternBasic)
|
||||
prof->xmatch_len = strlen(name);
|
||||
@@ -471,8 +440,8 @@ static int process_profile_name_xmatch(Profile *prof)
|
||||
int len;
|
||||
tbuf.clear();
|
||||
ptype = convert_aaregex_to_pcre(alt->name, 0,
|
||||
glob_default,
|
||||
tbuf, &len);
|
||||
tbuf,
|
||||
&len);
|
||||
if (ptype == ePatternBasic)
|
||||
len = strlen(alt->name);
|
||||
if (len < prof->xmatch_len)
|
||||
@@ -492,8 +461,6 @@ static int process_profile_name_xmatch(Profile *prof)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int warn_change_profile = 1;
|
||||
|
||||
static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
|
||||
{
|
||||
std::string tbuf;
|
||||
@@ -506,7 +473,7 @@ static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
|
||||
|
||||
if (entry->mode & ~AA_CHANGE_PROFILE)
|
||||
filter_slashes(entry->name);
|
||||
ptype = convert_aaregex_to_pcre(entry->name, 0, glob_default, tbuf, &pos);
|
||||
ptype = convert_aaregex_to_pcre(entry->name, 0, tbuf, &pos);
|
||||
if (ptype == ePatternInvalid)
|
||||
return FALSE;
|
||||
|
||||
@@ -516,9 +483,9 @@ static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
|
||||
* dfa states like it does for pcre
|
||||
*/
|
||||
if ((entry->mode >> AA_OTHER_SHIFT) & AA_EXEC_INHERIT)
|
||||
entry->mode |= AA_OLD_EXEC_MMAP << AA_OTHER_SHIFT;
|
||||
entry->mode |= AA_EXEC_MMAP << AA_OTHER_SHIFT;
|
||||
if ((entry->mode >> AA_USER_SHIFT) & AA_EXEC_INHERIT)
|
||||
entry->mode |= AA_OLD_EXEC_MMAP << AA_USER_SHIFT;
|
||||
entry->mode |= AA_EXEC_MMAP << AA_USER_SHIFT;
|
||||
|
||||
/* the link bit on the first pair entry should not get masked
|
||||
* out by a deny rule, as both pieces of the link pair must
|
||||
@@ -532,9 +499,8 @@ static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
|
||||
if (entry->deny) {
|
||||
if ((entry->mode & ~(AA_LINK_BITS | AA_CHANGE_PROFILE)) &&
|
||||
!dfarules->add_rule(tbuf.c_str(), entry->deny,
|
||||
entry->mode & ~(AA_LINK_BITS | AA_CHANGE_PROFILE),
|
||||
entry->audit & ~(AA_LINK_BITS | AA_CHANGE_PROFILE),
|
||||
dfaflags))
|
||||
entry->mode & ~AA_LINK_BITS,
|
||||
entry->audit & ~AA_LINK_BITS, dfaflags))
|
||||
return FALSE;
|
||||
} else if (entry->mode & ~AA_CHANGE_PROFILE) {
|
||||
if (!dfarules->add_rule(tbuf.c_str(), entry->deny, entry->mode,
|
||||
@@ -550,7 +516,7 @@ static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
|
||||
int pos;
|
||||
vec[0] = tbuf.c_str();
|
||||
if (entry->link_name) {
|
||||
ptype = convert_aaregex_to_pcre(entry->link_name, 0, glob_default, lbuf, &pos);
|
||||
ptype = convert_aaregex_to_pcre(entry->link_name, 0, lbuf, &pos);
|
||||
if (ptype == ePatternInvalid)
|
||||
return FALSE;
|
||||
if (entry->subset)
|
||||
@@ -565,40 +531,26 @@ static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
|
||||
}
|
||||
if (entry->mode & AA_CHANGE_PROFILE) {
|
||||
const char *vec[3];
|
||||
std::string lbuf, xbuf;
|
||||
std::string lbuf;
|
||||
int index = 1;
|
||||
|
||||
if ((warnflags & WARN_RULE_DOWNGRADED) && entry->audit && warn_change_profile) {
|
||||
/* don't have profile name here, so until this code
|
||||
* gets refactored just throw out a generic warning
|
||||
*/
|
||||
fprintf(stderr, "Warning kernel does not support audit modifier for change_profile rule.\n");
|
||||
warn_change_profile = 0;
|
||||
}
|
||||
|
||||
if (entry->onexec) {
|
||||
ptype = convert_aaregex_to_pcre(entry->onexec, 0, glob_default, xbuf, &pos);
|
||||
if (ptype == ePatternInvalid)
|
||||
return FALSE;
|
||||
vec[0] = xbuf.c_str();
|
||||
} else
|
||||
/* allow change_profile for all execs */
|
||||
vec[0] = "/[^/\\x00][^\\x00]*";
|
||||
/* allow change_profile for all execs */
|
||||
vec[0] = "/[^\\x00]*";
|
||||
|
||||
if (entry->ns) {
|
||||
int pos;
|
||||
ptype = convert_aaregex_to_pcre(entry->ns, 0, glob_default, lbuf, &pos);
|
||||
ptype = convert_aaregex_to_pcre(entry->ns, 0, lbuf, &pos);
|
||||
vec[index++] = lbuf.c_str();
|
||||
}
|
||||
vec[index++] = tbuf.c_str();
|
||||
|
||||
/* regular change_profile rule */
|
||||
if (!dfarules->add_rule_vec(entry->deny, AA_CHANGE_PROFILE | AA_ONEXEC, 0, index - 1, &vec[1], dfaflags))
|
||||
if (!dfarules->add_rule_vec(0, AA_CHANGE_PROFILE | AA_ONEXEC, 0, index - 1, &vec[1], dfaflags))
|
||||
return FALSE;
|
||||
/* onexec rules - both rules are needed for onexec */
|
||||
if (!dfarules->add_rule_vec(entry->deny, AA_ONEXEC, 0, 1, vec, dfaflags))
|
||||
if (!dfarules->add_rule_vec(0, AA_ONEXEC, 0, 1, vec, dfaflags))
|
||||
return FALSE;
|
||||
if (!dfarules->add_rule_vec(entry->deny, AA_ONEXEC, 0, index, vec, dfaflags))
|
||||
if (!dfarules->add_rule_vec(0, AA_ONEXEC, 0, index, vec, dfaflags))
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
@@ -669,13 +621,13 @@ int build_list_val_expr(std::string& buffer, struct value_list *list)
|
||||
|
||||
buffer.append("(");
|
||||
|
||||
ptype = convert_aaregex_to_pcre(list->value, 0, glob_default, buffer, &pos);
|
||||
ptype = convert_aaregex_to_pcre(list->value, 0, buffer, &pos);
|
||||
if (ptype == ePatternInvalid)
|
||||
goto fail;
|
||||
|
||||
list_for_each(list->next, ent) {
|
||||
buffer.append("|");
|
||||
ptype = convert_aaregex_to_pcre(ent->value, 0, glob_default, buffer, &pos);
|
||||
ptype = convert_aaregex_to_pcre(ent->value, 0, buffer, &pos);
|
||||
if (ptype == ePatternInvalid)
|
||||
goto fail;
|
||||
}
|
||||
@@ -692,7 +644,7 @@ int convert_entry(std::string& buffer, char *entry)
|
||||
int pos;
|
||||
|
||||
if (entry) {
|
||||
ptype = convert_aaregex_to_pcre(entry, 0, glob_default, buffer, &pos);
|
||||
ptype = convert_aaregex_to_pcre(entry, 0, buffer, &pos);
|
||||
if (ptype == ePatternInvalid)
|
||||
return FALSE;
|
||||
} else {
|
||||
@@ -779,6 +731,8 @@ int process_profile_policydb(Profile *prof)
|
||||
prof->policy.rules = NULL;
|
||||
}
|
||||
|
||||
aare_reset_matchflags();
|
||||
|
||||
error = 0;
|
||||
|
||||
out:
|
||||
@@ -788,6 +742,11 @@ out:
|
||||
return error;
|
||||
}
|
||||
|
||||
void reset_regex(void)
|
||||
{
|
||||
aare_reset_matchflags();
|
||||
}
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
|
||||
#include "unit_test.h"
|
||||
@@ -836,7 +795,7 @@ static int test_filter_slashes(void)
|
||||
return rc;
|
||||
}
|
||||
|
||||
#define MY_REGEX_EXT_TEST(glob, input, expected_str, expected_type) \
|
||||
#define MY_REGEX_TEST(input, expected_str, expected_type) \
|
||||
do { \
|
||||
std::string tbuf; \
|
||||
std::string tbuf2 = "testprefix"; \
|
||||
@@ -845,7 +804,7 @@ static int test_filter_slashes(void)
|
||||
pattern_t ptype; \
|
||||
int pos; \
|
||||
\
|
||||
ptype = convert_aaregex_to_pcre((input), 0, glob, tbuf, &pos); \
|
||||
ptype = convert_aaregex_to_pcre((input), 0, tbuf, &pos); \
|
||||
asprintf(&output_string, "simple regex conversion for '%s'\texpected = '%s'\tresult = '%s'", \
|
||||
(input), (expected_str), tbuf.c_str()); \
|
||||
MY_TEST(strcmp(tbuf.c_str(), (expected_str)) == 0, output_string); \
|
||||
@@ -854,25 +813,21 @@ static int test_filter_slashes(void)
|
||||
/* ensure convert_aaregex_to_pcre appends only to passed ref string */ \
|
||||
expected_str2 = tbuf2; \
|
||||
expected_str2.append((expected_str)); \
|
||||
ptype = convert_aaregex_to_pcre((input), 0, glob, tbuf2, &pos); \
|
||||
asprintf(&output_string, "simple regex conversion %sfor '%s'\texpected = '%s'\tresult = '%s'", \
|
||||
glob == glob_null ? "with null allowed in glob " : "",\
|
||||
ptype = convert_aaregex_to_pcre((input), 0, tbuf2, &pos); \
|
||||
asprintf(&output_string, "simple regex conversion for '%s'\texpected = '%s'\tresult = '%s'", \
|
||||
(input), expected_str2.c_str(), tbuf2.c_str()); \
|
||||
MY_TEST((tbuf2 == expected_str2), output_string); \
|
||||
free(output_string); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define MY_REGEX_TEST(input, expected_str, expected_type) MY_REGEX_EXT_TEST(glob_default, input, expected_str, expected_type)
|
||||
|
||||
|
||||
#define MY_REGEX_FAIL_TEST(input) \
|
||||
do { \
|
||||
std::string tbuf; \
|
||||
pattern_t ptype; \
|
||||
int pos; \
|
||||
\
|
||||
ptype = convert_aaregex_to_pcre((input), 0, glob_default, tbuf, &pos); \
|
||||
ptype = convert_aaregex_to_pcre((input), 0, tbuf, &pos); \
|
||||
MY_TEST(ptype == ePatternInvalid, "simple regex conversion invalid type check for '" input "'"); \
|
||||
} \
|
||||
while (0)
|
||||
@@ -977,9 +932,6 @@ static int test_aaregex_to_pcre(void)
|
||||
MY_REGEX_TEST("\\\\|", "\\\\\\|", ePatternBasic);
|
||||
MY_REGEX_TEST("\\\\(", "\\\\\\(", ePatternBasic);
|
||||
MY_REGEX_TEST("\\\\)", "\\\\\\)", ePatternBasic);
|
||||
MY_REGEX_TEST("\\000", "\\000", ePatternBasic);
|
||||
MY_REGEX_TEST("\\x00", "\\x00", ePatternBasic);
|
||||
MY_REGEX_TEST("\\d000", "\\d000", ePatternBasic);
|
||||
|
||||
/* more complicated character class tests */
|
||||
/* -- embedded alternations */
|
||||
@@ -993,27 +945,6 @@ static int test_aaregex_to_pcre(void)
|
||||
MY_REGEX_TEST("{alpha,b[\\{a,b\\}]t,gamma}", "(alpha|b[\\{a,b\\}]t|gamma)", ePatternRegex);
|
||||
MY_REGEX_TEST("{alpha,b[\\{a\\,b\\}]t,gamma}", "(alpha|b[\\{a\\,b\\}]t|gamma)", ePatternRegex);
|
||||
|
||||
/* test different globbing behavior conversion */
|
||||
MY_REGEX_EXT_TEST(glob_default, "/foo/**", "/foo/[^/\\x00][^\\x00]*", ePatternTailGlob);
|
||||
MY_REGEX_EXT_TEST(glob_null, "/foo/**", "/foo/[^/].*", ePatternTailGlob);
|
||||
MY_REGEX_EXT_TEST(glob_default, "/foo/f**", "/foo/f[^\\x00]*", ePatternTailGlob);
|
||||
MY_REGEX_EXT_TEST(glob_null, "/foo/f**", "/foo/f.*", ePatternTailGlob);
|
||||
|
||||
MY_REGEX_EXT_TEST(glob_default, "/foo/*", "/foo/[^/\\x00][^/\\x00]*", ePatternRegex);
|
||||
MY_REGEX_EXT_TEST(glob_null, "/foo/*", "/foo/[^/][^/]*", ePatternRegex);
|
||||
MY_REGEX_EXT_TEST(glob_default, "/foo/f*", "/foo/f[^/\\x00]*", ePatternRegex);
|
||||
MY_REGEX_EXT_TEST(glob_null, "/foo/f*", "/foo/f[^/]*", ePatternRegex);
|
||||
|
||||
MY_REGEX_EXT_TEST(glob_default, "/foo/**.ext", "/foo/[^\\x00]*\\.ext", ePatternRegex);
|
||||
MY_REGEX_EXT_TEST(glob_null, "/foo/**.ext", "/foo/.*\\.ext", ePatternRegex);
|
||||
MY_REGEX_EXT_TEST(glob_default, "/foo/f**.ext", "/foo/f[^\\x00]*\\.ext", ePatternRegex);
|
||||
MY_REGEX_EXT_TEST(glob_null, "/foo/f**.ext", "/foo/f.*\\.ext", ePatternRegex);
|
||||
|
||||
MY_REGEX_EXT_TEST(glob_default, "/foo/*.ext", "/foo/[^/\\x00]*\\.ext", ePatternRegex);
|
||||
MY_REGEX_EXT_TEST(glob_null, "/foo/*.ext", "/foo/[^/]*\\.ext", ePatternRegex);
|
||||
MY_REGEX_EXT_TEST(glob_default, "/foo/f*.ext", "/foo/f[^/\\x00]*\\.ext", ePatternRegex);
|
||||
MY_REGEX_EXT_TEST(glob_null, "/foo/f*.ext", "/foo/f[^/]*\\.ext", ePatternRegex);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@@ -254,11 +254,6 @@ static int process_variables_in_entries(struct cod_entry *entry_list)
|
||||
error = expand_entry_variables(&entry->name);
|
||||
if (error)
|
||||
return error;
|
||||
if (entry->link_name) {
|
||||
error = expand_entry_variables(&entry->link_name);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -275,51 +270,14 @@ static int process_variables_in_rules(Profile &prof)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_variables_in_name(Profile &prof)
|
||||
{
|
||||
/* this needs to be done before alias expansion, ie. altnames are
|
||||
* setup
|
||||
*/
|
||||
int error = expand_entry_variables(&prof.name);
|
||||
if (!error && prof.attachment)
|
||||
error = expand_entry_variables(&prof.attachment);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static std::string escape_re(std::string str)
|
||||
{
|
||||
for (size_t i = 0; i < str.length(); i++) {
|
||||
if (str[i] == '\\') {
|
||||
/* skip \ and follow char. Skipping \ and first
|
||||
* char is enough for multichar escape sequence
|
||||
*/
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (strchr("{}[]*?", str[i]) != NULL) {
|
||||
str.insert(i++, "\\");
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
int process_profile_variables(Profile *prof)
|
||||
{
|
||||
int error = 0, rc;
|
||||
std::string *buf = prof->get_name(true);
|
||||
|
||||
/* needs to be before PROFILE_NAME_VARIABLE so that variable will
|
||||
* have the correct name
|
||||
*/
|
||||
error = process_variables_in_name(*prof);
|
||||
|
||||
if (!error) {
|
||||
/* escape profile name elements that could be interpreted
|
||||
* as regular expressions.
|
||||
*/
|
||||
error = new_set_var(PROFILE_NAME_VARIABLE, escape_re(prof->get_name(false)).c_str());
|
||||
}
|
||||
error = new_set_var(PROFILE_NAME_VARIABLE, buf->c_str());
|
||||
delete buf;
|
||||
|
||||
if (!error)
|
||||
error = process_variables_in_entries(prof->entries);
|
||||
|
@@ -78,6 +78,7 @@ mnt_rule *do_mnt_rule(struct cond_entry *src_conds, char *src,
|
||||
int mode);
|
||||
mnt_rule *do_pivot_rule(struct cond_entry *old, char *root,
|
||||
char *transition);
|
||||
|
||||
void add_local_entry(Profile *prof);
|
||||
|
||||
%}
|
||||
@@ -243,7 +244,6 @@ void add_local_entry(Profile *prof);
|
||||
%type <flags> flagval
|
||||
%type <cap> caps
|
||||
%type <cap> capability
|
||||
%type <id> change_profile_head
|
||||
%type <user_entry> change_profile
|
||||
%type <set_var> TOK_SET_VAR
|
||||
%type <bool_var> TOK_BOOL_VAR
|
||||
@@ -251,13 +251,13 @@ void add_local_entry(Profile *prof);
|
||||
%type <val_list> valuelist
|
||||
%type <boolean> expr
|
||||
%type <id> id_or_var
|
||||
%type <id> opt_id_or_var
|
||||
%type <boolean> opt_subset_flag
|
||||
%type <boolean> opt_audit_flag
|
||||
%type <boolean> opt_owner_flag
|
||||
%type <boolean> opt_profile_flag
|
||||
%type <boolean> opt_flags
|
||||
%type <boolean> opt_perm_mode
|
||||
%type <id> opt_ns
|
||||
%type <id> opt_id
|
||||
%type <prefix> opt_prefix
|
||||
%type <fmode> dbus_perm
|
||||
@@ -297,13 +297,13 @@ opt_profile_flag: { /* nothing */ $$ = 0; }
|
||||
| TOK_PROFILE { $$ = 1; }
|
||||
| hat_start { $$ = 2; }
|
||||
|
||||
opt_ns: { /* nothing */ $$ = NULL; }
|
||||
| TOK_COLON TOK_ID TOK_COLON { $$ = $2; }
|
||||
|
||||
opt_id: { /* nothing */ $$ = NULL; }
|
||||
| TOK_ID { $$ = $1; }
|
||||
|
||||
opt_id_or_var: { /* nothing */ $$ = NULL; }
|
||||
| id_or_var { $$ = $1; }
|
||||
|
||||
profile_base: TOK_ID opt_id_or_var flags TOK_OPEN rules TOK_CLOSE
|
||||
profile_base: TOK_ID opt_id flags TOK_OPEN rules TOK_CLOSE
|
||||
{
|
||||
Profile *prof = $5;
|
||||
|
||||
@@ -311,29 +311,13 @@ profile_base: TOK_ID opt_id_or_var flags TOK_OPEN rules TOK_CLOSE
|
||||
yyerror(_("Memory allocation error."));
|
||||
}
|
||||
|
||||
parse_label(&prof->ns, &prof->name, $1);
|
||||
free($1);
|
||||
|
||||
/* Honor the --namespace-string command line option */
|
||||
if (profile_ns) {
|
||||
/**
|
||||
* Print warning if the profile specified a namespace
|
||||
* different than the one specified with the
|
||||
* --namespace-string command line option
|
||||
*/
|
||||
if (prof->ns && strcmp(prof->ns, profile_ns))
|
||||
pwarn("%s: -n %s overriding policy specified namespace :%s:\n",
|
||||
progname, profile_ns, prof->ns);
|
||||
|
||||
free(prof->ns);
|
||||
prof->ns = strdup(profile_ns);
|
||||
if (!prof->ns)
|
||||
yyerror(_("Memory allocation error."));
|
||||
}
|
||||
|
||||
prof->name = $1;
|
||||
prof->attachment = $2;
|
||||
if ($2 && !($2[0] == '/' || strncmp($2, "@{", 2) == 0))
|
||||
yyerror(_("Profile attachment must begin with a '/' or variable."));
|
||||
if ($2 && $2[0] != '/')
|
||||
/* we don't support variables as part of the profile
|
||||
* name or attachment atm
|
||||
*/
|
||||
yyerror(_("Profile attachment must begin with a '/'."));
|
||||
prof->flags = $3;
|
||||
if (force_complain && kernel_abi_version == 5)
|
||||
/* newer abis encode force complain as part of the
|
||||
@@ -352,18 +336,18 @@ profile_base: TOK_ID opt_id_or_var flags TOK_OPEN rules TOK_CLOSE
|
||||
|
||||
};
|
||||
|
||||
profile: opt_profile_flag profile_base
|
||||
profile: opt_profile_flag opt_ns profile_base
|
||||
{
|
||||
Profile *prof = $2;
|
||||
|
||||
if ($2->ns)
|
||||
PDEBUG("Matched: :%s://%s { ... }\n", $2->ns, $2->name);
|
||||
Profile *prof = $3;
|
||||
if ($2)
|
||||
PDEBUG("Matched: %s://%s { ... }\n", $2, $3->name);
|
||||
else
|
||||
PDEBUG("Matched: %s { ... }\n", $2->name);
|
||||
PDEBUG("Matched: %s { ... }\n", $3->name);
|
||||
|
||||
if ($2->name[0] != '/' && !($1 || $2->ns))
|
||||
if ($3->name[0] != '/' && !($1 || $2))
|
||||
yyerror(_("Profile names must begin with a '/', namespace or keyword 'profile' or 'hat'."));
|
||||
|
||||
prof->ns = $2;
|
||||
if ($1 == 2)
|
||||
prof->flags.hat = 1;
|
||||
$$ = prof;
|
||||
@@ -794,23 +778,13 @@ rules: rules opt_prefix unix_rule
|
||||
$$ = $1;
|
||||
}
|
||||
|
||||
rules: rules opt_prefix change_profile
|
||||
rules: rules change_profile
|
||||
{
|
||||
PDEBUG("matched: rules change_profile\n");
|
||||
PDEBUG("rules change_profile: (%s)\n", $3->name);
|
||||
if (!$3)
|
||||
PDEBUG("rules change_profile: (%s)\n", $2->name);
|
||||
if (!$2)
|
||||
yyerror(_("Assert: `change_profile' returned NULL."));
|
||||
if ($2.owner)
|
||||
yyerror(_("owner prefix not allowed on unix rules"));
|
||||
if ($2.deny && $2.audit) {
|
||||
$3->deny = 1;
|
||||
} else if ($2.deny) {
|
||||
$3->deny = 1;
|
||||
$3->audit = $3->mode;
|
||||
} else if ($2.audit) {
|
||||
$3->audit = $3->mode;
|
||||
}
|
||||
add_entry_to_policy($1, $3);
|
||||
add_entry_to_policy($1, $2);
|
||||
$$ = $1;
|
||||
};
|
||||
|
||||
@@ -858,7 +832,7 @@ rules: rules cond_rule
|
||||
$$ = merge_policy($1, $2);
|
||||
}
|
||||
|
||||
rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE opt_id TOK_END_OF_RULE
|
||||
rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE TOK_END_OF_RULE
|
||||
{
|
||||
rlim_t value = RLIM_INFINITY;
|
||||
long long tmp;
|
||||
@@ -871,6 +845,11 @@ rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE opt_id TOK_END_OF_RULE
|
||||
if (strcmp($6, "infinity") == 0) {
|
||||
value = RLIM_INFINITY;
|
||||
} else {
|
||||
const char *seconds = "seconds";
|
||||
const char *milliseconds = "ms";
|
||||
const char *minutes = "minutes";
|
||||
const char *hours = "hours";
|
||||
const char *days = "days";
|
||||
const char *kb = "KB";
|
||||
const char *mb = "MB";
|
||||
const char *gb = "GB";
|
||||
@@ -880,25 +859,34 @@ rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE opt_id TOK_END_OF_RULE
|
||||
case RLIMIT_CPU:
|
||||
if (!end || $6 == end || tmp < 0)
|
||||
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
|
||||
tmp = convert_time_units(tmp, SECONDS_P_MS, $7);
|
||||
if (tmp == -1LL)
|
||||
yyerror("RLIMIT '%s %s' < minimum value of 1s\n", $4, $6);
|
||||
else if (tmp < 0LL)
|
||||
if (*end == '\0' ||
|
||||
strstr(seconds, end) == seconds) {
|
||||
value = tmp;
|
||||
} else if (strstr(minutes, end) == minutes) {
|
||||
value = tmp * 60;
|
||||
} else if (strstr(hours, end) == hours) {
|
||||
value = tmp * 60 * 60;
|
||||
} else if (strstr(days, end) == days) {
|
||||
value = tmp * 60 * 60 * 24;
|
||||
} else {
|
||||
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
|
||||
if (!$7)
|
||||
pwarn(_("RLIMIT 'cpu' no units specified using default units of seconds\n"));
|
||||
value = tmp;
|
||||
}
|
||||
break;
|
||||
case RLIMIT_RTTIME:
|
||||
/* RTTIME is measured in microseconds */
|
||||
if (!end || $6 == end || tmp < 0)
|
||||
yyerror("RLIMIT '%s' invalid value %s %s\n", $4, $6, $7 ? $7 : "");
|
||||
tmp = convert_time_units(tmp, 1LL, $7);
|
||||
if (tmp < 0LL)
|
||||
yyerror("RLIMIT '%s' invalid value %s %s\n", $4, $6, $7 ? $7 : "");
|
||||
if (!$7)
|
||||
pwarn(_("RLIMIT 'rttime' no units specified using default units of microseconds\n"));
|
||||
value = tmp;
|
||||
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
|
||||
if (*end == '\0') {
|
||||
value = tmp;
|
||||
} else if (strstr(milliseconds, end) == milliseconds) {
|
||||
value = tmp * 1000;
|
||||
} else if (strstr(seconds, end) == seconds) {
|
||||
value = tmp * 1000 * 1000;
|
||||
} else if (strstr(minutes, end) == minutes) {
|
||||
value = tmp * 1000 * 1000 * 60;
|
||||
} else {
|
||||
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
|
||||
}
|
||||
break;
|
||||
case RLIMIT_NOFILE:
|
||||
case RLIMIT_NPROC:
|
||||
@@ -906,15 +894,15 @@ rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE opt_id TOK_END_OF_RULE
|
||||
case RLIMIT_SIGPENDING:
|
||||
#ifdef RLIMIT_RTPRIO
|
||||
case RLIMIT_RTPRIO:
|
||||
if (!end || $6 == end || $7 || tmp < 0)
|
||||
yyerror("RLIMIT '%s' invalid value %s %s\n", $4, $6, $7 ? $7 : "");
|
||||
if (!end || $6 == end || *end != '\0' || tmp < 0)
|
||||
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
|
||||
value = tmp;
|
||||
break;
|
||||
#endif
|
||||
#ifdef RLIMIT_NICE
|
||||
case RLIMIT_NICE:
|
||||
if (!end || $6 == end || $7)
|
||||
yyerror("RLIMIT '%s' invalid value %s %s\n", $4, $6, $7 ? $7 : "");
|
||||
if (!end || $6 == end || *end != '\0')
|
||||
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
|
||||
if (tmp < -20 || tmp > 19)
|
||||
yyerror("RLIMIT '%s' out of range (-20 .. 19) %d\n", $4, tmp);
|
||||
value = tmp + 20;
|
||||
@@ -929,17 +917,15 @@ rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE opt_id TOK_END_OF_RULE
|
||||
case RLIMIT_MEMLOCK:
|
||||
case RLIMIT_MSGQUEUE:
|
||||
if ($6 == end || tmp < 0)
|
||||
yyerror("RLIMIT '%s' invalid value %s %s\n", $4, $6, $7 ? $7 : "");
|
||||
if (!$7) {
|
||||
; /* use default of bytes */
|
||||
} else if (strstr(kb, $7) == kb) {
|
||||
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
|
||||
if (strstr(kb, end) == kb) {
|
||||
tmp *= 1024;
|
||||
} else if (strstr(mb, $7) == mb) {
|
||||
} else if (strstr(mb, end) == mb) {
|
||||
tmp *= 1024*1024;
|
||||
} else if (strstr(gb, $7) == gb) {
|
||||
} else if (strstr(gb, end) == gb) {
|
||||
tmp *= 1024*1024*1024;
|
||||
} else {
|
||||
yyerror("RLIMIT '%s' invalid value %s %s\n", $4, $6, $7);
|
||||
} else if (*end != '\0') {
|
||||
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
|
||||
}
|
||||
value = tmp;
|
||||
break;
|
||||
@@ -1040,12 +1026,21 @@ id_or_var: TOK_SET_VAR { $$ = $1; };
|
||||
|
||||
opt_named_transition:
|
||||
{ /* nothing */
|
||||
parse_named_transition_target(&$$, NULL);
|
||||
$$.present = 0;
|
||||
$$.ns = NULL;
|
||||
$$.name = NULL;
|
||||
}
|
||||
| TOK_ARROW id_or_var
|
||||
{
|
||||
parse_named_transition_target(&$$, $2);
|
||||
free($2);
|
||||
$$.present = 1;
|
||||
$$.ns = NULL;
|
||||
$$.name = $2;
|
||||
}
|
||||
| TOK_ARROW TOK_COLON id_or_var TOK_COLON id_or_var
|
||||
{
|
||||
$$.present = 1;
|
||||
$$.ns = $3;
|
||||
$$.name = $5;
|
||||
};
|
||||
|
||||
rule: file_rule { $$ = $1; }
|
||||
@@ -1118,7 +1113,7 @@ file_rule_tail: opt_unsafe id_or_var file_mode id_or_var
|
||||
yyerror(_("missing an end of line character? (entry: %s)"), $2);
|
||||
};
|
||||
|
||||
link_rule: TOK_LINK opt_subset_flag id_or_var TOK_ARROW id_or_var TOK_END_OF_RULE
|
||||
link_rule: TOK_LINK opt_subset_flag TOK_ID TOK_ARROW TOK_ID TOK_END_OF_RULE
|
||||
{
|
||||
struct cod_entry *entry;
|
||||
PDEBUG("Matched: link tok_id (%s) -> (%s)\n", $3, $5);
|
||||
@@ -1479,36 +1474,29 @@ file_mode: TOK_MODE
|
||||
free($1);
|
||||
}
|
||||
|
||||
change_profile_head: TOK_CHANGE_PROFILE opt_id
|
||||
{
|
||||
if ($2 && !($2[0] == '/' || strncmp($2, "@{", 2) == 0))
|
||||
yyerror(_("Exec condition must begin with '/'."));
|
||||
$$ = $2;
|
||||
}
|
||||
|
||||
change_profile: change_profile_head opt_named_transition TOK_END_OF_RULE
|
||||
change_profile: TOK_CHANGE_PROFILE TOK_ARROW TOK_ID TOK_END_OF_RULE
|
||||
{
|
||||
struct cod_entry *entry;
|
||||
|
||||
if ($2.present) {
|
||||
PDEBUG("Matched change_profile: tok_id (:%s://%s)\n",
|
||||
$2.ns ? $2.ns : "", $2.name);
|
||||
entry = new_entry($2.ns, $2.name, AA_CHANGE_PROFILE, $1);
|
||||
} else {
|
||||
char *rule = strdup("**");
|
||||
if (!rule)
|
||||
yyerror(_("Memory allocation error."));
|
||||
|
||||
PDEBUG("Matched change_profile,\n");
|
||||
entry = new_entry(NULL, rule, AA_CHANGE_PROFILE, $1);
|
||||
}
|
||||
PDEBUG("Matched change_profile: tok_id (%s)\n", $3);
|
||||
entry = new_entry(NULL, $3, AA_CHANGE_PROFILE, NULL);
|
||||
if (!entry)
|
||||
yyerror(_("Memory allocation error."));
|
||||
|
||||
PDEBUG("change_profile.entry: (%s)\n", entry->name);
|
||||
$$ = entry;
|
||||
};
|
||||
|
||||
change_profile: TOK_CHANGE_PROFILE TOK_ARROW TOK_COLON TOK_ID TOK_COLON TOK_ID TOK_END_OF_RULE
|
||||
{
|
||||
struct cod_entry *entry;
|
||||
PDEBUG("Matched change_profile: tok_id (%s:%s)\n", $4, $6);
|
||||
entry = new_entry($4, $6, AA_CHANGE_PROFILE, NULL);
|
||||
if (!entry)
|
||||
yyerror(_("Memory allocation error."));
|
||||
PDEBUG("change_profile.entry: (%s)\n", entry->name);
|
||||
$$ = entry;
|
||||
};
|
||||
|
||||
|
||||
capability: TOK_CAPABILITY caps TOK_END_OF_RULE
|
||||
{
|
||||
if ($2 == 0) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user