mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-09-01 14:55:10 +00:00
Compare commits
168 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
5afe0f8889 | ||
|
9c7e71358c | ||
|
d39706edf7 | ||
|
0e8d4e09f8 | ||
|
1765cbdc15 | ||
|
bee5ed218a | ||
|
be0fdfb03c | ||
|
ccc744b9ff | ||
|
8bba589d2d | ||
|
9c9b79faed | ||
|
3c53ad55df | ||
|
68b8bbeada | ||
|
509f56e22c | ||
|
52c3b487d0 | ||
|
a91c1e4329 | ||
|
e4fc384ae2 | ||
|
002fda8718 | ||
|
e5a72e8efb | ||
|
eaa7f03064 | ||
|
904536f7cf | ||
|
8248e01591 | ||
|
18d8ffe41e | ||
|
69d3d71cd9 | ||
|
299953ab7b | ||
|
2b447d3fce | ||
|
c8ca044d28 | ||
|
108e8afe6b | ||
|
1bb6223de4 | ||
|
4f8eaf8782 | ||
|
3e527d903f | ||
|
72232e0919 | ||
|
7c6a592ceb | ||
|
b0f55894f8 | ||
|
70ecff9962 | ||
|
8a32ae5143 | ||
|
cdaf5075cb | ||
|
9fb21a702c | ||
|
d55d99cbf1 | ||
|
e07ec63313 | ||
|
09050a8a8b | ||
|
5260be0518 | ||
|
7bf4f3a9a0 | ||
|
9af6d0ec0c | ||
|
b631b209f5 | ||
|
d1dd46d767 | ||
|
f6b497dd0c | ||
|
0e79f739be | ||
|
b8063e3342 | ||
|
660de9d4c3 | ||
|
d0ffb0fb48 | ||
|
a055c41207 | ||
|
582479235f | ||
|
4b178140d5 | ||
|
eb5f435c78 | ||
|
2fa87eb12e | ||
|
0a4a66a014 | ||
|
6c889d3a35 | ||
|
ca5386c09e | ||
|
65c7a474e7 | ||
|
4a817d423b | ||
|
ae4e230b05 | ||
|
0ce15469ec | ||
|
c0328fc460 | ||
|
64b8fdaa52 | ||
|
46fd2b39d1 | ||
|
ed2090a875 | ||
|
68d8fc0df7 | ||
|
cb37d500df | ||
|
44094c6514 | ||
|
1dc9f297e6 | ||
|
3350d482fe | ||
|
7d3c022651 | ||
|
fe621b37c4 | ||
|
c5bec9c0b5 | ||
|
9962cee942 | ||
|
6c81024701 | ||
|
ccc86d8036 | ||
|
e82683d3f5 | ||
|
b1e19b7563 | ||
|
6e068f9e25 | ||
|
9f0d4cb5b4 | ||
|
84b9b46fb0 | ||
|
11770f1702 | ||
|
93922246df | ||
|
ae29fb0749 | ||
|
a90e1eda2b | ||
|
39399257ea | ||
|
fdde7c9f74 | ||
|
aabcfa51d8 | ||
|
46ef60e979 | ||
|
3bdb7b1754 | ||
|
c0786268f3 | ||
|
b40e1eefbd | ||
|
e66ecb79e5 | ||
|
236c851bfe | ||
|
892d113ba4 | ||
|
65c010ccc3 | ||
|
2fdc5ca603 | ||
|
872d3f3bc2 | ||
|
54725ee516 | ||
|
4a76852648 | ||
|
c78752911e | ||
|
7f074b6677 | ||
|
1fe48e09dd | ||
|
fa1d2a1fc1 | ||
|
5e6948d2f9 | ||
|
4184b0c363 | ||
|
d97a8034fc | ||
|
41f29cbe1c | ||
|
1a3c0cd277 | ||
|
6cb59226bf | ||
|
2997b7d912 | ||
|
6ffe9f5fda | ||
|
8518a39a4b | ||
|
14ce11f608 | ||
|
2b7313cdd2 | ||
|
0ecc171de9 | ||
|
d4218f6ca4 | ||
|
d7bbfeee53 | ||
|
e9287e066b | ||
|
1872c52c6d | ||
|
da42248a95 | ||
|
fca085a352 | ||
|
2d0ab611b2 | ||
|
8316d34b2b | ||
|
bfbda33038 | ||
|
b4310a9366 | ||
|
67be2e9e9b | ||
|
d1da150d97 | ||
|
16c83927c0 | ||
|
535b3074ab | ||
|
50b7db4e3d | ||
|
f993585e77 | ||
|
728f02bf3c | ||
|
831c93294f | ||
|
89f3fb70a2 | ||
|
a29704b445 | ||
|
90e086986d | ||
|
0eedfe8319 | ||
|
8b81fe065f | ||
|
a8f5b8f0db | ||
|
878ebd4b33 | ||
|
bc5634f2af | ||
|
7c217b7413 | ||
|
86037e0a23 | ||
|
1f82a98029 | ||
|
7adbc4b3ee | ||
|
5199e44ef0 | ||
|
32e76985c9 | ||
|
54bdfd3565 | ||
|
bd68cd2d69 | ||
|
f076497f89 | ||
|
5089a941c8 | ||
|
3b490f9450 | ||
|
66928660f5 | ||
|
d2fc6ff1cc | ||
|
39fc9dc40f | ||
|
a90238e7c6 | ||
|
5246203c9e | ||
|
4b99f16b49 | ||
|
7777b1418e | ||
|
9d5934f5ff | ||
|
962d4afb3d | ||
|
eb8acf4b45 | ||
|
8217eb04af | ||
|
26e1200324 | ||
|
4181b187c3 | ||
|
485798c4f8 |
14
Makefile
14
Makefile
@@ -19,7 +19,7 @@ DIRS=libraries/libapparmor \
|
||||
|
||||
# with conversion to git, we don't export from the remote
|
||||
REPO_URL?=git@gitlab.com:apparmor/apparmor.git
|
||||
REPO_BRANCH?=apparmor-2.13
|
||||
REPO_BRANCH?=apparmor-2.11
|
||||
|
||||
COVERITY_DIR=cov-int
|
||||
RELEASE_DIR=apparmor-${VERSION}
|
||||
@@ -39,8 +39,8 @@ TAR_EXCLUSIONS=
|
||||
.PHONY: tarball
|
||||
tarball: clean
|
||||
REPO_VERSION=`$(value REPO_VERSION_CMD)` && \
|
||||
$(MAKE) export_dir __EXPORT_DIR=${RELEASE_DIR} __REPO_VERSION=$${REPO_VERSION} && \
|
||||
$(MAKE) setup __SETUP_DIR=${RELEASE_DIR} && \
|
||||
make export_dir __EXPORT_DIR=${RELEASE_DIR} __REPO_VERSION=$${REPO_VERSION} && \
|
||||
make setup __SETUP_DIR=${RELEASE_DIR} && \
|
||||
tar ${TAR_EXCLUSIONS} -cvzf ${RELEASE_DIR}.tar.gz ${RELEASE_DIR}
|
||||
|
||||
.PHONY: snapshot
|
||||
@@ -55,7 +55,7 @@ snapshot: clean
|
||||
coverity: snapshot
|
||||
cd $(SNAPSHOT_NAME)/libraries/libapparmor && ./configure --with-python
|
||||
$(foreach dir, $(filter-out utils profiles tests, $(DIRS)), \
|
||||
cov-build --dir $(COVERITY_DIR) -- $(MAKE) -C $(SNAPSHOT_NAME)/$(dir); \
|
||||
cov-build --dir $(COVERITY_DIR) -- make -C $(SNAPSHOT_NAME)/$(dir); \
|
||||
mv $(COVERITY_DIR)/build-log.txt $(COVERITY_DIR)/build-log-$(subst /,.,$(dir)).txt ;)
|
||||
$(foreach dir, libraries/libapparmor utils, \
|
||||
cov-build --dir $(COVERITY_DIR) --no-command --fs-capture-search $(SNAPSHOT_NAME)/$(dir); \
|
||||
@@ -72,18 +72,18 @@ export_dir:
|
||||
clean:
|
||||
-rm -rf ${RELEASE_DIR} ./apparmor-${VERSION}~* ${COVERITY_DIR}
|
||||
for dir in $(DIRS); do \
|
||||
$(MAKE) -C $$dir clean; \
|
||||
make -C $$dir clean; \
|
||||
done
|
||||
|
||||
.PHONY: setup
|
||||
setup:
|
||||
cd $(__SETUP_DIR)/libraries/libapparmor && ./autogen.sh
|
||||
# parser has an extra doc to build
|
||||
$(MAKE) -C $(__SETUP_DIR)/parser extra_docs
|
||||
make -C $(__SETUP_DIR)/parser extra_docs
|
||||
# libraries/libapparmor needs configure to have run before
|
||||
# building docs
|
||||
$(foreach dir, $(filter-out libraries/libapparmor tests, $(DIRS)), \
|
||||
$(MAKE) -C $(__SETUP_DIR)/$(dir) docs;)
|
||||
make -C $(__SETUP_DIR)/$(dir) docs;)
|
||||
|
||||
.PHONY: tag
|
||||
tag:
|
||||
|
@@ -56,27 +56,27 @@ Upon exiting, B<aa-enabled> will set its exit status to the following values:
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<0>
|
||||
=item 0:
|
||||
|
||||
if AppArmor is enabled.
|
||||
|
||||
=item B<1>
|
||||
=item 1:
|
||||
|
||||
if AppArmor is not enabled/loaded.
|
||||
|
||||
=item B<2>
|
||||
=item 2:
|
||||
|
||||
intentionally not used as an B<aa-enabled> exit status.
|
||||
|
||||
=item B<3>
|
||||
=item 3:
|
||||
|
||||
if the AppArmor control files aren't available under /sys/kernel/security/.
|
||||
|
||||
=item B<4>
|
||||
=item 4:
|
||||
|
||||
if B<aa-enabled> doesn't have enough privileges to read the apparmor control files.
|
||||
|
||||
=item B<64>
|
||||
=item 64:
|
||||
|
||||
if any unexpected error or condition is encountered.
|
||||
|
||||
|
@@ -8,14 +8,14 @@ msgstr ""
|
||||
"Project-Id-Version: apparmor\n"
|
||||
"Report-Msgid-Bugs-To: AppArmor list <apparmor@lists.ubuntu.com>\n"
|
||||
"POT-Creation-Date: 2015-11-28 10:23-0800\n"
|
||||
"PO-Revision-Date: 2018-02-09 23:55+0000\n"
|
||||
"Last-Translator: Tobias Bannert <tobannert@gmail.com>\n"
|
||||
"PO-Revision-Date: 2016-03-20 01:58+0000\n"
|
||||
"Last-Translator: Tobias Bannert <Unknown>\n"
|
||||
"Language-Team: German <de@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-02-11 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 18544)\n"
|
||||
"X-Launchpad-Export-Date: 2016-03-21 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 17947)\n"
|
||||
"Language: de\n"
|
||||
|
||||
#: ../aa_enabled.c:26
|
||||
@@ -26,20 +26,16 @@ msgid ""
|
||||
" -q | --quiet Don't print out any messages\n"
|
||||
" -h | --help Print help\n"
|
||||
msgstr ""
|
||||
"%s: [Optionen]\n"
|
||||
" Optionen:\n"
|
||||
" -q | --quiet Keine Nachrichten anzeigen\n"
|
||||
" -h | --help Hilfetext anzeigen\n"
|
||||
|
||||
#: ../aa_enabled.c:45
|
||||
#, c-format
|
||||
msgid "unknown or incompatible options\n"
|
||||
msgstr "unbekannte oder nicht kompatible Optionen\n"
|
||||
msgstr ""
|
||||
|
||||
#: ../aa_enabled.c:55
|
||||
#, c-format
|
||||
msgid "unknown option '%s'\n"
|
||||
msgstr "unbekannte Option »%s«\n"
|
||||
msgstr "Unbekannte Option »%s«\n"
|
||||
|
||||
#: ../aa_enabled.c:64
|
||||
#, c-format
|
||||
@@ -59,15 +55,14 @@ msgstr "Nein – beim Start deaktiviert.\n"
|
||||
#: ../aa_enabled.c:77
|
||||
#, c-format
|
||||
msgid "Maybe - policy interface not available.\n"
|
||||
msgstr "Vielleicht – Richtlinienschnittstelle nicht verfügbar.\n"
|
||||
msgstr ""
|
||||
|
||||
#: ../aa_enabled.c:81
|
||||
#, c-format
|
||||
msgid "Maybe - insufficient permissions to determine availability.\n"
|
||||
msgstr ""
|
||||
"Vielleicht – ungenügende Berechtigungen, um die Verfügbarkeit zu prüfen\n"
|
||||
|
||||
#: ../aa_enabled.c:84
|
||||
#, c-format
|
||||
msgid "Error - '%s'\n"
|
||||
msgstr "Fehler – »%s«\n"
|
||||
msgstr "Fehler - »%s«\n"
|
||||
|
@@ -14,8 +14,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-02-19 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17925)\n"
|
||||
"Language: en_GB\n"
|
||||
|
||||
#: ../aa_enabled.c:26
|
||||
|
@@ -14,8 +14,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-02-02 05:11+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"Language: id\n"
|
||||
|
||||
#: ../aa_enabled.c:26
|
||||
|
@@ -9,13 +9,13 @@ msgstr ""
|
||||
"Report-Msgid-Bugs-To: AppArmor list <apparmor@lists.ubuntu.com>\n"
|
||||
"POT-Creation-Date: 2015-11-28 10:23-0800\n"
|
||||
"PO-Revision-Date: 2016-03-03 08:34+0000\n"
|
||||
"Last-Translator: Ivo Xavier <ivoxavier.8@gmail.com>\n"
|
||||
"Last-Translator: Ivo Xavier <ivofernandes12@gmail.com>\n"
|
||||
"Language-Team: Portuguese <pt@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-03-04 04:35+0000\n"
|
||||
"X-Generator: Launchpad (build 17936)\n"
|
||||
"Language: pt\n"
|
||||
|
||||
#: ../aa_enabled.c:26
|
||||
|
@@ -14,8 +14,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-03-30 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 17967)\n"
|
||||
"Language: ru\n"
|
||||
|
||||
#: ../aa_enabled.c:26
|
||||
|
@@ -87,7 +87,7 @@ docs: ${MANPAGES} ${HTMLMANPAGES}
|
||||
install: ${TARGET} ${MANPAGES}
|
||||
mkdir -p ${DESTDIR}/${APXS_INSTALL_DIR}
|
||||
install -m 755 $< ${DESTDIR}/${APXS_INSTALL_DIR}
|
||||
$(MAKE) install_manpages DESTDIR=${DESTDIR}
|
||||
make install_manpages DESTDIR=${DESTDIR}
|
||||
|
||||
.PHONY: clean
|
||||
clean: pod_clean
|
||||
|
@@ -82,7 +82,7 @@ SECDIR ?= ${DESTDIR}/lib/security
|
||||
.PHONY: install
|
||||
install: $(NAME).so
|
||||
install -m 755 -d $(SECDIR)
|
||||
install -m 755 $(NAME).so $(SECDIR)/
|
||||
install -m 555 $(NAME).so $(SECDIR)/
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
|
@@ -1 +1 @@
|
||||
2.13.2
|
||||
2.11.2
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 50 KiB |
@@ -1,605 +0,0 @@
|
||||
From 97b3200925ba627346432edf521d49de8bb018a3 Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Mon, 4 Oct 2010 15:03:36 -0700
|
||||
Subject: [PATCH 1/3] UBUNTU: SAUCE: AppArmor: basic networking rules
|
||||
|
||||
Base support for network mediation.
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
---
|
||||
security/apparmor/.gitignore | 1 +
|
||||
security/apparmor/Makefile | 42 ++++++++++-
|
||||
security/apparmor/apparmorfs.c | 1 +
|
||||
security/apparmor/include/audit.h | 4 +
|
||||
security/apparmor/include/net.h | 59 +++++++++++++++
|
||||
security/apparmor/include/policy.h | 3 +
|
||||
security/apparmor/lsm.c | 112 ++++++++++++++++++++++++++++
|
||||
security/apparmor/net.c | 148 +++++++++++++++++++++++++++++++++++++
|
||||
security/apparmor/policy.c | 1 +
|
||||
security/apparmor/policy_unpack.c | 47 +++++++++++-
|
||||
10 files changed, 415 insertions(+), 3 deletions(-)
|
||||
create mode 100644 security/apparmor/include/net.h
|
||||
create mode 100644 security/apparmor/net.c
|
||||
|
||||
diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore
|
||||
index 9cdec70d72b8..d5b291e94264 100644
|
||||
--- a/security/apparmor/.gitignore
|
||||
+++ b/security/apparmor/.gitignore
|
||||
@@ -1,5 +1,6 @@
|
||||
#
|
||||
# Generated include files
|
||||
#
|
||||
+net_names.h
|
||||
capability_names.h
|
||||
rlim_names.h
|
||||
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
|
||||
index ad369a7aac24..a7dc10be232d 100644
|
||||
--- a/security/apparmor/Makefile
|
||||
+++ b/security/apparmor/Makefile
|
||||
@@ -4,10 +4,10 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
|
||||
|
||||
apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
|
||||
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
|
||||
- resource.o secid.o file.o policy_ns.o
|
||||
+ resource.o secid.o file.o policy_ns.o net.o
|
||||
apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o
|
||||
|
||||
-clean-files := capability_names.h rlim_names.h
|
||||
+clean-files := capability_names.h rlim_names.h net_names.h
|
||||
|
||||
|
||||
# Build a lower case string table of capability names
|
||||
@@ -25,6 +25,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\
|
||||
-e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/\L\1/p' | \
|
||||
tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@
|
||||
|
||||
+# Build a lower case string table of address family names
|
||||
+# Transform lines from
|
||||
+# define AF_LOCAL 1 /* POSIX name for AF_UNIX */
|
||||
+# #define AF_INET 2 /* Internet IP Protocol */
|
||||
+# to
|
||||
+# [1] = "local",
|
||||
+# [2] = "inet",
|
||||
+#
|
||||
+# and build the securityfs entries for the mapping.
|
||||
+# Transforms lines from
|
||||
+# #define AF_INET 2 /* Internet IP Protocol */
|
||||
+# to
|
||||
+# #define AA_FS_AF_MASK "local inet"
|
||||
+quiet_cmd_make-af = GEN $@
|
||||
+cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\
|
||||
+ sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \
|
||||
+ 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\
|
||||
+ echo "};" >> $@ ;\
|
||||
+ echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\
|
||||
+ sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\
|
||||
+ $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@
|
||||
+
|
||||
+# Build a lower case string table of sock type names
|
||||
+# Transform lines from
|
||||
+# SOCK_STREAM = 1,
|
||||
+# to
|
||||
+# [1] = "stream",
|
||||
+quiet_cmd_make-sock = GEN $@
|
||||
+cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\
|
||||
+ sed $^ >>$@ -r -n \
|
||||
+ -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\
|
||||
+ echo "};" >> $@
|
||||
|
||||
# Build a lower case string table of rlimit names.
|
||||
# Transforms lines from
|
||||
@@ -61,6 +93,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \
|
||||
tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@
|
||||
|
||||
$(obj)/capability.o : $(obj)/capability_names.h
|
||||
+$(obj)/net.o : $(obj)/net_names.h
|
||||
$(obj)/resource.o : $(obj)/rlim_names.h
|
||||
$(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \
|
||||
$(src)/Makefile
|
||||
@@ -68,3 +101,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \
|
||||
$(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \
|
||||
$(src)/Makefile
|
||||
$(call cmd,make-rlim)
|
||||
+$(obj)/net_names.h : $(srctree)/include/linux/socket.h \
|
||||
+ $(srctree)/include/linux/net.h \
|
||||
+ $(src)/Makefile
|
||||
+ $(call cmd,make-af)
|
||||
+ $(call cmd,make-sock)
|
||||
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
|
||||
index 41073f70eb41..4d236736cfb8 100644
|
||||
--- a/security/apparmor/apparmorfs.c
|
||||
+++ b/security/apparmor/apparmorfs.c
|
||||
@@ -1209,6 +1209,7 @@ static struct aa_fs_entry aa_fs_entry_features[] = {
|
||||
AA_FS_DIR("policy", aa_fs_entry_policy),
|
||||
AA_FS_DIR("domain", aa_fs_entry_domain),
|
||||
AA_FS_DIR("file", aa_fs_entry_file),
|
||||
+ AA_FS_DIR("network", aa_fs_entry_network),
|
||||
AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK),
|
||||
AA_FS_DIR("rlimit", aa_fs_entry_rlimit),
|
||||
AA_FS_DIR("caps", aa_fs_entry_caps),
|
||||
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
|
||||
index fdc4774318ba..0df708e8748b 100644
|
||||
--- a/security/apparmor/include/audit.h
|
||||
+++ b/security/apparmor/include/audit.h
|
||||
@@ -127,6 +127,10 @@ struct apparmor_audit_data {
|
||||
int rlim;
|
||||
unsigned long max;
|
||||
} rlim;
|
||||
+ struct {
|
||||
+ int type, protocol;
|
||||
+ struct sock *sk;
|
||||
+ } net;
|
||||
};
|
||||
};
|
||||
|
||||
diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h
|
||||
new file mode 100644
|
||||
index 000000000000..55da1dad8720
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/include/net.h
|
||||
@@ -0,0 +1,59 @@
|
||||
+/*
|
||||
+ * AppArmor security module
|
||||
+ *
|
||||
+ * This file contains AppArmor network mediation definitions.
|
||||
+ *
|
||||
+ * Copyright (C) 1998-2008 Novell/SUSE
|
||||
+ * Copyright 2009-2012 Canonical Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License as
|
||||
+ * published by the Free Software Foundation, version 2 of the
|
||||
+ * License.
|
||||
+ */
|
||||
+
|
||||
+#ifndef __AA_NET_H
|
||||
+#define __AA_NET_H
|
||||
+
|
||||
+#include <net/sock.h>
|
||||
+
|
||||
+#include "apparmorfs.h"
|
||||
+
|
||||
+/* struct aa_net - network confinement data
|
||||
+ * @allowed: basic network families permissions
|
||||
+ * @audit_network: which network permissions to force audit
|
||||
+ * @quiet_network: which network permissions to quiet rejects
|
||||
+ */
|
||||
+struct aa_net {
|
||||
+ u16 allow[AF_MAX];
|
||||
+ u16 audit[AF_MAX];
|
||||
+ u16 quiet[AF_MAX];
|
||||
+};
|
||||
+
|
||||
+extern struct aa_fs_entry aa_fs_entry_network[];
|
||||
+
|
||||
+#define DEFINE_AUDIT_NET(NAME, OP, SK, F, T, P) \
|
||||
+ struct lsm_network_audit NAME ## _net = { .sk = (SK), \
|
||||
+ .family = (F)}; \
|
||||
+ DEFINE_AUDIT_DATA(NAME, \
|
||||
+ ((SK) && (F) != AF_UNIX) ? LSM_AUDIT_DATA_NET : \
|
||||
+ LSM_AUDIT_DATA_NONE, \
|
||||
+ OP); \
|
||||
+ NAME.u.net = &(NAME ## _net); \
|
||||
+ aad(&NAME)->net.type = (T); \
|
||||
+ aad(&NAME)->net.protocol = (P)
|
||||
+
|
||||
+#define DEFINE_AUDIT_SK(NAME, OP, SK) \
|
||||
+ DEFINE_AUDIT_NET(NAME, OP, SK, (SK)->sk_family, (SK)->sk_type, \
|
||||
+ (SK)->sk_protocol)
|
||||
+
|
||||
+extern int aa_net_perm(const char *op, struct aa_profile *profile, u16 family,
|
||||
+ int type, int protocol, struct sock *sk);
|
||||
+extern int aa_revalidate_sk(const char *op, struct sock *sk);
|
||||
+
|
||||
+static inline void aa_free_net_rules(struct aa_net *new)
|
||||
+{
|
||||
+ /* NOP */
|
||||
+}
|
||||
+
|
||||
+#endif /* __AA_NET_H */
|
||||
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
|
||||
index 67bc96afe541..a3d18ea8d730 100644
|
||||
--- a/security/apparmor/include/policy.h
|
||||
+++ b/security/apparmor/include/policy.h
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "capability.h"
|
||||
#include "domain.h"
|
||||
#include "file.h"
|
||||
+#include "net.h"
|
||||
#include "lib.h"
|
||||
#include "resource.h"
|
||||
|
||||
@@ -132,6 +133,7 @@ struct aa_data {
|
||||
* @policy: general match rules governing policy
|
||||
* @file: The set of rules governing basic file access and domain transitions
|
||||
* @caps: capabilities for the profile
|
||||
+ * @net: network controls for the profile
|
||||
* @rlimits: rlimits for the profile
|
||||
*
|
||||
* @dents: dentries for the profiles file entries in apparmorfs
|
||||
@@ -174,6 +176,7 @@ struct aa_profile {
|
||||
struct aa_policydb policy;
|
||||
struct aa_file_rules file;
|
||||
struct aa_caps caps;
|
||||
+ struct aa_net net;
|
||||
struct aa_rlimit rlimits;
|
||||
|
||||
struct aa_loaddata *rawdata;
|
||||
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
|
||||
index 709eacd23909..e3017129a404 100644
|
||||
--- a/security/apparmor/lsm.c
|
||||
+++ b/security/apparmor/lsm.c
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "include/context.h"
|
||||
#include "include/file.h"
|
||||
#include "include/ipc.h"
|
||||
+#include "include/net.h"
|
||||
#include "include/path.h"
|
||||
#include "include/policy.h"
|
||||
#include "include/policy_ns.h"
|
||||
@@ -587,6 +588,104 @@ static int apparmor_task_setrlimit(struct task_struct *task,
|
||||
return error;
|
||||
}
|
||||
|
||||
+static int apparmor_socket_create(int family, int type, int protocol, int kern)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ int error = 0;
|
||||
+
|
||||
+ if (kern)
|
||||
+ return 0;
|
||||
+
|
||||
+ profile = __aa_current_profile();
|
||||
+ if (!unconfined(profile))
|
||||
+ error = aa_net_perm(OP_CREATE, profile, family, type, protocol,
|
||||
+ NULL);
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_bind(struct socket *sock,
|
||||
+ struct sockaddr *address, int addrlen)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_BIND, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_connect(struct socket *sock,
|
||||
+ struct sockaddr *address, int addrlen)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_CONNECT, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_listen(struct socket *sock, int backlog)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_LISTEN, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_accept(struct socket *sock, struct socket *newsock)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_ACCEPT, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_sendmsg(struct socket *sock,
|
||||
+ struct msghdr *msg, int size)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_SENDMSG, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_recvmsg(struct socket *sock,
|
||||
+ struct msghdr *msg, int size, int flags)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_RECVMSG, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_getsockname(struct socket *sock)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_GETSOCKNAME, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_getpeername(struct socket *sock)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_GETPEERNAME, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_getsockopt(struct socket *sock, int level,
|
||||
+ int optname)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_GETSOCKOPT, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_setsockopt(struct socket *sock, int level,
|
||||
+ int optname)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_SETSOCKOPT, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_shutdown(struct socket *sock, int how)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_SHUTDOWN, sk);
|
||||
+}
|
||||
+
|
||||
static struct security_hook_list apparmor_hooks[] = {
|
||||
LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check),
|
||||
LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme),
|
||||
@@ -616,6 +715,19 @@ static struct security_hook_list apparmor_hooks[] = {
|
||||
LSM_HOOK_INIT(getprocattr, apparmor_getprocattr),
|
||||
LSM_HOOK_INIT(setprocattr, apparmor_setprocattr),
|
||||
|
||||
+ LSM_HOOK_INIT(socket_create, apparmor_socket_create),
|
||||
+ LSM_HOOK_INIT(socket_bind, apparmor_socket_bind),
|
||||
+ LSM_HOOK_INIT(socket_connect, apparmor_socket_connect),
|
||||
+ LSM_HOOK_INIT(socket_listen, apparmor_socket_listen),
|
||||
+ LSM_HOOK_INIT(socket_accept, apparmor_socket_accept),
|
||||
+ LSM_HOOK_INIT(socket_sendmsg, apparmor_socket_sendmsg),
|
||||
+ LSM_HOOK_INIT(socket_recvmsg, apparmor_socket_recvmsg),
|
||||
+ LSM_HOOK_INIT(socket_getsockname, apparmor_socket_getsockname),
|
||||
+ LSM_HOOK_INIT(socket_getpeername, apparmor_socket_getpeername),
|
||||
+ LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt),
|
||||
+ LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt),
|
||||
+ LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown),
|
||||
+
|
||||
LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank),
|
||||
LSM_HOOK_INIT(cred_free, apparmor_cred_free),
|
||||
LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare),
|
||||
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
|
||||
new file mode 100644
|
||||
index 000000000000..b9c8cd0e882e
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/net.c
|
||||
@@ -0,0 +1,148 @@
|
||||
+/*
|
||||
+ * AppArmor security module
|
||||
+ *
|
||||
+ * This file contains AppArmor network mediation
|
||||
+ *
|
||||
+ * Copyright (C) 1998-2008 Novell/SUSE
|
||||
+ * Copyright 2009-2012 Canonical Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License as
|
||||
+ * published by the Free Software Foundation, version 2 of the
|
||||
+ * License.
|
||||
+ */
|
||||
+
|
||||
+#include "include/apparmor.h"
|
||||
+#include "include/audit.h"
|
||||
+#include "include/context.h"
|
||||
+#include "include/net.h"
|
||||
+#include "include/policy.h"
|
||||
+
|
||||
+#include "net_names.h"
|
||||
+
|
||||
+struct aa_fs_entry aa_fs_entry_network[] = {
|
||||
+ AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK),
|
||||
+ { }
|
||||
+};
|
||||
+
|
||||
+/* audit callback for net specific fields */
|
||||
+static void audit_cb(struct audit_buffer *ab, void *va)
|
||||
+{
|
||||
+ struct common_audit_data *sa = va;
|
||||
+
|
||||
+ audit_log_format(ab, " family=");
|
||||
+ if (address_family_names[sa->u.net->family]) {
|
||||
+ audit_log_string(ab, address_family_names[sa->u.net->family]);
|
||||
+ } else {
|
||||
+ audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family);
|
||||
+ }
|
||||
+ audit_log_format(ab, " sock_type=");
|
||||
+ if (sock_type_names[aad(sa)->net.type]) {
|
||||
+ audit_log_string(ab, sock_type_names[aad(sa)->net.type]);
|
||||
+ } else {
|
||||
+ audit_log_format(ab, "\"unknown(%d)\"", aad(sa)->net.type);
|
||||
+ }
|
||||
+ audit_log_format(ab, " protocol=%d", aad(sa)->net.protocol);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * audit_net - audit network access
|
||||
+ * @profile: profile being enforced (NOT NULL)
|
||||
+ * @op: operation being checked
|
||||
+ * @family: network family
|
||||
+ * @type: network type
|
||||
+ * @protocol: network protocol
|
||||
+ * @sk: socket auditing is being applied to
|
||||
+ * @error: error code for failure else 0
|
||||
+ *
|
||||
+ * Returns: %0 or sa->error else other errorcode on failure
|
||||
+ */
|
||||
+static int audit_net(struct aa_profile *profile, const char *op, u16 family,
|
||||
+ int type, int protocol, struct sock *sk, int error)
|
||||
+{
|
||||
+ int audit_type = AUDIT_APPARMOR_AUTO;
|
||||
+ DEFINE_AUDIT_NET(sa, op, sk, family, type, protocol);
|
||||
+
|
||||
+ aad(&sa)->error = error;
|
||||
+
|
||||
+ if (likely(!aad(&sa)->error)) {
|
||||
+ u16 audit_mask = profile->net.audit[sa.u.net->family];
|
||||
+ if (likely((AUDIT_MODE(profile) != AUDIT_ALL) &&
|
||||
+ !(1 << aad(&sa)->net.type & audit_mask)))
|
||||
+ return 0;
|
||||
+ audit_type = AUDIT_APPARMOR_AUDIT;
|
||||
+ } else {
|
||||
+ u16 quiet_mask = profile->net.quiet[sa.u.net->family];
|
||||
+ u16 kill_mask = 0;
|
||||
+ u16 denied = (1 << aad(&sa)->net.type) & ~quiet_mask;
|
||||
+
|
||||
+ if (denied & kill_mask)
|
||||
+ audit_type = AUDIT_APPARMOR_KILL;
|
||||
+
|
||||
+ if ((denied & quiet_mask) &&
|
||||
+ AUDIT_MODE(profile) != AUDIT_NOQUIET &&
|
||||
+ AUDIT_MODE(profile) != AUDIT_ALL)
|
||||
+ return COMPLAIN_MODE(profile) ? 0 : aad(&sa)->error;
|
||||
+ }
|
||||
+
|
||||
+ return aa_audit(audit_type, profile, &sa, audit_cb);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * aa_net_perm - very course network access check
|
||||
+ * @op: operation being checked
|
||||
+ * @profile: profile being enforced (NOT NULL)
|
||||
+ * @family: network family
|
||||
+ * @type: network type
|
||||
+ * @protocol: network protocol
|
||||
+ *
|
||||
+ * Returns: %0 else error if permission denied
|
||||
+ */
|
||||
+int aa_net_perm(const char *op, struct aa_profile *profile, u16 family,
|
||||
+ int type, int protocol, struct sock *sk)
|
||||
+{
|
||||
+ u16 family_mask;
|
||||
+ int error;
|
||||
+
|
||||
+ if ((family < 0) || (family >= AF_MAX))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if ((type < 0) || (type >= SOCK_MAX))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /* unix domain and netlink sockets are handled by ipc */
|
||||
+ if (family == AF_UNIX || family == AF_NETLINK)
|
||||
+ return 0;
|
||||
+
|
||||
+ family_mask = profile->net.allow[family];
|
||||
+
|
||||
+ error = (family_mask & (1 << type)) ? 0 : -EACCES;
|
||||
+
|
||||
+ return audit_net(profile, op, family, type, protocol, sk, error);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * aa_revalidate_sk - Revalidate access to a sock
|
||||
+ * @op: operation being checked
|
||||
+ * @sk: sock being revalidated (NOT NULL)
|
||||
+ *
|
||||
+ * Returns: %0 else error if permission denied
|
||||
+ */
|
||||
+int aa_revalidate_sk(const char *op, struct sock *sk)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ int error = 0;
|
||||
+
|
||||
+ /* aa_revalidate_sk should not be called from interrupt context
|
||||
+ * don't mediate these calls as they are not task related
|
||||
+ */
|
||||
+ if (in_interrupt())
|
||||
+ return 0;
|
||||
+
|
||||
+ profile = __aa_current_profile();
|
||||
+ if (!unconfined(profile))
|
||||
+ error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type,
|
||||
+ sk->sk_protocol, sk);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
|
||||
index def1fbd6bdfd..9fe7b9d4500f 100644
|
||||
--- a/security/apparmor/policy.c
|
||||
+++ b/security/apparmor/policy.c
|
||||
@@ -237,6 +237,7 @@ void aa_free_profile(struct aa_profile *profile)
|
||||
|
||||
aa_free_file_rules(&profile->file);
|
||||
aa_free_cap_rules(&profile->caps);
|
||||
+ aa_free_net_rules(&profile->net);
|
||||
aa_free_rlimit_rules(&profile->rlimits);
|
||||
|
||||
kzfree(profile->dirname);
|
||||
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
|
||||
index 2e37c9c26bbd..bc23a5b3b113 100644
|
||||
--- a/security/apparmor/policy_unpack.c
|
||||
+++ b/security/apparmor/policy_unpack.c
|
||||
@@ -217,6 +217,19 @@ static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name)
|
||||
+{
|
||||
+ if (unpack_nameX(e, AA_U16, name)) {
|
||||
+ if (!inbounds(e, sizeof(u16)))
|
||||
+ return 0;
|
||||
+ if (data)
|
||||
+ *data = le16_to_cpu(get_unaligned((u16 *) e->pos));
|
||||
+ e->pos += sizeof(u16);
|
||||
+ return 1;
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name)
|
||||
{
|
||||
if (unpack_nameX(e, AA_U32, name)) {
|
||||
@@ -519,7 +532,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
||||
{
|
||||
struct aa_profile *profile = NULL;
|
||||
const char *tmpname, *tmpns = NULL, *name = NULL;
|
||||
- size_t ns_len;
|
||||
+ size_t ns_len, size = 0;
|
||||
struct rhashtable_params params = { 0 };
|
||||
char *key = NULL;
|
||||
struct aa_data *data;
|
||||
@@ -635,6 +648,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
||||
if (!unpack_rlimits(e, profile))
|
||||
goto fail;
|
||||
|
||||
+ size = unpack_array(e, "net_allowed_af");
|
||||
+ if (size) {
|
||||
+
|
||||
+ for (i = 0; i < size; i++) {
|
||||
+ /* discard extraneous rules that this kernel will
|
||||
+ * never request
|
||||
+ */
|
||||
+ if (i >= AF_MAX) {
|
||||
+ u16 tmp;
|
||||
+ if (!unpack_u16(e, &tmp, NULL) ||
|
||||
+ !unpack_u16(e, &tmp, NULL) ||
|
||||
+ !unpack_u16(e, &tmp, NULL))
|
||||
+ goto fail;
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (!unpack_u16(e, &profile->net.allow[i], NULL))
|
||||
+ goto fail;
|
||||
+ if (!unpack_u16(e, &profile->net.audit[i], NULL))
|
||||
+ goto fail;
|
||||
+ if (!unpack_u16(e, &profile->net.quiet[i], NULL))
|
||||
+ goto fail;
|
||||
+ }
|
||||
+ if (!unpack_nameX(e, AA_ARRAYEND, NULL))
|
||||
+ goto fail;
|
||||
+ }
|
||||
+ /*
|
||||
+ * allow unix domain and netlink sockets they are handled
|
||||
+ * by IPC
|
||||
+ */
|
||||
+ profile->net.allow[AF_UNIX] = 0xffff;
|
||||
+ profile->net.allow[AF_NETLINK] = 0xffff;
|
||||
+
|
||||
if (unpack_nameX(e, AA_STRUCT, "policydb")) {
|
||||
/* generic policy dfa - optional and may be NULL */
|
||||
profile->policy.dfa = unpack_dfa(e);
|
||||
--
|
||||
2.11.0
|
||||
|
@@ -1,38 +0,0 @@
|
||||
From b866a43c2897f5469c9d787426144074a3713f6a Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Fri, 29 Jun 2012 17:34:00 -0700
|
||||
Subject: [PATCH 2/3] apparmor: Fix quieting of audit messages for network
|
||||
mediation
|
||||
|
||||
If a profile specified a quieting of network denials for a given rule by
|
||||
either the quiet or deny rule qualifiers, the resultant quiet mask for
|
||||
denied requests was applied incorrectly, resulting in two potential bugs.
|
||||
1. The misapplied quiet mask would prevent denials from being correctly
|
||||
tested against the kill mask/mode. Thus network access requests that
|
||||
should have resulted in the application being killed did not.
|
||||
|
||||
2. The actual quieting of the denied network request was not being applied.
|
||||
This would result in network rejections always being logged even when
|
||||
they had been specifically marked as quieted.
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
---
|
||||
security/apparmor/net.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
|
||||
index b9c8cd0e882e..5ba19ad1d65c 100644
|
||||
--- a/security/apparmor/net.c
|
||||
+++ b/security/apparmor/net.c
|
||||
@@ -74,7 +74,7 @@ static int audit_net(struct aa_profile *profile, const char *op, u16 family,
|
||||
} else {
|
||||
u16 quiet_mask = profile->net.quiet[sa.u.net->family];
|
||||
u16 kill_mask = 0;
|
||||
- u16 denied = (1 << aad(&sa)->net.type) & ~quiet_mask;
|
||||
+ u16 denied = (1 << aad(&sa)->net.type);
|
||||
|
||||
if (denied & kill_mask)
|
||||
audit_type = AUDIT_APPARMOR_KILL;
|
||||
--
|
||||
2.11.0
|
||||
|
@@ -1,938 +0,0 @@
|
||||
From 4429c3f9522b608300cfe1ae148dc6cdadf3d76c Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Wed, 16 May 2012 10:58:05 -0700
|
||||
Subject: [PATCH 3/3] UBUNTU: SAUCE: apparmor: Add the ability to mediate mount
|
||||
|
||||
Add the ability for apparmor to do mediation of mount operations. Mount
|
||||
rules require an updated apparmor_parser (2.8 series) for policy compilation.
|
||||
|
||||
The basic form of the rules are.
|
||||
|
||||
[audit] [deny] mount [conds]* [device] [ -> [conds] path],
|
||||
[audit] [deny] remount [conds]* [path],
|
||||
[audit] [deny] umount [conds]* [path],
|
||||
[audit] [deny] pivotroot [oldroot=<value>] <path>
|
||||
|
||||
remount is just a short cut for mount options=remount
|
||||
|
||||
where [conds] can be
|
||||
fstype=<expr>
|
||||
options=<expr>
|
||||
|
||||
Example mount commands
|
||||
mount, # allow all mounts, but not umount or pivotroot
|
||||
|
||||
mount fstype=procfs, # allow mounting procfs anywhere
|
||||
|
||||
mount options=(bind, ro) /foo -> /bar, # readonly bind mount
|
||||
|
||||
mount /dev/sda -> /mnt,
|
||||
|
||||
mount /dev/sd** -> /mnt/**,
|
||||
|
||||
mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/
|
||||
|
||||
umount,
|
||||
|
||||
umount /m*,
|
||||
|
||||
See the apparmor userspace for full documentation
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
Acked-by: Kees Cook <kees@ubuntu.com>
|
||||
---
|
||||
security/apparmor/Makefile | 2 +-
|
||||
security/apparmor/apparmorfs.c | 13 +
|
||||
security/apparmor/domain.c | 2 +-
|
||||
security/apparmor/include/apparmor.h | 3 +-
|
||||
security/apparmor/include/audit.h | 11 +
|
||||
security/apparmor/include/domain.h | 2 +
|
||||
security/apparmor/include/mount.h | 54 +++
|
||||
security/apparmor/lsm.c | 60 ++++
|
||||
security/apparmor/mount.c | 616 +++++++++++++++++++++++++++++++++++
|
||||
9 files changed, 760 insertions(+), 3 deletions(-)
|
||||
create mode 100644 security/apparmor/include/mount.h
|
||||
create mode 100644 security/apparmor/mount.c
|
||||
|
||||
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
|
||||
index a7dc10be232d..01368441f230 100644
|
||||
--- a/security/apparmor/Makefile
|
||||
+++ b/security/apparmor/Makefile
|
||||
@@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
|
||||
|
||||
apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
|
||||
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
|
||||
- resource.o secid.o file.o policy_ns.o net.o
|
||||
+ resource.o secid.o file.o policy_ns.o net.o mount.o
|
||||
apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o
|
||||
|
||||
clean-files := capability_names.h rlim_names.h net_names.h
|
||||
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
|
||||
index 4d236736cfb8..2e8d09e2368b 100644
|
||||
--- a/security/apparmor/apparmorfs.c
|
||||
+++ b/security/apparmor/apparmorfs.c
|
||||
@@ -1205,11 +1205,24 @@ static struct aa_fs_entry aa_fs_entry_policy[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
+static struct aa_fs_entry aa_fs_entry_mount[] = {
|
||||
+ AA_FS_FILE_STRING("mask", "mount umount"),
|
||||
+ { }
|
||||
+};
|
||||
+
|
||||
+static struct aa_fs_entry aa_fs_entry_namespaces[] = {
|
||||
+ AA_FS_FILE_BOOLEAN("profile", 1),
|
||||
+ AA_FS_FILE_BOOLEAN("pivot_root", 1),
|
||||
+ { }
|
||||
+};
|
||||
+
|
||||
static struct aa_fs_entry aa_fs_entry_features[] = {
|
||||
AA_FS_DIR("policy", aa_fs_entry_policy),
|
||||
AA_FS_DIR("domain", aa_fs_entry_domain),
|
||||
AA_FS_DIR("file", aa_fs_entry_file),
|
||||
AA_FS_DIR("network", aa_fs_entry_network),
|
||||
+ AA_FS_DIR("mount", aa_fs_entry_mount),
|
||||
+ AA_FS_DIR("namespaces", aa_fs_entry_namespaces),
|
||||
AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK),
|
||||
AA_FS_DIR("rlimit", aa_fs_entry_rlimit),
|
||||
AA_FS_DIR("caps", aa_fs_entry_caps),
|
||||
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
|
||||
index 001e133a3c8c..708b7e22b9b5 100644
|
||||
--- a/security/apparmor/domain.c
|
||||
+++ b/security/apparmor/domain.c
|
||||
@@ -237,7 +237,7 @@ static const char *next_name(int xtype, const char *name)
|
||||
*
|
||||
* Returns: refcounted profile, or NULL on failure (MAYBE NULL)
|
||||
*/
|
||||
-static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex)
|
||||
+struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex)
|
||||
{
|
||||
struct aa_profile *new_profile = NULL;
|
||||
struct aa_ns *ns = profile->ns;
|
||||
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h
|
||||
index 1750cc0721c1..3383dc66f30f 100644
|
||||
--- a/security/apparmor/include/apparmor.h
|
||||
+++ b/security/apparmor/include/apparmor.h
|
||||
@@ -27,8 +27,9 @@
|
||||
#define AA_CLASS_NET 4
|
||||
#define AA_CLASS_RLIMITS 5
|
||||
#define AA_CLASS_DOMAIN 6
|
||||
+#define AA_CLASS_MOUNT 7
|
||||
|
||||
-#define AA_CLASS_LAST AA_CLASS_DOMAIN
|
||||
+#define AA_CLASS_LAST AA_CLASS_MOUNT
|
||||
|
||||
/* Control parameters settable through module/boot flags */
|
||||
extern enum audit_mode aa_g_audit;
|
||||
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
|
||||
index 0df708e8748b..41374ad89547 100644
|
||||
--- a/security/apparmor/include/audit.h
|
||||
+++ b/security/apparmor/include/audit.h
|
||||
@@ -70,6 +70,10 @@ enum audit_type {
|
||||
#define OP_FMMAP "file_mmap"
|
||||
#define OP_FMPROT "file_mprotect"
|
||||
|
||||
+#define OP_PIVOTROOT "pivotroot"
|
||||
+#define OP_MOUNT "mount"
|
||||
+#define OP_UMOUNT "umount"
|
||||
+
|
||||
#define OP_CREATE "create"
|
||||
#define OP_POST_CREATE "post_create"
|
||||
#define OP_BIND "bind"
|
||||
@@ -127,6 +131,13 @@ struct apparmor_audit_data {
|
||||
int rlim;
|
||||
unsigned long max;
|
||||
} rlim;
|
||||
+ struct {
|
||||
+ const char *src_name;
|
||||
+ const char *type;
|
||||
+ const char *trans;
|
||||
+ const char *data;
|
||||
+ unsigned long flags;
|
||||
+ } mnt;
|
||||
struct {
|
||||
int type, protocol;
|
||||
struct sock *sk;
|
||||
diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h
|
||||
index 30544729878a..7bd21d20a2bd 100644
|
||||
--- a/security/apparmor/include/domain.h
|
||||
+++ b/security/apparmor/include/domain.h
|
||||
@@ -23,6 +23,8 @@ struct aa_domain {
|
||||
char **table;
|
||||
};
|
||||
|
||||
+struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex);
|
||||
+
|
||||
int apparmor_bprm_set_creds(struct linux_binprm *bprm);
|
||||
int apparmor_bprm_secureexec(struct linux_binprm *bprm);
|
||||
void apparmor_bprm_committing_creds(struct linux_binprm *bprm);
|
||||
diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h
|
||||
new file mode 100644
|
||||
index 000000000000..a43b1d62e428
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/include/mount.h
|
||||
@@ -0,0 +1,54 @@
|
||||
+/*
|
||||
+ * AppArmor security module
|
||||
+ *
|
||||
+ * This file contains AppArmor file mediation function definitions.
|
||||
+ *
|
||||
+ * Copyright 2012 Canonical Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License as
|
||||
+ * published by the Free Software Foundation, version 2 of the
|
||||
+ * License.
|
||||
+ */
|
||||
+
|
||||
+#ifndef __AA_MOUNT_H
|
||||
+#define __AA_MOUNT_H
|
||||
+
|
||||
+#include <linux/fs.h>
|
||||
+#include <linux/path.h>
|
||||
+
|
||||
+#include "domain.h"
|
||||
+#include "policy.h"
|
||||
+
|
||||
+/* mount perms */
|
||||
+#define AA_MAY_PIVOTROOT 0x01
|
||||
+#define AA_MAY_MOUNT 0x02
|
||||
+#define AA_MAY_UMOUNT 0x04
|
||||
+#define AA_AUDIT_DATA 0x40
|
||||
+#define AA_CONT_MATCH 0x40
|
||||
+
|
||||
+#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN)
|
||||
+
|
||||
+int aa_remount(struct aa_profile *profile, const struct path *path,
|
||||
+ unsigned long flags, void *data);
|
||||
+
|
||||
+int aa_bind_mount(struct aa_profile *profile, const struct path *path,
|
||||
+ const char *old_name, unsigned long flags);
|
||||
+
|
||||
+
|
||||
+int aa_mount_change_type(struct aa_profile *profile, const struct path *path,
|
||||
+ unsigned long flags);
|
||||
+
|
||||
+int aa_move_mount(struct aa_profile *profile, const struct path *path,
|
||||
+ const char *old_name);
|
||||
+
|
||||
+int aa_new_mount(struct aa_profile *profile, const char *dev_name,
|
||||
+ const struct path *path, const char *type, unsigned long flags,
|
||||
+ void *data);
|
||||
+
|
||||
+int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags);
|
||||
+
|
||||
+int aa_pivotroot(struct aa_profile *profile, const struct path *old_path,
|
||||
+ const struct path *new_path);
|
||||
+
|
||||
+#endif /* __AA_MOUNT_H */
|
||||
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
|
||||
index e3017129a404..ee58a2cca74f 100644
|
||||
--- a/security/apparmor/lsm.c
|
||||
+++ b/security/apparmor/lsm.c
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "include/policy.h"
|
||||
#include "include/policy_ns.h"
|
||||
#include "include/procattr.h"
|
||||
+#include "include/mount.h"
|
||||
|
||||
/* Flag indicating whether initialization completed */
|
||||
int apparmor_initialized __initdata;
|
||||
@@ -479,6 +480,61 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma,
|
||||
!(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0);
|
||||
}
|
||||
|
||||
+static int apparmor_sb_mount(const char *dev_name, const struct path *path,
|
||||
+ const char *type, unsigned long flags, void *data)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ int error = 0;
|
||||
+
|
||||
+ /* Discard magic */
|
||||
+ if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
|
||||
+ flags &= ~MS_MGC_MSK;
|
||||
+
|
||||
+ flags &= ~AA_MS_IGNORE_MASK;
|
||||
+
|
||||
+ profile = __aa_current_profile();
|
||||
+ if (!unconfined(profile)) {
|
||||
+ if (flags & MS_REMOUNT)
|
||||
+ error = aa_remount(profile, path, flags, data);
|
||||
+ else if (flags & MS_BIND)
|
||||
+ error = aa_bind_mount(profile, path, dev_name, flags);
|
||||
+ else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE |
|
||||
+ MS_UNBINDABLE))
|
||||
+ error = aa_mount_change_type(profile, path, flags);
|
||||
+ else if (flags & MS_MOVE)
|
||||
+ error = aa_move_mount(profile, path, dev_name);
|
||||
+ else
|
||||
+ error = aa_new_mount(profile, dev_name, path, type,
|
||||
+ flags, data);
|
||||
+ }
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+static int apparmor_sb_umount(struct vfsmount *mnt, int flags)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ int error = 0;
|
||||
+
|
||||
+ profile = __aa_current_profile();
|
||||
+ if (!unconfined(profile))
|
||||
+ error = aa_umount(profile, mnt, flags);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+static int apparmor_sb_pivotroot(const struct path *old_path,
|
||||
+ const struct path *new_path)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ int error = 0;
|
||||
+
|
||||
+ profile = __aa_current_profile();
|
||||
+ if (!unconfined(profile))
|
||||
+ error = aa_pivotroot(profile, old_path, new_path);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
static int apparmor_getprocattr(struct task_struct *task, char *name,
|
||||
char **value)
|
||||
{
|
||||
@@ -692,6 +748,10 @@ static struct security_hook_list apparmor_hooks[] = {
|
||||
LSM_HOOK_INIT(capget, apparmor_capget),
|
||||
LSM_HOOK_INIT(capable, apparmor_capable),
|
||||
|
||||
+ LSM_HOOK_INIT(sb_mount, apparmor_sb_mount),
|
||||
+ LSM_HOOK_INIT(sb_umount, apparmor_sb_umount),
|
||||
+ LSM_HOOK_INIT(sb_pivotroot, apparmor_sb_pivotroot),
|
||||
+
|
||||
LSM_HOOK_INIT(path_link, apparmor_path_link),
|
||||
LSM_HOOK_INIT(path_unlink, apparmor_path_unlink),
|
||||
LSM_HOOK_INIT(path_symlink, apparmor_path_symlink),
|
||||
diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c
|
||||
new file mode 100644
|
||||
index 000000000000..9e95a41c015c
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/mount.c
|
||||
@@ -0,0 +1,616 @@
|
||||
+/*
|
||||
+ * AppArmor security module
|
||||
+ *
|
||||
+ * This file contains AppArmor mediation of files
|
||||
+ *
|
||||
+ * Copyright (C) 1998-2008 Novell/SUSE
|
||||
+ * Copyright 2009-2012 Canonical Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License as
|
||||
+ * published by the Free Software Foundation, version 2 of the
|
||||
+ * License.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/fs.h>
|
||||
+#include <linux/mount.h>
|
||||
+#include <linux/namei.h>
|
||||
+
|
||||
+#include "include/apparmor.h"
|
||||
+#include "include/audit.h"
|
||||
+#include "include/context.h"
|
||||
+#include "include/domain.h"
|
||||
+#include "include/file.h"
|
||||
+#include "include/match.h"
|
||||
+#include "include/mount.h"
|
||||
+#include "include/path.h"
|
||||
+#include "include/policy.h"
|
||||
+
|
||||
+
|
||||
+static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags)
|
||||
+{
|
||||
+ if (flags & MS_RDONLY)
|
||||
+ audit_log_format(ab, "ro");
|
||||
+ else
|
||||
+ audit_log_format(ab, "rw");
|
||||
+ if (flags & MS_NOSUID)
|
||||
+ audit_log_format(ab, ", nosuid");
|
||||
+ if (flags & MS_NODEV)
|
||||
+ audit_log_format(ab, ", nodev");
|
||||
+ if (flags & MS_NOEXEC)
|
||||
+ audit_log_format(ab, ", noexec");
|
||||
+ if (flags & MS_SYNCHRONOUS)
|
||||
+ audit_log_format(ab, ", sync");
|
||||
+ if (flags & MS_REMOUNT)
|
||||
+ audit_log_format(ab, ", remount");
|
||||
+ if (flags & MS_MANDLOCK)
|
||||
+ audit_log_format(ab, ", mand");
|
||||
+ if (flags & MS_DIRSYNC)
|
||||
+ audit_log_format(ab, ", dirsync");
|
||||
+ if (flags & MS_NOATIME)
|
||||
+ audit_log_format(ab, ", noatime");
|
||||
+ if (flags & MS_NODIRATIME)
|
||||
+ audit_log_format(ab, ", nodiratime");
|
||||
+ if (flags & MS_BIND)
|
||||
+ audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind");
|
||||
+ if (flags & MS_MOVE)
|
||||
+ audit_log_format(ab, ", move");
|
||||
+ if (flags & MS_SILENT)
|
||||
+ audit_log_format(ab, ", silent");
|
||||
+ if (flags & MS_POSIXACL)
|
||||
+ audit_log_format(ab, ", acl");
|
||||
+ if (flags & MS_UNBINDABLE)
|
||||
+ audit_log_format(ab, flags & MS_REC ? ", runbindable" :
|
||||
+ ", unbindable");
|
||||
+ if (flags & MS_PRIVATE)
|
||||
+ audit_log_format(ab, flags & MS_REC ? ", rprivate" :
|
||||
+ ", private");
|
||||
+ if (flags & MS_SLAVE)
|
||||
+ audit_log_format(ab, flags & MS_REC ? ", rslave" :
|
||||
+ ", slave");
|
||||
+ if (flags & MS_SHARED)
|
||||
+ audit_log_format(ab, flags & MS_REC ? ", rshared" :
|
||||
+ ", shared");
|
||||
+ if (flags & MS_RELATIME)
|
||||
+ audit_log_format(ab, ", relatime");
|
||||
+ if (flags & MS_I_VERSION)
|
||||
+ audit_log_format(ab, ", iversion");
|
||||
+ if (flags & MS_STRICTATIME)
|
||||
+ audit_log_format(ab, ", strictatime");
|
||||
+ if (flags & MS_NOUSER)
|
||||
+ audit_log_format(ab, ", nouser");
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * audit_cb - call back for mount specific audit fields
|
||||
+ * @ab: audit_buffer (NOT NULL)
|
||||
+ * @va: audit struct to audit values of (NOT NULL)
|
||||
+ */
|
||||
+static void audit_cb(struct audit_buffer *ab, void *va)
|
||||
+{
|
||||
+ struct common_audit_data *sa = va;
|
||||
+
|
||||
+ if (aad(sa)->mnt.type) {
|
||||
+ audit_log_format(ab, " fstype=");
|
||||
+ audit_log_untrustedstring(ab, aad(sa)->mnt.type);
|
||||
+ }
|
||||
+ if (aad(sa)->mnt.src_name) {
|
||||
+ audit_log_format(ab, " srcname=");
|
||||
+ audit_log_untrustedstring(ab, aad(sa)->mnt.src_name);
|
||||
+ }
|
||||
+ if (aad(sa)->mnt.trans) {
|
||||
+ audit_log_format(ab, " trans=");
|
||||
+ audit_log_untrustedstring(ab, aad(sa)->mnt.trans);
|
||||
+ }
|
||||
+ if (aad(sa)->mnt.flags) {
|
||||
+ audit_log_format(ab, " flags=\"");
|
||||
+ audit_mnt_flags(ab, aad(sa)->mnt.flags);
|
||||
+ audit_log_format(ab, "\"");
|
||||
+ }
|
||||
+ if (aad(sa)->mnt.data) {
|
||||
+ audit_log_format(ab, " options=");
|
||||
+ audit_log_untrustedstring(ab, aad(sa)->mnt.data);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * audit_mount - handle the auditing of mount operations
|
||||
+ * @profile: the profile being enforced (NOT NULL)
|
||||
+ * @gfp: allocation flags
|
||||
+ * @op: operation being mediated (NOT NULL)
|
||||
+ * @name: name of object being mediated (MAYBE NULL)
|
||||
+ * @src_name: src_name of object being mediated (MAYBE_NULL)
|
||||
+ * @type: type of filesystem (MAYBE_NULL)
|
||||
+ * @trans: name of trans (MAYBE NULL)
|
||||
+ * @flags: filesystem idependent mount flags
|
||||
+ * @data: filesystem mount flags
|
||||
+ * @request: permissions requested
|
||||
+ * @perms: the permissions computed for the request (NOT NULL)
|
||||
+ * @info: extra information message (MAYBE NULL)
|
||||
+ * @error: 0 if operation allowed else failure error code
|
||||
+ *
|
||||
+ * Returns: %0 or error on failure
|
||||
+ */
|
||||
+static int audit_mount(struct aa_profile *profile, gfp_t gfp, const char *op,
|
||||
+ const char *name, const char *src_name,
|
||||
+ const char *type, const char *trans,
|
||||
+ unsigned long flags, const void *data, u32 request,
|
||||
+ struct file_perms *perms, const char *info, int error)
|
||||
+{
|
||||
+ int audit_type = AUDIT_APPARMOR_AUTO;
|
||||
+ DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op);
|
||||
+
|
||||
+ if (likely(!error)) {
|
||||
+ u32 mask = perms->audit;
|
||||
+
|
||||
+ if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL))
|
||||
+ mask = 0xffff;
|
||||
+
|
||||
+ /* mask off perms that are not being force audited */
|
||||
+ request &= mask;
|
||||
+
|
||||
+ if (likely(!request))
|
||||
+ return 0;
|
||||
+ audit_type = AUDIT_APPARMOR_AUDIT;
|
||||
+ } else {
|
||||
+ /* only report permissions that were denied */
|
||||
+ request = request & ~perms->allow;
|
||||
+
|
||||
+ if (request & perms->kill)
|
||||
+ audit_type = AUDIT_APPARMOR_KILL;
|
||||
+
|
||||
+ /* quiet known rejects, assumes quiet and kill do not overlap */
|
||||
+ if ((request & perms->quiet) &&
|
||||
+ AUDIT_MODE(profile) != AUDIT_NOQUIET &&
|
||||
+ AUDIT_MODE(profile) != AUDIT_ALL)
|
||||
+ request &= ~perms->quiet;
|
||||
+
|
||||
+ if (!request)
|
||||
+ return COMPLAIN_MODE(profile) ?
|
||||
+ complain_error(error) : error;
|
||||
+ }
|
||||
+
|
||||
+ aad(&sa)->name = name;
|
||||
+ aad(&sa)->mnt.src_name = src_name;
|
||||
+ aad(&sa)->mnt.type = type;
|
||||
+ aad(&sa)->mnt.trans = trans;
|
||||
+ aad(&sa)->mnt.flags = flags;
|
||||
+ if (data && (perms->audit & AA_AUDIT_DATA))
|
||||
+ aad(&sa)->mnt.data = data;
|
||||
+ aad(&sa)->info = info;
|
||||
+ aad(&sa)->error = error;
|
||||
+
|
||||
+ return aa_audit(audit_type, profile, &sa, audit_cb);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * match_mnt_flags - Do an ordered match on mount flags
|
||||
+ * @dfa: dfa to match against
|
||||
+ * @state: state to start in
|
||||
+ * @flags: mount flags to match against
|
||||
+ *
|
||||
+ * Mount flags are encoded as an ordered match. This is done instead of
|
||||
+ * checking against a simple bitmask, to allow for logical operations
|
||||
+ * on the flags.
|
||||
+ *
|
||||
+ * Returns: next state after flags match
|
||||
+ */
|
||||
+static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state,
|
||||
+ unsigned long flags)
|
||||
+{
|
||||
+ unsigned int i;
|
||||
+
|
||||
+ for (i = 0; i <= 31 ; ++i) {
|
||||
+ if ((1 << i) & flags)
|
||||
+ state = aa_dfa_next(dfa, state, i + 1);
|
||||
+ }
|
||||
+
|
||||
+ return state;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * compute_mnt_perms - compute mount permission associated with @state
|
||||
+ * @dfa: dfa to match against (NOT NULL)
|
||||
+ * @state: state match finished in
|
||||
+ *
|
||||
+ * Returns: mount permissions
|
||||
+ */
|
||||
+static struct file_perms compute_mnt_perms(struct aa_dfa *dfa,
|
||||
+ unsigned int state)
|
||||
+{
|
||||
+ struct file_perms perms;
|
||||
+
|
||||
+ perms.kill = 0;
|
||||
+ perms.allow = dfa_user_allow(dfa, state);
|
||||
+ perms.audit = dfa_user_audit(dfa, state);
|
||||
+ perms.quiet = dfa_user_quiet(dfa, state);
|
||||
+ perms.xindex = dfa_user_xindex(dfa, state);
|
||||
+
|
||||
+ return perms;
|
||||
+}
|
||||
+
|
||||
+static const char *mnt_info_table[] = {
|
||||
+ "match succeeded",
|
||||
+ "failed mntpnt match",
|
||||
+ "failed srcname match",
|
||||
+ "failed type match",
|
||||
+ "failed flags match",
|
||||
+ "failed data match"
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * Returns 0 on success else element that match failed in, this is the
|
||||
+ * index into the mnt_info_table above
|
||||
+ */
|
||||
+static int do_match_mnt(struct aa_dfa *dfa, unsigned int start,
|
||||
+ const char *mntpnt, const char *devname,
|
||||
+ const char *type, unsigned long flags,
|
||||
+ void *data, bool binary, struct file_perms *perms)
|
||||
+{
|
||||
+ unsigned int state;
|
||||
+
|
||||
+ state = aa_dfa_match(dfa, start, mntpnt);
|
||||
+ state = aa_dfa_null_transition(dfa, state);
|
||||
+ if (!state)
|
||||
+ return 1;
|
||||
+
|
||||
+ if (devname)
|
||||
+ state = aa_dfa_match(dfa, state, devname);
|
||||
+ state = aa_dfa_null_transition(dfa, state);
|
||||
+ if (!state)
|
||||
+ return 2;
|
||||
+
|
||||
+ if (type)
|
||||
+ state = aa_dfa_match(dfa, state, type);
|
||||
+ state = aa_dfa_null_transition(dfa, state);
|
||||
+ if (!state)
|
||||
+ return 3;
|
||||
+
|
||||
+ state = match_mnt_flags(dfa, state, flags);
|
||||
+ if (!state)
|
||||
+ return 4;
|
||||
+ *perms = compute_mnt_perms(dfa, state);
|
||||
+ if (perms->allow & AA_MAY_MOUNT)
|
||||
+ return 0;
|
||||
+
|
||||
+ /* only match data if not binary and the DFA flags data is expected */
|
||||
+ if (data && !binary && (perms->allow & AA_CONT_MATCH)) {
|
||||
+ state = aa_dfa_null_transition(dfa, state);
|
||||
+ if (!state)
|
||||
+ return 4;
|
||||
+
|
||||
+ state = aa_dfa_match(dfa, state, data);
|
||||
+ if (!state)
|
||||
+ return 5;
|
||||
+ *perms = compute_mnt_perms(dfa, state);
|
||||
+ if (perms->allow & AA_MAY_MOUNT)
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ /* failed at end of flags match */
|
||||
+ return 4;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * match_mnt - handle path matching for mount
|
||||
+ * @profile: the confining profile
|
||||
+ * @mntpnt: string for the mntpnt (NOT NULL)
|
||||
+ * @devname: string for the devname/src_name (MAYBE NULL)
|
||||
+ * @type: string for the dev type (MAYBE NULL)
|
||||
+ * @flags: mount flags to match
|
||||
+ * @data: fs mount data (MAYBE NULL)
|
||||
+ * @binary: whether @data is binary
|
||||
+ * @perms: Returns: permission found by the match
|
||||
+ * @info: Returns: infomation string about the match for logging
|
||||
+ *
|
||||
+ * Returns: 0 on success else error
|
||||
+ */
|
||||
+static int match_mnt(struct aa_profile *profile, const char *mntpnt,
|
||||
+ const char *devname, const char *type,
|
||||
+ unsigned long flags, void *data, bool binary,
|
||||
+ struct file_perms *perms, const char **info)
|
||||
+{
|
||||
+ int pos;
|
||||
+
|
||||
+ if (!profile->policy.dfa)
|
||||
+ return -EACCES;
|
||||
+
|
||||
+ pos = do_match_mnt(profile->policy.dfa,
|
||||
+ profile->policy.start[AA_CLASS_MOUNT],
|
||||
+ mntpnt, devname, type, flags, data, binary, perms);
|
||||
+ if (pos) {
|
||||
+ *info = mnt_info_table[pos];
|
||||
+ return -EACCES;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int path_flags(struct aa_profile *profile, const struct path *path)
|
||||
+{
|
||||
+ return profile->path_flags |
|
||||
+ S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0;
|
||||
+}
|
||||
+
|
||||
+int aa_remount(struct aa_profile *profile, const struct path *path,
|
||||
+ unsigned long flags, void *data)
|
||||
+{
|
||||
+ struct file_perms perms = { };
|
||||
+ const char *name, *info = NULL;
|
||||
+ char *buffer = NULL;
|
||||
+ int binary, error;
|
||||
+
|
||||
+ binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA;
|
||||
+
|
||||
+ error = aa_path_name(path, path_flags(profile, path), &buffer, &name,
|
||||
+ &info);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = match_mnt(profile, name, NULL, NULL, flags, data, binary,
|
||||
+ &perms, &info);
|
||||
+
|
||||
+audit:
|
||||
+ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL,
|
||||
+ NULL, flags, data, AA_MAY_MOUNT, &perms, info,
|
||||
+ error);
|
||||
+ kfree(buffer);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+int aa_bind_mount(struct aa_profile *profile, const struct path *path,
|
||||
+ const char *dev_name, unsigned long flags)
|
||||
+{
|
||||
+ struct file_perms perms = { };
|
||||
+ char *buffer = NULL, *old_buffer = NULL;
|
||||
+ const char *name, *old_name = NULL, *info = NULL;
|
||||
+ struct path old_path;
|
||||
+ int error;
|
||||
+
|
||||
+ if (!dev_name || !*dev_name)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ flags &= MS_REC | MS_BIND;
|
||||
+
|
||||
+ error = aa_path_name(path, path_flags(profile, path), &buffer, &name,
|
||||
+ &info);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = aa_path_name(&old_path, path_flags(profile, &old_path),
|
||||
+ &old_buffer, &old_name, &info);
|
||||
+ path_put(&old_path);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0,
|
||||
+ &perms, &info);
|
||||
+
|
||||
+audit:
|
||||
+ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name,
|
||||
+ NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms,
|
||||
+ info, error);
|
||||
+ kfree(buffer);
|
||||
+ kfree(old_buffer);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+int aa_mount_change_type(struct aa_profile *profile, const struct path *path,
|
||||
+ unsigned long flags)
|
||||
+{
|
||||
+ struct file_perms perms = { };
|
||||
+ char *buffer = NULL;
|
||||
+ const char *name, *info = NULL;
|
||||
+ int error;
|
||||
+
|
||||
+ /* These are the flags allowed by do_change_type() */
|
||||
+ flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE |
|
||||
+ MS_UNBINDABLE);
|
||||
+
|
||||
+ error = aa_path_name(path, path_flags(profile, path), &buffer, &name,
|
||||
+ &info);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms,
|
||||
+ &info);
|
||||
+
|
||||
+audit:
|
||||
+ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL,
|
||||
+ NULL, flags, NULL, AA_MAY_MOUNT, &perms, info,
|
||||
+ error);
|
||||
+ kfree(buffer);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+int aa_move_mount(struct aa_profile *profile, const struct path *path,
|
||||
+ const char *orig_name)
|
||||
+{
|
||||
+ struct file_perms perms = { };
|
||||
+ char *buffer = NULL, *old_buffer = NULL;
|
||||
+ const char *name, *old_name = NULL, *info = NULL;
|
||||
+ struct path old_path;
|
||||
+ int error;
|
||||
+
|
||||
+ if (!orig_name || !*orig_name)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ error = aa_path_name(path, path_flags(profile, path), &buffer, &name,
|
||||
+ &info);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = aa_path_name(&old_path, path_flags(profile, &old_path),
|
||||
+ &old_buffer, &old_name, &info);
|
||||
+ path_put(&old_path);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0,
|
||||
+ &perms, &info);
|
||||
+
|
||||
+audit:
|
||||
+ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name,
|
||||
+ NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms,
|
||||
+ info, error);
|
||||
+ kfree(buffer);
|
||||
+ kfree(old_buffer);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name,
|
||||
+ const struct path *path, const char *type, unsigned long flags,
|
||||
+ void *data)
|
||||
+{
|
||||
+ struct file_perms perms = { };
|
||||
+ char *buffer = NULL, *dev_buffer = NULL;
|
||||
+ const char *name = NULL, *dev_name = NULL, *info = NULL;
|
||||
+ int binary = 1;
|
||||
+ int error;
|
||||
+
|
||||
+ dev_name = orig_dev_name;
|
||||
+ if (type) {
|
||||
+ int requires_dev;
|
||||
+ struct file_system_type *fstype = get_fs_type(type);
|
||||
+ if (!fstype)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ binary = fstype->fs_flags & FS_BINARY_MOUNTDATA;
|
||||
+ requires_dev = fstype->fs_flags & FS_REQUIRES_DEV;
|
||||
+ put_filesystem(fstype);
|
||||
+
|
||||
+ if (requires_dev) {
|
||||
+ struct path dev_path;
|
||||
+
|
||||
+ if (!dev_name || !*dev_name) {
|
||||
+ error = -ENOENT;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = aa_path_name(&dev_path,
|
||||
+ path_flags(profile, &dev_path),
|
||||
+ &dev_buffer, &dev_name, &info);
|
||||
+ path_put(&dev_path);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ error = aa_path_name(path, path_flags(profile, path), &buffer, &name,
|
||||
+ &info);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = match_mnt(profile, name, dev_name, type, flags, data, binary,
|
||||
+ &perms, &info);
|
||||
+
|
||||
+audit:
|
||||
+ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name,
|
||||
+ type, NULL, flags, data, AA_MAY_MOUNT, &perms, info,
|
||||
+ error);
|
||||
+ kfree(buffer);
|
||||
+ kfree(dev_buffer);
|
||||
+
|
||||
+out:
|
||||
+ return error;
|
||||
+
|
||||
+}
|
||||
+
|
||||
+int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags)
|
||||
+{
|
||||
+ struct file_perms perms = { };
|
||||
+ char *buffer = NULL;
|
||||
+ const char *name, *info = NULL;
|
||||
+ int error;
|
||||
+
|
||||
+ struct path path = { mnt, mnt->mnt_root };
|
||||
+ error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name,
|
||||
+ &info);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ if (!error && profile->policy.dfa) {
|
||||
+ unsigned int state;
|
||||
+ state = aa_dfa_match(profile->policy.dfa,
|
||||
+ profile->policy.start[AA_CLASS_MOUNT],
|
||||
+ name);
|
||||
+ perms = compute_mnt_perms(profile->policy.dfa, state);
|
||||
+ }
|
||||
+
|
||||
+ if (AA_MAY_UMOUNT & ~perms.allow)
|
||||
+ error = -EACCES;
|
||||
+
|
||||
+audit:
|
||||
+ error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL,
|
||||
+ NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error);
|
||||
+ kfree(buffer);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+int aa_pivotroot(struct aa_profile *profile, const struct path *old_path,
|
||||
+ const struct path *new_path)
|
||||
+{
|
||||
+ struct file_perms perms = { };
|
||||
+ struct aa_profile *target = NULL;
|
||||
+ char *old_buffer = NULL, *new_buffer = NULL;
|
||||
+ const char *old_name, *new_name = NULL, *info = NULL;
|
||||
+ int error;
|
||||
+
|
||||
+ error = aa_path_name(old_path, path_flags(profile, old_path),
|
||||
+ &old_buffer, &old_name, &info);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = aa_path_name(new_path, path_flags(profile, new_path),
|
||||
+ &new_buffer, &new_name, &info);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ if (profile->policy.dfa) {
|
||||
+ unsigned int state;
|
||||
+ state = aa_dfa_match(profile->policy.dfa,
|
||||
+ profile->policy.start[AA_CLASS_MOUNT],
|
||||
+ new_name);
|
||||
+ state = aa_dfa_null_transition(profile->policy.dfa, state);
|
||||
+ state = aa_dfa_match(profile->policy.dfa, state, old_name);
|
||||
+ perms = compute_mnt_perms(profile->policy.dfa, state);
|
||||
+ }
|
||||
+
|
||||
+ if (AA_MAY_PIVOTROOT & perms.allow) {
|
||||
+ if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) {
|
||||
+ target = x_table_lookup(profile, perms.xindex);
|
||||
+ if (!target)
|
||||
+ error = -ENOENT;
|
||||
+ else
|
||||
+ error = aa_replace_current_profile(target);
|
||||
+ }
|
||||
+ } else
|
||||
+ error = -EACCES;
|
||||
+
|
||||
+audit:
|
||||
+ error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name,
|
||||
+ old_name, NULL, target ? target->base.name : NULL,
|
||||
+ 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error);
|
||||
+ aa_put_profile(target);
|
||||
+ kfree(old_buffer);
|
||||
+ kfree(new_buffer);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
--
|
||||
2.11.0
|
||||
|
@@ -1,605 +0,0 @@
|
||||
From adbeb027cbafd78a76d5786e082d7c7abb19a591 Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Mon, 4 Oct 2010 15:03:36 -0700
|
||||
Subject: [PATCH 1/3] UBUNTU: SAUCE: AppArmor: basic networking rules
|
||||
|
||||
Base support for network mediation.
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
---
|
||||
security/apparmor/.gitignore | 1 +
|
||||
security/apparmor/Makefile | 42 ++++++++++-
|
||||
security/apparmor/apparmorfs.c | 1 +
|
||||
security/apparmor/include/audit.h | 4 +
|
||||
security/apparmor/include/net.h | 59 +++++++++++++++
|
||||
security/apparmor/include/policy.h | 3 +
|
||||
security/apparmor/lsm.c | 112 ++++++++++++++++++++++++++++
|
||||
security/apparmor/net.c | 148 +++++++++++++++++++++++++++++++++++++
|
||||
security/apparmor/policy.c | 1 +
|
||||
security/apparmor/policy_unpack.c | 47 +++++++++++-
|
||||
10 files changed, 415 insertions(+), 3 deletions(-)
|
||||
create mode 100644 security/apparmor/include/net.h
|
||||
create mode 100644 security/apparmor/net.c
|
||||
|
||||
diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore
|
||||
index 9cdec70d72b8..d5b291e94264 100644
|
||||
--- a/security/apparmor/.gitignore
|
||||
+++ b/security/apparmor/.gitignore
|
||||
@@ -1,5 +1,6 @@
|
||||
#
|
||||
# Generated include files
|
||||
#
|
||||
+net_names.h
|
||||
capability_names.h
|
||||
rlim_names.h
|
||||
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
|
||||
index ad369a7aac24..a7dc10be232d 100644
|
||||
--- a/security/apparmor/Makefile
|
||||
+++ b/security/apparmor/Makefile
|
||||
@@ -4,10 +4,10 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
|
||||
|
||||
apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
|
||||
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
|
||||
- resource.o secid.o file.o policy_ns.o
|
||||
+ resource.o secid.o file.o policy_ns.o net.o
|
||||
apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o
|
||||
|
||||
-clean-files := capability_names.h rlim_names.h
|
||||
+clean-files := capability_names.h rlim_names.h net_names.h
|
||||
|
||||
|
||||
# Build a lower case string table of capability names
|
||||
@@ -25,6 +25,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\
|
||||
-e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/\L\1/p' | \
|
||||
tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@
|
||||
|
||||
+# Build a lower case string table of address family names
|
||||
+# Transform lines from
|
||||
+# define AF_LOCAL 1 /* POSIX name for AF_UNIX */
|
||||
+# #define AF_INET 2 /* Internet IP Protocol */
|
||||
+# to
|
||||
+# [1] = "local",
|
||||
+# [2] = "inet",
|
||||
+#
|
||||
+# and build the securityfs entries for the mapping.
|
||||
+# Transforms lines from
|
||||
+# #define AF_INET 2 /* Internet IP Protocol */
|
||||
+# to
|
||||
+# #define AA_FS_AF_MASK "local inet"
|
||||
+quiet_cmd_make-af = GEN $@
|
||||
+cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\
|
||||
+ sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \
|
||||
+ 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\
|
||||
+ echo "};" >> $@ ;\
|
||||
+ echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\
|
||||
+ sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\
|
||||
+ $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@
|
||||
+
|
||||
+# Build a lower case string table of sock type names
|
||||
+# Transform lines from
|
||||
+# SOCK_STREAM = 1,
|
||||
+# to
|
||||
+# [1] = "stream",
|
||||
+quiet_cmd_make-sock = GEN $@
|
||||
+cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\
|
||||
+ sed $^ >>$@ -r -n \
|
||||
+ -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\
|
||||
+ echo "};" >> $@
|
||||
|
||||
# Build a lower case string table of rlimit names.
|
||||
# Transforms lines from
|
||||
@@ -61,6 +93,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \
|
||||
tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@
|
||||
|
||||
$(obj)/capability.o : $(obj)/capability_names.h
|
||||
+$(obj)/net.o : $(obj)/net_names.h
|
||||
$(obj)/resource.o : $(obj)/rlim_names.h
|
||||
$(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \
|
||||
$(src)/Makefile
|
||||
@@ -68,3 +101,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \
|
||||
$(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \
|
||||
$(src)/Makefile
|
||||
$(call cmd,make-rlim)
|
||||
+$(obj)/net_names.h : $(srctree)/include/linux/socket.h \
|
||||
+ $(srctree)/include/linux/net.h \
|
||||
+ $(src)/Makefile
|
||||
+ $(call cmd,make-af)
|
||||
+ $(call cmd,make-sock)
|
||||
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
|
||||
index 4f6ac9dbc65d..4b121211e5e7 100644
|
||||
--- a/security/apparmor/apparmorfs.c
|
||||
+++ b/security/apparmor/apparmorfs.c
|
||||
@@ -1209,6 +1209,7 @@ static struct aa_fs_entry aa_fs_entry_features[] = {
|
||||
AA_FS_DIR("policy", aa_fs_entry_policy),
|
||||
AA_FS_DIR("domain", aa_fs_entry_domain),
|
||||
AA_FS_DIR("file", aa_fs_entry_file),
|
||||
+ AA_FS_DIR("network", aa_fs_entry_network),
|
||||
AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK),
|
||||
AA_FS_DIR("rlimit", aa_fs_entry_rlimit),
|
||||
AA_FS_DIR("caps", aa_fs_entry_caps),
|
||||
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
|
||||
index fdc4774318ba..0df708e8748b 100644
|
||||
--- a/security/apparmor/include/audit.h
|
||||
+++ b/security/apparmor/include/audit.h
|
||||
@@ -127,6 +127,10 @@ struct apparmor_audit_data {
|
||||
int rlim;
|
||||
unsigned long max;
|
||||
} rlim;
|
||||
+ struct {
|
||||
+ int type, protocol;
|
||||
+ struct sock *sk;
|
||||
+ } net;
|
||||
};
|
||||
};
|
||||
|
||||
diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h
|
||||
new file mode 100644
|
||||
index 000000000000..55da1dad8720
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/include/net.h
|
||||
@@ -0,0 +1,59 @@
|
||||
+/*
|
||||
+ * AppArmor security module
|
||||
+ *
|
||||
+ * This file contains AppArmor network mediation definitions.
|
||||
+ *
|
||||
+ * Copyright (C) 1998-2008 Novell/SUSE
|
||||
+ * Copyright 2009-2012 Canonical Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License as
|
||||
+ * published by the Free Software Foundation, version 2 of the
|
||||
+ * License.
|
||||
+ */
|
||||
+
|
||||
+#ifndef __AA_NET_H
|
||||
+#define __AA_NET_H
|
||||
+
|
||||
+#include <net/sock.h>
|
||||
+
|
||||
+#include "apparmorfs.h"
|
||||
+
|
||||
+/* struct aa_net - network confinement data
|
||||
+ * @allowed: basic network families permissions
|
||||
+ * @audit_network: which network permissions to force audit
|
||||
+ * @quiet_network: which network permissions to quiet rejects
|
||||
+ */
|
||||
+struct aa_net {
|
||||
+ u16 allow[AF_MAX];
|
||||
+ u16 audit[AF_MAX];
|
||||
+ u16 quiet[AF_MAX];
|
||||
+};
|
||||
+
|
||||
+extern struct aa_fs_entry aa_fs_entry_network[];
|
||||
+
|
||||
+#define DEFINE_AUDIT_NET(NAME, OP, SK, F, T, P) \
|
||||
+ struct lsm_network_audit NAME ## _net = { .sk = (SK), \
|
||||
+ .family = (F)}; \
|
||||
+ DEFINE_AUDIT_DATA(NAME, \
|
||||
+ ((SK) && (F) != AF_UNIX) ? LSM_AUDIT_DATA_NET : \
|
||||
+ LSM_AUDIT_DATA_NONE, \
|
||||
+ OP); \
|
||||
+ NAME.u.net = &(NAME ## _net); \
|
||||
+ aad(&NAME)->net.type = (T); \
|
||||
+ aad(&NAME)->net.protocol = (P)
|
||||
+
|
||||
+#define DEFINE_AUDIT_SK(NAME, OP, SK) \
|
||||
+ DEFINE_AUDIT_NET(NAME, OP, SK, (SK)->sk_family, (SK)->sk_type, \
|
||||
+ (SK)->sk_protocol)
|
||||
+
|
||||
+extern int aa_net_perm(const char *op, struct aa_profile *profile, u16 family,
|
||||
+ int type, int protocol, struct sock *sk);
|
||||
+extern int aa_revalidate_sk(const char *op, struct sock *sk);
|
||||
+
|
||||
+static inline void aa_free_net_rules(struct aa_net *new)
|
||||
+{
|
||||
+ /* NOP */
|
||||
+}
|
||||
+
|
||||
+#endif /* __AA_NET_H */
|
||||
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
|
||||
index 67bc96afe541..a3d18ea8d730 100644
|
||||
--- a/security/apparmor/include/policy.h
|
||||
+++ b/security/apparmor/include/policy.h
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "capability.h"
|
||||
#include "domain.h"
|
||||
#include "file.h"
|
||||
+#include "net.h"
|
||||
#include "lib.h"
|
||||
#include "resource.h"
|
||||
|
||||
@@ -132,6 +133,7 @@ struct aa_data {
|
||||
* @policy: general match rules governing policy
|
||||
* @file: The set of rules governing basic file access and domain transitions
|
||||
* @caps: capabilities for the profile
|
||||
+ * @net: network controls for the profile
|
||||
* @rlimits: rlimits for the profile
|
||||
*
|
||||
* @dents: dentries for the profiles file entries in apparmorfs
|
||||
@@ -174,6 +176,7 @@ struct aa_profile {
|
||||
struct aa_policydb policy;
|
||||
struct aa_file_rules file;
|
||||
struct aa_caps caps;
|
||||
+ struct aa_net net;
|
||||
struct aa_rlimit rlimits;
|
||||
|
||||
struct aa_loaddata *rawdata;
|
||||
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
|
||||
index 8f3c0f7aca5a..758ddf4a0791 100644
|
||||
--- a/security/apparmor/lsm.c
|
||||
+++ b/security/apparmor/lsm.c
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "include/context.h"
|
||||
#include "include/file.h"
|
||||
#include "include/ipc.h"
|
||||
+#include "include/net.h"
|
||||
#include "include/path.h"
|
||||
#include "include/policy.h"
|
||||
#include "include/policy_ns.h"
|
||||
@@ -587,6 +588,104 @@ static int apparmor_task_setrlimit(struct task_struct *task,
|
||||
return error;
|
||||
}
|
||||
|
||||
+static int apparmor_socket_create(int family, int type, int protocol, int kern)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ int error = 0;
|
||||
+
|
||||
+ if (kern)
|
||||
+ return 0;
|
||||
+
|
||||
+ profile = __aa_current_profile();
|
||||
+ if (!unconfined(profile))
|
||||
+ error = aa_net_perm(OP_CREATE, profile, family, type, protocol,
|
||||
+ NULL);
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_bind(struct socket *sock,
|
||||
+ struct sockaddr *address, int addrlen)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_BIND, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_connect(struct socket *sock,
|
||||
+ struct sockaddr *address, int addrlen)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_CONNECT, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_listen(struct socket *sock, int backlog)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_LISTEN, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_accept(struct socket *sock, struct socket *newsock)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_ACCEPT, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_sendmsg(struct socket *sock,
|
||||
+ struct msghdr *msg, int size)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_SENDMSG, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_recvmsg(struct socket *sock,
|
||||
+ struct msghdr *msg, int size, int flags)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_RECVMSG, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_getsockname(struct socket *sock)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_GETSOCKNAME, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_getpeername(struct socket *sock)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_GETPEERNAME, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_getsockopt(struct socket *sock, int level,
|
||||
+ int optname)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_GETSOCKOPT, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_setsockopt(struct socket *sock, int level,
|
||||
+ int optname)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_SETSOCKOPT, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_shutdown(struct socket *sock, int how)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_SHUTDOWN, sk);
|
||||
+}
|
||||
+
|
||||
static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check),
|
||||
LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme),
|
||||
@@ -616,6 +715,19 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(getprocattr, apparmor_getprocattr),
|
||||
LSM_HOOK_INIT(setprocattr, apparmor_setprocattr),
|
||||
|
||||
+ LSM_HOOK_INIT(socket_create, apparmor_socket_create),
|
||||
+ LSM_HOOK_INIT(socket_bind, apparmor_socket_bind),
|
||||
+ LSM_HOOK_INIT(socket_connect, apparmor_socket_connect),
|
||||
+ LSM_HOOK_INIT(socket_listen, apparmor_socket_listen),
|
||||
+ LSM_HOOK_INIT(socket_accept, apparmor_socket_accept),
|
||||
+ LSM_HOOK_INIT(socket_sendmsg, apparmor_socket_sendmsg),
|
||||
+ LSM_HOOK_INIT(socket_recvmsg, apparmor_socket_recvmsg),
|
||||
+ LSM_HOOK_INIT(socket_getsockname, apparmor_socket_getsockname),
|
||||
+ LSM_HOOK_INIT(socket_getpeername, apparmor_socket_getpeername),
|
||||
+ LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt),
|
||||
+ LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt),
|
||||
+ LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown),
|
||||
+
|
||||
LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank),
|
||||
LSM_HOOK_INIT(cred_free, apparmor_cred_free),
|
||||
LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare),
|
||||
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
|
||||
new file mode 100644
|
||||
index 000000000000..b9c8cd0e882e
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/net.c
|
||||
@@ -0,0 +1,148 @@
|
||||
+/*
|
||||
+ * AppArmor security module
|
||||
+ *
|
||||
+ * This file contains AppArmor network mediation
|
||||
+ *
|
||||
+ * Copyright (C) 1998-2008 Novell/SUSE
|
||||
+ * Copyright 2009-2012 Canonical Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License as
|
||||
+ * published by the Free Software Foundation, version 2 of the
|
||||
+ * License.
|
||||
+ */
|
||||
+
|
||||
+#include "include/apparmor.h"
|
||||
+#include "include/audit.h"
|
||||
+#include "include/context.h"
|
||||
+#include "include/net.h"
|
||||
+#include "include/policy.h"
|
||||
+
|
||||
+#include "net_names.h"
|
||||
+
|
||||
+struct aa_fs_entry aa_fs_entry_network[] = {
|
||||
+ AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK),
|
||||
+ { }
|
||||
+};
|
||||
+
|
||||
+/* audit callback for net specific fields */
|
||||
+static void audit_cb(struct audit_buffer *ab, void *va)
|
||||
+{
|
||||
+ struct common_audit_data *sa = va;
|
||||
+
|
||||
+ audit_log_format(ab, " family=");
|
||||
+ if (address_family_names[sa->u.net->family]) {
|
||||
+ audit_log_string(ab, address_family_names[sa->u.net->family]);
|
||||
+ } else {
|
||||
+ audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family);
|
||||
+ }
|
||||
+ audit_log_format(ab, " sock_type=");
|
||||
+ if (sock_type_names[aad(sa)->net.type]) {
|
||||
+ audit_log_string(ab, sock_type_names[aad(sa)->net.type]);
|
||||
+ } else {
|
||||
+ audit_log_format(ab, "\"unknown(%d)\"", aad(sa)->net.type);
|
||||
+ }
|
||||
+ audit_log_format(ab, " protocol=%d", aad(sa)->net.protocol);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * audit_net - audit network access
|
||||
+ * @profile: profile being enforced (NOT NULL)
|
||||
+ * @op: operation being checked
|
||||
+ * @family: network family
|
||||
+ * @type: network type
|
||||
+ * @protocol: network protocol
|
||||
+ * @sk: socket auditing is being applied to
|
||||
+ * @error: error code for failure else 0
|
||||
+ *
|
||||
+ * Returns: %0 or sa->error else other errorcode on failure
|
||||
+ */
|
||||
+static int audit_net(struct aa_profile *profile, const char *op, u16 family,
|
||||
+ int type, int protocol, struct sock *sk, int error)
|
||||
+{
|
||||
+ int audit_type = AUDIT_APPARMOR_AUTO;
|
||||
+ DEFINE_AUDIT_NET(sa, op, sk, family, type, protocol);
|
||||
+
|
||||
+ aad(&sa)->error = error;
|
||||
+
|
||||
+ if (likely(!aad(&sa)->error)) {
|
||||
+ u16 audit_mask = profile->net.audit[sa.u.net->family];
|
||||
+ if (likely((AUDIT_MODE(profile) != AUDIT_ALL) &&
|
||||
+ !(1 << aad(&sa)->net.type & audit_mask)))
|
||||
+ return 0;
|
||||
+ audit_type = AUDIT_APPARMOR_AUDIT;
|
||||
+ } else {
|
||||
+ u16 quiet_mask = profile->net.quiet[sa.u.net->family];
|
||||
+ u16 kill_mask = 0;
|
||||
+ u16 denied = (1 << aad(&sa)->net.type) & ~quiet_mask;
|
||||
+
|
||||
+ if (denied & kill_mask)
|
||||
+ audit_type = AUDIT_APPARMOR_KILL;
|
||||
+
|
||||
+ if ((denied & quiet_mask) &&
|
||||
+ AUDIT_MODE(profile) != AUDIT_NOQUIET &&
|
||||
+ AUDIT_MODE(profile) != AUDIT_ALL)
|
||||
+ return COMPLAIN_MODE(profile) ? 0 : aad(&sa)->error;
|
||||
+ }
|
||||
+
|
||||
+ return aa_audit(audit_type, profile, &sa, audit_cb);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * aa_net_perm - very course network access check
|
||||
+ * @op: operation being checked
|
||||
+ * @profile: profile being enforced (NOT NULL)
|
||||
+ * @family: network family
|
||||
+ * @type: network type
|
||||
+ * @protocol: network protocol
|
||||
+ *
|
||||
+ * Returns: %0 else error if permission denied
|
||||
+ */
|
||||
+int aa_net_perm(const char *op, struct aa_profile *profile, u16 family,
|
||||
+ int type, int protocol, struct sock *sk)
|
||||
+{
|
||||
+ u16 family_mask;
|
||||
+ int error;
|
||||
+
|
||||
+ if ((family < 0) || (family >= AF_MAX))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if ((type < 0) || (type >= SOCK_MAX))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /* unix domain and netlink sockets are handled by ipc */
|
||||
+ if (family == AF_UNIX || family == AF_NETLINK)
|
||||
+ return 0;
|
||||
+
|
||||
+ family_mask = profile->net.allow[family];
|
||||
+
|
||||
+ error = (family_mask & (1 << type)) ? 0 : -EACCES;
|
||||
+
|
||||
+ return audit_net(profile, op, family, type, protocol, sk, error);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * aa_revalidate_sk - Revalidate access to a sock
|
||||
+ * @op: operation being checked
|
||||
+ * @sk: sock being revalidated (NOT NULL)
|
||||
+ *
|
||||
+ * Returns: %0 else error if permission denied
|
||||
+ */
|
||||
+int aa_revalidate_sk(const char *op, struct sock *sk)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ int error = 0;
|
||||
+
|
||||
+ /* aa_revalidate_sk should not be called from interrupt context
|
||||
+ * don't mediate these calls as they are not task related
|
||||
+ */
|
||||
+ if (in_interrupt())
|
||||
+ return 0;
|
||||
+
|
||||
+ profile = __aa_current_profile();
|
||||
+ if (!unconfined(profile))
|
||||
+ error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type,
|
||||
+ sk->sk_protocol, sk);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
|
||||
index cf9d670dca94..0eea92aeb02d 100644
|
||||
--- a/security/apparmor/policy.c
|
||||
+++ b/security/apparmor/policy.c
|
||||
@@ -237,6 +237,7 @@ void aa_free_profile(struct aa_profile *profile)
|
||||
|
||||
aa_free_file_rules(&profile->file);
|
||||
aa_free_cap_rules(&profile->caps);
|
||||
+ aa_free_net_rules(&profile->net);
|
||||
aa_free_rlimit_rules(&profile->rlimits);
|
||||
|
||||
kzfree(profile->dirname);
|
||||
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
|
||||
index f3422a91353c..89a1bd78f765 100644
|
||||
--- a/security/apparmor/policy_unpack.c
|
||||
+++ b/security/apparmor/policy_unpack.c
|
||||
@@ -217,6 +217,19 @@ static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name)
|
||||
+{
|
||||
+ if (unpack_nameX(e, AA_U16, name)) {
|
||||
+ if (!inbounds(e, sizeof(u16)))
|
||||
+ return 0;
|
||||
+ if (data)
|
||||
+ *data = le16_to_cpu(get_unaligned((u16 *) e->pos));
|
||||
+ e->pos += sizeof(u16);
|
||||
+ return 1;
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name)
|
||||
{
|
||||
if (unpack_nameX(e, AA_U32, name)) {
|
||||
@@ -519,7 +532,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
||||
{
|
||||
struct aa_profile *profile = NULL;
|
||||
const char *tmpname, *tmpns = NULL, *name = NULL;
|
||||
- size_t ns_len;
|
||||
+ size_t ns_len, size = 0;
|
||||
struct rhashtable_params params = { 0 };
|
||||
char *key = NULL;
|
||||
struct aa_data *data;
|
||||
@@ -635,6 +648,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
||||
if (!unpack_rlimits(e, profile))
|
||||
goto fail;
|
||||
|
||||
+ size = unpack_array(e, "net_allowed_af");
|
||||
+ if (size) {
|
||||
+
|
||||
+ for (i = 0; i < size; i++) {
|
||||
+ /* discard extraneous rules that this kernel will
|
||||
+ * never request
|
||||
+ */
|
||||
+ if (i >= AF_MAX) {
|
||||
+ u16 tmp;
|
||||
+ if (!unpack_u16(e, &tmp, NULL) ||
|
||||
+ !unpack_u16(e, &tmp, NULL) ||
|
||||
+ !unpack_u16(e, &tmp, NULL))
|
||||
+ goto fail;
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (!unpack_u16(e, &profile->net.allow[i], NULL))
|
||||
+ goto fail;
|
||||
+ if (!unpack_u16(e, &profile->net.audit[i], NULL))
|
||||
+ goto fail;
|
||||
+ if (!unpack_u16(e, &profile->net.quiet[i], NULL))
|
||||
+ goto fail;
|
||||
+ }
|
||||
+ if (!unpack_nameX(e, AA_ARRAYEND, NULL))
|
||||
+ goto fail;
|
||||
+ }
|
||||
+ /*
|
||||
+ * allow unix domain and netlink sockets they are handled
|
||||
+ * by IPC
|
||||
+ */
|
||||
+ profile->net.allow[AF_UNIX] = 0xffff;
|
||||
+ profile->net.allow[AF_NETLINK] = 0xffff;
|
||||
+
|
||||
if (unpack_nameX(e, AA_STRUCT, "policydb")) {
|
||||
/* generic policy dfa - optional and may be NULL */
|
||||
profile->policy.dfa = unpack_dfa(e);
|
||||
--
|
||||
2.11.0
|
||||
|
@@ -1,38 +0,0 @@
|
||||
From 7ed04b256a6313a83a2d9c94f7295d81acf11848 Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Fri, 29 Jun 2012 17:34:00 -0700
|
||||
Subject: [PATCH 2/3] apparmor: Fix quieting of audit messages for network
|
||||
mediation
|
||||
|
||||
If a profile specified a quieting of network denials for a given rule by
|
||||
either the quiet or deny rule qualifiers, the resultant quiet mask for
|
||||
denied requests was applied incorrectly, resulting in two potential bugs.
|
||||
1. The misapplied quiet mask would prevent denials from being correctly
|
||||
tested against the kill mask/mode. Thus network access requests that
|
||||
should have resulted in the application being killed did not.
|
||||
|
||||
2. The actual quieting of the denied network request was not being applied.
|
||||
This would result in network rejections always being logged even when
|
||||
they had been specifically marked as quieted.
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
---
|
||||
security/apparmor/net.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
|
||||
index b9c8cd0e882e..5ba19ad1d65c 100644
|
||||
--- a/security/apparmor/net.c
|
||||
+++ b/security/apparmor/net.c
|
||||
@@ -74,7 +74,7 @@ static int audit_net(struct aa_profile *profile, const char *op, u16 family,
|
||||
} else {
|
||||
u16 quiet_mask = profile->net.quiet[sa.u.net->family];
|
||||
u16 kill_mask = 0;
|
||||
- u16 denied = (1 << aad(&sa)->net.type) & ~quiet_mask;
|
||||
+ u16 denied = (1 << aad(&sa)->net.type);
|
||||
|
||||
if (denied & kill_mask)
|
||||
audit_type = AUDIT_APPARMOR_KILL;
|
||||
--
|
||||
2.11.0
|
||||
|
@@ -1,938 +0,0 @@
|
||||
From 13765e11a34d38ce04b3c28f21fe94d420746a90 Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Wed, 16 May 2012 10:58:05 -0700
|
||||
Subject: [PATCH 3/3] UBUNTU: SAUCE: apparmor: Add the ability to mediate mount
|
||||
|
||||
Add the ability for apparmor to do mediation of mount operations. Mount
|
||||
rules require an updated apparmor_parser (2.8 series) for policy compilation.
|
||||
|
||||
The basic form of the rules are.
|
||||
|
||||
[audit] [deny] mount [conds]* [device] [ -> [conds] path],
|
||||
[audit] [deny] remount [conds]* [path],
|
||||
[audit] [deny] umount [conds]* [path],
|
||||
[audit] [deny] pivotroot [oldroot=<value>] <path>
|
||||
|
||||
remount is just a short cut for mount options=remount
|
||||
|
||||
where [conds] can be
|
||||
fstype=<expr>
|
||||
options=<expr>
|
||||
|
||||
Example mount commands
|
||||
mount, # allow all mounts, but not umount or pivotroot
|
||||
|
||||
mount fstype=procfs, # allow mounting procfs anywhere
|
||||
|
||||
mount options=(bind, ro) /foo -> /bar, # readonly bind mount
|
||||
|
||||
mount /dev/sda -> /mnt,
|
||||
|
||||
mount /dev/sd** -> /mnt/**,
|
||||
|
||||
mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/
|
||||
|
||||
umount,
|
||||
|
||||
umount /m*,
|
||||
|
||||
See the apparmor userspace for full documentation
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
Acked-by: Kees Cook <kees@ubuntu.com>
|
||||
---
|
||||
security/apparmor/Makefile | 2 +-
|
||||
security/apparmor/apparmorfs.c | 13 +
|
||||
security/apparmor/domain.c | 2 +-
|
||||
security/apparmor/include/apparmor.h | 3 +-
|
||||
security/apparmor/include/audit.h | 11 +
|
||||
security/apparmor/include/domain.h | 2 +
|
||||
security/apparmor/include/mount.h | 54 +++
|
||||
security/apparmor/lsm.c | 60 ++++
|
||||
security/apparmor/mount.c | 616 +++++++++++++++++++++++++++++++++++
|
||||
9 files changed, 760 insertions(+), 3 deletions(-)
|
||||
create mode 100644 security/apparmor/include/mount.h
|
||||
create mode 100644 security/apparmor/mount.c
|
||||
|
||||
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
|
||||
index a7dc10be232d..01368441f230 100644
|
||||
--- a/security/apparmor/Makefile
|
||||
+++ b/security/apparmor/Makefile
|
||||
@@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
|
||||
|
||||
apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
|
||||
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
|
||||
- resource.o secid.o file.o policy_ns.o net.o
|
||||
+ resource.o secid.o file.o policy_ns.o net.o mount.o
|
||||
apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o
|
||||
|
||||
clean-files := capability_names.h rlim_names.h net_names.h
|
||||
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
|
||||
index 4b121211e5e7..8e1c18b23d75 100644
|
||||
--- a/security/apparmor/apparmorfs.c
|
||||
+++ b/security/apparmor/apparmorfs.c
|
||||
@@ -1205,11 +1205,24 @@ static struct aa_fs_entry aa_fs_entry_policy[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
+static struct aa_fs_entry aa_fs_entry_mount[] = {
|
||||
+ AA_FS_FILE_STRING("mask", "mount umount"),
|
||||
+ { }
|
||||
+};
|
||||
+
|
||||
+static struct aa_fs_entry aa_fs_entry_namespaces[] = {
|
||||
+ AA_FS_FILE_BOOLEAN("profile", 1),
|
||||
+ AA_FS_FILE_BOOLEAN("pivot_root", 1),
|
||||
+ { }
|
||||
+};
|
||||
+
|
||||
static struct aa_fs_entry aa_fs_entry_features[] = {
|
||||
AA_FS_DIR("policy", aa_fs_entry_policy),
|
||||
AA_FS_DIR("domain", aa_fs_entry_domain),
|
||||
AA_FS_DIR("file", aa_fs_entry_file),
|
||||
AA_FS_DIR("network", aa_fs_entry_network),
|
||||
+ AA_FS_DIR("mount", aa_fs_entry_mount),
|
||||
+ AA_FS_DIR("namespaces", aa_fs_entry_namespaces),
|
||||
AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK),
|
||||
AA_FS_DIR("rlimit", aa_fs_entry_rlimit),
|
||||
AA_FS_DIR("caps", aa_fs_entry_caps),
|
||||
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
|
||||
index 001e133a3c8c..708b7e22b9b5 100644
|
||||
--- a/security/apparmor/domain.c
|
||||
+++ b/security/apparmor/domain.c
|
||||
@@ -237,7 +237,7 @@ static const char *next_name(int xtype, const char *name)
|
||||
*
|
||||
* Returns: refcounted profile, or NULL on failure (MAYBE NULL)
|
||||
*/
|
||||
-static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex)
|
||||
+struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex)
|
||||
{
|
||||
struct aa_profile *new_profile = NULL;
|
||||
struct aa_ns *ns = profile->ns;
|
||||
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h
|
||||
index 1750cc0721c1..3383dc66f30f 100644
|
||||
--- a/security/apparmor/include/apparmor.h
|
||||
+++ b/security/apparmor/include/apparmor.h
|
||||
@@ -27,8 +27,9 @@
|
||||
#define AA_CLASS_NET 4
|
||||
#define AA_CLASS_RLIMITS 5
|
||||
#define AA_CLASS_DOMAIN 6
|
||||
+#define AA_CLASS_MOUNT 7
|
||||
|
||||
-#define AA_CLASS_LAST AA_CLASS_DOMAIN
|
||||
+#define AA_CLASS_LAST AA_CLASS_MOUNT
|
||||
|
||||
/* Control parameters settable through module/boot flags */
|
||||
extern enum audit_mode aa_g_audit;
|
||||
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
|
||||
index 0df708e8748b..41374ad89547 100644
|
||||
--- a/security/apparmor/include/audit.h
|
||||
+++ b/security/apparmor/include/audit.h
|
||||
@@ -70,6 +70,10 @@ enum audit_type {
|
||||
#define OP_FMMAP "file_mmap"
|
||||
#define OP_FMPROT "file_mprotect"
|
||||
|
||||
+#define OP_PIVOTROOT "pivotroot"
|
||||
+#define OP_MOUNT "mount"
|
||||
+#define OP_UMOUNT "umount"
|
||||
+
|
||||
#define OP_CREATE "create"
|
||||
#define OP_POST_CREATE "post_create"
|
||||
#define OP_BIND "bind"
|
||||
@@ -127,6 +131,13 @@ struct apparmor_audit_data {
|
||||
int rlim;
|
||||
unsigned long max;
|
||||
} rlim;
|
||||
+ struct {
|
||||
+ const char *src_name;
|
||||
+ const char *type;
|
||||
+ const char *trans;
|
||||
+ const char *data;
|
||||
+ unsigned long flags;
|
||||
+ } mnt;
|
||||
struct {
|
||||
int type, protocol;
|
||||
struct sock *sk;
|
||||
diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h
|
||||
index 30544729878a..7bd21d20a2bd 100644
|
||||
--- a/security/apparmor/include/domain.h
|
||||
+++ b/security/apparmor/include/domain.h
|
||||
@@ -23,6 +23,8 @@ struct aa_domain {
|
||||
char **table;
|
||||
};
|
||||
|
||||
+struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex);
|
||||
+
|
||||
int apparmor_bprm_set_creds(struct linux_binprm *bprm);
|
||||
int apparmor_bprm_secureexec(struct linux_binprm *bprm);
|
||||
void apparmor_bprm_committing_creds(struct linux_binprm *bprm);
|
||||
diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h
|
||||
new file mode 100644
|
||||
index 000000000000..a43b1d62e428
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/include/mount.h
|
||||
@@ -0,0 +1,54 @@
|
||||
+/*
|
||||
+ * AppArmor security module
|
||||
+ *
|
||||
+ * This file contains AppArmor file mediation function definitions.
|
||||
+ *
|
||||
+ * Copyright 2012 Canonical Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License as
|
||||
+ * published by the Free Software Foundation, version 2 of the
|
||||
+ * License.
|
||||
+ */
|
||||
+
|
||||
+#ifndef __AA_MOUNT_H
|
||||
+#define __AA_MOUNT_H
|
||||
+
|
||||
+#include <linux/fs.h>
|
||||
+#include <linux/path.h>
|
||||
+
|
||||
+#include "domain.h"
|
||||
+#include "policy.h"
|
||||
+
|
||||
+/* mount perms */
|
||||
+#define AA_MAY_PIVOTROOT 0x01
|
||||
+#define AA_MAY_MOUNT 0x02
|
||||
+#define AA_MAY_UMOUNT 0x04
|
||||
+#define AA_AUDIT_DATA 0x40
|
||||
+#define AA_CONT_MATCH 0x40
|
||||
+
|
||||
+#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN)
|
||||
+
|
||||
+int aa_remount(struct aa_profile *profile, const struct path *path,
|
||||
+ unsigned long flags, void *data);
|
||||
+
|
||||
+int aa_bind_mount(struct aa_profile *profile, const struct path *path,
|
||||
+ const char *old_name, unsigned long flags);
|
||||
+
|
||||
+
|
||||
+int aa_mount_change_type(struct aa_profile *profile, const struct path *path,
|
||||
+ unsigned long flags);
|
||||
+
|
||||
+int aa_move_mount(struct aa_profile *profile, const struct path *path,
|
||||
+ const char *old_name);
|
||||
+
|
||||
+int aa_new_mount(struct aa_profile *profile, const char *dev_name,
|
||||
+ const struct path *path, const char *type, unsigned long flags,
|
||||
+ void *data);
|
||||
+
|
||||
+int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags);
|
||||
+
|
||||
+int aa_pivotroot(struct aa_profile *profile, const struct path *old_path,
|
||||
+ const struct path *new_path);
|
||||
+
|
||||
+#endif /* __AA_MOUNT_H */
|
||||
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
|
||||
index 758ddf4a0791..b57f24045c0d 100644
|
||||
--- a/security/apparmor/lsm.c
|
||||
+++ b/security/apparmor/lsm.c
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "include/policy.h"
|
||||
#include "include/policy_ns.h"
|
||||
#include "include/procattr.h"
|
||||
+#include "include/mount.h"
|
||||
|
||||
/* Flag indicating whether initialization completed */
|
||||
int apparmor_initialized;
|
||||
@@ -479,6 +480,61 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma,
|
||||
!(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0);
|
||||
}
|
||||
|
||||
+static int apparmor_sb_mount(const char *dev_name, const struct path *path,
|
||||
+ const char *type, unsigned long flags, void *data)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ int error = 0;
|
||||
+
|
||||
+ /* Discard magic */
|
||||
+ if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
|
||||
+ flags &= ~MS_MGC_MSK;
|
||||
+
|
||||
+ flags &= ~AA_MS_IGNORE_MASK;
|
||||
+
|
||||
+ profile = __aa_current_profile();
|
||||
+ if (!unconfined(profile)) {
|
||||
+ if (flags & MS_REMOUNT)
|
||||
+ error = aa_remount(profile, path, flags, data);
|
||||
+ else if (flags & MS_BIND)
|
||||
+ error = aa_bind_mount(profile, path, dev_name, flags);
|
||||
+ else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE |
|
||||
+ MS_UNBINDABLE))
|
||||
+ error = aa_mount_change_type(profile, path, flags);
|
||||
+ else if (flags & MS_MOVE)
|
||||
+ error = aa_move_mount(profile, path, dev_name);
|
||||
+ else
|
||||
+ error = aa_new_mount(profile, dev_name, path, type,
|
||||
+ flags, data);
|
||||
+ }
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+static int apparmor_sb_umount(struct vfsmount *mnt, int flags)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ int error = 0;
|
||||
+
|
||||
+ profile = __aa_current_profile();
|
||||
+ if (!unconfined(profile))
|
||||
+ error = aa_umount(profile, mnt, flags);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+static int apparmor_sb_pivotroot(const struct path *old_path,
|
||||
+ const struct path *new_path)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ int error = 0;
|
||||
+
|
||||
+ profile = __aa_current_profile();
|
||||
+ if (!unconfined(profile))
|
||||
+ error = aa_pivotroot(profile, old_path, new_path);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
static int apparmor_getprocattr(struct task_struct *task, char *name,
|
||||
char **value)
|
||||
{
|
||||
@@ -692,6 +748,10 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(capget, apparmor_capget),
|
||||
LSM_HOOK_INIT(capable, apparmor_capable),
|
||||
|
||||
+ LSM_HOOK_INIT(sb_mount, apparmor_sb_mount),
|
||||
+ LSM_HOOK_INIT(sb_umount, apparmor_sb_umount),
|
||||
+ LSM_HOOK_INIT(sb_pivotroot, apparmor_sb_pivotroot),
|
||||
+
|
||||
LSM_HOOK_INIT(path_link, apparmor_path_link),
|
||||
LSM_HOOK_INIT(path_unlink, apparmor_path_unlink),
|
||||
LSM_HOOK_INIT(path_symlink, apparmor_path_symlink),
|
||||
diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c
|
||||
new file mode 100644
|
||||
index 000000000000..9e95a41c015c
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/mount.c
|
||||
@@ -0,0 +1,616 @@
|
||||
+/*
|
||||
+ * AppArmor security module
|
||||
+ *
|
||||
+ * This file contains AppArmor mediation of files
|
||||
+ *
|
||||
+ * Copyright (C) 1998-2008 Novell/SUSE
|
||||
+ * Copyright 2009-2012 Canonical Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License as
|
||||
+ * published by the Free Software Foundation, version 2 of the
|
||||
+ * License.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/fs.h>
|
||||
+#include <linux/mount.h>
|
||||
+#include <linux/namei.h>
|
||||
+
|
||||
+#include "include/apparmor.h"
|
||||
+#include "include/audit.h"
|
||||
+#include "include/context.h"
|
||||
+#include "include/domain.h"
|
||||
+#include "include/file.h"
|
||||
+#include "include/match.h"
|
||||
+#include "include/mount.h"
|
||||
+#include "include/path.h"
|
||||
+#include "include/policy.h"
|
||||
+
|
||||
+
|
||||
+static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags)
|
||||
+{
|
||||
+ if (flags & MS_RDONLY)
|
||||
+ audit_log_format(ab, "ro");
|
||||
+ else
|
||||
+ audit_log_format(ab, "rw");
|
||||
+ if (flags & MS_NOSUID)
|
||||
+ audit_log_format(ab, ", nosuid");
|
||||
+ if (flags & MS_NODEV)
|
||||
+ audit_log_format(ab, ", nodev");
|
||||
+ if (flags & MS_NOEXEC)
|
||||
+ audit_log_format(ab, ", noexec");
|
||||
+ if (flags & MS_SYNCHRONOUS)
|
||||
+ audit_log_format(ab, ", sync");
|
||||
+ if (flags & MS_REMOUNT)
|
||||
+ audit_log_format(ab, ", remount");
|
||||
+ if (flags & MS_MANDLOCK)
|
||||
+ audit_log_format(ab, ", mand");
|
||||
+ if (flags & MS_DIRSYNC)
|
||||
+ audit_log_format(ab, ", dirsync");
|
||||
+ if (flags & MS_NOATIME)
|
||||
+ audit_log_format(ab, ", noatime");
|
||||
+ if (flags & MS_NODIRATIME)
|
||||
+ audit_log_format(ab, ", nodiratime");
|
||||
+ if (flags & MS_BIND)
|
||||
+ audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind");
|
||||
+ if (flags & MS_MOVE)
|
||||
+ audit_log_format(ab, ", move");
|
||||
+ if (flags & MS_SILENT)
|
||||
+ audit_log_format(ab, ", silent");
|
||||
+ if (flags & MS_POSIXACL)
|
||||
+ audit_log_format(ab, ", acl");
|
||||
+ if (flags & MS_UNBINDABLE)
|
||||
+ audit_log_format(ab, flags & MS_REC ? ", runbindable" :
|
||||
+ ", unbindable");
|
||||
+ if (flags & MS_PRIVATE)
|
||||
+ audit_log_format(ab, flags & MS_REC ? ", rprivate" :
|
||||
+ ", private");
|
||||
+ if (flags & MS_SLAVE)
|
||||
+ audit_log_format(ab, flags & MS_REC ? ", rslave" :
|
||||
+ ", slave");
|
||||
+ if (flags & MS_SHARED)
|
||||
+ audit_log_format(ab, flags & MS_REC ? ", rshared" :
|
||||
+ ", shared");
|
||||
+ if (flags & MS_RELATIME)
|
||||
+ audit_log_format(ab, ", relatime");
|
||||
+ if (flags & MS_I_VERSION)
|
||||
+ audit_log_format(ab, ", iversion");
|
||||
+ if (flags & MS_STRICTATIME)
|
||||
+ audit_log_format(ab, ", strictatime");
|
||||
+ if (flags & MS_NOUSER)
|
||||
+ audit_log_format(ab, ", nouser");
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * audit_cb - call back for mount specific audit fields
|
||||
+ * @ab: audit_buffer (NOT NULL)
|
||||
+ * @va: audit struct to audit values of (NOT NULL)
|
||||
+ */
|
||||
+static void audit_cb(struct audit_buffer *ab, void *va)
|
||||
+{
|
||||
+ struct common_audit_data *sa = va;
|
||||
+
|
||||
+ if (aad(sa)->mnt.type) {
|
||||
+ audit_log_format(ab, " fstype=");
|
||||
+ audit_log_untrustedstring(ab, aad(sa)->mnt.type);
|
||||
+ }
|
||||
+ if (aad(sa)->mnt.src_name) {
|
||||
+ audit_log_format(ab, " srcname=");
|
||||
+ audit_log_untrustedstring(ab, aad(sa)->mnt.src_name);
|
||||
+ }
|
||||
+ if (aad(sa)->mnt.trans) {
|
||||
+ audit_log_format(ab, " trans=");
|
||||
+ audit_log_untrustedstring(ab, aad(sa)->mnt.trans);
|
||||
+ }
|
||||
+ if (aad(sa)->mnt.flags) {
|
||||
+ audit_log_format(ab, " flags=\"");
|
||||
+ audit_mnt_flags(ab, aad(sa)->mnt.flags);
|
||||
+ audit_log_format(ab, "\"");
|
||||
+ }
|
||||
+ if (aad(sa)->mnt.data) {
|
||||
+ audit_log_format(ab, " options=");
|
||||
+ audit_log_untrustedstring(ab, aad(sa)->mnt.data);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * audit_mount - handle the auditing of mount operations
|
||||
+ * @profile: the profile being enforced (NOT NULL)
|
||||
+ * @gfp: allocation flags
|
||||
+ * @op: operation being mediated (NOT NULL)
|
||||
+ * @name: name of object being mediated (MAYBE NULL)
|
||||
+ * @src_name: src_name of object being mediated (MAYBE_NULL)
|
||||
+ * @type: type of filesystem (MAYBE_NULL)
|
||||
+ * @trans: name of trans (MAYBE NULL)
|
||||
+ * @flags: filesystem idependent mount flags
|
||||
+ * @data: filesystem mount flags
|
||||
+ * @request: permissions requested
|
||||
+ * @perms: the permissions computed for the request (NOT NULL)
|
||||
+ * @info: extra information message (MAYBE NULL)
|
||||
+ * @error: 0 if operation allowed else failure error code
|
||||
+ *
|
||||
+ * Returns: %0 or error on failure
|
||||
+ */
|
||||
+static int audit_mount(struct aa_profile *profile, gfp_t gfp, const char *op,
|
||||
+ const char *name, const char *src_name,
|
||||
+ const char *type, const char *trans,
|
||||
+ unsigned long flags, const void *data, u32 request,
|
||||
+ struct file_perms *perms, const char *info, int error)
|
||||
+{
|
||||
+ int audit_type = AUDIT_APPARMOR_AUTO;
|
||||
+ DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op);
|
||||
+
|
||||
+ if (likely(!error)) {
|
||||
+ u32 mask = perms->audit;
|
||||
+
|
||||
+ if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL))
|
||||
+ mask = 0xffff;
|
||||
+
|
||||
+ /* mask off perms that are not being force audited */
|
||||
+ request &= mask;
|
||||
+
|
||||
+ if (likely(!request))
|
||||
+ return 0;
|
||||
+ audit_type = AUDIT_APPARMOR_AUDIT;
|
||||
+ } else {
|
||||
+ /* only report permissions that were denied */
|
||||
+ request = request & ~perms->allow;
|
||||
+
|
||||
+ if (request & perms->kill)
|
||||
+ audit_type = AUDIT_APPARMOR_KILL;
|
||||
+
|
||||
+ /* quiet known rejects, assumes quiet and kill do not overlap */
|
||||
+ if ((request & perms->quiet) &&
|
||||
+ AUDIT_MODE(profile) != AUDIT_NOQUIET &&
|
||||
+ AUDIT_MODE(profile) != AUDIT_ALL)
|
||||
+ request &= ~perms->quiet;
|
||||
+
|
||||
+ if (!request)
|
||||
+ return COMPLAIN_MODE(profile) ?
|
||||
+ complain_error(error) : error;
|
||||
+ }
|
||||
+
|
||||
+ aad(&sa)->name = name;
|
||||
+ aad(&sa)->mnt.src_name = src_name;
|
||||
+ aad(&sa)->mnt.type = type;
|
||||
+ aad(&sa)->mnt.trans = trans;
|
||||
+ aad(&sa)->mnt.flags = flags;
|
||||
+ if (data && (perms->audit & AA_AUDIT_DATA))
|
||||
+ aad(&sa)->mnt.data = data;
|
||||
+ aad(&sa)->info = info;
|
||||
+ aad(&sa)->error = error;
|
||||
+
|
||||
+ return aa_audit(audit_type, profile, &sa, audit_cb);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * match_mnt_flags - Do an ordered match on mount flags
|
||||
+ * @dfa: dfa to match against
|
||||
+ * @state: state to start in
|
||||
+ * @flags: mount flags to match against
|
||||
+ *
|
||||
+ * Mount flags are encoded as an ordered match. This is done instead of
|
||||
+ * checking against a simple bitmask, to allow for logical operations
|
||||
+ * on the flags.
|
||||
+ *
|
||||
+ * Returns: next state after flags match
|
||||
+ */
|
||||
+static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state,
|
||||
+ unsigned long flags)
|
||||
+{
|
||||
+ unsigned int i;
|
||||
+
|
||||
+ for (i = 0; i <= 31 ; ++i) {
|
||||
+ if ((1 << i) & flags)
|
||||
+ state = aa_dfa_next(dfa, state, i + 1);
|
||||
+ }
|
||||
+
|
||||
+ return state;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * compute_mnt_perms - compute mount permission associated with @state
|
||||
+ * @dfa: dfa to match against (NOT NULL)
|
||||
+ * @state: state match finished in
|
||||
+ *
|
||||
+ * Returns: mount permissions
|
||||
+ */
|
||||
+static struct file_perms compute_mnt_perms(struct aa_dfa *dfa,
|
||||
+ unsigned int state)
|
||||
+{
|
||||
+ struct file_perms perms;
|
||||
+
|
||||
+ perms.kill = 0;
|
||||
+ perms.allow = dfa_user_allow(dfa, state);
|
||||
+ perms.audit = dfa_user_audit(dfa, state);
|
||||
+ perms.quiet = dfa_user_quiet(dfa, state);
|
||||
+ perms.xindex = dfa_user_xindex(dfa, state);
|
||||
+
|
||||
+ return perms;
|
||||
+}
|
||||
+
|
||||
+static const char *mnt_info_table[] = {
|
||||
+ "match succeeded",
|
||||
+ "failed mntpnt match",
|
||||
+ "failed srcname match",
|
||||
+ "failed type match",
|
||||
+ "failed flags match",
|
||||
+ "failed data match"
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * Returns 0 on success else element that match failed in, this is the
|
||||
+ * index into the mnt_info_table above
|
||||
+ */
|
||||
+static int do_match_mnt(struct aa_dfa *dfa, unsigned int start,
|
||||
+ const char *mntpnt, const char *devname,
|
||||
+ const char *type, unsigned long flags,
|
||||
+ void *data, bool binary, struct file_perms *perms)
|
||||
+{
|
||||
+ unsigned int state;
|
||||
+
|
||||
+ state = aa_dfa_match(dfa, start, mntpnt);
|
||||
+ state = aa_dfa_null_transition(dfa, state);
|
||||
+ if (!state)
|
||||
+ return 1;
|
||||
+
|
||||
+ if (devname)
|
||||
+ state = aa_dfa_match(dfa, state, devname);
|
||||
+ state = aa_dfa_null_transition(dfa, state);
|
||||
+ if (!state)
|
||||
+ return 2;
|
||||
+
|
||||
+ if (type)
|
||||
+ state = aa_dfa_match(dfa, state, type);
|
||||
+ state = aa_dfa_null_transition(dfa, state);
|
||||
+ if (!state)
|
||||
+ return 3;
|
||||
+
|
||||
+ state = match_mnt_flags(dfa, state, flags);
|
||||
+ if (!state)
|
||||
+ return 4;
|
||||
+ *perms = compute_mnt_perms(dfa, state);
|
||||
+ if (perms->allow & AA_MAY_MOUNT)
|
||||
+ return 0;
|
||||
+
|
||||
+ /* only match data if not binary and the DFA flags data is expected */
|
||||
+ if (data && !binary && (perms->allow & AA_CONT_MATCH)) {
|
||||
+ state = aa_dfa_null_transition(dfa, state);
|
||||
+ if (!state)
|
||||
+ return 4;
|
||||
+
|
||||
+ state = aa_dfa_match(dfa, state, data);
|
||||
+ if (!state)
|
||||
+ return 5;
|
||||
+ *perms = compute_mnt_perms(dfa, state);
|
||||
+ if (perms->allow & AA_MAY_MOUNT)
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ /* failed at end of flags match */
|
||||
+ return 4;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * match_mnt - handle path matching for mount
|
||||
+ * @profile: the confining profile
|
||||
+ * @mntpnt: string for the mntpnt (NOT NULL)
|
||||
+ * @devname: string for the devname/src_name (MAYBE NULL)
|
||||
+ * @type: string for the dev type (MAYBE NULL)
|
||||
+ * @flags: mount flags to match
|
||||
+ * @data: fs mount data (MAYBE NULL)
|
||||
+ * @binary: whether @data is binary
|
||||
+ * @perms: Returns: permission found by the match
|
||||
+ * @info: Returns: infomation string about the match for logging
|
||||
+ *
|
||||
+ * Returns: 0 on success else error
|
||||
+ */
|
||||
+static int match_mnt(struct aa_profile *profile, const char *mntpnt,
|
||||
+ const char *devname, const char *type,
|
||||
+ unsigned long flags, void *data, bool binary,
|
||||
+ struct file_perms *perms, const char **info)
|
||||
+{
|
||||
+ int pos;
|
||||
+
|
||||
+ if (!profile->policy.dfa)
|
||||
+ return -EACCES;
|
||||
+
|
||||
+ pos = do_match_mnt(profile->policy.dfa,
|
||||
+ profile->policy.start[AA_CLASS_MOUNT],
|
||||
+ mntpnt, devname, type, flags, data, binary, perms);
|
||||
+ if (pos) {
|
||||
+ *info = mnt_info_table[pos];
|
||||
+ return -EACCES;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int path_flags(struct aa_profile *profile, const struct path *path)
|
||||
+{
|
||||
+ return profile->path_flags |
|
||||
+ S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0;
|
||||
+}
|
||||
+
|
||||
+int aa_remount(struct aa_profile *profile, const struct path *path,
|
||||
+ unsigned long flags, void *data)
|
||||
+{
|
||||
+ struct file_perms perms = { };
|
||||
+ const char *name, *info = NULL;
|
||||
+ char *buffer = NULL;
|
||||
+ int binary, error;
|
||||
+
|
||||
+ binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA;
|
||||
+
|
||||
+ error = aa_path_name(path, path_flags(profile, path), &buffer, &name,
|
||||
+ &info);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = match_mnt(profile, name, NULL, NULL, flags, data, binary,
|
||||
+ &perms, &info);
|
||||
+
|
||||
+audit:
|
||||
+ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL,
|
||||
+ NULL, flags, data, AA_MAY_MOUNT, &perms, info,
|
||||
+ error);
|
||||
+ kfree(buffer);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+int aa_bind_mount(struct aa_profile *profile, const struct path *path,
|
||||
+ const char *dev_name, unsigned long flags)
|
||||
+{
|
||||
+ struct file_perms perms = { };
|
||||
+ char *buffer = NULL, *old_buffer = NULL;
|
||||
+ const char *name, *old_name = NULL, *info = NULL;
|
||||
+ struct path old_path;
|
||||
+ int error;
|
||||
+
|
||||
+ if (!dev_name || !*dev_name)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ flags &= MS_REC | MS_BIND;
|
||||
+
|
||||
+ error = aa_path_name(path, path_flags(profile, path), &buffer, &name,
|
||||
+ &info);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = aa_path_name(&old_path, path_flags(profile, &old_path),
|
||||
+ &old_buffer, &old_name, &info);
|
||||
+ path_put(&old_path);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0,
|
||||
+ &perms, &info);
|
||||
+
|
||||
+audit:
|
||||
+ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name,
|
||||
+ NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms,
|
||||
+ info, error);
|
||||
+ kfree(buffer);
|
||||
+ kfree(old_buffer);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+int aa_mount_change_type(struct aa_profile *profile, const struct path *path,
|
||||
+ unsigned long flags)
|
||||
+{
|
||||
+ struct file_perms perms = { };
|
||||
+ char *buffer = NULL;
|
||||
+ const char *name, *info = NULL;
|
||||
+ int error;
|
||||
+
|
||||
+ /* These are the flags allowed by do_change_type() */
|
||||
+ flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE |
|
||||
+ MS_UNBINDABLE);
|
||||
+
|
||||
+ error = aa_path_name(path, path_flags(profile, path), &buffer, &name,
|
||||
+ &info);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms,
|
||||
+ &info);
|
||||
+
|
||||
+audit:
|
||||
+ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL,
|
||||
+ NULL, flags, NULL, AA_MAY_MOUNT, &perms, info,
|
||||
+ error);
|
||||
+ kfree(buffer);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+int aa_move_mount(struct aa_profile *profile, const struct path *path,
|
||||
+ const char *orig_name)
|
||||
+{
|
||||
+ struct file_perms perms = { };
|
||||
+ char *buffer = NULL, *old_buffer = NULL;
|
||||
+ const char *name, *old_name = NULL, *info = NULL;
|
||||
+ struct path old_path;
|
||||
+ int error;
|
||||
+
|
||||
+ if (!orig_name || !*orig_name)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ error = aa_path_name(path, path_flags(profile, path), &buffer, &name,
|
||||
+ &info);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = aa_path_name(&old_path, path_flags(profile, &old_path),
|
||||
+ &old_buffer, &old_name, &info);
|
||||
+ path_put(&old_path);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0,
|
||||
+ &perms, &info);
|
||||
+
|
||||
+audit:
|
||||
+ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name,
|
||||
+ NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms,
|
||||
+ info, error);
|
||||
+ kfree(buffer);
|
||||
+ kfree(old_buffer);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name,
|
||||
+ const struct path *path, const char *type, unsigned long flags,
|
||||
+ void *data)
|
||||
+{
|
||||
+ struct file_perms perms = { };
|
||||
+ char *buffer = NULL, *dev_buffer = NULL;
|
||||
+ const char *name = NULL, *dev_name = NULL, *info = NULL;
|
||||
+ int binary = 1;
|
||||
+ int error;
|
||||
+
|
||||
+ dev_name = orig_dev_name;
|
||||
+ if (type) {
|
||||
+ int requires_dev;
|
||||
+ struct file_system_type *fstype = get_fs_type(type);
|
||||
+ if (!fstype)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ binary = fstype->fs_flags & FS_BINARY_MOUNTDATA;
|
||||
+ requires_dev = fstype->fs_flags & FS_REQUIRES_DEV;
|
||||
+ put_filesystem(fstype);
|
||||
+
|
||||
+ if (requires_dev) {
|
||||
+ struct path dev_path;
|
||||
+
|
||||
+ if (!dev_name || !*dev_name) {
|
||||
+ error = -ENOENT;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = aa_path_name(&dev_path,
|
||||
+ path_flags(profile, &dev_path),
|
||||
+ &dev_buffer, &dev_name, &info);
|
||||
+ path_put(&dev_path);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ error = aa_path_name(path, path_flags(profile, path), &buffer, &name,
|
||||
+ &info);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = match_mnt(profile, name, dev_name, type, flags, data, binary,
|
||||
+ &perms, &info);
|
||||
+
|
||||
+audit:
|
||||
+ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name,
|
||||
+ type, NULL, flags, data, AA_MAY_MOUNT, &perms, info,
|
||||
+ error);
|
||||
+ kfree(buffer);
|
||||
+ kfree(dev_buffer);
|
||||
+
|
||||
+out:
|
||||
+ return error;
|
||||
+
|
||||
+}
|
||||
+
|
||||
+int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags)
|
||||
+{
|
||||
+ struct file_perms perms = { };
|
||||
+ char *buffer = NULL;
|
||||
+ const char *name, *info = NULL;
|
||||
+ int error;
|
||||
+
|
||||
+ struct path path = { mnt, mnt->mnt_root };
|
||||
+ error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name,
|
||||
+ &info);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ if (!error && profile->policy.dfa) {
|
||||
+ unsigned int state;
|
||||
+ state = aa_dfa_match(profile->policy.dfa,
|
||||
+ profile->policy.start[AA_CLASS_MOUNT],
|
||||
+ name);
|
||||
+ perms = compute_mnt_perms(profile->policy.dfa, state);
|
||||
+ }
|
||||
+
|
||||
+ if (AA_MAY_UMOUNT & ~perms.allow)
|
||||
+ error = -EACCES;
|
||||
+
|
||||
+audit:
|
||||
+ error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL,
|
||||
+ NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error);
|
||||
+ kfree(buffer);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+int aa_pivotroot(struct aa_profile *profile, const struct path *old_path,
|
||||
+ const struct path *new_path)
|
||||
+{
|
||||
+ struct file_perms perms = { };
|
||||
+ struct aa_profile *target = NULL;
|
||||
+ char *old_buffer = NULL, *new_buffer = NULL;
|
||||
+ const char *old_name, *new_name = NULL, *info = NULL;
|
||||
+ int error;
|
||||
+
|
||||
+ error = aa_path_name(old_path, path_flags(profile, old_path),
|
||||
+ &old_buffer, &old_name, &info);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = aa_path_name(new_path, path_flags(profile, new_path),
|
||||
+ &new_buffer, &new_name, &info);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ if (profile->policy.dfa) {
|
||||
+ unsigned int state;
|
||||
+ state = aa_dfa_match(profile->policy.dfa,
|
||||
+ profile->policy.start[AA_CLASS_MOUNT],
|
||||
+ new_name);
|
||||
+ state = aa_dfa_null_transition(profile->policy.dfa, state);
|
||||
+ state = aa_dfa_match(profile->policy.dfa, state, old_name);
|
||||
+ perms = compute_mnt_perms(profile->policy.dfa, state);
|
||||
+ }
|
||||
+
|
||||
+ if (AA_MAY_PIVOTROOT & perms.allow) {
|
||||
+ if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) {
|
||||
+ target = x_table_lookup(profile, perms.xindex);
|
||||
+ if (!target)
|
||||
+ error = -ENOENT;
|
||||
+ else
|
||||
+ error = aa_replace_current_profile(target);
|
||||
+ }
|
||||
+ } else
|
||||
+ error = -EACCES;
|
||||
+
|
||||
+audit:
|
||||
+ error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name,
|
||||
+ old_name, NULL, target ? target->base.name : NULL,
|
||||
+ 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error);
|
||||
+ aa_put_profile(target);
|
||||
+ kfree(old_buffer);
|
||||
+ kfree(new_buffer);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
--
|
||||
2.11.0
|
||||
|
@@ -1,41 +0,0 @@
|
||||
From 00c72bc198aa85e5da02de2c0c4cc423c82a54f1 Mon Sep 17 00:00:00 2001
|
||||
From: Fedora Kernel Team <kernel-team@fedoraproject.org>
|
||||
Date: Thu, 3 Aug 2017 13:46:51 -0500
|
||||
Subject: [PATCH 01/17] UBUNTU: SAUCE: (efi-lockdown) MODSIGN: Fix module
|
||||
signature verification
|
||||
|
||||
BugLink: http://bugs.launchpad.net/bugs/1712168
|
||||
|
||||
Currently mod_verify_sig() calls verify_pkcs_7_signature() with
|
||||
trusted_keys=NULL, which causes only the builtin keys to be used
|
||||
to verify the signature. This breaks self-signing of modules with
|
||||
a MOK, as the MOK is loaded into the secondary trusted keyring.
|
||||
Fix this by passing the spacial value trusted_keys=(void *)1UL,
|
||||
which tells verify_pkcs_7_signature() to use the secondary
|
||||
keyring instead.
|
||||
|
||||
(cherry picked from commit cff4523d65b848f9c41c9e998a735ae2a820da2d
|
||||
git://git.kernel.org/pub/scm/linux/kernel/git/jwboyer/fedora.git)
|
||||
[ saf: Taken from fedora commit without authorship information or much
|
||||
of a commit message; modified so that commit will describe the
|
||||
problem being fixed. ]
|
||||
Signed-off-by: Seth Forshee <seth.forshee@canonical.com>
|
||||
---
|
||||
kernel/module_signing.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/kernel/module_signing.c b/kernel/module_signing.c
|
||||
index 937c844bee4a..d3d6f95a96b4 100644
|
||||
--- a/kernel/module_signing.c
|
||||
+++ b/kernel/module_signing.c
|
||||
@@ -81,6 +81,6 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen)
|
||||
}
|
||||
|
||||
return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len,
|
||||
- NULL, VERIFYING_MODULE_SIGNATURE,
|
||||
+ (void *)1UL, VERIFYING_MODULE_SIGNATURE,
|
||||
NULL, NULL);
|
||||
}
|
||||
--
|
||||
2.11.0
|
||||
|
@@ -1,51 +0,0 @@
|
||||
From c6cad5e65a23dcafa1821ca381901297664d9c64 Mon Sep 17 00:00:00 2001
|
||||
From: Geert Uytterhoeven <geert@linux-m68k.org>
|
||||
Date: Thu, 6 Jul 2017 10:56:21 +0200
|
||||
Subject: [PATCH 02/17] apparmor: Fix shadowed local variable in
|
||||
unpack_trans_table()
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
with W=2:
|
||||
|
||||
security/apparmor/policy_unpack.c: In function ‘unpack_trans_table’:
|
||||
security/apparmor/policy_unpack.c:469: warning: declaration of ‘pos’ shadows a previous local
|
||||
security/apparmor/policy_unpack.c:451: warning: shadowed declaration is here
|
||||
|
||||
Rename the old "pos" to "saved_pos" to fix this.
|
||||
|
||||
Fixes: 5379a3312024a8be ("apparmor: support v7 transition format compatible with label_parse")
|
||||
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
|
||||
Reviewed-by: Serge Hallyn <serge@hallyn.com>
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
(cherry picked from commit 966d631935a578fadb5770f17a957ee1a969d868)
|
||||
---
|
||||
security/apparmor/policy_unpack.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
|
||||
index c600f4dd1783..2d5a1a007b06 100644
|
||||
--- a/security/apparmor/policy_unpack.c
|
||||
+++ b/security/apparmor/policy_unpack.c
|
||||
@@ -448,7 +448,7 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e)
|
||||
*/
|
||||
static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
|
||||
{
|
||||
- void *pos = e->pos;
|
||||
+ void *saved_pos = e->pos;
|
||||
|
||||
/* exec table is optional */
|
||||
if (unpack_nameX(e, AA_STRUCT, "xtable")) {
|
||||
@@ -511,7 +511,7 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
|
||||
|
||||
fail:
|
||||
aa_free_domain_entries(&profile->file.trans);
|
||||
- e->pos = pos;
|
||||
+ e->pos = saved_pos;
|
||||
return 0;
|
||||
}
|
||||
|
||||
--
|
||||
2.11.0
|
||||
|
@@ -1,32 +0,0 @@
|
||||
From 9934296cba701d429a0fc0cf071a40c8c3a1587e Mon Sep 17 00:00:00 2001
|
||||
From: Christos Gkekas <chris.gekas@gmail.com>
|
||||
Date: Sat, 8 Jul 2017 20:50:21 +0100
|
||||
Subject: [PATCH 03/17] apparmor: Fix logical error in verify_header()
|
||||
|
||||
verify_header() is currently checking whether interface version is less
|
||||
than 5 *and* greater than 7, which always evaluates to false. Instead it
|
||||
should check whether it is less than 5 *or* greater than 7.
|
||||
|
||||
Signed-off-by: Christos Gkekas <chris.gekas@gmail.com>
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
(cherry picked from commit c54a2175e3a6bf6c697d249bba1aa729e06c7ba8)
|
||||
---
|
||||
security/apparmor/policy_unpack.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
|
||||
index 2d5a1a007b06..bda0dce3b582 100644
|
||||
--- a/security/apparmor/policy_unpack.c
|
||||
+++ b/security/apparmor/policy_unpack.c
|
||||
@@ -832,7 +832,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns)
|
||||
* if not specified use previous version
|
||||
* Mask off everything that is not kernel abi version
|
||||
*/
|
||||
- if (VERSION_LT(e->version, v5) && VERSION_GT(e->version, v7)) {
|
||||
+ if (VERSION_LT(e->version, v5) || VERSION_GT(e->version, v7)) {
|
||||
audit_iface(NULL, NULL, NULL, "unsupported interface version",
|
||||
e, error);
|
||||
return error;
|
||||
--
|
||||
2.11.0
|
||||
|
@@ -1,37 +0,0 @@
|
||||
From 8b3851c7b83f32f2be9d4b48371ddf033afedf62 Mon Sep 17 00:00:00 2001
|
||||
From: Dan Carpenter <dan.carpenter@oracle.com>
|
||||
Date: Thu, 13 Jul 2017 10:39:20 +0300
|
||||
Subject: [PATCH 04/17] apparmor: Fix an error code in aafs_create()
|
||||
|
||||
We accidentally forgot to set the error code on this path. It means we
|
||||
return NULL instead of an error pointer. I looked through a bunch of
|
||||
callers and I don't think it really causes a big issue, but the
|
||||
documentation says we're supposed to return error pointers here.
|
||||
|
||||
Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
|
||||
Acked-by: Serge Hallyn <serge@hallyn.com>
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
(cherry picked from commit aee58bf341db52a3a3563c6b972bfd4fc2d41e46)
|
||||
---
|
||||
security/apparmor/apparmorfs.c | 4 +++-
|
||||
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
|
||||
index 853c2ec8e0c9..2caeb748070c 100644
|
||||
--- a/security/apparmor/apparmorfs.c
|
||||
+++ b/security/apparmor/apparmorfs.c
|
||||
@@ -248,8 +248,10 @@ static struct dentry *aafs_create(const char *name, umode_t mode,
|
||||
|
||||
inode_lock(dir);
|
||||
dentry = lookup_one_len(name, parent, strlen(name));
|
||||
- if (IS_ERR(dentry))
|
||||
+ if (IS_ERR(dentry)) {
|
||||
+ error = PTR_ERR(dentry);
|
||||
goto fail_lock;
|
||||
+ }
|
||||
|
||||
if (d_really_is_positive(dentry)) {
|
||||
error = -EEXIST;
|
||||
--
|
||||
2.11.0
|
||||
|
@@ -1,29 +0,0 @@
|
||||
From 4b56e146905bbad2c79ea92e3f49e210ca527572 Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Mon, 31 Jul 2017 23:44:37 -0700
|
||||
Subject: [PATCH 05/17] apparmor: Redundant condition: prev_ns. in
|
||||
[label.c:1498]
|
||||
|
||||
Reported-by: David Binderman <dcb314@hotmail.com>
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
(cherry picked from commit d323d2c17cfcc54b6845bfc1d13bca5cef210fc7)
|
||||
---
|
||||
security/apparmor/label.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/security/apparmor/label.c b/security/apparmor/label.c
|
||||
index e052eaba1cf6..e324f4df3e34 100644
|
||||
--- a/security/apparmor/label.c
|
||||
+++ b/security/apparmor/label.c
|
||||
@@ -1495,7 +1495,7 @@ static int aa_profile_snxprint(char *str, size_t size, struct aa_ns *view,
|
||||
view = profiles_ns(profile);
|
||||
|
||||
if (view != profile->ns &&
|
||||
- (!prev_ns || (prev_ns && *prev_ns != profile->ns))) {
|
||||
+ (!prev_ns || (*prev_ns != profile->ns))) {
|
||||
if (prev_ns)
|
||||
*prev_ns = profile->ns;
|
||||
ns_name = aa_ns_name(view, profile->ns,
|
||||
--
|
||||
2.11.0
|
||||
|
@@ -1,397 +0,0 @@
|
||||
From f9e20353a6c5726775867db81b6085e8ab425a36 Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Tue, 18 Jul 2017 22:56:22 -0700
|
||||
Subject: [PATCH 06/17] apparmor: add the ability to mediate signals
|
||||
|
||||
Add signal mediation where the signal can be mediated based on the
|
||||
signal, direction, or the label or the peer/target. The signal perms
|
||||
are verified on a cross check to ensure policy consistency in the case
|
||||
of incremental policy load/replacement.
|
||||
|
||||
The optimization of skipping the cross check when policy is guaranteed
|
||||
to be consistent (single compile unit) remains to be done.
|
||||
|
||||
policy rules have the form of
|
||||
SIGNAL_RULE = [ QUALIFIERS ] 'signal' [ SIGNAL ACCESS PERMISSIONS ]
|
||||
[ SIGNAL SET ] [ SIGNAL PEER ]
|
||||
|
||||
SIGNAL ACCESS PERMISSIONS = SIGNAL ACCESS | SIGNAL ACCESS LIST
|
||||
|
||||
SIGNAL ACCESS LIST = '(' Comma or space separated list of SIGNAL
|
||||
ACCESS ')'
|
||||
|
||||
SIGNAL ACCESS = ( 'r' | 'w' | 'rw' | 'read' | 'write' | 'send' |
|
||||
'receive' )
|
||||
|
||||
SIGNAL SET = 'set' '=' '(' SIGNAL LIST ')'
|
||||
|
||||
SIGNAL LIST = Comma or space separated list of SIGNALS
|
||||
|
||||
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'
|
||||
)
|
||||
|
||||
SIGNAL PEER = 'peer' '=' AARE
|
||||
|
||||
eg.
|
||||
signal, # allow all signals
|
||||
signal send set=(hup, kill) peer=foo,
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
Acked-by: Seth Arnold <seth.arnold@canonical.com>
|
||||
(cherry picked from commit c6bf1adaecaa719d7c56338cc43b2982214f2f44)
|
||||
---
|
||||
security/apparmor/apparmorfs.c | 7 +++
|
||||
security/apparmor/include/apparmor.h | 1 +
|
||||
security/apparmor/include/audit.h | 2 +
|
||||
security/apparmor/include/ipc.h | 6 +++
|
||||
security/apparmor/include/sig_names.h | 95 +++++++++++++++++++++++++++++++++
|
||||
security/apparmor/ipc.c | 99 +++++++++++++++++++++++++++++++++++
|
||||
security/apparmor/lsm.c | 21 ++++++++
|
||||
7 files changed, 231 insertions(+)
|
||||
create mode 100644 security/apparmor/include/sig_names.h
|
||||
|
||||
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
|
||||
index 2caeb748070c..a5f9e1aa51f7 100644
|
||||
--- a/security/apparmor/apparmorfs.c
|
||||
+++ b/security/apparmor/apparmorfs.c
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "include/audit.h"
|
||||
#include "include/context.h"
|
||||
#include "include/crypto.h"
|
||||
+#include "include/ipc.h"
|
||||
#include "include/policy_ns.h"
|
||||
#include "include/label.h"
|
||||
#include "include/policy.h"
|
||||
@@ -2129,6 +2130,11 @@ static struct aa_sfs_entry aa_sfs_entry_ptrace[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
+static struct aa_sfs_entry aa_sfs_entry_signal[] = {
|
||||
+ AA_SFS_FILE_STRING("mask", AA_SFS_SIG_MASK),
|
||||
+ { }
|
||||
+};
|
||||
+
|
||||
static struct aa_sfs_entry aa_sfs_entry_domain[] = {
|
||||
AA_SFS_FILE_BOOLEAN("change_hat", 1),
|
||||
AA_SFS_FILE_BOOLEAN("change_hatv", 1),
|
||||
@@ -2179,6 +2185,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = {
|
||||
AA_SFS_DIR("rlimit", aa_sfs_entry_rlimit),
|
||||
AA_SFS_DIR("caps", aa_sfs_entry_caps),
|
||||
AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace),
|
||||
+ AA_SFS_DIR("signal", aa_sfs_entry_signal),
|
||||
AA_SFS_DIR("query", aa_sfs_entry_query),
|
||||
{ }
|
||||
};
|
||||
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h
|
||||
index aaf893f4e4f5..962a20a75e01 100644
|
||||
--- a/security/apparmor/include/apparmor.h
|
||||
+++ b/security/apparmor/include/apparmor.h
|
||||
@@ -28,6 +28,7 @@
|
||||
#define AA_CLASS_RLIMITS 5
|
||||
#define AA_CLASS_DOMAIN 6
|
||||
#define AA_CLASS_PTRACE 9
|
||||
+#define AA_CLASS_SIGNAL 10
|
||||
#define AA_CLASS_LABEL 16
|
||||
|
||||
#define AA_CLASS_LAST AA_CLASS_LABEL
|
||||
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
|
||||
index c68839a44351..d9a156ae11b9 100644
|
||||
--- a/security/apparmor/include/audit.h
|
||||
+++ b/security/apparmor/include/audit.h
|
||||
@@ -86,6 +86,7 @@ enum audit_type {
|
||||
#define OP_SHUTDOWN "socket_shutdown"
|
||||
|
||||
#define OP_PTRACE "ptrace"
|
||||
+#define OP_SIGNAL "signal"
|
||||
|
||||
#define OP_EXEC "exec"
|
||||
|
||||
@@ -126,6 +127,7 @@ struct apparmor_audit_data {
|
||||
long pos;
|
||||
const char *ns;
|
||||
} iface;
|
||||
+ int signal;
|
||||
struct {
|
||||
int rlim;
|
||||
unsigned long max;
|
||||
diff --git a/security/apparmor/include/ipc.h b/security/apparmor/include/ipc.h
|
||||
index 656fdb81c8a0..5ffc218d1e74 100644
|
||||
--- a/security/apparmor/include/ipc.h
|
||||
+++ b/security/apparmor/include/ipc.h
|
||||
@@ -27,8 +27,14 @@ struct aa_profile;
|
||||
|
||||
#define AA_PTRACE_PERM_MASK (AA_PTRACE_READ | AA_PTRACE_TRACE | \
|
||||
AA_MAY_BE_READ | AA_MAY_BE_TRACED)
|
||||
+#define AA_SIGNAL_PERM_MASK (MAY_READ | MAY_WRITE)
|
||||
+
|
||||
+#define AA_SFS_SIG_MASK "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 lost"
|
||||
|
||||
int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee,
|
||||
u32 request);
|
||||
+int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig);
|
||||
|
||||
#endif /* __AA_IPC_H */
|
||||
diff --git a/security/apparmor/include/sig_names.h b/security/apparmor/include/sig_names.h
|
||||
new file mode 100644
|
||||
index 000000000000..0d4395f231ca
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/include/sig_names.h
|
||||
@@ -0,0 +1,95 @@
|
||||
+#include <linux/signal.h>
|
||||
+
|
||||
+#define SIGUNKNOWN 0
|
||||
+#define MAXMAPPED_SIG 35
|
||||
+/* provide a mapping of arch signal to internal signal # for mediation
|
||||
+ * those that are always an alias SIGCLD for SIGCLHD and SIGPOLL for SIGIO
|
||||
+ * map to the same entry those that may/or may not get a separate entry
|
||||
+ */
|
||||
+static const int sig_map[MAXMAPPED_SIG] = {
|
||||
+ [0] = MAXMAPPED_SIG, /* existence test */
|
||||
+ [SIGHUP] = 1,
|
||||
+ [SIGINT] = 2,
|
||||
+ [SIGQUIT] = 3,
|
||||
+ [SIGILL] = 4,
|
||||
+ [SIGTRAP] = 5, /* -, 5, - */
|
||||
+ [SIGABRT] = 6, /* SIGIOT: -, 6, - */
|
||||
+ [SIGBUS] = 7, /* 10, 7, 10 */
|
||||
+ [SIGFPE] = 8,
|
||||
+ [SIGKILL] = 9,
|
||||
+ [SIGUSR1] = 10, /* 30, 10, 16 */
|
||||
+ [SIGSEGV] = 11,
|
||||
+ [SIGUSR2] = 12, /* 31, 12, 17 */
|
||||
+ [SIGPIPE] = 13,
|
||||
+ [SIGALRM] = 14,
|
||||
+ [SIGTERM] = 15,
|
||||
+ [SIGSTKFLT] = 16, /* -, 16, - */
|
||||
+ [SIGCHLD] = 17, /* 20, 17, 18. SIGCHLD -, -, 18 */
|
||||
+ [SIGCONT] = 18, /* 19, 18, 25 */
|
||||
+ [SIGSTOP] = 19, /* 17, 19, 23 */
|
||||
+ [SIGTSTP] = 20, /* 18, 20, 24 */
|
||||
+ [SIGTTIN] = 21, /* 21, 21, 26 */
|
||||
+ [SIGTTOU] = 22, /* 22, 22, 27 */
|
||||
+ [SIGURG] = 23, /* 16, 23, 21 */
|
||||
+ [SIGXCPU] = 24, /* 24, 24, 30 */
|
||||
+ [SIGXFSZ] = 25, /* 25, 25, 31 */
|
||||
+ [SIGVTALRM] = 26, /* 26, 26, 28 */
|
||||
+ [SIGPROF] = 27, /* 27, 27, 29 */
|
||||
+ [SIGWINCH] = 28, /* 28, 28, 20 */
|
||||
+ [SIGIO] = 29, /* SIGPOLL: 23, 29, 22 */
|
||||
+ [SIGPWR] = 30, /* 29, 30, 19. SIGINFO 29, -, - */
|
||||
+#ifdef SIGSYS
|
||||
+ [SIGSYS] = 31, /* 12, 31, 12. often SIG LOST/UNUSED */
|
||||
+#endif
|
||||
+#ifdef SIGEMT
|
||||
+ [SIGEMT] = 32, /* 7, - , 7 */
|
||||
+#endif
|
||||
+#if defined(SIGLOST) && SIGPWR != SIGLOST /* sparc */
|
||||
+ [SIGLOST] = 33, /* unused on Linux */
|
||||
+#endif
|
||||
+#if defined(SIGLOST) && defined(SIGSYS) && SIGLOST != SIGSYS
|
||||
+ [SIGUNUSED] = 34, /* -, 31, - */
|
||||
+#endif
|
||||
+};
|
||||
+
|
||||
+/* this table is ordered post sig_map[sig] mapping */
|
||||
+static const char *const sig_names[MAXMAPPED_SIG + 1] = {
|
||||
+ "unknown",
|
||||
+ "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",
|
||||
+ "lost",
|
||||
+ "unused",
|
||||
+
|
||||
+ "exists", /* always last existence test mapped to MAXMAPPED_SIG */
|
||||
+};
|
||||
+
|
||||
diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c
|
||||
index 11e66b5bbc42..66fb9ede9447 100644
|
||||
--- a/security/apparmor/ipc.c
|
||||
+++ b/security/apparmor/ipc.c
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "include/context.h"
|
||||
#include "include/policy.h"
|
||||
#include "include/ipc.h"
|
||||
+#include "include/sig_names.h"
|
||||
|
||||
/**
|
||||
* audit_ptrace_mask - convert mask to permission string
|
||||
@@ -121,3 +122,101 @@ int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee,
|
||||
}
|
||||
|
||||
|
||||
+static inline int map_signal_num(int sig)
|
||||
+{
|
||||
+ if (sig > SIGRTMAX)
|
||||
+ return SIGUNKNOWN;
|
||||
+ else if (sig >= SIGRTMIN)
|
||||
+ return sig - SIGRTMIN + 128; /* rt sigs mapped to 128 */
|
||||
+ else if (sig <= MAXMAPPED_SIG)
|
||||
+ return sig_map[sig];
|
||||
+ return SIGUNKNOWN;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * audit_file_mask - convert mask to permission string
|
||||
+ * @buffer: buffer to write string to (NOT NULL)
|
||||
+ * @mask: permission mask to convert
|
||||
+ */
|
||||
+static void audit_signal_mask(struct audit_buffer *ab, u32 mask)
|
||||
+{
|
||||
+ if (mask & MAY_READ)
|
||||
+ audit_log_string(ab, "receive");
|
||||
+ if (mask & MAY_WRITE)
|
||||
+ audit_log_string(ab, "send");
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * audit_cb - call back for signal specific audit fields
|
||||
+ * @ab: audit_buffer (NOT NULL)
|
||||
+ * @va: audit struct to audit values of (NOT NULL)
|
||||
+ */
|
||||
+static void audit_signal_cb(struct audit_buffer *ab, void *va)
|
||||
+{
|
||||
+ struct common_audit_data *sa = va;
|
||||
+
|
||||
+ if (aad(sa)->request & AA_SIGNAL_PERM_MASK) {
|
||||
+ audit_log_format(ab, " requested_mask=");
|
||||
+ audit_signal_mask(ab, aad(sa)->request);
|
||||
+ if (aad(sa)->denied & AA_SIGNAL_PERM_MASK) {
|
||||
+ audit_log_format(ab, " denied_mask=");
|
||||
+ audit_signal_mask(ab, aad(sa)->denied);
|
||||
+ }
|
||||
+ }
|
||||
+ if (aad(sa)->signal <= MAXMAPPED_SIG)
|
||||
+ audit_log_format(ab, " signal=%s", sig_names[aad(sa)->signal]);
|
||||
+ else
|
||||
+ audit_log_format(ab, " signal=rtmin+%d",
|
||||
+ aad(sa)->signal - 128);
|
||||
+ audit_log_format(ab, " peer=");
|
||||
+ aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
|
||||
+ FLAGS_NONE, GFP_ATOMIC);
|
||||
+}
|
||||
+
|
||||
+/* TODO: update to handle compound name&name2, conditionals */
|
||||
+static void profile_match_signal(struct aa_profile *profile, const char *label,
|
||||
+ int signal, struct aa_perms *perms)
|
||||
+{
|
||||
+ unsigned int state;
|
||||
+
|
||||
+ /* TODO: secondary cache check <profile, profile, perm> */
|
||||
+ state = aa_dfa_next(profile->policy.dfa,
|
||||
+ profile->policy.start[AA_CLASS_SIGNAL],
|
||||
+ signal);
|
||||
+ state = aa_dfa_match(profile->policy.dfa, state, label);
|
||||
+ aa_compute_perms(profile->policy.dfa, state, perms);
|
||||
+}
|
||||
+
|
||||
+static int profile_signal_perm(struct aa_profile *profile,
|
||||
+ struct aa_profile *peer, u32 request,
|
||||
+ struct common_audit_data *sa)
|
||||
+{
|
||||
+ struct aa_perms perms;
|
||||
+
|
||||
+ if (profile_unconfined(profile) ||
|
||||
+ !PROFILE_MEDIATES(profile, AA_CLASS_SIGNAL))
|
||||
+ return 0;
|
||||
+
|
||||
+ aad(sa)->peer = &peer->label;
|
||||
+ profile_match_signal(profile, peer->base.hname, aad(sa)->signal,
|
||||
+ &perms);
|
||||
+ aa_apply_modes_to_perms(profile, &perms);
|
||||
+ return aa_check_perms(profile, &perms, request, sa, audit_signal_cb);
|
||||
+}
|
||||
+
|
||||
+static int aa_signal_cross_perm(struct aa_profile *sender,
|
||||
+ struct aa_profile *target,
|
||||
+ struct common_audit_data *sa)
|
||||
+{
|
||||
+ return xcheck(profile_signal_perm(sender, target, MAY_WRITE, sa),
|
||||
+ profile_signal_perm(target, sender, MAY_READ, sa));
|
||||
+}
|
||||
+
|
||||
+int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig)
|
||||
+{
|
||||
+ DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SIGNAL);
|
||||
+
|
||||
+ aad(&sa)->signal = map_signal_num(sig);
|
||||
+ return xcheck_labels_profiles(sender, target, aa_signal_cross_perm,
|
||||
+ &sa);
|
||||
+}
|
||||
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
|
||||
index 867bcd154c7e..af22f3dfbcce 100644
|
||||
--- a/security/apparmor/lsm.c
|
||||
+++ b/security/apparmor/lsm.c
|
||||
@@ -656,6 +656,26 @@ static int apparmor_task_setrlimit(struct task_struct *task,
|
||||
return error;
|
||||
}
|
||||
|
||||
+static int apparmor_task_kill(struct task_struct *target, struct siginfo *info,
|
||||
+ int sig, u32 secid)
|
||||
+{
|
||||
+ struct aa_label *cl, *tl;
|
||||
+ int error;
|
||||
+
|
||||
+ if (secid)
|
||||
+ /* TODO: after secid to label mapping is done.
|
||||
+ * Dealing with USB IO specific behavior
|
||||
+ */
|
||||
+ return 0;
|
||||
+ cl = __begin_current_label_crit_section();
|
||||
+ tl = aa_get_task_label(target);
|
||||
+ error = aa_may_signal(cl, tl, sig);
|
||||
+ aa_put_label(tl);
|
||||
+ __end_current_label_crit_section(cl);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check),
|
||||
LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme),
|
||||
@@ -697,6 +717,7 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(bprm_secureexec, apparmor_bprm_secureexec),
|
||||
|
||||
LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit),
|
||||
+ LSM_HOOK_INIT(task_kill, apparmor_task_kill),
|
||||
};
|
||||
|
||||
/*
|
||||
--
|
||||
2.11.0
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,71 +0,0 @@
|
||||
From 763d17c9a18b0df7dbec2740f10dc40d378e3cc1 Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Sun, 6 Aug 2017 05:36:40 -0700
|
||||
Subject: [PATCH 08/17] apparmor: cleanup conditional check for label in
|
||||
label_print
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
Acked-by: Seth Arnold <seth.arnold@canonical.com>
|
||||
(cherry picked from commit 7e57939b9d67dcfc2c8348fd0e2c76a2f0349c75)
|
||||
---
|
||||
security/apparmor/label.c | 22 ++++++++--------------
|
||||
1 file changed, 8 insertions(+), 14 deletions(-)
|
||||
|
||||
diff --git a/security/apparmor/label.c b/security/apparmor/label.c
|
||||
index e324f4df3e34..38be7a89cc31 100644
|
||||
--- a/security/apparmor/label.c
|
||||
+++ b/security/apparmor/label.c
|
||||
@@ -1450,9 +1450,11 @@ bool aa_update_label_name(struct aa_ns *ns, struct aa_label *label, gfp_t gfp)
|
||||
* cached label name is present and visible
|
||||
* @label->hname only exists if label is namespace hierachical
|
||||
*/
|
||||
-static inline bool use_label_hname(struct aa_ns *ns, struct aa_label *label)
|
||||
+static inline bool use_label_hname(struct aa_ns *ns, struct aa_label *label,
|
||||
+ int flags)
|
||||
{
|
||||
- if (label->hname && labels_ns(label) == ns)
|
||||
+ if (label->hname && (!ns || labels_ns(label) == ns) &&
|
||||
+ !(flags & ~FLAG_SHOW_MODE))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -1710,10 +1712,8 @@ void aa_label_xaudit(struct audit_buffer *ab, struct aa_ns *ns,
|
||||
AA_BUG(!ab);
|
||||
AA_BUG(!label);
|
||||
|
||||
- if (!ns)
|
||||
- ns = labels_ns(label);
|
||||
-
|
||||
- if (!use_label_hname(ns, label) || display_mode(ns, label, flags)) {
|
||||
+ if (!use_label_hname(ns, label, flags) ||
|
||||
+ display_mode(ns, label, flags)) {
|
||||
len = aa_label_asxprint(&name, ns, label, flags, gfp);
|
||||
if (len == -1) {
|
||||
AA_DEBUG("label print error");
|
||||
@@ -1738,10 +1738,7 @@ void aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns,
|
||||
AA_BUG(!f);
|
||||
AA_BUG(!label);
|
||||
|
||||
- if (!ns)
|
||||
- ns = labels_ns(label);
|
||||
-
|
||||
- if (!use_label_hname(ns, label)) {
|
||||
+ if (!use_label_hname(ns, label, flags)) {
|
||||
char *str;
|
||||
int len;
|
||||
|
||||
@@ -1764,10 +1761,7 @@ void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags,
|
||||
{
|
||||
AA_BUG(!label);
|
||||
|
||||
- if (!ns)
|
||||
- ns = labels_ns(label);
|
||||
-
|
||||
- if (!use_label_hname(ns, label)) {
|
||||
+ if (!use_label_hname(ns, label, flags)) {
|
||||
char *str;
|
||||
int len;
|
||||
|
||||
--
|
||||
2.11.0
|
||||
|
@@ -1,63 +0,0 @@
|
||||
From 6b092bbbf9e17b10f709d11b3bc2d7e493617934 Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Sun, 6 Aug 2017 05:39:08 -0700
|
||||
Subject: [PATCH 09/17] apparmor: add support for absolute root view based
|
||||
labels
|
||||
|
||||
With apparmor policy virtualization based on policy namespace View's
|
||||
we don't generally want/need absolute root based views, however there
|
||||
are cases like debugging and some secid based conversions where
|
||||
using a root based view is important.
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
Acked-by: Seth Arnold <seth.arnold@canonical.com>
|
||||
(cherry picked from commit eadfbf0898eda94cee0d982626aa24a3146db48b)
|
||||
---
|
||||
security/apparmor/include/label.h | 1 +
|
||||
security/apparmor/label.c | 10 +++++++++-
|
||||
2 files changed, 10 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h
|
||||
index 9a283b722755..af22dcbbcb8a 100644
|
||||
--- a/security/apparmor/include/label.h
|
||||
+++ b/security/apparmor/include/label.h
|
||||
@@ -310,6 +310,7 @@ bool aa_update_label_name(struct aa_ns *ns, struct aa_label *label, gfp_t gfp);
|
||||
#define FLAG_SHOW_MODE 1
|
||||
#define FLAG_VIEW_SUBNS 2
|
||||
#define FLAG_HIDDEN_UNCONFINED 4
|
||||
+#define FLAG_ABS_ROOT 8
|
||||
int aa_label_snxprint(char *str, size_t size, struct aa_ns *view,
|
||||
struct aa_label *label, int flags);
|
||||
int aa_label_asxprint(char **strp, struct aa_ns *ns, struct aa_label *label,
|
||||
diff --git a/security/apparmor/label.c b/security/apparmor/label.c
|
||||
index 38be7a89cc31..52b4ef14840d 100644
|
||||
--- a/security/apparmor/label.c
|
||||
+++ b/security/apparmor/label.c
|
||||
@@ -1607,8 +1607,13 @@ int aa_label_snxprint(char *str, size_t size, struct aa_ns *ns,
|
||||
AA_BUG(!str && size != 0);
|
||||
AA_BUG(!label);
|
||||
|
||||
- if (!ns)
|
||||
+ if (flags & FLAG_ABS_ROOT) {
|
||||
+ ns = root_ns;
|
||||
+ len = snprintf(str, size, "=");
|
||||
+ update_for_len(total, len, size, str);
|
||||
+ } else if (!ns) {
|
||||
ns = labels_ns(label);
|
||||
+ }
|
||||
|
||||
label_for_each(i, label, profile) {
|
||||
if (aa_ns_visible(ns, profile->ns, flags & FLAG_VIEW_SUBNS)) {
|
||||
@@ -1868,6 +1873,9 @@ struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
|
||||
if (*str == '&')
|
||||
str++;
|
||||
}
|
||||
+ if (*str == '=')
|
||||
+ base = &root_ns->unconfined->label;
|
||||
+
|
||||
error = vec_setup(profile, vec, len, gfp);
|
||||
if (error)
|
||||
return ERR_PTR(error);
|
||||
--
|
||||
2.11.0
|
||||
|
@@ -1,219 +0,0 @@
|
||||
From aa4b6bded85552bc5f9f22d2e18ce86c5c17947c Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Tue, 18 Jul 2017 23:37:18 -0700
|
||||
Subject: [PATCH 10/17] apparmor: make policy_unpack able to audit different
|
||||
info messages
|
||||
|
||||
Switch unpack auditing to using the generic name field in the audit
|
||||
struct and make it so we can start adding new info messages about
|
||||
why an unpack failed.
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
Acked-by: Seth Arnold <seth.arnold@canonical.com>
|
||||
(cherry picked from commit 1489d896c5649e9ce1b6000b4857f8baa7a6ab63)
|
||||
---
|
||||
security/apparmor/include/audit.h | 4 +--
|
||||
security/apparmor/policy_unpack.c | 52 ++++++++++++++++++++++++++++-----------
|
||||
2 files changed, 40 insertions(+), 16 deletions(-)
|
||||
|
||||
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
|
||||
index c3fe1c5ef3bc..620e81169659 100644
|
||||
--- a/security/apparmor/include/audit.h
|
||||
+++ b/security/apparmor/include/audit.h
|
||||
@@ -127,9 +127,9 @@ struct apparmor_audit_data {
|
||||
} fs;
|
||||
};
|
||||
struct {
|
||||
- const char *name;
|
||||
- long pos;
|
||||
+ struct aa_profile *profile;
|
||||
const char *ns;
|
||||
+ long pos;
|
||||
} iface;
|
||||
int signal;
|
||||
struct {
|
||||
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
|
||||
index bda0dce3b582..4ede87c30f8b 100644
|
||||
--- a/security/apparmor/policy_unpack.c
|
||||
+++ b/security/apparmor/policy_unpack.c
|
||||
@@ -85,9 +85,9 @@ static void audit_cb(struct audit_buffer *ab, void *va)
|
||||
audit_log_format(ab, " ns=");
|
||||
audit_log_untrustedstring(ab, aad(sa)->iface.ns);
|
||||
}
|
||||
- if (aad(sa)->iface.name) {
|
||||
+ if (aad(sa)->name) {
|
||||
audit_log_format(ab, " name=");
|
||||
- audit_log_untrustedstring(ab, aad(sa)->iface.name);
|
||||
+ audit_log_untrustedstring(ab, aad(sa)->name);
|
||||
}
|
||||
if (aad(sa)->iface.pos)
|
||||
audit_log_format(ab, " offset=%ld", aad(sa)->iface.pos);
|
||||
@@ -114,9 +114,9 @@ static int audit_iface(struct aa_profile *new, const char *ns_name,
|
||||
aad(&sa)->iface.pos = e->pos - e->start;
|
||||
aad(&sa)->iface.ns = ns_name;
|
||||
if (new)
|
||||
- aad(&sa)->iface.name = new->base.hname;
|
||||
+ aad(&sa)->name = new->base.hname;
|
||||
else
|
||||
- aad(&sa)->iface.name = name;
|
||||
+ aad(&sa)->name = name;
|
||||
aad(&sa)->info = info;
|
||||
aad(&sa)->error = error;
|
||||
|
||||
@@ -583,6 +583,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
||||
{
|
||||
struct aa_profile *profile = NULL;
|
||||
const char *tmpname, *tmpns = NULL, *name = NULL;
|
||||
+ const char *info = "failed to unpack profile";
|
||||
size_t ns_len;
|
||||
struct rhashtable_params params = { 0 };
|
||||
char *key = NULL;
|
||||
@@ -604,8 +605,10 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
||||
tmpname = aa_splitn_fqname(name, strlen(name), &tmpns, &ns_len);
|
||||
if (tmpns) {
|
||||
*ns_name = kstrndup(tmpns, ns_len, GFP_KERNEL);
|
||||
- if (!*ns_name)
|
||||
+ if (!*ns_name) {
|
||||
+ info = "out of memory";
|
||||
goto fail;
|
||||
+ }
|
||||
name = tmpname;
|
||||
}
|
||||
|
||||
@@ -624,12 +627,15 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
||||
if (IS_ERR(profile->xmatch)) {
|
||||
error = PTR_ERR(profile->xmatch);
|
||||
profile->xmatch = NULL;
|
||||
+ info = "bad xmatch";
|
||||
goto fail;
|
||||
}
|
||||
/* xmatch_len is not optional if xmatch is set */
|
||||
if (profile->xmatch) {
|
||||
- if (!unpack_u32(e, &tmp, NULL))
|
||||
+ if (!unpack_u32(e, &tmp, NULL)) {
|
||||
+ info = "missing xmatch len";
|
||||
goto fail;
|
||||
+ }
|
||||
profile->xmatch_len = tmp;
|
||||
}
|
||||
|
||||
@@ -637,8 +643,11 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
||||
(void) unpack_str(e, &profile->disconnected, "disconnected");
|
||||
|
||||
/* per profile debug flags (complain, audit) */
|
||||
- if (!unpack_nameX(e, AA_STRUCT, "flags"))
|
||||
+ if (!unpack_nameX(e, AA_STRUCT, "flags")) {
|
||||
+ info = "profile missing flags";
|
||||
goto fail;
|
||||
+ }
|
||||
+ info = "failed to unpack profile flags";
|
||||
if (!unpack_u32(e, &tmp, NULL))
|
||||
goto fail;
|
||||
if (tmp & PACKED_FLAG_HAT)
|
||||
@@ -667,6 +676,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
||||
/* set a default value if path_flags field is not present */
|
||||
profile->path_flags = PATH_MEDIATE_DELETED;
|
||||
|
||||
+ info = "failed to unpack profile capabilities";
|
||||
if (!unpack_u32(e, &(profile->caps.allow.cap[0]), NULL))
|
||||
goto fail;
|
||||
if (!unpack_u32(e, &(profile->caps.audit.cap[0]), NULL))
|
||||
@@ -676,6 +686,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
||||
if (!unpack_u32(e, &tmpcap.cap[0], NULL))
|
||||
goto fail;
|
||||
|
||||
+ info = "failed to unpack upper profile capabilities";
|
||||
if (unpack_nameX(e, AA_STRUCT, "caps64")) {
|
||||
/* optional upper half of 64 bit caps */
|
||||
if (!unpack_u32(e, &(profile->caps.allow.cap[1]), NULL))
|
||||
@@ -690,6 +701,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
+ info = "failed to unpack extended profile capabilities";
|
||||
if (unpack_nameX(e, AA_STRUCT, "capsx")) {
|
||||
/* optional extended caps mediation mask */
|
||||
if (!unpack_u32(e, &(profile->caps.extended.cap[0]), NULL))
|
||||
@@ -700,11 +712,14 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
- if (!unpack_rlimits(e, profile))
|
||||
+ if (!unpack_rlimits(e, profile)) {
|
||||
+ info = "failed to unpack profile rlimits";
|
||||
goto fail;
|
||||
+ }
|
||||
|
||||
if (unpack_nameX(e, AA_STRUCT, "policydb")) {
|
||||
/* generic policy dfa - optional and may be NULL */
|
||||
+ info = "failed to unpack policydb";
|
||||
profile->policy.dfa = unpack_dfa(e);
|
||||
if (IS_ERR(profile->policy.dfa)) {
|
||||
error = PTR_ERR(profile->policy.dfa);
|
||||
@@ -734,6 +749,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
||||
if (IS_ERR(profile->file.dfa)) {
|
||||
error = PTR_ERR(profile->file.dfa);
|
||||
profile->file.dfa = NULL;
|
||||
+ info = "failed to unpack profile file rules";
|
||||
goto fail;
|
||||
} else if (profile->file.dfa) {
|
||||
if (!unpack_u32(e, &profile->file.start, "dfa_start"))
|
||||
@@ -746,10 +762,13 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
||||
} else
|
||||
profile->file.dfa = aa_get_dfa(nulldfa);
|
||||
|
||||
- if (!unpack_trans_table(e, profile))
|
||||
+ if (!unpack_trans_table(e, profile)) {
|
||||
+ info = "failed to unpack profile transition table";
|
||||
goto fail;
|
||||
+ }
|
||||
|
||||
if (unpack_nameX(e, AA_STRUCT, "data")) {
|
||||
+ info = "out of memory";
|
||||
profile->data = kzalloc(sizeof(*profile->data), GFP_KERNEL);
|
||||
if (!profile->data)
|
||||
goto fail;
|
||||
@@ -761,8 +780,10 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
||||
params.hashfn = strhash;
|
||||
params.obj_cmpfn = datacmp;
|
||||
|
||||
- if (rhashtable_init(profile->data, ¶ms))
|
||||
+ if (rhashtable_init(profile->data, ¶ms)) {
|
||||
+ info = "failed to init key, value hash table";
|
||||
goto fail;
|
||||
+ }
|
||||
|
||||
while (unpack_strdup(e, &key, NULL)) {
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
@@ -784,12 +805,16 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
||||
profile->data->p);
|
||||
}
|
||||
|
||||
- if (!unpack_nameX(e, AA_STRUCTEND, NULL))
|
||||
+ if (!unpack_nameX(e, AA_STRUCTEND, NULL)) {
|
||||
+ info = "failed to unpack end of key, value data table";
|
||||
goto fail;
|
||||
+ }
|
||||
}
|
||||
|
||||
- if (!unpack_nameX(e, AA_STRUCTEND, NULL))
|
||||
+ if (!unpack_nameX(e, AA_STRUCTEND, NULL)) {
|
||||
+ info = "failed to unpack end of profile";
|
||||
goto fail;
|
||||
+ }
|
||||
|
||||
return profile;
|
||||
|
||||
@@ -798,8 +823,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
||||
name = NULL;
|
||||
else if (!name)
|
||||
name = "unknown";
|
||||
- audit_iface(profile, NULL, name, "failed to unpack profile", e,
|
||||
- error);
|
||||
+ audit_iface(profile, NULL, name, info, e, error);
|
||||
aa_free_profile(profile);
|
||||
|
||||
return ERR_PTR(error);
|
||||
--
|
||||
2.11.0
|
||||
|
@@ -1,78 +0,0 @@
|
||||
From ba3f778a2ef31454032c2ca9c99d9212feb4dcf1 Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Tue, 18 Jul 2017 23:41:13 -0700
|
||||
Subject: [PATCH 11/17] apparmor: add more debug asserts to apparmorfs
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
Acked-by: Seth Arnold <seth.arnold@canonical.com>
|
||||
(cherry picked from commit 52c9542126fb04df1f12c605b6c22719c9096794)
|
||||
---
|
||||
security/apparmor/apparmorfs.c | 17 +++++++++++++++++
|
||||
1 file changed, 17 insertions(+)
|
||||
|
||||
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
|
||||
index 8fa6c898c44b..7acea14c850b 100644
|
||||
--- a/security/apparmor/apparmorfs.c
|
||||
+++ b/security/apparmor/apparmorfs.c
|
||||
@@ -1446,6 +1446,10 @@ void __aafs_profile_migrate_dents(struct aa_profile *old,
|
||||
{
|
||||
int i;
|
||||
|
||||
+ AA_BUG(!old);
|
||||
+ AA_BUG(!new);
|
||||
+ AA_BUG(!mutex_is_locked(&profiles_ns(old)->lock));
|
||||
+
|
||||
for (i = 0; i < AAFS_PROF_SIZEOF; i++) {
|
||||
new->dents[i] = old->dents[i];
|
||||
if (new->dents[i])
|
||||
@@ -1509,6 +1513,9 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
|
||||
struct dentry *dent = NULL, *dir;
|
||||
int error;
|
||||
|
||||
+ AA_BUG(!profile);
|
||||
+ AA_BUG(!mutex_is_locked(&profiles_ns(profile)->lock));
|
||||
+
|
||||
if (!parent) {
|
||||
struct aa_profile *p;
|
||||
p = aa_deref_parent(profile);
|
||||
@@ -1734,6 +1741,7 @@ void __aafs_ns_rmdir(struct aa_ns *ns)
|
||||
|
||||
if (!ns)
|
||||
return;
|
||||
+ AA_BUG(!mutex_is_locked(&ns->lock));
|
||||
|
||||
list_for_each_entry(child, &ns->base.profiles, base.list)
|
||||
__aafs_profile_rmdir(child);
|
||||
@@ -1906,6 +1914,10 @@ static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns)
|
||||
{
|
||||
struct aa_ns *parent, *next;
|
||||
|
||||
+ AA_BUG(!root);
|
||||
+ AA_BUG(!ns);
|
||||
+ AA_BUG(ns != root && !mutex_is_locked(&ns->parent->lock));
|
||||
+
|
||||
/* is next namespace a child */
|
||||
if (!list_empty(&ns->sub_ns)) {
|
||||
next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list);
|
||||
@@ -1940,6 +1952,9 @@ static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns)
|
||||
static struct aa_profile *__first_profile(struct aa_ns *root,
|
||||
struct aa_ns *ns)
|
||||
{
|
||||
+ AA_BUG(!root);
|
||||
+ AA_BUG(ns && !mutex_is_locked(&ns->lock));
|
||||
+
|
||||
for (; ns; ns = __next_ns(root, ns)) {
|
||||
if (!list_empty(&ns->base.profiles))
|
||||
return list_first_entry(&ns->base.profiles,
|
||||
@@ -1962,6 +1977,8 @@ static struct aa_profile *__next_profile(struct aa_profile *p)
|
||||
struct aa_profile *parent;
|
||||
struct aa_ns *ns = p->ns;
|
||||
|
||||
+ AA_BUG(!mutex_is_locked(&profiles_ns(p)->lock));
|
||||
+
|
||||
/* is next profile a child */
|
||||
if (!list_empty(&p->base.profiles))
|
||||
return list_first_entry(&p->base.profiles, typeof(*p),
|
||||
--
|
||||
2.11.0
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,194 +0,0 @@
|
||||
From 50d30adbef98a0b6cc531a9413d05f564eb633ee Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Wed, 16 Aug 2017 08:59:57 -0700
|
||||
Subject: [PATCH 13/17] apparmor: move new_null_profile to after profile lookup
|
||||
fns()
|
||||
|
||||
new_null_profile will need to use some of the profile lookup fns()
|
||||
so move instead of doing forward fn declarations.
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
(cherry picked from commit cf1e50dfc6f627bc2989b57076b129c330fb3f0a)
|
||||
---
|
||||
security/apparmor/policy.c | 158 ++++++++++++++++++++++-----------------------
|
||||
1 file changed, 79 insertions(+), 79 deletions(-)
|
||||
|
||||
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
|
||||
index 244ea4a4a8f0..a81a384a63b1 100644
|
||||
--- a/security/apparmor/policy.c
|
||||
+++ b/security/apparmor/policy.c
|
||||
@@ -289,85 +289,6 @@ struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
-/**
|
||||
- * aa_new_null_profile - create or find a null-X learning profile
|
||||
- * @parent: profile that caused this profile to be created (NOT NULL)
|
||||
- * @hat: true if the null- learning profile is a hat
|
||||
- * @base: name to base the null profile off of
|
||||
- * @gfp: type of allocation
|
||||
- *
|
||||
- * Find/Create a null- complain mode profile used in learning mode. The
|
||||
- * name of the profile is unique and follows the format of parent//null-XXX.
|
||||
- * where XXX is based on the @name or if that fails or is not supplied
|
||||
- * a unique number
|
||||
- *
|
||||
- * null profiles are added to the profile list but the list does not
|
||||
- * hold a count on them so that they are automatically released when
|
||||
- * not in use.
|
||||
- *
|
||||
- * Returns: new refcounted profile else NULL on failure
|
||||
- */
|
||||
-struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat,
|
||||
- const char *base, gfp_t gfp)
|
||||
-{
|
||||
- struct aa_profile *profile;
|
||||
- char *name;
|
||||
-
|
||||
- AA_BUG(!parent);
|
||||
-
|
||||
- if (base) {
|
||||
- name = kmalloc(strlen(parent->base.hname) + 8 + strlen(base),
|
||||
- gfp);
|
||||
- if (name) {
|
||||
- sprintf(name, "%s//null-%s", parent->base.hname, base);
|
||||
- goto name;
|
||||
- }
|
||||
- /* fall through to try shorter uniq */
|
||||
- }
|
||||
-
|
||||
- name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, gfp);
|
||||
- if (!name)
|
||||
- return NULL;
|
||||
- sprintf(name, "%s//null-%x", parent->base.hname,
|
||||
- atomic_inc_return(&parent->ns->uniq_null));
|
||||
-
|
||||
-name:
|
||||
- /* lookup to see if this is a dup creation */
|
||||
- profile = aa_find_child(parent, basename(name));
|
||||
- if (profile)
|
||||
- goto out;
|
||||
-
|
||||
- profile = aa_alloc_profile(name, NULL, gfp);
|
||||
- if (!profile)
|
||||
- goto fail;
|
||||
-
|
||||
- profile->mode = APPARMOR_COMPLAIN;
|
||||
- profile->label.flags |= FLAG_NULL;
|
||||
- if (hat)
|
||||
- profile->label.flags |= FLAG_HAT;
|
||||
- profile->path_flags = parent->path_flags;
|
||||
-
|
||||
- /* released on free_profile */
|
||||
- rcu_assign_pointer(profile->parent, aa_get_profile(parent));
|
||||
- profile->ns = aa_get_ns(parent->ns);
|
||||
- profile->file.dfa = aa_get_dfa(nulldfa);
|
||||
- profile->policy.dfa = aa_get_dfa(nulldfa);
|
||||
-
|
||||
- mutex_lock(&profile->ns->lock);
|
||||
- __add_profile(&parent->base.profiles, profile);
|
||||
- mutex_unlock(&profile->ns->lock);
|
||||
-
|
||||
- /* refcount released by caller */
|
||||
-out:
|
||||
- kfree(name);
|
||||
-
|
||||
- return profile;
|
||||
-
|
||||
-fail:
|
||||
- aa_free_profile(profile);
|
||||
- return NULL;
|
||||
-}
|
||||
-
|
||||
/* TODO: profile accounting - setup in remove */
|
||||
|
||||
/**
|
||||
@@ -559,6 +480,85 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
|
||||
}
|
||||
|
||||
/**
|
||||
+ * aa_new_null_profile - create or find a null-X learning profile
|
||||
+ * @parent: profile that caused this profile to be created (NOT NULL)
|
||||
+ * @hat: true if the null- learning profile is a hat
|
||||
+ * @base: name to base the null profile off of
|
||||
+ * @gfp: type of allocation
|
||||
+ *
|
||||
+ * Find/Create a null- complain mode profile used in learning mode. The
|
||||
+ * name of the profile is unique and follows the format of parent//null-XXX.
|
||||
+ * where XXX is based on the @name or if that fails or is not supplied
|
||||
+ * a unique number
|
||||
+ *
|
||||
+ * null profiles are added to the profile list but the list does not
|
||||
+ * hold a count on them so that they are automatically released when
|
||||
+ * not in use.
|
||||
+ *
|
||||
+ * Returns: new refcounted profile else NULL on failure
|
||||
+ */
|
||||
+struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat,
|
||||
+ const char *base, gfp_t gfp)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ char *name;
|
||||
+
|
||||
+ AA_BUG(!parent);
|
||||
+
|
||||
+ if (base) {
|
||||
+ name = kmalloc(strlen(parent->base.hname) + 8 + strlen(base),
|
||||
+ gfp);
|
||||
+ if (name) {
|
||||
+ sprintf(name, "%s//null-%s", parent->base.hname, base);
|
||||
+ goto name;
|
||||
+ }
|
||||
+ /* fall through to try shorter uniq */
|
||||
+ }
|
||||
+
|
||||
+ name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, gfp);
|
||||
+ if (!name)
|
||||
+ return NULL;
|
||||
+ sprintf(name, "%s//null-%x", parent->base.hname,
|
||||
+ atomic_inc_return(&parent->ns->uniq_null));
|
||||
+
|
||||
+name:
|
||||
+ /* lookup to see if this is a dup creation */
|
||||
+ profile = aa_find_child(parent, basename(name));
|
||||
+ if (profile)
|
||||
+ goto out;
|
||||
+
|
||||
+ profile = aa_alloc_profile(name, NULL, gfp);
|
||||
+ if (!profile)
|
||||
+ goto fail;
|
||||
+
|
||||
+ profile->mode = APPARMOR_COMPLAIN;
|
||||
+ profile->label.flags |= FLAG_NULL;
|
||||
+ if (hat)
|
||||
+ profile->label.flags |= FLAG_HAT;
|
||||
+ profile->path_flags = parent->path_flags;
|
||||
+
|
||||
+ /* released on free_profile */
|
||||
+ rcu_assign_pointer(profile->parent, aa_get_profile(parent));
|
||||
+ profile->ns = aa_get_ns(parent->ns);
|
||||
+ profile->file.dfa = aa_get_dfa(nulldfa);
|
||||
+ profile->policy.dfa = aa_get_dfa(nulldfa);
|
||||
+
|
||||
+ mutex_lock(&profile->ns->lock);
|
||||
+ __add_profile(&parent->base.profiles, profile);
|
||||
+ mutex_unlock(&profile->ns->lock);
|
||||
+
|
||||
+ /* refcount released by caller */
|
||||
+out:
|
||||
+ kfree(name);
|
||||
+
|
||||
+ return profile;
|
||||
+
|
||||
+fail:
|
||||
+ aa_free_profile(profile);
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
* replacement_allowed - test to see if replacement is allowed
|
||||
* @profile: profile to test if it can be replaced (MAYBE NULL)
|
||||
* @noreplace: true if replacement shouldn't be allowed but addition is okay
|
||||
--
|
||||
2.11.0
|
||||
|
@@ -1,60 +0,0 @@
|
||||
From ab3b869791b6122c7be7e68ca4c08e2c2e8815ac Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Wed, 16 Aug 2017 05:40:49 -0700
|
||||
Subject: [PATCH 14/17] apparmor: fix race condition in null profile creation
|
||||
|
||||
There is a race when null- profile is being created between the
|
||||
initial lookup/creation of the profile and lock/addition of the
|
||||
profile. This could result in multiple version of a profile being
|
||||
added to the list which need to be removed/replaced.
|
||||
|
||||
Since these are learning profile their is no affect on mediation.
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
(cherry picked from commit 3aa3de2a4fb8f33ec62b00998bc6b6c6850d41b1)
|
||||
---
|
||||
security/apparmor/policy.c | 14 +++++++++++---
|
||||
1 file changed, 11 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
|
||||
index a81a384a63b1..4243b0c3f0e4 100644
|
||||
--- a/security/apparmor/policy.c
|
||||
+++ b/security/apparmor/policy.c
|
||||
@@ -500,7 +500,8 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
|
||||
struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat,
|
||||
const char *base, gfp_t gfp)
|
||||
{
|
||||
- struct aa_profile *profile;
|
||||
+ struct aa_profile *p, *profile;
|
||||
+ const char *bname;
|
||||
char *name;
|
||||
|
||||
AA_BUG(!parent);
|
||||
@@ -523,7 +524,8 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat,
|
||||
|
||||
name:
|
||||
/* lookup to see if this is a dup creation */
|
||||
- profile = aa_find_child(parent, basename(name));
|
||||
+ bname = basename(name);
|
||||
+ profile = aa_find_child(parent, bname);
|
||||
if (profile)
|
||||
goto out;
|
||||
|
||||
@@ -544,7 +546,13 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat,
|
||||
profile->policy.dfa = aa_get_dfa(nulldfa);
|
||||
|
||||
mutex_lock(&profile->ns->lock);
|
||||
- __add_profile(&parent->base.profiles, profile);
|
||||
+ p = __find_child(&parent->base.profiles, bname);
|
||||
+ if (p) {
|
||||
+ aa_free_profile(profile);
|
||||
+ profile = aa_get_profile(p);
|
||||
+ } else {
|
||||
+ __add_profile(&parent->base.profiles, profile);
|
||||
+ }
|
||||
mutex_unlock(&profile->ns->lock);
|
||||
|
||||
/* refcount released by caller */
|
||||
--
|
||||
2.11.0
|
||||
|
@@ -1,36 +0,0 @@
|
||||
From 7f2cdd6453518ff76c3855255c91306a2b928c9a Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Wed, 16 Aug 2017 05:48:06 -0700
|
||||
Subject: [PATCH 15/17] apparmor: ensure unconfined profiles have dfas
|
||||
initialized
|
||||
|
||||
Generally unconfined has early bailout tests and does not need the
|
||||
dfas initialized, however if an early bailout test is ever missed
|
||||
it will result in an oops.
|
||||
|
||||
Be defensive and initialize the unconfined profile to have null dfas
|
||||
(no permission) so if an early bailout test is missed we fail
|
||||
closed (no perms granted) instead of oopsing.
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
(cherry picked from commit 034ad2d248927722bdcd1aedb62634cdc2049113)
|
||||
---
|
||||
security/apparmor/policy_ns.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c
|
||||
index 351d3bab3a3d..62a3589c62ab 100644
|
||||
--- a/security/apparmor/policy_ns.c
|
||||
+++ b/security/apparmor/policy_ns.c
|
||||
@@ -112,6 +112,8 @@ static struct aa_ns *alloc_ns(const char *prefix, const char *name)
|
||||
ns->unconfined->label.flags |= FLAG_IX_ON_NAME_ERROR |
|
||||
FLAG_IMMUTIBLE | FLAG_NS_COUNT | FLAG_UNCONFINED;
|
||||
ns->unconfined->mode = APPARMOR_UNCONFINED;
|
||||
+ ns->unconfined->file.dfa = aa_get_dfa(nulldfa);
|
||||
+ ns->unconfined->policy.dfa = aa_get_dfa(nulldfa);
|
||||
|
||||
/* ns and ns->unconfined share ns->unconfined refcount */
|
||||
ns->unconfined->ns = ns;
|
||||
--
|
||||
2.11.0
|
||||
|
@@ -1,39 +0,0 @@
|
||||
From 8daf877473653c06a28c86bf72d63ce7e5c1d542 Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Wed, 16 Aug 2017 09:33:48 -0700
|
||||
Subject: [PATCH 16/17] apparmor: fix incorrect type assignment when freeing
|
||||
proxies
|
||||
|
||||
sparse reports
|
||||
|
||||
poisoning the proxy->label before freeing the struct is resulting in
|
||||
a sparse build warning.
|
||||
../security/apparmor/label.c:52:30: warning: incorrect type in assignment (different address spaces)
|
||||
../security/apparmor/label.c:52:30: expected struct aa_label [noderef] <asn:4>*label
|
||||
../security/apparmor/label.c:52:30: got struct aa_label *<noident>
|
||||
|
||||
fix with RCU_INIT_POINTER as this is one of those cases where
|
||||
rcu_assign_pointer() is not needed.
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
(cherry picked from commit 76e22e212a850bbd16cf49f9c586d4635507e0b5)
|
||||
---
|
||||
security/apparmor/label.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/security/apparmor/label.c b/security/apparmor/label.c
|
||||
index 52b4ef14840d..c5b99b954580 100644
|
||||
--- a/security/apparmor/label.c
|
||||
+++ b/security/apparmor/label.c
|
||||
@@ -49,7 +49,7 @@ static void free_proxy(struct aa_proxy *proxy)
|
||||
/* p->label will not updated any more as p is dead */
|
||||
aa_put_label(rcu_dereference_protected(proxy->label, true));
|
||||
memset(proxy, 0, sizeof(*proxy));
|
||||
- proxy->label = (struct aa_label *) PROXY_POISON;
|
||||
+ RCU_INIT_POINTER(proxy->label, (struct aa_label *)PROXY_POISON);
|
||||
kfree(proxy);
|
||||
}
|
||||
}
|
||||
--
|
||||
2.11.0
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,5 +0,0 @@
|
||||
The old out of tree patches have been dropped.
|
||||
|
||||
This series is a backport of the patches currently in security-next
|
||||
scheduled for 4.14, with the exception of the last patch for af_unix
|
||||
mediation.
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,4 +0,0 @@
|
||||
This is based on v4.14 final
|
||||
|
||||
base socket mediation and af_unix-mediation are the last two remaining
|
||||
patches that are out of tree
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,603 +0,0 @@
|
||||
From 269384ead6a3c82ac31fd3778e899ccc6a54358e Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Mon, 4 Oct 2010 15:03:36 -0700
|
||||
Subject: [PATCH 1/3] UBUNTU: SAUCE: AppArmor: basic networking rules
|
||||
|
||||
Base support for network mediation.
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
---
|
||||
security/apparmor/.gitignore | 1 +
|
||||
security/apparmor/Makefile | 42 +++++++++-
|
||||
security/apparmor/apparmorfs.c | 1 +
|
||||
security/apparmor/include/audit.h | 4 +
|
||||
security/apparmor/include/net.h | 44 ++++++++++
|
||||
security/apparmor/include/policy.h | 3 +
|
||||
security/apparmor/lsm.c | 112 +++++++++++++++++++++++++
|
||||
security/apparmor/net.c | 162 +++++++++++++++++++++++++++++++++++++
|
||||
security/apparmor/policy.c | 1 +
|
||||
security/apparmor/policy_unpack.c | 46 +++++++++++
|
||||
10 files changed, 414 insertions(+), 2 deletions(-)
|
||||
create mode 100644 security/apparmor/include/net.h
|
||||
create mode 100644 security/apparmor/net.c
|
||||
|
||||
diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore
|
||||
index 9cdec70d72b8..d5b291e94264 100644
|
||||
--- a/security/apparmor/.gitignore
|
||||
+++ b/security/apparmor/.gitignore
|
||||
@@ -1,5 +1,6 @@
|
||||
#
|
||||
# Generated include files
|
||||
#
|
||||
+net_names.h
|
||||
capability_names.h
|
||||
rlim_names.h
|
||||
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
|
||||
index d693df874818..5dbb72f46452 100644
|
||||
--- a/security/apparmor/Makefile
|
||||
+++ b/security/apparmor/Makefile
|
||||
@@ -4,10 +4,10 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
|
||||
|
||||
apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
|
||||
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
|
||||
- resource.o sid.o file.o
|
||||
+ resource.o sid.o file.o net.o
|
||||
apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o
|
||||
|
||||
-clean-files := capability_names.h rlim_names.h
|
||||
+clean-files := capability_names.h rlim_names.h net_names.h
|
||||
|
||||
|
||||
# Build a lower case string table of capability names
|
||||
@@ -25,6 +25,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\
|
||||
-e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/\L\1/p' | \
|
||||
tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@
|
||||
|
||||
+# Build a lower case string table of address family names
|
||||
+# Transform lines from
|
||||
+# define AF_LOCAL 1 /* POSIX name for AF_UNIX */
|
||||
+# #define AF_INET 2 /* Internet IP Protocol */
|
||||
+# to
|
||||
+# [1] = "local",
|
||||
+# [2] = "inet",
|
||||
+#
|
||||
+# and build the securityfs entries for the mapping.
|
||||
+# Transforms lines from
|
||||
+# #define AF_INET 2 /* Internet IP Protocol */
|
||||
+# to
|
||||
+# #define AA_FS_AF_MASK "local inet"
|
||||
+quiet_cmd_make-af = GEN $@
|
||||
+cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\
|
||||
+ sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \
|
||||
+ 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\
|
||||
+ echo "};" >> $@ ;\
|
||||
+ echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\
|
||||
+ sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\
|
||||
+ $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@
|
||||
+
|
||||
+# Build a lower case string table of sock type names
|
||||
+# Transform lines from
|
||||
+# SOCK_STREAM = 1,
|
||||
+# to
|
||||
+# [1] = "stream",
|
||||
+quiet_cmd_make-sock = GEN $@
|
||||
+cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\
|
||||
+ sed $^ >>$@ -r -n \
|
||||
+ -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\
|
||||
+ echo "};" >> $@
|
||||
|
||||
# Build a lower case string table of rlimit names.
|
||||
# Transforms lines from
|
||||
@@ -61,6 +93,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \
|
||||
tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@
|
||||
|
||||
$(obj)/capability.o : $(obj)/capability_names.h
|
||||
+$(obj)/net.o : $(obj)/net_names.h
|
||||
$(obj)/resource.o : $(obj)/rlim_names.h
|
||||
$(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \
|
||||
$(src)/Makefile
|
||||
@@ -68,3 +101,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \
|
||||
$(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \
|
||||
$(src)/Makefile
|
||||
$(call cmd,make-rlim)
|
||||
+$(obj)/net_names.h : $(srctree)/include/linux/socket.h \
|
||||
+ $(srctree)/include/linux/net.h \
|
||||
+ $(src)/Makefile
|
||||
+ $(call cmd,make-af)
|
||||
+ $(call cmd,make-sock)
|
||||
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
|
||||
index 729e595119ed..181d961e6d58 100644
|
||||
--- a/security/apparmor/apparmorfs.c
|
||||
+++ b/security/apparmor/apparmorfs.c
|
||||
@@ -807,6 +807,7 @@ static struct aa_fs_entry aa_fs_entry_features[] = {
|
||||
AA_FS_DIR("policy", aa_fs_entry_policy),
|
||||
AA_FS_DIR("domain", aa_fs_entry_domain),
|
||||
AA_FS_DIR("file", aa_fs_entry_file),
|
||||
+ AA_FS_DIR("network", aa_fs_entry_network),
|
||||
AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK),
|
||||
AA_FS_DIR("rlimit", aa_fs_entry_rlimit),
|
||||
AA_FS_DIR("caps", aa_fs_entry_caps),
|
||||
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
|
||||
index ba3dfd17f23f..5d3c419b17d9 100644
|
||||
--- a/security/apparmor/include/audit.h
|
||||
+++ b/security/apparmor/include/audit.h
|
||||
@@ -125,6 +125,10 @@ struct apparmor_audit_data {
|
||||
u32 denied;
|
||||
kuid_t ouid;
|
||||
} fs;
|
||||
+ struct {
|
||||
+ int type, protocol;
|
||||
+ struct sock *sk;
|
||||
+ } net;
|
||||
};
|
||||
};
|
||||
|
||||
diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h
|
||||
new file mode 100644
|
||||
index 000000000000..cb8a12109b7a
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/include/net.h
|
||||
@@ -0,0 +1,44 @@
|
||||
+/*
|
||||
+ * AppArmor security module
|
||||
+ *
|
||||
+ * This file contains AppArmor network mediation definitions.
|
||||
+ *
|
||||
+ * Copyright (C) 1998-2008 Novell/SUSE
|
||||
+ * Copyright 2009-2012 Canonical Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License as
|
||||
+ * published by the Free Software Foundation, version 2 of the
|
||||
+ * License.
|
||||
+ */
|
||||
+
|
||||
+#ifndef __AA_NET_H
|
||||
+#define __AA_NET_H
|
||||
+
|
||||
+#include <net/sock.h>
|
||||
+
|
||||
+#include "apparmorfs.h"
|
||||
+
|
||||
+/* struct aa_net - network confinement data
|
||||
+ * @allowed: basic network families permissions
|
||||
+ * @audit_network: which network permissions to force audit
|
||||
+ * @quiet_network: which network permissions to quiet rejects
|
||||
+ */
|
||||
+struct aa_net {
|
||||
+ u16 allow[AF_MAX];
|
||||
+ u16 audit[AF_MAX];
|
||||
+ u16 quiet[AF_MAX];
|
||||
+};
|
||||
+
|
||||
+extern struct aa_fs_entry aa_fs_entry_network[];
|
||||
+
|
||||
+extern int aa_net_perm(int op, struct aa_profile *profile, u16 family,
|
||||
+ int type, int protocol, struct sock *sk);
|
||||
+extern int aa_revalidate_sk(int op, struct sock *sk);
|
||||
+
|
||||
+static inline void aa_free_net_rules(struct aa_net *new)
|
||||
+{
|
||||
+ /* NOP */
|
||||
+}
|
||||
+
|
||||
+#endif /* __AA_NET_H */
|
||||
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
|
||||
index 52275f040a5f..4fc4dacc1101 100644
|
||||
--- a/security/apparmor/include/policy.h
|
||||
+++ b/security/apparmor/include/policy.h
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "capability.h"
|
||||
#include "domain.h"
|
||||
#include "file.h"
|
||||
+#include "net.h"
|
||||
#include "resource.h"
|
||||
|
||||
extern const char *const aa_profile_mode_names[];
|
||||
@@ -176,6 +177,7 @@ struct aa_replacedby {
|
||||
* @policy: general match rules governing policy
|
||||
* @file: The set of rules governing basic file access and domain transitions
|
||||
* @caps: capabilities for the profile
|
||||
+ * @net: network controls for the profile
|
||||
* @rlimits: rlimits for the profile
|
||||
*
|
||||
* @dents: dentries for the profiles file entries in apparmorfs
|
||||
@@ -217,6 +219,7 @@ struct aa_profile {
|
||||
struct aa_policydb policy;
|
||||
struct aa_file_rules file;
|
||||
struct aa_caps caps;
|
||||
+ struct aa_net net;
|
||||
struct aa_rlimit rlimits;
|
||||
|
||||
unsigned char *hash;
|
||||
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
|
||||
index 41b8cb115801..d96b5f7c1912 100644
|
||||
--- a/security/apparmor/lsm.c
|
||||
+++ b/security/apparmor/lsm.c
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "include/context.h"
|
||||
#include "include/file.h"
|
||||
#include "include/ipc.h"
|
||||
+#include "include/net.h"
|
||||
#include "include/path.h"
|
||||
#include "include/policy.h"
|
||||
#include "include/procattr.h"
|
||||
@@ -584,6 +585,104 @@ static int apparmor_task_setrlimit(struct task_struct *task,
|
||||
return error;
|
||||
}
|
||||
|
||||
+static int apparmor_socket_create(int family, int type, int protocol, int kern)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ int error = 0;
|
||||
+
|
||||
+ if (kern)
|
||||
+ return 0;
|
||||
+
|
||||
+ profile = __aa_current_profile();
|
||||
+ if (!unconfined(profile))
|
||||
+ error = aa_net_perm(OP_CREATE, profile, family, type, protocol,
|
||||
+ NULL);
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_bind(struct socket *sock,
|
||||
+ struct sockaddr *address, int addrlen)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_BIND, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_connect(struct socket *sock,
|
||||
+ struct sockaddr *address, int addrlen)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_CONNECT, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_listen(struct socket *sock, int backlog)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_LISTEN, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_accept(struct socket *sock, struct socket *newsock)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_ACCEPT, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_sendmsg(struct socket *sock,
|
||||
+ struct msghdr *msg, int size)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_SENDMSG, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_recvmsg(struct socket *sock,
|
||||
+ struct msghdr *msg, int size, int flags)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_RECVMSG, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_getsockname(struct socket *sock)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_GETSOCKNAME, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_getpeername(struct socket *sock)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_GETPEERNAME, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_getsockopt(struct socket *sock, int level,
|
||||
+ int optname)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_GETSOCKOPT, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_setsockopt(struct socket *sock, int level,
|
||||
+ int optname)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_SETSOCKOPT, sk);
|
||||
+}
|
||||
+
|
||||
+static int apparmor_socket_shutdown(struct socket *sock, int how)
|
||||
+{
|
||||
+ struct sock *sk = sock->sk;
|
||||
+
|
||||
+ return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk);
|
||||
+}
|
||||
+
|
||||
static struct security_hook_list apparmor_hooks[] = {
|
||||
LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check),
|
||||
LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme),
|
||||
@@ -613,6 +712,19 @@ static struct security_hook_list apparmor_hooks[] = {
|
||||
LSM_HOOK_INIT(getprocattr, apparmor_getprocattr),
|
||||
LSM_HOOK_INIT(setprocattr, apparmor_setprocattr),
|
||||
|
||||
+ LSM_HOOK_INIT(socket_create, apparmor_socket_create),
|
||||
+ LSM_HOOK_INIT(socket_bind, apparmor_socket_bind),
|
||||
+ LSM_HOOK_INIT(socket_connect, apparmor_socket_connect),
|
||||
+ LSM_HOOK_INIT(socket_listen, apparmor_socket_listen),
|
||||
+ LSM_HOOK_INIT(socket_accept, apparmor_socket_accept),
|
||||
+ LSM_HOOK_INIT(socket_sendmsg, apparmor_socket_sendmsg),
|
||||
+ LSM_HOOK_INIT(socket_recvmsg, apparmor_socket_recvmsg),
|
||||
+ LSM_HOOK_INIT(socket_getsockname, apparmor_socket_getsockname),
|
||||
+ LSM_HOOK_INIT(socket_getpeername, apparmor_socket_getpeername),
|
||||
+ LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt),
|
||||
+ LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt),
|
||||
+ LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown),
|
||||
+
|
||||
LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank),
|
||||
LSM_HOOK_INIT(cred_free, apparmor_cred_free),
|
||||
LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare),
|
||||
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
|
||||
new file mode 100644
|
||||
index 000000000000..003dd18c61a5
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/net.c
|
||||
@@ -0,0 +1,162 @@
|
||||
+/*
|
||||
+ * AppArmor security module
|
||||
+ *
|
||||
+ * This file contains AppArmor network mediation
|
||||
+ *
|
||||
+ * Copyright (C) 1998-2008 Novell/SUSE
|
||||
+ * Copyright 2009-2012 Canonical Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License as
|
||||
+ * published by the Free Software Foundation, version 2 of the
|
||||
+ * License.
|
||||
+ */
|
||||
+
|
||||
+#include "include/apparmor.h"
|
||||
+#include "include/audit.h"
|
||||
+#include "include/context.h"
|
||||
+#include "include/net.h"
|
||||
+#include "include/policy.h"
|
||||
+
|
||||
+#include "net_names.h"
|
||||
+
|
||||
+struct aa_fs_entry aa_fs_entry_network[] = {
|
||||
+ AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK),
|
||||
+ { }
|
||||
+};
|
||||
+
|
||||
+/* audit callback for net specific fields */
|
||||
+static void audit_cb(struct audit_buffer *ab, void *va)
|
||||
+{
|
||||
+ struct common_audit_data *sa = va;
|
||||
+
|
||||
+ audit_log_format(ab, " family=");
|
||||
+ if (address_family_names[sa->u.net->family]) {
|
||||
+ audit_log_string(ab, address_family_names[sa->u.net->family]);
|
||||
+ } else {
|
||||
+ audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family);
|
||||
+ }
|
||||
+ audit_log_format(ab, " sock_type=");
|
||||
+ if (sock_type_names[sa->aad->net.type]) {
|
||||
+ audit_log_string(ab, sock_type_names[sa->aad->net.type]);
|
||||
+ } else {
|
||||
+ audit_log_format(ab, "\"unknown(%d)\"", sa->aad->net.type);
|
||||
+ }
|
||||
+ audit_log_format(ab, " protocol=%d", sa->aad->net.protocol);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * audit_net - audit network access
|
||||
+ * @profile: profile being enforced (NOT NULL)
|
||||
+ * @op: operation being checked
|
||||
+ * @family: network family
|
||||
+ * @type: network type
|
||||
+ * @protocol: network protocol
|
||||
+ * @sk: socket auditing is being applied to
|
||||
+ * @error: error code for failure else 0
|
||||
+ *
|
||||
+ * Returns: %0 or sa->error else other errorcode on failure
|
||||
+ */
|
||||
+static int audit_net(struct aa_profile *profile, int op, u16 family, int type,
|
||||
+ int protocol, struct sock *sk, int error)
|
||||
+{
|
||||
+ int audit_type = AUDIT_APPARMOR_AUTO;
|
||||
+ struct common_audit_data sa;
|
||||
+ struct apparmor_audit_data aad = { };
|
||||
+ struct lsm_network_audit net = { };
|
||||
+ if (sk) {
|
||||
+ sa.type = LSM_AUDIT_DATA_NET;
|
||||
+ } else {
|
||||
+ sa.type = LSM_AUDIT_DATA_NONE;
|
||||
+ }
|
||||
+ /* todo fill in socket addr info */
|
||||
+ sa.aad = &aad;
|
||||
+ sa.u.net = &net;
|
||||
+ sa.aad->op = op,
|
||||
+ sa.u.net->family = family;
|
||||
+ sa.u.net->sk = sk;
|
||||
+ sa.aad->net.type = type;
|
||||
+ sa.aad->net.protocol = protocol;
|
||||
+ sa.aad->error = error;
|
||||
+
|
||||
+ if (likely(!sa.aad->error)) {
|
||||
+ u16 audit_mask = profile->net.audit[sa.u.net->family];
|
||||
+ if (likely((AUDIT_MODE(profile) != AUDIT_ALL) &&
|
||||
+ !(1 << sa.aad->net.type & audit_mask)))
|
||||
+ return 0;
|
||||
+ audit_type = AUDIT_APPARMOR_AUDIT;
|
||||
+ } else {
|
||||
+ u16 quiet_mask = profile->net.quiet[sa.u.net->family];
|
||||
+ u16 kill_mask = 0;
|
||||
+ u16 denied = (1 << sa.aad->net.type) & ~quiet_mask;
|
||||
+
|
||||
+ if (denied & kill_mask)
|
||||
+ audit_type = AUDIT_APPARMOR_KILL;
|
||||
+
|
||||
+ if ((denied & quiet_mask) &&
|
||||
+ AUDIT_MODE(profile) != AUDIT_NOQUIET &&
|
||||
+ AUDIT_MODE(profile) != AUDIT_ALL)
|
||||
+ return COMPLAIN_MODE(profile) ? 0 : sa.aad->error;
|
||||
+ }
|
||||
+
|
||||
+ return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * aa_net_perm - very course network access check
|
||||
+ * @op: operation being checked
|
||||
+ * @profile: profile being enforced (NOT NULL)
|
||||
+ * @family: network family
|
||||
+ * @type: network type
|
||||
+ * @protocol: network protocol
|
||||
+ *
|
||||
+ * Returns: %0 else error if permission denied
|
||||
+ */
|
||||
+int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type,
|
||||
+ int protocol, struct sock *sk)
|
||||
+{
|
||||
+ u16 family_mask;
|
||||
+ int error;
|
||||
+
|
||||
+ if ((family < 0) || (family >= AF_MAX))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if ((type < 0) || (type >= SOCK_MAX))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /* unix domain and netlink sockets are handled by ipc */
|
||||
+ if (family == AF_UNIX || family == AF_NETLINK)
|
||||
+ return 0;
|
||||
+
|
||||
+ family_mask = profile->net.allow[family];
|
||||
+
|
||||
+ error = (family_mask & (1 << type)) ? 0 : -EACCES;
|
||||
+
|
||||
+ return audit_net(profile, op, family, type, protocol, sk, error);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * aa_revalidate_sk - Revalidate access to a sock
|
||||
+ * @op: operation being checked
|
||||
+ * @sk: sock being revalidated (NOT NULL)
|
||||
+ *
|
||||
+ * Returns: %0 else error if permission denied
|
||||
+ */
|
||||
+int aa_revalidate_sk(int op, struct sock *sk)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ int error = 0;
|
||||
+
|
||||
+ /* aa_revalidate_sk should not be called from interrupt context
|
||||
+ * don't mediate these calls as they are not task related
|
||||
+ */
|
||||
+ if (in_interrupt())
|
||||
+ return 0;
|
||||
+
|
||||
+ profile = __aa_current_profile();
|
||||
+ if (!unconfined(profile))
|
||||
+ error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type,
|
||||
+ sk->sk_protocol, sk);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
|
||||
index 179e68d7dc5f..f1a8541760e8 100644
|
||||
--- a/security/apparmor/policy.c
|
||||
+++ b/security/apparmor/policy.c
|
||||
@@ -603,6 +603,7 @@ void aa_free_profile(struct aa_profile *profile)
|
||||
|
||||
aa_free_file_rules(&profile->file);
|
||||
aa_free_cap_rules(&profile->caps);
|
||||
+ aa_free_net_rules(&profile->net);
|
||||
aa_free_rlimit_rules(&profile->rlimits);
|
||||
|
||||
kzfree(profile->dirname);
|
||||
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
|
||||
index 138120698f83..7dc15ff91299 100644
|
||||
--- a/security/apparmor/policy_unpack.c
|
||||
+++ b/security/apparmor/policy_unpack.c
|
||||
@@ -193,6 +193,19 @@ fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name)
|
||||
+{
|
||||
+ if (unpack_nameX(e, AA_U16, name)) {
|
||||
+ if (!inbounds(e, sizeof(u16)))
|
||||
+ return 0;
|
||||
+ if (data)
|
||||
+ *data = le16_to_cpu(get_unaligned((u16 *) e->pos));
|
||||
+ e->pos += sizeof(u16);
|
||||
+ return 1;
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name)
|
||||
{
|
||||
if (unpack_nameX(e, AA_U32, name)) {
|
||||
@@ -476,6 +489,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
|
||||
{
|
||||
struct aa_profile *profile = NULL;
|
||||
const char *name = NULL;
|
||||
+ size_t size = 0;
|
||||
int i, error = -EPROTO;
|
||||
kernel_cap_t tmpcap;
|
||||
u32 tmp;
|
||||
@@ -576,6 +590,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
|
||||
if (!unpack_rlimits(e, profile))
|
||||
goto fail;
|
||||
|
||||
+ size = unpack_array(e, "net_allowed_af");
|
||||
+ if (size) {
|
||||
+
|
||||
+ for (i = 0; i < size; i++) {
|
||||
+ /* discard extraneous rules that this kernel will
|
||||
+ * never request
|
||||
+ */
|
||||
+ if (i >= AF_MAX) {
|
||||
+ u16 tmp;
|
||||
+ if (!unpack_u16(e, &tmp, NULL) ||
|
||||
+ !unpack_u16(e, &tmp, NULL) ||
|
||||
+ !unpack_u16(e, &tmp, NULL))
|
||||
+ goto fail;
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (!unpack_u16(e, &profile->net.allow[i], NULL))
|
||||
+ goto fail;
|
||||
+ if (!unpack_u16(e, &profile->net.audit[i], NULL))
|
||||
+ goto fail;
|
||||
+ if (!unpack_u16(e, &profile->net.quiet[i], NULL))
|
||||
+ goto fail;
|
||||
+ }
|
||||
+ if (!unpack_nameX(e, AA_ARRAYEND, NULL))
|
||||
+ goto fail;
|
||||
+ }
|
||||
+ /*
|
||||
+ * allow unix domain and netlink sockets they are handled
|
||||
+ * by IPC
|
||||
+ */
|
||||
+ profile->net.allow[AF_UNIX] = 0xffff;
|
||||
+ profile->net.allow[AF_NETLINK] = 0xffff;
|
||||
+
|
||||
if (unpack_nameX(e, AA_STRUCT, "policydb")) {
|
||||
/* generic policy dfa - optional and may be NULL */
|
||||
profile->policy.dfa = unpack_dfa(e);
|
||||
--
|
||||
2.11.0
|
||||
|
@@ -1,38 +0,0 @@
|
||||
From 88ba6f37ed824ca24901fca7e399168db5e46f12 Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Fri, 29 Jun 2012 17:34:00 -0700
|
||||
Subject: [PATCH 2/3] apparmor: Fix quieting of audit messages for network
|
||||
mediation
|
||||
|
||||
If a profile specified a quieting of network denials for a given rule by
|
||||
either the quiet or deny rule qualifiers, the resultant quiet mask for
|
||||
denied requests was applied incorrectly, resulting in two potential bugs.
|
||||
1. The misapplied quiet mask would prevent denials from being correctly
|
||||
tested against the kill mask/mode. Thus network access requests that
|
||||
should have resulted in the application being killed did not.
|
||||
|
||||
2. The actual quieting of the denied network request was not being applied.
|
||||
This would result in network rejections always being logged even when
|
||||
they had been specifically marked as quieted.
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
---
|
||||
security/apparmor/net.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
|
||||
index 003dd18c61a5..6e6e5c981006 100644
|
||||
--- a/security/apparmor/net.c
|
||||
+++ b/security/apparmor/net.c
|
||||
@@ -88,7 +88,7 @@ static int audit_net(struct aa_profile *profile, int op, u16 family, int type,
|
||||
} else {
|
||||
u16 quiet_mask = profile->net.quiet[sa.u.net->family];
|
||||
u16 kill_mask = 0;
|
||||
- u16 denied = (1 << sa.aad->net.type) & ~quiet_mask;
|
||||
+ u16 denied = (1 << sa.aad->net.type);
|
||||
|
||||
if (denied & kill_mask)
|
||||
audit_type = AUDIT_APPARMOR_KILL;
|
||||
--
|
||||
2.11.0
|
||||
|
@@ -1,962 +0,0 @@
|
||||
From 6556d6523f74e90a801503d28e7b8dcc5caa6a1b Mon Sep 17 00:00:00 2001
|
||||
From: John Johansen <john.johansen@canonical.com>
|
||||
Date: Wed, 16 May 2012 10:58:05 -0700
|
||||
Subject: [PATCH 3/3] UBUNTU: SAUCE: apparmor: Add the ability to mediate mount
|
||||
|
||||
Add the ability for apparmor to do mediation of mount operations. Mount
|
||||
rules require an updated apparmor_parser (2.8 series) for policy compilation.
|
||||
|
||||
The basic form of the rules are.
|
||||
|
||||
[audit] [deny] mount [conds]* [device] [ -> [conds] path],
|
||||
[audit] [deny] remount [conds]* [path],
|
||||
[audit] [deny] umount [conds]* [path],
|
||||
[audit] [deny] pivotroot [oldroot=<value>] <path>
|
||||
|
||||
remount is just a short cut for mount options=remount
|
||||
|
||||
where [conds] can be
|
||||
fstype=<expr>
|
||||
options=<expr>
|
||||
|
||||
Example mount commands
|
||||
mount, # allow all mounts, but not umount or pivotroot
|
||||
|
||||
mount fstype=procfs, # allow mounting procfs anywhere
|
||||
|
||||
mount options=(bind, ro) /foo -> /bar, # readonly bind mount
|
||||
|
||||
mount /dev/sda -> /mnt,
|
||||
|
||||
mount /dev/sd** -> /mnt/**,
|
||||
|
||||
mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/
|
||||
|
||||
umount,
|
||||
|
||||
umount /m*,
|
||||
|
||||
See the apparmor userspace for full documentation
|
||||
|
||||
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
||||
Acked-by: Kees Cook <kees@ubuntu.com>
|
||||
---
|
||||
security/apparmor/Makefile | 2 +-
|
||||
security/apparmor/apparmorfs.c | 15 +-
|
||||
security/apparmor/audit.c | 4 +
|
||||
security/apparmor/domain.c | 2 +-
|
||||
security/apparmor/include/apparmor.h | 3 +-
|
||||
security/apparmor/include/audit.h | 11 +
|
||||
security/apparmor/include/domain.h | 2 +
|
||||
security/apparmor/include/mount.h | 54 +++
|
||||
security/apparmor/lsm.c | 60 ++++
|
||||
security/apparmor/mount.c | 620 +++++++++++++++++++++++++++++++++++
|
||||
10 files changed, 769 insertions(+), 4 deletions(-)
|
||||
create mode 100644 security/apparmor/include/mount.h
|
||||
create mode 100644 security/apparmor/mount.c
|
||||
|
||||
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
|
||||
index 5dbb72f46452..89b344541868 100644
|
||||
--- a/security/apparmor/Makefile
|
||||
+++ b/security/apparmor/Makefile
|
||||
@@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
|
||||
|
||||
apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
|
||||
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
|
||||
- resource.o sid.o file.o net.o
|
||||
+ resource.o sid.o file.o net.o mount.o
|
||||
apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o
|
||||
|
||||
clean-files := capability_names.h rlim_names.h net_names.h
|
||||
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
|
||||
index 181d961e6d58..5fb67f60bace 100644
|
||||
--- a/security/apparmor/apparmorfs.c
|
||||
+++ b/security/apparmor/apparmorfs.c
|
||||
@@ -800,7 +800,18 @@ static struct aa_fs_entry aa_fs_entry_domain[] = {
|
||||
|
||||
static struct aa_fs_entry aa_fs_entry_policy[] = {
|
||||
AA_FS_FILE_BOOLEAN("set_load", 1),
|
||||
- {}
|
||||
+ { }
|
||||
+};
|
||||
+
|
||||
+static struct aa_fs_entry aa_fs_entry_mount[] = {
|
||||
+ AA_FS_FILE_STRING("mask", "mount umount"),
|
||||
+ { }
|
||||
+};
|
||||
+
|
||||
+static struct aa_fs_entry aa_fs_entry_namespaces[] = {
|
||||
+ AA_FS_FILE_BOOLEAN("profile", 1),
|
||||
+ AA_FS_FILE_BOOLEAN("pivot_root", 1),
|
||||
+ { }
|
||||
};
|
||||
|
||||
static struct aa_fs_entry aa_fs_entry_features[] = {
|
||||
@@ -808,6 +819,8 @@ static struct aa_fs_entry aa_fs_entry_features[] = {
|
||||
AA_FS_DIR("domain", aa_fs_entry_domain),
|
||||
AA_FS_DIR("file", aa_fs_entry_file),
|
||||
AA_FS_DIR("network", aa_fs_entry_network),
|
||||
+ AA_FS_DIR("mount", aa_fs_entry_mount),
|
||||
+ AA_FS_DIR("namespaces", aa_fs_entry_namespaces),
|
||||
AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK),
|
||||
AA_FS_DIR("rlimit", aa_fs_entry_rlimit),
|
||||
AA_FS_DIR("caps", aa_fs_entry_caps),
|
||||
diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c
|
||||
index 3a7f1da1425e..c2a8b8ac38a7 100644
|
||||
--- a/security/apparmor/audit.c
|
||||
+++ b/security/apparmor/audit.c
|
||||
@@ -44,6 +44,10 @@ const char *const op_table[] = {
|
||||
"file_mmap",
|
||||
"file_mprotect",
|
||||
|
||||
+ "pivotroot",
|
||||
+ "mount",
|
||||
+ "umount",
|
||||
+
|
||||
"create",
|
||||
"post_create",
|
||||
"bind",
|
||||
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
|
||||
index fc3036b34e51..f2a83b4430db 100644
|
||||
--- a/security/apparmor/domain.c
|
||||
+++ b/security/apparmor/domain.c
|
||||
@@ -236,7 +236,7 @@ static const char *next_name(int xtype, const char *name)
|
||||
*
|
||||
* Returns: refcounted profile, or NULL on failure (MAYBE NULL)
|
||||
*/
|
||||
-static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex)
|
||||
+struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex)
|
||||
{
|
||||
struct aa_profile *new_profile = NULL;
|
||||
struct aa_namespace *ns = profile->ns;
|
||||
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h
|
||||
index 5d721e990876..b57da7b9f8bd 100644
|
||||
--- a/security/apparmor/include/apparmor.h
|
||||
+++ b/security/apparmor/include/apparmor.h
|
||||
@@ -30,8 +30,9 @@
|
||||
#define AA_CLASS_NET 4
|
||||
#define AA_CLASS_RLIMITS 5
|
||||
#define AA_CLASS_DOMAIN 6
|
||||
+#define AA_CLASS_MOUNT 7
|
||||
|
||||
-#define AA_CLASS_LAST AA_CLASS_DOMAIN
|
||||
+#define AA_CLASS_LAST AA_CLASS_MOUNT
|
||||
|
||||
/* Control parameters settable through module/boot flags */
|
||||
extern enum audit_mode aa_g_audit;
|
||||
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
|
||||
index 5d3c419b17d9..b9f1d57984ca 100644
|
||||
--- a/security/apparmor/include/audit.h
|
||||
+++ b/security/apparmor/include/audit.h
|
||||
@@ -72,6 +72,10 @@ enum aa_ops {
|
||||
OP_FMMAP,
|
||||
OP_FMPROT,
|
||||
|
||||
+ OP_PIVOTROOT,
|
||||
+ OP_MOUNT,
|
||||
+ OP_UMOUNT,
|
||||
+
|
||||
OP_CREATE,
|
||||
OP_POST_CREATE,
|
||||
OP_BIND,
|
||||
@@ -120,6 +124,13 @@ struct apparmor_audit_data {
|
||||
unsigned long max;
|
||||
} rlim;
|
||||
struct {
|
||||
+ const char *src_name;
|
||||
+ const char *type;
|
||||
+ const char *trans;
|
||||
+ const char *data;
|
||||
+ unsigned long flags;
|
||||
+ } mnt;
|
||||
+ struct {
|
||||
const char *target;
|
||||
u32 request;
|
||||
u32 denied;
|
||||
diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h
|
||||
index de04464f0a3f..a3f70c58ef3d 100644
|
||||
--- a/security/apparmor/include/domain.h
|
||||
+++ b/security/apparmor/include/domain.h
|
||||
@@ -23,6 +23,8 @@ struct aa_domain {
|
||||
char **table;
|
||||
};
|
||||
|
||||
+struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex);
|
||||
+
|
||||
int apparmor_bprm_set_creds(struct linux_binprm *bprm);
|
||||
int apparmor_bprm_secureexec(struct linux_binprm *bprm);
|
||||
void apparmor_bprm_committing_creds(struct linux_binprm *bprm);
|
||||
diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h
|
||||
new file mode 100644
|
||||
index 000000000000..a43b1d62e428
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/include/mount.h
|
||||
@@ -0,0 +1,54 @@
|
||||
+/*
|
||||
+ * AppArmor security module
|
||||
+ *
|
||||
+ * This file contains AppArmor file mediation function definitions.
|
||||
+ *
|
||||
+ * Copyright 2012 Canonical Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License as
|
||||
+ * published by the Free Software Foundation, version 2 of the
|
||||
+ * License.
|
||||
+ */
|
||||
+
|
||||
+#ifndef __AA_MOUNT_H
|
||||
+#define __AA_MOUNT_H
|
||||
+
|
||||
+#include <linux/fs.h>
|
||||
+#include <linux/path.h>
|
||||
+
|
||||
+#include "domain.h"
|
||||
+#include "policy.h"
|
||||
+
|
||||
+/* mount perms */
|
||||
+#define AA_MAY_PIVOTROOT 0x01
|
||||
+#define AA_MAY_MOUNT 0x02
|
||||
+#define AA_MAY_UMOUNT 0x04
|
||||
+#define AA_AUDIT_DATA 0x40
|
||||
+#define AA_CONT_MATCH 0x40
|
||||
+
|
||||
+#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN)
|
||||
+
|
||||
+int aa_remount(struct aa_profile *profile, const struct path *path,
|
||||
+ unsigned long flags, void *data);
|
||||
+
|
||||
+int aa_bind_mount(struct aa_profile *profile, const struct path *path,
|
||||
+ const char *old_name, unsigned long flags);
|
||||
+
|
||||
+
|
||||
+int aa_mount_change_type(struct aa_profile *profile, const struct path *path,
|
||||
+ unsigned long flags);
|
||||
+
|
||||
+int aa_move_mount(struct aa_profile *profile, const struct path *path,
|
||||
+ const char *old_name);
|
||||
+
|
||||
+int aa_new_mount(struct aa_profile *profile, const char *dev_name,
|
||||
+ const struct path *path, const char *type, unsigned long flags,
|
||||
+ void *data);
|
||||
+
|
||||
+int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags);
|
||||
+
|
||||
+int aa_pivotroot(struct aa_profile *profile, const struct path *old_path,
|
||||
+ const struct path *new_path);
|
||||
+
|
||||
+#endif /* __AA_MOUNT_H */
|
||||
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
|
||||
index d96b5f7c1912..5ff9984cba5a 100644
|
||||
--- a/security/apparmor/lsm.c
|
||||
+++ b/security/apparmor/lsm.c
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "include/path.h"
|
||||
#include "include/policy.h"
|
||||
#include "include/procattr.h"
|
||||
+#include "include/mount.h"
|
||||
|
||||
/* Flag indicating whether initialization completed */
|
||||
int apparmor_initialized __initdata;
|
||||
@@ -469,6 +470,61 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma,
|
||||
!(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0);
|
||||
}
|
||||
|
||||
+static int apparmor_sb_mount(const char *dev_name, const struct path *path,
|
||||
+ const char *type, unsigned long flags, void *data)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ int error = 0;
|
||||
+
|
||||
+ /* Discard magic */
|
||||
+ if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
|
||||
+ flags &= ~MS_MGC_MSK;
|
||||
+
|
||||
+ flags &= ~AA_MS_IGNORE_MASK;
|
||||
+
|
||||
+ profile = __aa_current_profile();
|
||||
+ if (!unconfined(profile)) {
|
||||
+ if (flags & MS_REMOUNT)
|
||||
+ error = aa_remount(profile, path, flags, data);
|
||||
+ else if (flags & MS_BIND)
|
||||
+ error = aa_bind_mount(profile, path, dev_name, flags);
|
||||
+ else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE |
|
||||
+ MS_UNBINDABLE))
|
||||
+ error = aa_mount_change_type(profile, path, flags);
|
||||
+ else if (flags & MS_MOVE)
|
||||
+ error = aa_move_mount(profile, path, dev_name);
|
||||
+ else
|
||||
+ error = aa_new_mount(profile, dev_name, path, type,
|
||||
+ flags, data);
|
||||
+ }
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+static int apparmor_sb_umount(struct vfsmount *mnt, int flags)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ int error = 0;
|
||||
+
|
||||
+ profile = __aa_current_profile();
|
||||
+ if (!unconfined(profile))
|
||||
+ error = aa_umount(profile, mnt, flags);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+static int apparmor_sb_pivotroot(const struct path *old_path,
|
||||
+ const struct path *new_path)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ int error = 0;
|
||||
+
|
||||
+ profile = __aa_current_profile();
|
||||
+ if (!unconfined(profile))
|
||||
+ error = aa_pivotroot(profile, old_path, new_path);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
static int apparmor_getprocattr(struct task_struct *task, char *name,
|
||||
char **value)
|
||||
{
|
||||
@@ -689,6 +745,10 @@ static struct security_hook_list apparmor_hooks[] = {
|
||||
LSM_HOOK_INIT(capget, apparmor_capget),
|
||||
LSM_HOOK_INIT(capable, apparmor_capable),
|
||||
|
||||
+ LSM_HOOK_INIT(sb_mount, apparmor_sb_mount),
|
||||
+ LSM_HOOK_INIT(sb_umount, apparmor_sb_umount),
|
||||
+ LSM_HOOK_INIT(sb_pivotroot, apparmor_sb_pivotroot),
|
||||
+
|
||||
LSM_HOOK_INIT(path_link, apparmor_path_link),
|
||||
LSM_HOOK_INIT(path_unlink, apparmor_path_unlink),
|
||||
LSM_HOOK_INIT(path_symlink, apparmor_path_symlink),
|
||||
diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c
|
||||
new file mode 100644
|
||||
index 000000000000..9cf9170b4976
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/mount.c
|
||||
@@ -0,0 +1,620 @@
|
||||
+/*
|
||||
+ * AppArmor security module
|
||||
+ *
|
||||
+ * This file contains AppArmor mediation of files
|
||||
+ *
|
||||
+ * Copyright (C) 1998-2008 Novell/SUSE
|
||||
+ * Copyright 2009-2012 Canonical Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License as
|
||||
+ * published by the Free Software Foundation, version 2 of the
|
||||
+ * License.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/fs.h>
|
||||
+#include <linux/mount.h>
|
||||
+#include <linux/namei.h>
|
||||
+
|
||||
+#include "include/apparmor.h"
|
||||
+#include "include/audit.h"
|
||||
+#include "include/context.h"
|
||||
+#include "include/domain.h"
|
||||
+#include "include/file.h"
|
||||
+#include "include/match.h"
|
||||
+#include "include/mount.h"
|
||||
+#include "include/path.h"
|
||||
+#include "include/policy.h"
|
||||
+
|
||||
+
|
||||
+static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags)
|
||||
+{
|
||||
+ if (flags & MS_RDONLY)
|
||||
+ audit_log_format(ab, "ro");
|
||||
+ else
|
||||
+ audit_log_format(ab, "rw");
|
||||
+ if (flags & MS_NOSUID)
|
||||
+ audit_log_format(ab, ", nosuid");
|
||||
+ if (flags & MS_NODEV)
|
||||
+ audit_log_format(ab, ", nodev");
|
||||
+ if (flags & MS_NOEXEC)
|
||||
+ audit_log_format(ab, ", noexec");
|
||||
+ if (flags & MS_SYNCHRONOUS)
|
||||
+ audit_log_format(ab, ", sync");
|
||||
+ if (flags & MS_REMOUNT)
|
||||
+ audit_log_format(ab, ", remount");
|
||||
+ if (flags & MS_MANDLOCK)
|
||||
+ audit_log_format(ab, ", mand");
|
||||
+ if (flags & MS_DIRSYNC)
|
||||
+ audit_log_format(ab, ", dirsync");
|
||||
+ if (flags & MS_NOATIME)
|
||||
+ audit_log_format(ab, ", noatime");
|
||||
+ if (flags & MS_NODIRATIME)
|
||||
+ audit_log_format(ab, ", nodiratime");
|
||||
+ if (flags & MS_BIND)
|
||||
+ audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind");
|
||||
+ if (flags & MS_MOVE)
|
||||
+ audit_log_format(ab, ", move");
|
||||
+ if (flags & MS_SILENT)
|
||||
+ audit_log_format(ab, ", silent");
|
||||
+ if (flags & MS_POSIXACL)
|
||||
+ audit_log_format(ab, ", acl");
|
||||
+ if (flags & MS_UNBINDABLE)
|
||||
+ audit_log_format(ab, flags & MS_REC ? ", runbindable" :
|
||||
+ ", unbindable");
|
||||
+ if (flags & MS_PRIVATE)
|
||||
+ audit_log_format(ab, flags & MS_REC ? ", rprivate" :
|
||||
+ ", private");
|
||||
+ if (flags & MS_SLAVE)
|
||||
+ audit_log_format(ab, flags & MS_REC ? ", rslave" :
|
||||
+ ", slave");
|
||||
+ if (flags & MS_SHARED)
|
||||
+ audit_log_format(ab, flags & MS_REC ? ", rshared" :
|
||||
+ ", shared");
|
||||
+ if (flags & MS_RELATIME)
|
||||
+ audit_log_format(ab, ", relatime");
|
||||
+ if (flags & MS_I_VERSION)
|
||||
+ audit_log_format(ab, ", iversion");
|
||||
+ if (flags & MS_STRICTATIME)
|
||||
+ audit_log_format(ab, ", strictatime");
|
||||
+ if (flags & MS_NOUSER)
|
||||
+ audit_log_format(ab, ", nouser");
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * audit_cb - call back for mount specific audit fields
|
||||
+ * @ab: audit_buffer (NOT NULL)
|
||||
+ * @va: audit struct to audit values of (NOT NULL)
|
||||
+ */
|
||||
+static void audit_cb(struct audit_buffer *ab, void *va)
|
||||
+{
|
||||
+ struct common_audit_data *sa = va;
|
||||
+
|
||||
+ if (sa->aad->mnt.type) {
|
||||
+ audit_log_format(ab, " fstype=");
|
||||
+ audit_log_untrustedstring(ab, sa->aad->mnt.type);
|
||||
+ }
|
||||
+ if (sa->aad->mnt.src_name) {
|
||||
+ audit_log_format(ab, " srcname=");
|
||||
+ audit_log_untrustedstring(ab, sa->aad->mnt.src_name);
|
||||
+ }
|
||||
+ if (sa->aad->mnt.trans) {
|
||||
+ audit_log_format(ab, " trans=");
|
||||
+ audit_log_untrustedstring(ab, sa->aad->mnt.trans);
|
||||
+ }
|
||||
+ if (sa->aad->mnt.flags || sa->aad->op == OP_MOUNT) {
|
||||
+ audit_log_format(ab, " flags=\"");
|
||||
+ audit_mnt_flags(ab, sa->aad->mnt.flags);
|
||||
+ audit_log_format(ab, "\"");
|
||||
+ }
|
||||
+ if (sa->aad->mnt.data) {
|
||||
+ audit_log_format(ab, " options=");
|
||||
+ audit_log_untrustedstring(ab, sa->aad->mnt.data);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * audit_mount - handle the auditing of mount operations
|
||||
+ * @profile: the profile being enforced (NOT NULL)
|
||||
+ * @gfp: allocation flags
|
||||
+ * @op: operation being mediated (NOT NULL)
|
||||
+ * @name: name of object being mediated (MAYBE NULL)
|
||||
+ * @src_name: src_name of object being mediated (MAYBE_NULL)
|
||||
+ * @type: type of filesystem (MAYBE_NULL)
|
||||
+ * @trans: name of trans (MAYBE NULL)
|
||||
+ * @flags: filesystem idependent mount flags
|
||||
+ * @data: filesystem mount flags
|
||||
+ * @request: permissions requested
|
||||
+ * @perms: the permissions computed for the request (NOT NULL)
|
||||
+ * @info: extra information message (MAYBE NULL)
|
||||
+ * @error: 0 if operation allowed else failure error code
|
||||
+ *
|
||||
+ * Returns: %0 or error on failure
|
||||
+ */
|
||||
+static int audit_mount(struct aa_profile *profile, gfp_t gfp, int op,
|
||||
+ const char *name, const char *src_name,
|
||||
+ const char *type, const char *trans,
|
||||
+ unsigned long flags, const void *data, u32 request,
|
||||
+ struct file_perms *perms, const char *info, int error)
|
||||
+{
|
||||
+ int audit_type = AUDIT_APPARMOR_AUTO;
|
||||
+ struct common_audit_data sa = { };
|
||||
+ struct apparmor_audit_data aad = { };
|
||||
+
|
||||
+ if (likely(!error)) {
|
||||
+ u32 mask = perms->audit;
|
||||
+
|
||||
+ if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL))
|
||||
+ mask = 0xffff;
|
||||
+
|
||||
+ /* mask off perms that are not being force audited */
|
||||
+ request &= mask;
|
||||
+
|
||||
+ if (likely(!request))
|
||||
+ return 0;
|
||||
+ audit_type = AUDIT_APPARMOR_AUDIT;
|
||||
+ } else {
|
||||
+ /* only report permissions that were denied */
|
||||
+ request = request & ~perms->allow;
|
||||
+
|
||||
+ if (request & perms->kill)
|
||||
+ audit_type = AUDIT_APPARMOR_KILL;
|
||||
+
|
||||
+ /* quiet known rejects, assumes quiet and kill do not overlap */
|
||||
+ if ((request & perms->quiet) &&
|
||||
+ AUDIT_MODE(profile) != AUDIT_NOQUIET &&
|
||||
+ AUDIT_MODE(profile) != AUDIT_ALL)
|
||||
+ request &= ~perms->quiet;
|
||||
+
|
||||
+ if (!request)
|
||||
+ return COMPLAIN_MODE(profile) ?
|
||||
+ complain_error(error) : error;
|
||||
+ }
|
||||
+
|
||||
+ sa.type = LSM_AUDIT_DATA_NONE;
|
||||
+ sa.aad = &aad;
|
||||
+ sa.aad->op = op;
|
||||
+ sa.aad->name = name;
|
||||
+ sa.aad->mnt.src_name = src_name;
|
||||
+ sa.aad->mnt.type = type;
|
||||
+ sa.aad->mnt.trans = trans;
|
||||
+ sa.aad->mnt.flags = flags;
|
||||
+ if (data && (perms->audit & AA_AUDIT_DATA))
|
||||
+ sa.aad->mnt.data = data;
|
||||
+ sa.aad->info = info;
|
||||
+ sa.aad->error = error;
|
||||
+
|
||||
+ return aa_audit(audit_type, profile, gfp, &sa, audit_cb);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * match_mnt_flags - Do an ordered match on mount flags
|
||||
+ * @dfa: dfa to match against
|
||||
+ * @state: state to start in
|
||||
+ * @flags: mount flags to match against
|
||||
+ *
|
||||
+ * Mount flags are encoded as an ordered match. This is done instead of
|
||||
+ * checking against a simple bitmask, to allow for logical operations
|
||||
+ * on the flags.
|
||||
+ *
|
||||
+ * Returns: next state after flags match
|
||||
+ */
|
||||
+static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state,
|
||||
+ unsigned long flags)
|
||||
+{
|
||||
+ unsigned int i;
|
||||
+
|
||||
+ for (i = 0; i <= 31 ; ++i) {
|
||||
+ if ((1 << i) & flags)
|
||||
+ state = aa_dfa_next(dfa, state, i + 1);
|
||||
+ }
|
||||
+
|
||||
+ return state;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * compute_mnt_perms - compute mount permission associated with @state
|
||||
+ * @dfa: dfa to match against (NOT NULL)
|
||||
+ * @state: state match finished in
|
||||
+ *
|
||||
+ * Returns: mount permissions
|
||||
+ */
|
||||
+static struct file_perms compute_mnt_perms(struct aa_dfa *dfa,
|
||||
+ unsigned int state)
|
||||
+{
|
||||
+ struct file_perms perms;
|
||||
+
|
||||
+ perms.kill = 0;
|
||||
+ perms.allow = dfa_user_allow(dfa, state);
|
||||
+ perms.audit = dfa_user_audit(dfa, state);
|
||||
+ perms.quiet = dfa_user_quiet(dfa, state);
|
||||
+ perms.xindex = dfa_user_xindex(dfa, state);
|
||||
+
|
||||
+ return perms;
|
||||
+}
|
||||
+
|
||||
+static const char const *mnt_info_table[] = {
|
||||
+ "match succeeded",
|
||||
+ "failed mntpnt match",
|
||||
+ "failed srcname match",
|
||||
+ "failed type match",
|
||||
+ "failed flags match",
|
||||
+ "failed data match"
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * Returns 0 on success else element that match failed in, this is the
|
||||
+ * index into the mnt_info_table above
|
||||
+ */
|
||||
+static int do_match_mnt(struct aa_dfa *dfa, unsigned int start,
|
||||
+ const char *mntpnt, const char *devname,
|
||||
+ const char *type, unsigned long flags,
|
||||
+ void *data, bool binary, struct file_perms *perms)
|
||||
+{
|
||||
+ unsigned int state;
|
||||
+
|
||||
+ state = aa_dfa_match(dfa, start, mntpnt);
|
||||
+ state = aa_dfa_null_transition(dfa, state);
|
||||
+ if (!state)
|
||||
+ return 1;
|
||||
+
|
||||
+ if (devname)
|
||||
+ state = aa_dfa_match(dfa, state, devname);
|
||||
+ state = aa_dfa_null_transition(dfa, state);
|
||||
+ if (!state)
|
||||
+ return 2;
|
||||
+
|
||||
+ if (type)
|
||||
+ state = aa_dfa_match(dfa, state, type);
|
||||
+ state = aa_dfa_null_transition(dfa, state);
|
||||
+ if (!state)
|
||||
+ return 3;
|
||||
+
|
||||
+ state = match_mnt_flags(dfa, state, flags);
|
||||
+ if (!state)
|
||||
+ return 4;
|
||||
+ *perms = compute_mnt_perms(dfa, state);
|
||||
+ if (perms->allow & AA_MAY_MOUNT)
|
||||
+ return 0;
|
||||
+
|
||||
+ /* only match data if not binary and the DFA flags data is expected */
|
||||
+ if (data && !binary && (perms->allow & AA_CONT_MATCH)) {
|
||||
+ state = aa_dfa_null_transition(dfa, state);
|
||||
+ if (!state)
|
||||
+ return 4;
|
||||
+
|
||||
+ state = aa_dfa_match(dfa, state, data);
|
||||
+ if (!state)
|
||||
+ return 5;
|
||||
+ *perms = compute_mnt_perms(dfa, state);
|
||||
+ if (perms->allow & AA_MAY_MOUNT)
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ /* failed at end of flags match */
|
||||
+ return 4;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * match_mnt - handle path matching for mount
|
||||
+ * @profile: the confining profile
|
||||
+ * @mntpnt: string for the mntpnt (NOT NULL)
|
||||
+ * @devname: string for the devname/src_name (MAYBE NULL)
|
||||
+ * @type: string for the dev type (MAYBE NULL)
|
||||
+ * @flags: mount flags to match
|
||||
+ * @data: fs mount data (MAYBE NULL)
|
||||
+ * @binary: whether @data is binary
|
||||
+ * @perms: Returns: permission found by the match
|
||||
+ * @info: Returns: infomation string about the match for logging
|
||||
+ *
|
||||
+ * Returns: 0 on success else error
|
||||
+ */
|
||||
+static int match_mnt(struct aa_profile *profile, const char *mntpnt,
|
||||
+ const char *devname, const char *type,
|
||||
+ unsigned long flags, void *data, bool binary,
|
||||
+ struct file_perms *perms, const char **info)
|
||||
+{
|
||||
+ int pos;
|
||||
+
|
||||
+ if (!profile->policy.dfa)
|
||||
+ return -EACCES;
|
||||
+
|
||||
+ pos = do_match_mnt(profile->policy.dfa,
|
||||
+ profile->policy.start[AA_CLASS_MOUNT],
|
||||
+ mntpnt, devname, type, flags, data, binary, perms);
|
||||
+ if (pos) {
|
||||
+ *info = mnt_info_table[pos];
|
||||
+ return -EACCES;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int path_flags(struct aa_profile *profile, const struct path *path)
|
||||
+{
|
||||
+ return profile->path_flags |
|
||||
+ S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0;
|
||||
+}
|
||||
+
|
||||
+int aa_remount(struct aa_profile *profile, const struct path *path,
|
||||
+ unsigned long flags, void *data)
|
||||
+{
|
||||
+ struct file_perms perms = { };
|
||||
+ const char *name, *info = NULL;
|
||||
+ char *buffer = NULL;
|
||||
+ int binary, error;
|
||||
+
|
||||
+ binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA;
|
||||
+
|
||||
+ error = aa_path_name(path, path_flags(profile, path), &buffer, &name,
|
||||
+ &info);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = match_mnt(profile, name, NULL, NULL, flags, data, binary,
|
||||
+ &perms, &info);
|
||||
+
|
||||
+audit:
|
||||
+ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL,
|
||||
+ NULL, flags, data, AA_MAY_MOUNT, &perms, info,
|
||||
+ error);
|
||||
+ kfree(buffer);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+int aa_bind_mount(struct aa_profile *profile, const struct path *path,
|
||||
+ const char *dev_name, unsigned long flags)
|
||||
+{
|
||||
+ struct file_perms perms = { };
|
||||
+ char *buffer = NULL, *old_buffer = NULL;
|
||||
+ const char *name, *old_name = NULL, *info = NULL;
|
||||
+ struct path old_path;
|
||||
+ int error;
|
||||
+
|
||||
+ if (!dev_name || !*dev_name)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ flags &= MS_REC | MS_BIND;
|
||||
+
|
||||
+ error = aa_path_name(path, path_flags(profile, path), &buffer, &name,
|
||||
+ &info);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = aa_path_name(&old_path, path_flags(profile, &old_path),
|
||||
+ &old_buffer, &old_name, &info);
|
||||
+ path_put(&old_path);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0,
|
||||
+ &perms, &info);
|
||||
+
|
||||
+audit:
|
||||
+ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name,
|
||||
+ NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms,
|
||||
+ info, error);
|
||||
+ kfree(buffer);
|
||||
+ kfree(old_buffer);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+int aa_mount_change_type(struct aa_profile *profile, const struct path *path,
|
||||
+ unsigned long flags)
|
||||
+{
|
||||
+ struct file_perms perms = { };
|
||||
+ char *buffer = NULL;
|
||||
+ const char *name, *info = NULL;
|
||||
+ int error;
|
||||
+
|
||||
+ /* These are the flags allowed by do_change_type() */
|
||||
+ flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE |
|
||||
+ MS_UNBINDABLE);
|
||||
+
|
||||
+ error = aa_path_name(path, path_flags(profile, path), &buffer, &name,
|
||||
+ &info);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms,
|
||||
+ &info);
|
||||
+
|
||||
+audit:
|
||||
+ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL,
|
||||
+ NULL, flags, NULL, AA_MAY_MOUNT, &perms, info,
|
||||
+ error);
|
||||
+ kfree(buffer);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+int aa_move_mount(struct aa_profile *profile, const struct path *path,
|
||||
+ const char *orig_name)
|
||||
+{
|
||||
+ struct file_perms perms = { };
|
||||
+ char *buffer = NULL, *old_buffer = NULL;
|
||||
+ const char *name, *old_name = NULL, *info = NULL;
|
||||
+ struct path old_path;
|
||||
+ int error;
|
||||
+
|
||||
+ if (!orig_name || !*orig_name)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ error = aa_path_name(path, path_flags(profile, path), &buffer, &name,
|
||||
+ &info);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = aa_path_name(&old_path, path_flags(profile, &old_path),
|
||||
+ &old_buffer, &old_name, &info);
|
||||
+ path_put(&old_path);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0,
|
||||
+ &perms, &info);
|
||||
+
|
||||
+audit:
|
||||
+ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name,
|
||||
+ NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms,
|
||||
+ info, error);
|
||||
+ kfree(buffer);
|
||||
+ kfree(old_buffer);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name,
|
||||
+ const struct path *path, const char *type, unsigned long flags,
|
||||
+ void *data)
|
||||
+{
|
||||
+ struct file_perms perms = { };
|
||||
+ char *buffer = NULL, *dev_buffer = NULL;
|
||||
+ const char *name = NULL, *dev_name = NULL, *info = NULL;
|
||||
+ int binary = 1;
|
||||
+ int error;
|
||||
+
|
||||
+ dev_name = orig_dev_name;
|
||||
+ if (type) {
|
||||
+ int requires_dev;
|
||||
+ struct file_system_type *fstype = get_fs_type(type);
|
||||
+ if (!fstype)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ binary = fstype->fs_flags & FS_BINARY_MOUNTDATA;
|
||||
+ requires_dev = fstype->fs_flags & FS_REQUIRES_DEV;
|
||||
+ put_filesystem(fstype);
|
||||
+
|
||||
+ if (requires_dev) {
|
||||
+ struct path dev_path;
|
||||
+
|
||||
+ if (!dev_name || !*dev_name) {
|
||||
+ error = -ENOENT;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = aa_path_name(&dev_path,
|
||||
+ path_flags(profile, &dev_path),
|
||||
+ &dev_buffer, &dev_name, &info);
|
||||
+ path_put(&dev_path);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ error = aa_path_name(path, path_flags(profile, path), &buffer, &name,
|
||||
+ &info);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = match_mnt(profile, name, dev_name, type, flags, data, binary,
|
||||
+ &perms, &info);
|
||||
+
|
||||
+audit:
|
||||
+ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name,
|
||||
+ type, NULL, flags, data, AA_MAY_MOUNT, &perms, info,
|
||||
+ error);
|
||||
+ kfree(buffer);
|
||||
+ kfree(dev_buffer);
|
||||
+
|
||||
+out:
|
||||
+ return error;
|
||||
+
|
||||
+}
|
||||
+
|
||||
+int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags)
|
||||
+{
|
||||
+ struct file_perms perms = { };
|
||||
+ char *buffer = NULL;
|
||||
+ const char *name, *info = NULL;
|
||||
+ int error;
|
||||
+
|
||||
+ struct path path = { mnt, mnt->mnt_root };
|
||||
+ error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name,
|
||||
+ &info);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ if (!error && profile->policy.dfa) {
|
||||
+ unsigned int state;
|
||||
+ state = aa_dfa_match(profile->policy.dfa,
|
||||
+ profile->policy.start[AA_CLASS_MOUNT],
|
||||
+ name);
|
||||
+ perms = compute_mnt_perms(profile->policy.dfa, state);
|
||||
+ }
|
||||
+
|
||||
+ if (AA_MAY_UMOUNT & ~perms.allow)
|
||||
+ error = -EACCES;
|
||||
+
|
||||
+audit:
|
||||
+ error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL,
|
||||
+ NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error);
|
||||
+ kfree(buffer);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+int aa_pivotroot(struct aa_profile *profile, const struct path *old_path,
|
||||
+ const struct path *new_path)
|
||||
+{
|
||||
+ struct file_perms perms = { };
|
||||
+ struct aa_profile *target = NULL;
|
||||
+ char *old_buffer = NULL, *new_buffer = NULL;
|
||||
+ const char *old_name, *new_name = NULL, *info = NULL;
|
||||
+ int error;
|
||||
+
|
||||
+ error = aa_path_name(old_path, path_flags(profile, old_path),
|
||||
+ &old_buffer, &old_name, &info);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ error = aa_path_name(new_path, path_flags(profile, new_path),
|
||||
+ &new_buffer, &new_name, &info);
|
||||
+ if (error)
|
||||
+ goto audit;
|
||||
+
|
||||
+ if (profile->policy.dfa) {
|
||||
+ unsigned int state;
|
||||
+ state = aa_dfa_match(profile->policy.dfa,
|
||||
+ profile->policy.start[AA_CLASS_MOUNT],
|
||||
+ new_name);
|
||||
+ state = aa_dfa_null_transition(profile->policy.dfa, state);
|
||||
+ state = aa_dfa_match(profile->policy.dfa, state, old_name);
|
||||
+ perms = compute_mnt_perms(profile->policy.dfa, state);
|
||||
+ }
|
||||
+
|
||||
+ if (AA_MAY_PIVOTROOT & perms.allow) {
|
||||
+ if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) {
|
||||
+ target = x_table_lookup(profile, perms.xindex);
|
||||
+ if (!target)
|
||||
+ error = -ENOENT;
|
||||
+ else
|
||||
+ error = aa_replace_current_profile(target);
|
||||
+ }
|
||||
+ } else
|
||||
+ error = -EACCES;
|
||||
+
|
||||
+audit:
|
||||
+ error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name,
|
||||
+ old_name, NULL, target ? target->base.name : NULL,
|
||||
+ 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error);
|
||||
+ aa_put_profile(target);
|
||||
+ kfree(old_buffer);
|
||||
+ kfree(new_buffer);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
--
|
||||
2.11.0
|
||||
|
@@ -81,7 +81,7 @@ AM_CONDITIONAL(HAVE_RUBY, test x$with_ruby = xyes)
|
||||
AC_HEADER_STDC
|
||||
AC_CHECK_HEADERS(unistd.h stdint.h syslog.h)
|
||||
|
||||
AC_CHECK_FUNCS([asprintf __secure_getenv secure_getenv reallocarray])
|
||||
AC_CHECK_FUNCS([asprintf __secure_getenv secure_getenv])
|
||||
|
||||
AM_PROG_CC_C_O
|
||||
AC_C_CONST
|
||||
|
@@ -40,8 +40,6 @@ aa_features_is_equal - equality test for two aa_features objects
|
||||
|
||||
aa_features_supports - provides aa_features object support status
|
||||
|
||||
aa_features_id - provides unique identifier for an aa_features object
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
B<#include E<lt>sys/apparmor.hE<gt>>
|
||||
@@ -64,8 +62,6 @@ B<bool aa_features_is_equal(aa_features *features1, aa_features *features2);>
|
||||
|
||||
B<bool aa_features_supports(aa_features *features, const char *str);>
|
||||
|
||||
B<char *aa_features_id(aa_features *features);>
|
||||
|
||||
Link with B<-lapparmor> when compiling.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
@@ -112,12 +108,6 @@ 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".
|
||||
|
||||
The aa_features_id() function returns a string representation of an
|
||||
identifier that can be used to uniquely identify an I<aa_features> object.
|
||||
The mechanism for generating the string representation is internal to
|
||||
libapparmor and subject to change but an example implementation is
|
||||
applying a hash function to the features string.
|
||||
|
||||
=head1 RETURN VALUE
|
||||
|
||||
The aa_features_new() family of functions return 0 on success and I<*features>
|
||||
@@ -136,23 +126,15 @@ 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.
|
||||
|
||||
aa_features_id() returns a string identifying I<features> which must be
|
||||
freed by the caller. NULL 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_features> family of functions that return -1 or NULL on error.
|
||||
I<aa_features> family of functions that return -1 on error.
|
||||
|
||||
=head1 NOTES
|
||||
|
||||
The aa_features_id() function can be found in libapparmor version
|
||||
2.13. All the other aa_feature functions described above are present
|
||||
in libapparmor version 2.10.
|
||||
|
||||
aa_features_unref() saves the value of errno when called and restores errno
|
||||
before exiting in libapparmor version 2.12 and newer.
|
||||
All aa_features functions described above are present in libapparmor version
|
||||
2.10 and newer.
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
|
@@ -133,7 +133,7 @@ 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_interface_ref() returns the value of I<kernel_interface>.
|
||||
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,
|
||||
@@ -150,9 +150,6 @@ I<aa_kernel_interface> family of functions that return -1 on error.
|
||||
All aa_kernel_interface functions described above are present in libapparmor
|
||||
version 2.10 and newer.
|
||||
|
||||
aa_kernel_interface_unref() saves the value of errno when called and restores
|
||||
errno before exiting in libapparmor version 2.12 and newer.
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
None known. If you find any, please report them at
|
||||
|
@@ -34,10 +34,6 @@ 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
|
||||
|
||||
aa_policy_cache_dir_path - returns the path to the aa_policy_cache directory
|
||||
|
||||
aa_policy_cache_dir_path_preview - returns a preview of the path to the aa_policy_cache directory without an existing aa_policy_cache object
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
B<#include E<lt>sys/apparmor.hE<gt>>
|
||||
@@ -46,8 +42,6 @@ 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<int aa_policy_cache_add_ro_dir(aa_policy_cache *policy_cache, int dirfd, const char *path);>
|
||||
|
||||
B<aa_policy_cache *aa_policy_cache_ref(aa_policy_cache *policy_cache);>
|
||||
|
||||
B<void aa_policy_cache_unref(aa_policy_cache *policy_cache);>
|
||||
@@ -56,10 +50,6 @@ 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);>
|
||||
|
||||
B<char *aa_policy_cache_dir_path(aa_policy_cache *policy_cache, int level);>
|
||||
|
||||
B<char *aa_policy_cache_dir_path_preview(aa_features *kernel_features, int dirfd, const char *path);>
|
||||
|
||||
Link with B<-lapparmor> when compiling.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
@@ -69,35 +59,19 @@ 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. See the
|
||||
openat(2) man page for examples of I<dirfd> and I<path>. The I<path>
|
||||
must point to a directory and it will be used as the basis for the
|
||||
location of policy cache files. See I<aa_policy_cache_dir_path> to
|
||||
find out which directory will be used to store the binary policy cache
|
||||
files. If additional overlay cache directories are used (see
|
||||
I<aa_policy_cache_add_ro_dir>) the directory specified in
|
||||
I<aa_policy_cache_new> is the first directory searched and is the
|
||||
writable overlay. 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 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().
|
||||
|
||||
The aa_policy_cache_add_ro_dir() function adds an existing cache directory
|
||||
to the policy cache, as a readonly layer under the primary directory
|
||||
the cache was created with. When the cache is searched for an existing
|
||||
cache file the primary directory will be searched and then the readonly
|
||||
directories in the order that they were added to the policy cache.
|
||||
This allows the policy cache to be seeded with precompiled policy
|
||||
that can be updated by overlaying the read only cache file with one
|
||||
written to the primary cache dir.
|
||||
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.
|
||||
@@ -116,18 +90,6 @@ 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.
|
||||
|
||||
The aa_policy_cache_dir_path() function provides the path to the cache
|
||||
directory for a I<policy_cache> object at I<level> in the policy cache
|
||||
overlay of cache directories. A I<level> of 0 will always be present
|
||||
and is the first directory to search in an overlay of cache
|
||||
directories, and will also be the writable cache directory
|
||||
layer. Binary policy cache files will be located in the directory
|
||||
returned by this function.
|
||||
|
||||
The aa_policy_cache_dir_levels() function provides access to the number
|
||||
of directories that are being overlayed to create the policy cache.
|
||||
|
||||
|
||||
=head1 RETURN VALUE
|
||||
|
||||
The aa_policy_cache_new() function returns 0 on success and I<*policy_cache>
|
||||
@@ -140,32 +102,15 @@ 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.
|
||||
|
||||
aa_policy_cache_dir_path() returns a path string which must be freed by the
|
||||
caller. NULL is returned on error, with errno set appropriately.
|
||||
|
||||
aa_policy_cache_dir_levels() returns a number indicating the number of
|
||||
directory levels there are associated with the I<policy_cache>.
|
||||
|
||||
aa_policy_cache_dir_path_preview() is the same as
|
||||
aa_policy_cache_dir_path() except that it doesn't require an existing
|
||||
I<aa_policy_cache> object. This is useful if the calling program cannot
|
||||
create an I<aa_policy_cache> object due to lack of privileges needed to
|
||||
create the cache directory.
|
||||
|
||||
=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 or NULL on error.
|
||||
I<aa_policy_cache> family of functions that return -1 on error.
|
||||
|
||||
=head1 NOTES
|
||||
|
||||
All aa_policy_cache functions described above, except for the
|
||||
aa_policy_cache_dir_path() function was added in libapparmor version
|
||||
2.13. All the other aa_policy_cache functions described above are
|
||||
present in libapparmor version 2.10.
|
||||
|
||||
aa_policy_cache_unref() saves the value of errno when called and restores errno
|
||||
before exiting in libapparmor version 2.12 and newer.
|
||||
All aa_policy_cache functions described above are present in libapparmor
|
||||
version 2.10 and newer.
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
|
@@ -22,9 +22,7 @@
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
__BEGIN_DECLS
|
||||
|
||||
/*
|
||||
* Class of public mediation types in the AppArmor policy db
|
||||
@@ -154,7 +152,6 @@ extern int aa_features_write_to_file(aa_features *features,
|
||||
extern bool aa_features_is_equal(aa_features *features1,
|
||||
aa_features *features2);
|
||||
extern bool aa_features_supports(aa_features *features, const char *str);
|
||||
extern char *aa_features_id(aa_features *features);
|
||||
|
||||
typedef struct aa_kernel_interface aa_kernel_interface;
|
||||
extern int aa_kernel_interface_new(aa_kernel_interface **kernel_interface,
|
||||
@@ -187,25 +184,13 @@ 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 int aa_policy_cache_add_ro_dir(aa_policy_cache *policy_cache, int dirfd,
|
||||
const char *path);
|
||||
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);
|
||||
extern int aa_policy_cache_no_dirs(aa_policy_cache *policy_cache);
|
||||
extern char *aa_policy_cache_dir_path(aa_policy_cache *policy_cache, int n);
|
||||
extern int aa_policy_cache_dirfd(aa_policy_cache *policy_cache, int dir);
|
||||
extern int aa_policy_cache_open(aa_policy_cache *policy_cache, const char *name,
|
||||
int flags);
|
||||
extern char *aa_policy_cache_filename(aa_policy_cache *policy_cache, const char *name);
|
||||
extern char *aa_policy_cache_dir_path_preview(aa_features *kernel_features,
|
||||
int dirfd, const char *path);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
__END_DECLS
|
||||
|
||||
#endif /* sys/apparmor.h */
|
||||
|
@@ -20,9 +20,7 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
__BEGIN_DECLS
|
||||
|
||||
int _aa_is_blacklisted(const char *name);
|
||||
|
||||
@@ -34,11 +32,7 @@ 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 *));
|
||||
int _aa_overlaydirat_for_each(int dirfd[], int n, void *data,
|
||||
int (* cb)(int, const char *, struct stat *, void *));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
__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 = 7
|
||||
AA_LIB_REVISION = 0
|
||||
AA_LIB_AGE = 6
|
||||
AA_LIB_CURRENT = 5
|
||||
AA_LIB_REVISION = 2
|
||||
AA_LIB_AGE = 4
|
||||
|
||||
SUFFIXES = .pc.in .pc
|
||||
|
||||
@@ -42,13 +42,13 @@ scanner.h: scanner.l
|
||||
|
||||
scanner.c: scanner.l
|
||||
|
||||
af_protos.h:
|
||||
echo '#include <netinet/in.h>' | $(CC) -E -dM - | 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" > $@
|
||||
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 PMurHash.h
|
||||
noinst_HEADERS = grammar.h parser.h scanner.h af_protos.h private.h
|
||||
|
||||
libapparmor_la_SOURCES = grammar.y libaalogparse.c kernel.c scanner.c private.c features.c kernel_interface.c policy_cache.c PMurHash.c
|
||||
libapparmor_la_SOURCES = grammar.y libaalogparse.c kernel.c scanner.c private.c features.c kernel_interface.c policy_cache.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
|
||||
|
||||
|
@@ -1,317 +0,0 @@
|
||||
/*-----------------------------------------------------------------------------
|
||||
* MurmurHash3 was written by Austin Appleby, and is placed in the public
|
||||
* domain.
|
||||
*
|
||||
* This implementation was written by Shane Day, and is also public domain.
|
||||
*
|
||||
* This is a portable ANSI C implementation of MurmurHash3_x86_32 (Murmur3A)
|
||||
* with support for progressive processing.
|
||||
*/
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
|
||||
If you want to understand the MurmurHash algorithm you would be much better
|
||||
off reading the original source. Just point your browser at:
|
||||
http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp
|
||||
|
||||
|
||||
What this version provides?
|
||||
|
||||
1. Progressive data feeding. Useful when the entire payload to be hashed
|
||||
does not fit in memory or when the data is streamed through the application.
|
||||
Also useful when hashing a number of strings with a common prefix. A partial
|
||||
hash of a prefix string can be generated and reused for each suffix string.
|
||||
|
||||
2. Portability. Plain old C so that it should compile on any old compiler.
|
||||
Both CPU endian and access-alignment neutral, but avoiding inefficient code
|
||||
when possible depending on CPU capabilities.
|
||||
|
||||
3. Drop in. I personally like nice self contained public domain code, making it
|
||||
easy to pilfer without loads of refactoring to work properly in the existing
|
||||
application code & makefile structure and mucking around with licence files.
|
||||
Just copy PMurHash.h and PMurHash.c and you're ready to go.
|
||||
|
||||
|
||||
How does it work?
|
||||
|
||||
We can only process entire 32 bit chunks of input, except for the very end
|
||||
that may be shorter. So along with the partial hash we need to give back to
|
||||
the caller a carry containing up to 3 bytes that we were unable to process.
|
||||
This carry also needs to record the number of bytes the carry holds. I use
|
||||
the low 2 bits as a count (0..3) and the carry bytes are shifted into the
|
||||
high byte in stream order.
|
||||
|
||||
To handle endianess I simply use a macro that reads a uint32_t and define
|
||||
that macro to be a direct read on little endian machines, a read and swap
|
||||
on big endian machines, or a byte-by-byte read if the endianess is unknown.
|
||||
|
||||
-----------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include "PMurHash.h"
|
||||
|
||||
/* I used ugly type names in the header to avoid potential conflicts with
|
||||
* application or system typedefs & defines. Since I'm not including any more
|
||||
* headers below here I can rename these so that the code reads like C99 */
|
||||
#undef uint32_t
|
||||
#define uint32_t MH_UINT32
|
||||
#undef uint8_t
|
||||
#define uint8_t MH_UINT8
|
||||
|
||||
/* MSVC warnings we choose to ignore */
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(disable: 4127) /* conditional expression is constant */
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Endianess, misalignment capabilities and util macros
|
||||
*
|
||||
* The following 3 macros are defined in this section. The other macros defined
|
||||
* are only needed to help derive these 3.
|
||||
*
|
||||
* READ_UINT32(x) Read a little endian unsigned 32-bit int
|
||||
* UNALIGNED_SAFE Defined if READ_UINT32 works on non-word boundaries
|
||||
* ROTL32(x,r) Rotate x left by r bits
|
||||
*/
|
||||
|
||||
/* Convention is to define __BYTE_ORDER == to one of these values */
|
||||
#if !defined(__BIG_ENDIAN)
|
||||
#define __BIG_ENDIAN 4321
|
||||
#endif
|
||||
#if !defined(__LITTLE_ENDIAN)
|
||||
#define __LITTLE_ENDIAN 1234
|
||||
#endif
|
||||
|
||||
/* I386 */
|
||||
#if defined(_M_IX86) || defined(__i386__) || defined(__i386) || defined(i386)
|
||||
#define __BYTE_ORDER __LITTLE_ENDIAN
|
||||
#define UNALIGNED_SAFE
|
||||
#endif
|
||||
|
||||
/* gcc 'may' define __LITTLE_ENDIAN__ or __BIG_ENDIAN__ to 1 (Note the trailing __),
|
||||
* or even _LITTLE_ENDIAN or _BIG_ENDIAN (Note the single _ prefix) */
|
||||
#if !defined(__BYTE_ORDER)
|
||||
#if defined(__LITTLE_ENDIAN__) && __LITTLE_ENDIAN__==1 || defined(_LITTLE_ENDIAN) && _LITTLE_ENDIAN==1
|
||||
#define __BYTE_ORDER __LITTLE_ENDIAN
|
||||
#elif defined(__BIG_ENDIAN__) && __BIG_ENDIAN__==1 || defined(_BIG_ENDIAN) && _BIG_ENDIAN==1
|
||||
#define __BYTE_ORDER __BIG_ENDIAN
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* gcc (usually) defines xEL/EB macros for ARM and MIPS endianess */
|
||||
#if !defined(__BYTE_ORDER)
|
||||
#if defined(__ARMEL__) || defined(__MIPSEL__)
|
||||
#define __BYTE_ORDER __LITTLE_ENDIAN
|
||||
#endif
|
||||
#if defined(__ARMEB__) || defined(__MIPSEB__)
|
||||
#define __BYTE_ORDER __BIG_ENDIAN
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Now find best way we can to READ_UINT32 */
|
||||
#if __BYTE_ORDER==__LITTLE_ENDIAN
|
||||
/* CPU endian matches murmurhash algorithm, so read 32-bit word directly */
|
||||
#define READ_UINT32(ptr) (*((uint32_t*)(ptr)))
|
||||
#elif __BYTE_ORDER==__BIG_ENDIAN
|
||||
/* TODO: Add additional cases below where a compiler provided bswap32 is available */
|
||||
#if defined(__GNUC__) && (__GNUC__>4 || (__GNUC__==4 && __GNUC_MINOR__>=3))
|
||||
#define READ_UINT32(ptr) (__builtin_bswap32(*((uint32_t*)(ptr))))
|
||||
#else
|
||||
/* Without a known fast bswap32 we're just as well off doing this */
|
||||
#define READ_UINT32(ptr) (ptr[0]|ptr[1]<<8|ptr[2]<<16|ptr[3]<<24)
|
||||
#define UNALIGNED_SAFE
|
||||
#endif
|
||||
#else
|
||||
/* Unknown endianess so last resort is to read individual bytes */
|
||||
#define READ_UINT32(ptr) (ptr[0]|ptr[1]<<8|ptr[2]<<16|ptr[3]<<24)
|
||||
|
||||
/* Since we're not doing word-reads we can skip the messing about with realignment */
|
||||
#define UNALIGNED_SAFE
|
||||
#endif
|
||||
|
||||
/* Find best way to ROTL32 */
|
||||
#if defined(_MSC_VER)
|
||||
#include <stdlib.h> /* Microsoft put _rotl declaration in here */
|
||||
#define ROTL32(x,r) _rotl(x,r)
|
||||
#else
|
||||
/* gcc recognises this code and generates a rotate instruction for CPUs with one */
|
||||
#define ROTL32(x,r) (((uint32_t)x << r) | ((uint32_t)x >> (32 - r)))
|
||||
#endif
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Core murmurhash algorithm macros */
|
||||
|
||||
#define C1 (0xcc9e2d51)
|
||||
#define C2 (0x1b873593)
|
||||
|
||||
/* This is the main processing body of the algorithm. It operates
|
||||
* on each full 32-bits of input. */
|
||||
#define DOBLOCK(h1, k1) do{ \
|
||||
k1 *= C1; \
|
||||
k1 = ROTL32(k1,15); \
|
||||
k1 *= C2; \
|
||||
\
|
||||
h1 ^= k1; \
|
||||
h1 = ROTL32(h1,13); \
|
||||
h1 = h1*5+0xe6546b64; \
|
||||
}while(0)
|
||||
|
||||
|
||||
/* Append unaligned bytes to carry, forcing hash churn if we have 4 bytes */
|
||||
/* cnt=bytes to process, h1=name of h1 var, c=carry, n=bytes in c, ptr/len=payload */
|
||||
#define DOBYTES(cnt, h1, c, n, ptr, len) do{ \
|
||||
int _i = cnt; \
|
||||
while(_i--) { \
|
||||
c = c>>8 | *ptr++<<24; \
|
||||
n++; len--; \
|
||||
if(n==4) { \
|
||||
DOBLOCK(h1, c); \
|
||||
n = 0; \
|
||||
} \
|
||||
} }while(0)
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
/* Main hashing function. Initialise carry to 0 and h1 to 0 or an initial seed
|
||||
* if wanted. Both ph1 and pcarry are required arguments. */
|
||||
void PMurHash32_Process(uint32_t *ph1, uint32_t *pcarry, const void *key, int len)
|
||||
{
|
||||
uint32_t h1 = *ph1;
|
||||
uint32_t c = *pcarry;
|
||||
|
||||
const uint8_t *ptr = (uint8_t*)key;
|
||||
const uint8_t *end;
|
||||
|
||||
/* Extract carry count from low 2 bits of c value */
|
||||
int n = c & 3;
|
||||
|
||||
#if defined(UNALIGNED_SAFE)
|
||||
/* This CPU handles unaligned word access */
|
||||
|
||||
/* Consume any carry bytes */
|
||||
int i = (4-n) & 3;
|
||||
if(i && i <= len) {
|
||||
DOBYTES(i, h1, c, n, ptr, len);
|
||||
}
|
||||
|
||||
/* Process 32-bit chunks */
|
||||
end = ptr + len/4*4;
|
||||
for( ; ptr < end ; ptr+=4) {
|
||||
uint32_t k1 = READ_UINT32(ptr);
|
||||
DOBLOCK(h1, k1);
|
||||
}
|
||||
|
||||
#else /*UNALIGNED_SAFE*/
|
||||
/* This CPU does not handle unaligned word access */
|
||||
|
||||
/* Consume enough so that the next data byte is word aligned */
|
||||
int i = -(long)ptr & 3;
|
||||
if(i && i <= len) {
|
||||
DOBYTES(i, h1, c, n, ptr, len);
|
||||
}
|
||||
|
||||
/* We're now aligned. Process in aligned blocks. Specialise for each possible carry count */
|
||||
end = ptr + len/4*4;
|
||||
switch(n) { /* how many bytes in c */
|
||||
case 0: /* c=[----] w=[3210] b=[3210]=w c'=[----] */
|
||||
for( ; ptr < end ; ptr+=4) {
|
||||
uint32_t k1 = READ_UINT32(ptr);
|
||||
DOBLOCK(h1, k1);
|
||||
}
|
||||
break;
|
||||
case 1: /* c=[0---] w=[4321] b=[3210]=c>>24|w<<8 c'=[4---] */
|
||||
for( ; ptr < end ; ptr+=4) {
|
||||
uint32_t k1 = c>>24;
|
||||
c = READ_UINT32(ptr);
|
||||
k1 |= c<<8;
|
||||
DOBLOCK(h1, k1);
|
||||
}
|
||||
break;
|
||||
case 2: /* c=[10--] w=[5432] b=[3210]=c>>16|w<<16 c'=[54--] */
|
||||
for( ; ptr < end ; ptr+=4) {
|
||||
uint32_t k1 = c>>16;
|
||||
c = READ_UINT32(ptr);
|
||||
k1 |= c<<16;
|
||||
DOBLOCK(h1, k1);
|
||||
}
|
||||
break;
|
||||
case 3: /* c=[210-] w=[6543] b=[3210]=c>>8|w<<24 c'=[654-] */
|
||||
for( ; ptr < end ; ptr+=4) {
|
||||
uint32_t k1 = c>>8;
|
||||
c = READ_UINT32(ptr);
|
||||
k1 |= c<<24;
|
||||
DOBLOCK(h1, k1);
|
||||
}
|
||||
}
|
||||
#endif /*UNALIGNED_SAFE*/
|
||||
|
||||
/* Advance over whole 32-bit chunks, possibly leaving 1..3 bytes */
|
||||
len -= len/4*4;
|
||||
|
||||
/* Append any remaining bytes into carry */
|
||||
DOBYTES(len, h1, c, n, ptr, len);
|
||||
|
||||
/* Copy out new running hash and carry */
|
||||
*ph1 = h1;
|
||||
*pcarry = (c & ~0xff) | n;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
/* Finalize a hash. To match the original Murmur3A the total_length must be provided */
|
||||
uint32_t PMurHash32_Result(uint32_t h, uint32_t carry, uint32_t total_length)
|
||||
{
|
||||
uint32_t k1;
|
||||
int n = carry & 3;
|
||||
if(n) {
|
||||
k1 = carry >> (4-n)*8;
|
||||
k1 *= C1; k1 = ROTL32(k1,15); k1 *= C2; h ^= k1;
|
||||
}
|
||||
h ^= total_length;
|
||||
|
||||
/* fmix */
|
||||
h ^= h >> 16;
|
||||
h *= 0x85ebca6b;
|
||||
h ^= h >> 13;
|
||||
h *= 0xc2b2ae35;
|
||||
h ^= h >> 16;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
/* Murmur3A compatable all-at-once */
|
||||
uint32_t PMurHash32(uint32_t seed, const void *key, int len)
|
||||
{
|
||||
uint32_t h1=seed, carry=0;
|
||||
PMurHash32_Process(&h1, &carry, key, len);
|
||||
return PMurHash32_Result(h1, carry, len);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
/* Provide an API suitable for smhasher */
|
||||
void PMurHash32_test(const void *key, int len, uint32_t seed, void *out)
|
||||
{
|
||||
uint32_t h1=seed, carry=0;
|
||||
const uint8_t *ptr = (uint8_t*)key;
|
||||
const uint8_t *end = ptr + len;
|
||||
|
||||
#if 0 /* Exercise the progressive processing */
|
||||
while(ptr < end) {
|
||||
//const uint8_t *mid = ptr + rand()%(end-ptr)+1;
|
||||
const uint8_t *mid = ptr + (rand()&0xF);
|
||||
mid = mid<end?mid:end;
|
||||
PMurHash32_Process(&h1, &carry, ptr, mid-ptr);
|
||||
ptr = mid;
|
||||
}
|
||||
#else
|
||||
PMurHash32_Process(&h1, &carry, ptr, (int)(end-ptr));
|
||||
#endif
|
||||
h1 = PMurHash32_Result(h1, carry, len);
|
||||
*(uint32_t*)out = h1;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
@@ -1,64 +0,0 @@
|
||||
/*-----------------------------------------------------------------------------
|
||||
* MurmurHash3 was written by Austin Appleby, and is placed in the public
|
||||
* domain.
|
||||
*
|
||||
* This implementation was written by Shane Day, and is also public domain.
|
||||
*
|
||||
* This is a portable ANSI C implementation of MurmurHash3_x86_32 (Murmur3A)
|
||||
* with support for progressive processing.
|
||||
*/
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Determine what native type to use for uint32_t */
|
||||
|
||||
/* We can't use the name 'uint32_t' here because it will conflict with
|
||||
* any version provided by the system headers or application. */
|
||||
|
||||
/* First look for special cases */
|
||||
#if defined(_MSC_VER)
|
||||
#define MH_UINT32 unsigned long
|
||||
#endif
|
||||
|
||||
/* If the compiler says it's C99 then take its word for it */
|
||||
#if !defined(MH_UINT32) && ( \
|
||||
defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L )
|
||||
#include <stdint.h>
|
||||
#define MH_UINT32 uint32_t
|
||||
#endif
|
||||
|
||||
/* Otherwise try testing against max value macros from limit.h */
|
||||
#if !defined(MH_UINT32)
|
||||
#include <limits.h>
|
||||
#if (USHRT_MAX == 0xffffffffUL)
|
||||
#define MH_UINT32 unsigned short
|
||||
#elif (UINT_MAX == 0xffffffffUL)
|
||||
#define MH_UINT32 unsigned int
|
||||
#elif (ULONG_MAX == 0xffffffffUL)
|
||||
#define MH_UINT32 unsigned long
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(MH_UINT32)
|
||||
#error Unable to determine type name for unsigned 32-bit int
|
||||
#endif
|
||||
|
||||
/* I'm yet to work on a platform where 'unsigned char' is not 8 bits */
|
||||
#define MH_UINT8 unsigned char
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Prototypes */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void PMurHash32_Process(MH_UINT32 *ph1, MH_UINT32 *pcarry, const void *key, int len);
|
||||
MH_UINT32 PMurHash32_Result(MH_UINT32 h1, MH_UINT32 carry, MH_UINT32 total_length);
|
||||
MH_UINT32 PMurHash32(MH_UINT32 seed, const void *key, int len);
|
||||
|
||||
void PMurHash32_test(const void *key, int len, MH_UINT32 seed, void *out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2017
|
||||
* Copyright (c) 2014
|
||||
* Canonical, Ltd. (All rights reserved)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@@ -20,7 +20,6 @@
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
@@ -32,16 +31,13 @@
|
||||
#include <sys/apparmor.h>
|
||||
|
||||
#include "private.h"
|
||||
#include "PMurHash.h"
|
||||
|
||||
#define FEATURES_FILE "/sys/kernel/security/apparmor/features"
|
||||
|
||||
#define HASH_SIZE (8 + 1) /* 32 bits binary to hex + NUL terminator */
|
||||
#define STRING_SIZE 8192
|
||||
|
||||
struct aa_features {
|
||||
unsigned int ref_count;
|
||||
char hash[HASH_SIZE];
|
||||
char string[STRING_SIZE];
|
||||
};
|
||||
|
||||
@@ -209,29 +205,6 @@ static ssize_t load_features_dir(int dirfd, const char *path,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int init_features_hash(aa_features *features)
|
||||
{
|
||||
const char *string = features->string;
|
||||
uint32_t seed = 5381;
|
||||
uint32_t hash = seed, carry = 0;
|
||||
size_t len = strlen(string);
|
||||
|
||||
/* portable murmur3 hash
|
||||
* https://github.com/aappleby/smhasher/wiki/MurmurHash3
|
||||
*/
|
||||
PMurHash32_Process(&hash, &carry, features, len);
|
||||
hash = PMurHash32_Result(hash, carry, len);
|
||||
|
||||
if (snprintf(features->hash, HASH_SIZE,
|
||||
"%08" PRIx32, hash) >= HASH_SIZE) {
|
||||
errno = ENOBUFS;
|
||||
PERROR("Hash buffer full.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool islbrace(int c)
|
||||
{
|
||||
return c == '{';
|
||||
@@ -431,11 +404,6 @@ int aa_features_new(aa_features **features, int dirfd, const char *path)
|
||||
load_features_dir(dirfd, path, f->string, STRING_SIZE) :
|
||||
load_features_file(dirfd, path, f->string, STRING_SIZE);
|
||||
if (retval == -1) {
|
||||
aa_features_unref(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (init_features_hash(f) == -1) {
|
||||
int save = errno;
|
||||
|
||||
aa_features_unref(f);
|
||||
@@ -478,15 +446,6 @@ int aa_features_new_from_string(aa_features **features,
|
||||
|
||||
memcpy(f->string, string, size);
|
||||
f->string[size] = '\0';
|
||||
|
||||
if (init_features_hash(f) == -1) {
|
||||
int save = errno;
|
||||
|
||||
aa_features_unref(f);
|
||||
errno = save;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*features = f;
|
||||
|
||||
return 0;
|
||||
@@ -523,12 +482,8 @@ aa_features *aa_features_ref(aa_features *features)
|
||||
*/
|
||||
void aa_features_unref(aa_features *features)
|
||||
{
|
||||
int save = errno;
|
||||
|
||||
if (features && atomic_dec_and_test(&features->ref_count))
|
||||
free(features);
|
||||
|
||||
errno = save;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -621,21 +576,3 @@ bool aa_features_supports(aa_features *features, const char *str)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_features_id - provides unique identifier for an aa_features object
|
||||
* @features: the features
|
||||
*
|
||||
* Allocates and returns a string representation of an identifier that can
|
||||
* be used to uniquely identify an aa_features object. The mechanism for
|
||||
* generating the string representation is internal to libapparmor and
|
||||
* subject to change but an example implementation is applying a hash
|
||||
* function to the features string.
|
||||
*
|
||||
* Returns: a string identifying @features which must be freed by the
|
||||
* caller or NULL, with errno set, upon error
|
||||
*/
|
||||
char *aa_features_id(aa_features *features)
|
||||
{
|
||||
return strdup(features->hash);
|
||||
}
|
||||
|
@@ -229,7 +229,10 @@ int aa_kernel_interface_new(aa_kernel_interface **kernel_interface,
|
||||
if (kernel_features) {
|
||||
aa_features_ref(kernel_features);
|
||||
} else if (aa_features_new_from_kernel(&kernel_features) == -1) {
|
||||
int save = errno;
|
||||
|
||||
aa_kernel_interface_unref(ki);
|
||||
errno = save;
|
||||
return -1;
|
||||
}
|
||||
ki->supports_setload = aa_features_supports(kernel_features, set_load);
|
||||
@@ -237,8 +240,11 @@ int aa_kernel_interface_new(aa_kernel_interface **kernel_interface,
|
||||
|
||||
if (!apparmorfs) {
|
||||
if (find_iface_dir(&alloced_apparmorfs) == -1) {
|
||||
int save = errno;
|
||||
|
||||
alloced_apparmorfs = NULL;
|
||||
aa_kernel_interface_unref(ki);
|
||||
errno = save;
|
||||
return -1;
|
||||
}
|
||||
/* alloced_apparmorfs will be autofree'ed */
|
||||
@@ -247,7 +253,10 @@ int aa_kernel_interface_new(aa_kernel_interface **kernel_interface,
|
||||
|
||||
ki->dirfd = open(apparmorfs, O_RDONLY | O_CLOEXEC | O_DIRECTORY);
|
||||
if (ki->dirfd < 0) {
|
||||
int save = errno;
|
||||
|
||||
aa_kernel_interface_unref(ki);
|
||||
errno = save;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -274,16 +283,12 @@ aa_kernel_interface *aa_kernel_interface_ref(aa_kernel_interface *kernel_interfa
|
||||
*/
|
||||
void aa_kernel_interface_unref(aa_kernel_interface *kernel_interface)
|
||||
{
|
||||
int save = errno;
|
||||
|
||||
if (kernel_interface &&
|
||||
atomic_dec_and_test(&kernel_interface->ref_count)) {
|
||||
if (kernel_interface->dirfd >= 0)
|
||||
close(kernel_interface->dirfd);
|
||||
free(kernel_interface);
|
||||
}
|
||||
|
||||
errno = save;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -95,26 +95,6 @@ APPARMOR_2.11 {
|
||||
*;
|
||||
} APPARMOR_2.10;
|
||||
|
||||
APPARMOR_2.13 {
|
||||
global:
|
||||
aa_policy_cache_dir_path;
|
||||
aa_policy_cache_dir_path_preview;
|
||||
aa_policy_cache_no_dirs;
|
||||
aa_policy_cache_dirfd;
|
||||
aa_policy_cache_open;
|
||||
aa_policy_cache_filename;
|
||||
aa_features_id;
|
||||
local:
|
||||
*;
|
||||
} APPARMOR_2.11;
|
||||
|
||||
APPARMOR_2.13.1 {
|
||||
global:
|
||||
aa_policy_cache_add_ro_dir;
|
||||
local:
|
||||
*;
|
||||
} APPARMOR_2.13;
|
||||
|
||||
PRIVATE {
|
||||
global:
|
||||
_aa_is_blacklisted;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2017
|
||||
* Copyright (c) 2014
|
||||
* Canonical, Ltd. (All rights reserved)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@@ -16,11 +16,8 @@
|
||||
* Ltd.
|
||||
*/
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <fnmatch.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -32,155 +29,31 @@
|
||||
#include "private.h"
|
||||
|
||||
#define CACHE_FEATURES_FILE ".features"
|
||||
#define MAX_POLICY_CACHE_OVERLAY_DIRS 4
|
||||
|
||||
struct aa_policy_cache {
|
||||
unsigned int ref_count;
|
||||
aa_features *features;
|
||||
aa_features *kernel_features;
|
||||
int n;
|
||||
int dirfd[MAX_POLICY_CACHE_OVERLAY_DIRS];
|
||||
int dirfd;
|
||||
};
|
||||
|
||||
static int clear_cache_cb(int dirfd, const char *path, struct stat *st,
|
||||
void *data unused)
|
||||
{
|
||||
if (S_ISREG(st->st_mode)) {
|
||||
/* remove regular files */
|
||||
/* remove regular files */
|
||||
if (S_ISREG(st->st_mode))
|
||||
return unlinkat(dirfd, path, 0);
|
||||
} else if (S_ISDIR(st->st_mode)) {
|
||||
int retval;
|
||||
|
||||
retval = _aa_dirat_for_each(dirfd, path, NULL, clear_cache_cb);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return unlinkat(dirfd, path, AT_REMOVEDIR);
|
||||
}
|
||||
|
||||
/* do nothing with other file types */
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct replace_all_cb_data {
|
||||
aa_policy_cache *policy_cache;
|
||||
aa_kernel_interface *kernel_interface;
|
||||
};
|
||||
|
||||
static int replace_all_cb(int dirfd, const char *name, struct stat *st,
|
||||
void *cb_data)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
if (S_ISLNK(st->st_mode)) {
|
||||
/*
|
||||
* symlinks in that cache are used to track file merging.
|
||||
* In the scanned overlay situation they can be skipped
|
||||
* as the combined entry will be one of none skipped
|
||||
* entries
|
||||
*/
|
||||
} else if (S_ISREG(st->st_mode) && st->st_size == 0) {
|
||||
/*
|
||||
* empty file in the cache dir is used as a whiteout
|
||||
* to hide files in a lower layer. skip
|
||||
*/
|
||||
} else 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,
|
||||
dirfd,
|
||||
name);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static char *path_from_fd(int fd)
|
||||
{
|
||||
autofree char *proc_path = NULL;
|
||||
autoclose int proc_fd = -1;
|
||||
struct stat proc_stat;
|
||||
char *path;
|
||||
ssize_t size, path_len;
|
||||
|
||||
if (asprintf(&proc_path, "/proc/self/fd/%d", fd) == -1) {
|
||||
proc_path = NULL;
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
proc_fd = open(proc_path, O_RDONLY | O_CLOEXEC | O_PATH | O_NOFOLLOW);
|
||||
if (proc_fd == -1)
|
||||
return NULL;
|
||||
|
||||
if (fstat(proc_fd, &proc_stat) == -1)
|
||||
return NULL;
|
||||
|
||||
if (!S_ISLNK(proc_stat.st_mode)) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size = proc_stat.st_size;
|
||||
repeat:
|
||||
path = malloc(size + 1);
|
||||
if (!path)
|
||||
return NULL;
|
||||
|
||||
/**
|
||||
* Since 2.6.39, symlink file descriptors opened with
|
||||
* (O_PATH | O_NOFOLLOW) can be used as the dirfd with an empty string
|
||||
* as the path. readlinkat() will operate on the symlink inode.
|
||||
*/
|
||||
path_len = readlinkat(proc_fd, "", path, size);
|
||||
if (path_len == -1)
|
||||
return NULL;
|
||||
if (path_len == size) {
|
||||
free(path);
|
||||
size = size * 2;
|
||||
goto repeat;
|
||||
}
|
||||
path[path_len] = '\0';
|
||||
return path;
|
||||
}
|
||||
|
||||
static int cache_check_features(int dirfd, const char *cache_name,
|
||||
aa_features *features)
|
||||
{
|
||||
aa_features *local_features = NULL;
|
||||
autofree char *name = NULL;
|
||||
bool rc;
|
||||
int len;
|
||||
|
||||
len = asprintf(&name, "%s/%s", cache_name, CACHE_FEATURES_FILE);
|
||||
if (len == -1) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* verify that cache dir .features matches */
|
||||
if (aa_features_new(&local_features, dirfd, name)) {
|
||||
PDEBUG("could not setup new features object for dirfd '%d' '%s'\n", dirfd, name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = aa_features_is_equal(local_features, features);
|
||||
aa_features_unref(local_features);
|
||||
if (!rc) {
|
||||
errno = EEXIST;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_cache(aa_policy_cache *policy_cache, aa_features *features)
|
||||
{
|
||||
if (aa_policy_cache_remove(policy_cache->dirfd[0], "."))
|
||||
if (aa_policy_cache_remove(policy_cache->dirfd, "."))
|
||||
return -1;
|
||||
|
||||
if (aa_features_write_to_file(features, policy_cache->dirfd[0],
|
||||
if (aa_features_write_to_file(features, policy_cache->dirfd,
|
||||
CACHE_FEATURES_FILE) == -1)
|
||||
return -1;
|
||||
|
||||
@@ -192,176 +65,51 @@ static int create_cache(aa_policy_cache *policy_cache, aa_features *features)
|
||||
static int init_cache_features(aa_policy_cache *policy_cache,
|
||||
aa_features *kernel_features, bool create)
|
||||
{
|
||||
if (cache_check_features(policy_cache->dirfd[0], ".",
|
||||
kernel_features)) {
|
||||
/* EEXIST must come before ENOENT for short circuit eval */
|
||||
if (!create || errno == EEXIST || errno != ENOENT)
|
||||
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;
|
||||
} else
|
||||
return 0;
|
||||
|
||||
return create_cache(policy_cache, kernel_features);
|
||||
}
|
||||
|
||||
struct miss_cb_data {
|
||||
aa_features *features;
|
||||
const char *path;
|
||||
char *pattern;
|
||||
char *cache_name; /* return */
|
||||
long n;
|
||||
};
|
||||
|
||||
/* called on cache collision or miss where cache isn't present */
|
||||
static int cache_miss_cb(int dirfd, const struct dirent *ent, void *arg)
|
||||
{
|
||||
struct miss_cb_data *data = arg;
|
||||
char *cache_name, *pos, *tmp;
|
||||
long n;
|
||||
int len;
|
||||
|
||||
/* TODO: update to tighter pattern match of just trailing #s */
|
||||
if (fnmatch(data->pattern, ent->d_name, 0))
|
||||
return 0;
|
||||
|
||||
/* entry matches <feature_id>.<n> pattern */
|
||||
len = asprintf(&cache_name, "%s/%s", data->path, ent->d_name);
|
||||
if (len == -1) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
if (!cache_check_features(dirfd, cache_name, data->features) || errno == ENOENT) {
|
||||
/* found cache dir matching pattern */
|
||||
data->cache_name = cache_name;
|
||||
/* return 1 to stop iteration and signal dir found */
|
||||
return 1;
|
||||
} else if (errno != EEXIST) {
|
||||
PDEBUG("cache_check_features() failed for dirfd '%d' '%s'\n", dirfd, cache_name);
|
||||
free(cache_name);
|
||||
return -1;
|
||||
}
|
||||
free(cache_name);
|
||||
|
||||
/* check the cache dir # */
|
||||
pos = strchr(ent->d_name, '.');
|
||||
n = strtol(pos+1, &tmp, 10);
|
||||
if (n == LONG_MIN || n == LONG_MAX || tmp == pos + 1)
|
||||
return -1;
|
||||
if (n > data->n)
|
||||
data->n = n;
|
||||
|
||||
/* continue processing */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* will return cache_path on error if there is a collision */
|
||||
static int cache_dir_from_path_and_features(char **cache_path,
|
||||
int dirfd, const char *path,
|
||||
aa_features *features)
|
||||
{
|
||||
autofree const char *features_id = NULL;
|
||||
char *cache_dir;
|
||||
size_t len;
|
||||
int rc;
|
||||
|
||||
features_id = aa_features_id(features);
|
||||
if (!features_id)
|
||||
return -1;
|
||||
|
||||
len = asprintf(&cache_dir, "%s/%s.0", path, features_id);
|
||||
if (len == -1)
|
||||
return -1;
|
||||
|
||||
if (!cache_check_features(dirfd, cache_dir, features) || errno == ENOENT) {
|
||||
PDEBUG("cache_dir_from_path_and_features() found '%s'\n", cache_dir);
|
||||
*cache_path = cache_dir;
|
||||
return 0;
|
||||
} else if (errno != EEXIST) {
|
||||
PDEBUG("cache_check_features() failed for dirfd '%d' %s\n", dirfd, cache_dir);
|
||||
free(cache_dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
PDEBUG("Cache collision '%s' falling back to next dir on fd '%d' path %s", cache_dir, dirfd, path);
|
||||
free(cache_dir);
|
||||
|
||||
struct miss_cb_data data = {
|
||||
.features = features,
|
||||
.path = path,
|
||||
.cache_name = NULL,
|
||||
.n = -1,
|
||||
};
|
||||
|
||||
if (asprintf(&data.pattern, "%s.*", features_id) == -1)
|
||||
return -1;
|
||||
|
||||
rc = _aa_dirat_for_each2(dirfd, path, &data, cache_miss_cb);
|
||||
free(data.pattern);
|
||||
if (rc == 1) {
|
||||
/* found matching feature dir */
|
||||
PDEBUG("cache_dir_from_path_and_features() callback found '%s'\n", data.cache_name);
|
||||
*cache_path = data.cache_name;
|
||||
return 0;
|
||||
} else if (rc) {
|
||||
PDEBUG("cache_dir_from_path_and_features() callback returned an error'%m'\n");
|
||||
return -1;
|
||||
}
|
||||
/* no dir found use 1 higher than highest dir n searched */
|
||||
len = asprintf(&cache_dir, "%s/%s.%d", path, features_id, data.n + 1);
|
||||
if (len == -1)
|
||||
return -1;
|
||||
|
||||
PDEBUG("Cache collision no dir found using %d + 1 = %s\n", data.n + 1, cache_dir);
|
||||
*cache_path = cache_dir;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* will return the cache_dir or NULL */
|
||||
static int open_or_create_cache_dir(aa_features *features, int dirfd,
|
||||
const char *path, bool create,
|
||||
char **cache_dir)
|
||||
{
|
||||
int fd;
|
||||
|
||||
*cache_dir = NULL;
|
||||
if (cache_dir_from_path_and_features(cache_dir, dirfd, path,
|
||||
features))
|
||||
return -1;
|
||||
|
||||
open:
|
||||
fd = openat(dirfd, *cache_dir, O_RDONLY | O_CLOEXEC | O_DIRECTORY);
|
||||
if (fd < 0) {
|
||||
/* does the dir exist? */
|
||||
if (create && errno == ENOENT) {
|
||||
/**
|
||||
* 1) Attempt to create the cache location, such as
|
||||
* /etc/apparmor.d/cache.d/
|
||||
* 2) Attempt to create the cache directory, for the
|
||||
* passed in aa_features, such as
|
||||
* /etc/apparmor.d/cache.d/<features_id>/
|
||||
* 3) Try to reopen the cache directory
|
||||
*/
|
||||
if (mkdirat(dirfd, path, 0700) == -1 &&
|
||||
errno != EEXIST) {
|
||||
PERROR("Can't create cache location '%s': %m\n",
|
||||
path);
|
||||
} else if (mkdirat(dirfd, *cache_dir, 0700) == -1 &&
|
||||
errno != EEXIST) {
|
||||
PERROR("Can't create cache directory '%s': %m\n",
|
||||
*cache_dir);
|
||||
} else {
|
||||
goto open;
|
||||
}
|
||||
} else if (create) {
|
||||
PERROR("Can't update cache directory '%s': %m\n", *cache_dir);
|
||||
} else {
|
||||
PDEBUG("Cache directory '%s' does not exist\n", *cache_dir);
|
||||
/* 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;
|
||||
}
|
||||
|
||||
PDEBUG("Could not open cache_dir: %m");
|
||||
return -1;
|
||||
/* The cache directory needs to be refreshed */
|
||||
call_create_cache = true;
|
||||
}
|
||||
|
||||
return fd;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -385,11 +133,8 @@ int aa_policy_cache_new(aa_policy_cache **policy_cache,
|
||||
aa_features *kernel_features,
|
||||
int dirfd, const char *path, uint16_t max_caches)
|
||||
{
|
||||
autofree char *cache_dir = NULL;
|
||||
aa_policy_cache *pc;
|
||||
bool create = max_caches > 0;
|
||||
autofree const char *features_id = NULL;
|
||||
int i, fd;
|
||||
|
||||
*policy_cache = NULL;
|
||||
|
||||
@@ -398,79 +143,65 @@ int aa_policy_cache_new(aa_policy_cache **policy_cache,
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* TODO: currently no reaping of caches in excess of max_caches */
|
||||
if (max_caches > 1) {
|
||||
errno = ENOTSUP;
|
||||
return -1;
|
||||
}
|
||||
|
||||
pc = calloc(1, sizeof(*pc));
|
||||
if (!pc) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
pc->n = 0;
|
||||
for (i = 0; i < MAX_POLICY_CACHE_OVERLAY_DIRS; i++)
|
||||
pc->dirfd[i] = -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) {
|
||||
aa_policy_cache_unref(pc);
|
||||
PDEBUG("%s: Failed to obtain features %m\n", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
pc->features = kernel_features;
|
||||
int save = errno;
|
||||
|
||||
fd = open_or_create_cache_dir(kernel_features, dirfd, path, create,
|
||||
&cache_dir);
|
||||
if (fd == -1) {
|
||||
aa_policy_cache_unref(pc);
|
||||
PDEBUG("%s: Failed to open_or_create_dir %m\n", __FUNCTION__);
|
||||
errno = save;
|
||||
return -1;
|
||||
}
|
||||
pc->dirfd[0] = fd;
|
||||
pc->n = 1;
|
||||
pc->kernel_features = kernel_features;
|
||||
|
||||
if (init_cache_features(pc, kernel_features, create)) {
|
||||
PDEBUG("%s: failed init_cache_features for dirfd '%d' name '%s' opened as pc->dirfd '%d'\n", __FUNCTION__, dirfd, cache_dir, pc->dirfd);
|
||||
int save = errno;
|
||||
|
||||
aa_policy_cache_unref(pc);
|
||||
errno = save;
|
||||
return -1;
|
||||
}
|
||||
|
||||
PDEBUG("%s: created policy_cache for dirfd '%d' name '%s' opened as pc->dirfd '%d'\n", __FUNCTION__, dirfd, cache_dir, pc->dirfd);
|
||||
*policy_cache = pc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_policy_cache_add_ro_dir - add an readonly dir layer to the policy cache
|
||||
* @policy_cache: policy_cache to add the readonly dir to
|
||||
* @dirfd: directory file descriptor or AT_FDCWD (see openat(2))
|
||||
* @path: path to the readonly policy cache
|
||||
*
|
||||
* Returns: 0 on success, -1 on error with errno set
|
||||
*/
|
||||
|
||||
int aa_policy_cache_add_ro_dir(aa_policy_cache *policy_cache, int dirfd,
|
||||
const char *path)
|
||||
{
|
||||
autofree char *cache_dir = NULL;
|
||||
int fd;
|
||||
|
||||
if (policy_cache->n >= MAX_POLICY_CACHE_OVERLAY_DIRS) {
|
||||
errno = ENOSPC;
|
||||
PDEBUG("%s: exceeded number of supported cache overlays\n", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
fd = open_or_create_cache_dir(policy_cache->features, dirfd, path,
|
||||
false, &cache_dir);
|
||||
if (fd == -1) {
|
||||
PDEBUG("%s: failed to open_or_create_cache_dir %m\n", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
policy_cache->dirfd[policy_cache->n++] = fd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_policy_cache_ref - increments the ref count of an aa_policy_cache object
|
||||
* @policy_cache: the policy_cache
|
||||
@@ -489,18 +220,13 @@ aa_policy_cache *aa_policy_cache_ref(aa_policy_cache *policy_cache)
|
||||
*/
|
||||
void aa_policy_cache_unref(aa_policy_cache *policy_cache)
|
||||
{
|
||||
int i, save = errno;
|
||||
|
||||
if (policy_cache && atomic_dec_and_test(&policy_cache->ref_count)) {
|
||||
aa_features_unref(policy_cache->features);
|
||||
for (i = 0; i < MAX_POLICY_CACHE_OVERLAY_DIRS; i++) {
|
||||
if (policy_cache->dirfd[i] != -1)
|
||||
close(policy_cache->dirfd[i]);
|
||||
}
|
||||
aa_features_unref(policy_cache->kernel_features);
|
||||
if (policy_cache->dirfd != -1)
|
||||
close(policy_cache->dirfd);
|
||||
free(policy_cache);
|
||||
}
|
||||
|
||||
errno = save;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -534,7 +260,7 @@ int aa_policy_cache_replace_all(aa_policy_cache *policy_cache,
|
||||
if (kernel_interface) {
|
||||
aa_kernel_interface_ref(kernel_interface);
|
||||
} else if (aa_kernel_interface_new(&kernel_interface,
|
||||
policy_cache->features,
|
||||
policy_cache->kernel_features,
|
||||
NULL) == -1) {
|
||||
kernel_interface = NULL;
|
||||
return -1;
|
||||
@@ -542,158 +268,10 @@ int aa_policy_cache_replace_all(aa_policy_cache *policy_cache,
|
||||
|
||||
cb_data.policy_cache = policy_cache;
|
||||
cb_data.kernel_interface = kernel_interface;
|
||||
retval = _aa_overlaydirat_for_each(policy_cache->dirfd, policy_cache->n,
|
||||
&cb_data, replace_all_cb);
|
||||
retval = _aa_dirat_for_each(policy_cache->dirfd, ".", &cb_data,
|
||||
replace_all_cb);
|
||||
|
||||
aa_kernel_interface_unref(kernel_interface);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_policy_cache_no_dirs - return the number of dirs making up the cache
|
||||
* @policy_cache: the policy_cache
|
||||
*
|
||||
* Returns: The number of directories that the policy cache is composed of
|
||||
*/
|
||||
int aa_policy_cache_no_dirs(aa_policy_cache *policy_cache)
|
||||
{
|
||||
return policy_cache->n;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_policy_cache_dir_path - returns the path to the aa_policy_cache directory
|
||||
* @policy_cache: the policy_cache
|
||||
* @dir: which dir in the policy cache to return the name of
|
||||
*
|
||||
* Returns: The path to the policy cache directory on success, NULL on
|
||||
* error with errno set.
|
||||
*/
|
||||
char *aa_policy_cache_dir_path(aa_policy_cache *policy_cache, int dir)
|
||||
{
|
||||
char *path = NULL;
|
||||
|
||||
if (dir < 0 || dir >= policy_cache->n) {
|
||||
PERROR("aa_policy_cache directory: %d does not exist\n", dir);
|
||||
errno = ERANGE;
|
||||
} else {
|
||||
path = path_from_fd(policy_cache->dirfd[dir]);
|
||||
}
|
||||
|
||||
if (!path)
|
||||
PERROR("Can't return the path to the aa_policy_cache directory: %m\n");
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_policy_cache_dirfd - returns the dirfd for a aa_policy_cache directory
|
||||
* @policy_cache: the policy_cache
|
||||
* @dir: which dir in the policy cache to return the dirfd of
|
||||
*
|
||||
* Returns: The dirfd to the @dir policy cache directory on success, -1 on
|
||||
* error with errno set.
|
||||
*
|
||||
* caller is responsible for closing the returned dirfd
|
||||
*/
|
||||
int aa_policy_cache_dirfd(aa_policy_cache *policy_cache, int dir)
|
||||
{
|
||||
if (dir < 0 || dir >= policy_cache->n) {
|
||||
PERROR("aa_policy_cache directory: %d does not exist\n", dir);
|
||||
errno = ERANGE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return dup(policy_cache->dirfd[dir]);
|
||||
}
|
||||
|
||||
/* open cache file corresponding to name */
|
||||
int aa_policy_cache_open(aa_policy_cache *policy_cache, const char *name,
|
||||
int flags)
|
||||
{
|
||||
int i, fd;
|
||||
|
||||
for (i = 0; i < policy_cache->n; i++) {
|
||||
fd = openat(policy_cache->dirfd[i], name, flags);
|
||||
if (fd != -1)
|
||||
return fd;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *aa_policy_cache_filename(aa_policy_cache *policy_cache, const char *name)
|
||||
{
|
||||
char *path;
|
||||
autoclose int fd = aa_policy_cache_open(policy_cache, name, O_RDONLY);
|
||||
|
||||
if (fd == -1)
|
||||
return NULL;
|
||||
path = path_from_fd(fd);
|
||||
if (!path)
|
||||
PERROR("Can't return the path to the aa_policy_cache cachename: %m\n");
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_policy_cache_dir_path_preview - returns the path to the aa_policy_cache directory
|
||||
* @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
|
||||
*
|
||||
* Returns: The path to the policy cache directory on success, NULL on
|
||||
* error with errno set.
|
||||
*/
|
||||
char *aa_policy_cache_dir_path_preview(aa_features *kernel_features,
|
||||
int dirfd, const char *path)
|
||||
{
|
||||
autofree char *cache_loc = NULL;
|
||||
autofree char *cache_dir = NULL;
|
||||
char *dir_path;
|
||||
|
||||
if (kernel_features) {
|
||||
aa_features_ref(kernel_features);
|
||||
} else if (aa_features_new_from_kernel(&kernel_features) == -1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Leave cache_loc set to NULL if dirfd is AT_FDCWD and handle a
|
||||
* NULL cache_loc in the asprintf() below
|
||||
*/
|
||||
if (dirfd != AT_FDCWD) {
|
||||
cache_loc = path_from_fd(dirfd);
|
||||
if (!cache_loc) {
|
||||
int save = errno;
|
||||
|
||||
PERROR("Can't return the path to the aa_policy_cache cache location: %m\n");
|
||||
aa_features_unref(kernel_features);
|
||||
errno = save;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
PDEBUG("Looking up cachedir path for AT_FDCWD\n");
|
||||
if (cache_dir_from_path_and_features(&cache_dir, dirfd, path,
|
||||
kernel_features)) {
|
||||
int save = errno;
|
||||
|
||||
PERROR("Can't return the path to the aa_policy_cache directory: %m\n");
|
||||
aa_features_unref(kernel_features);
|
||||
errno = save;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
aa_features_unref(kernel_features);
|
||||
|
||||
if (asprintf(&dir_path, "%s%s%s",
|
||||
cache_loc ? : "", cache_loc ? "/" : "", cache_dir) == -1) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PDEBUG("aa_policy_cache_dir_path_preview() returning '%s'\n", dir_path);
|
||||
return dir_path;
|
||||
}
|
||||
|
@@ -38,24 +38,11 @@
|
||||
#ifndef HAVE_SECURE_GETENV
|
||||
#ifdef HAVE___SECURE_GETENV
|
||||
#define secure_getenv __secure_getenv
|
||||
#elif ENABLE_DEBUG_OUTPUT
|
||||
#error Debug output is not possible without a secure_getenv() implementation.
|
||||
#else
|
||||
#define secure_getenv(env) NULL
|
||||
#error neither secure_getenv nor __secure_getenv is available
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Allow libapparmor to build on older glibcs and other libcs that do
|
||||
* not support reallocarray.
|
||||
*/
|
||||
#ifndef HAVE_REALLOCARRY
|
||||
void *reallocarray(void *ptr, size_t nmemb, size_t size)
|
||||
{
|
||||
return realloc(ptr, nmemb * size);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct ignored_suffix_t {
|
||||
const char * text;
|
||||
int len;
|
||||
@@ -69,10 +56,6 @@ static struct ignored_suffix_t ignored_suffixes[] = {
|
||||
{ ".dpkg-old", 9, 1 },
|
||||
{ ".dpkg-dist", 10, 1 },
|
||||
{ ".dpkg-bak", 9, 1 },
|
||||
{ ".dpkg-remove", 12, 1 },
|
||||
/* Archlinux packaging files */
|
||||
{ ".pacsave", 8, 1 },
|
||||
{ ".pacnew", 7, 1 },
|
||||
/* RPM packaging files have traditionally not been silently
|
||||
ignored */
|
||||
{ ".rpmnew", 7, 0 },
|
||||
@@ -186,252 +169,13 @@ int _aa_asprintf(char **strp, const char *fmt, ...)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* stops on first error, can use errno or return value to communicate
|
||||
* the goal is to use this to replace _aa_dirat_for_each, but that will
|
||||
* be a different patch.
|
||||
*/
|
||||
int _aa_dirat_for_each2(int dirfd, const char *name, void *data,
|
||||
int (* cb)(int, const struct dirent *, void *))
|
||||
static int dot_or_dot_dot_filter(const struct dirent *ent)
|
||||
{
|
||||
autoclose int cb_dirfd = -1;
|
||||
int fd_for_dir = -1;
|
||||
const struct dirent *ent;
|
||||
DIR *dir;
|
||||
int save, rc;
|
||||
if (strcmp(ent->d_name, ".") == 0 ||
|
||||
strcmp(ent->d_name, "..") == 0)
|
||||
return 0;
|
||||
|
||||
if (!cb || !name) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
save = errno;
|
||||
|
||||
cb_dirfd = openat(dirfd, name, O_RDONLY | O_CLOEXEC | O_DIRECTORY);
|
||||
if (cb_dirfd == -1) {
|
||||
PDEBUG("could not open directory fd '%d' '%s': %m\n", dirfd, name);
|
||||
return -1;
|
||||
}
|
||||
/* dup cd_dirfd because fdopendir has claimed the fd passed to it */
|
||||
fd_for_dir = dup(cb_dirfd);
|
||||
if (fd_for_dir == -1) {
|
||||
PDEBUG("could not dup directory fd '%s': %m\n", name);
|
||||
return -1;
|
||||
}
|
||||
dir = fdopendir(fd_for_dir);
|
||||
if (!dir) {
|
||||
PDEBUG("could not open directory '%s' from fd '%d': %m\n", name, fd_for_dir);
|
||||
close(fd_for_dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((ent = readdir(dir))) {
|
||||
if (cb) {
|
||||
rc = (*cb)(cb_dirfd, ent, data);
|
||||
if (rc) {
|
||||
PDEBUG("dir_for_each callback failed for '%s'\n",
|
||||
ent->d_name);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
errno = save;
|
||||
|
||||
out:
|
||||
closedir(dir);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
#define max(a, b) \
|
||||
({ __typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
_a > _b ? _a : _b; })
|
||||
#define CHUNK 32
|
||||
struct overlaydir {
|
||||
int dirfd;
|
||||
struct dirent *dent;
|
||||
};
|
||||
|
||||
static int insert(struct overlaydir **overlayptr, int *max_size, int *size,
|
||||
int pos, int remaining, int dirfd, struct dirent *ent)
|
||||
{
|
||||
struct overlaydir *overlay = *overlayptr;
|
||||
int i, chunk = max(remaining, CHUNK);
|
||||
|
||||
if (size + 1 >= max_size) {
|
||||
struct overlaydir *tmp = reallocarray(overlay,
|
||||
*max_size + chunk,
|
||||
sizeof(*overlay));
|
||||
if (tmp == NULL)
|
||||
return -1;
|
||||
overlay = tmp;
|
||||
}
|
||||
*max_size += chunk;
|
||||
(*size)++;
|
||||
for (i = *size; i > pos; i--)
|
||||
overlay[i] = overlay[i - 1];
|
||||
overlay[pos].dirfd = dirfd;
|
||||
overlay[pos].dent = ent;
|
||||
|
||||
*overlayptr = overlay;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define merge(overlay, n_overlay, max_size, list, n_list, dirfd) \
|
||||
({ \
|
||||
int i, j; \
|
||||
int rc = 0; \
|
||||
\
|
||||
for (i = 0, j = 0; i < n_overlay && j < n_list; ) { \
|
||||
int res = strcmp(overlay[i].dent->d_name, list[j]->d_name);\
|
||||
if (res < 0) { \
|
||||
i++; \
|
||||
continue; \
|
||||
} else if (res == 0) { \
|
||||
free(list[j]); \
|
||||
list[j] = NULL; \
|
||||
i++; \
|
||||
j++; \
|
||||
} else { \
|
||||
if ((rc = insert(&overlay, &max_size, &n_overlay, i,\
|
||||
n_list - j, dirfd, list[j]))) \
|
||||
goto fail; \
|
||||
i++; \
|
||||
list[j++] = NULL; \
|
||||
} \
|
||||
} \
|
||||
while (j < n_list) { \
|
||||
if ((rc = insert(&overlay, &max_size, &n_overlay, i, \
|
||||
n_list - j, dirfd,list[j]))) \
|
||||
goto fail; \
|
||||
i++; \
|
||||
list[j++] = NULL; \
|
||||
} \
|
||||
\
|
||||
fail: \
|
||||
rc; \
|
||||
})
|
||||
|
||||
static ssize_t readdirfd(int dirfd, struct dirent ***out,
|
||||
int (*dircmp)(const struct dirent **, const struct dirent **))
|
||||
{
|
||||
struct dirent **dents = NULL, *dent;
|
||||
ssize_t n = 0;
|
||||
size_t i;
|
||||
int save;
|
||||
DIR *dir;
|
||||
|
||||
*out = NULL;
|
||||
|
||||
/*
|
||||
* closedir(dir) will close the underlying fd, so we need
|
||||
* to dup first
|
||||
*/
|
||||
if ((dirfd = dup(dirfd)) < 0) {
|
||||
PDEBUG("dup of dirfd failed: %m\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((dir = fdopendir(dirfd)) == NULL) {
|
||||
PDEBUG("fdopendir of dirfd failed: %m\n");
|
||||
close(dirfd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get number of directory entries */
|
||||
while ((dent = readdir(dir)) != NULL) {
|
||||
if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
|
||||
continue;
|
||||
n++;
|
||||
}
|
||||
rewinddir(dir);
|
||||
|
||||
dents = calloc(n, sizeof(struct dirent *));
|
||||
if (!dents)
|
||||
goto fail;
|
||||
|
||||
for (i = 0; i < n; ) {
|
||||
if ((dent = readdir(dir)) == NULL) {
|
||||
PDEBUG("readdir of entry[%d] failed: %m\n", i);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
|
||||
continue;
|
||||
|
||||
dents[i] = malloc(sizeof(*dents[i]));
|
||||
if (!dents[i])
|
||||
goto fail;
|
||||
memcpy(dents[i], dent, sizeof(*dent));
|
||||
i++;
|
||||
}
|
||||
|
||||
if (dircmp)
|
||||
qsort(dents, n, sizeof(*dent), (int (*)(const void *, const void *))dircmp);
|
||||
|
||||
*out = dents;
|
||||
closedir(dir);
|
||||
return n;
|
||||
|
||||
fail:
|
||||
save = errno;
|
||||
if (dents) {
|
||||
for (i = 0; i < n; i++)
|
||||
free(dents[i]);
|
||||
}
|
||||
free(dents);
|
||||
closedir(dir);
|
||||
errno = save;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _aa_overlaydirat_for_each(int dirfd[], int n, void *data,
|
||||
int (* cb)(int, const char *, struct stat *, void *))
|
||||
{
|
||||
autofree struct dirent **list = NULL;
|
||||
autofree struct overlaydir *overlay = NULL;
|
||||
int i, k;
|
||||
int n_list, size = 0, max_size = 0;
|
||||
int rc = 0;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
n_list = readdirfd(dirfd[i], &list, alphasort);
|
||||
if (n_list == -1) {
|
||||
PDEBUG("scandirat of dirfd[%d] failed: %m\n", i);
|
||||
return -1;
|
||||
}
|
||||
if (merge(overlay, size, max_size, list, n_list, dirfd[i])) {
|
||||
for (k = 0; k < n_list; k++)
|
||||
free(list[i]);
|
||||
for (k = 0; k < size; k++)
|
||||
free(overlay[k].dent);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (rc = 0, i = 0; i < size; i++) {
|
||||
/* Must cycle through all dirs so that each one is autofreed */
|
||||
autofree struct dirent *dent = overlay[i].dent;
|
||||
struct stat my_stat;
|
||||
|
||||
if (rc)
|
||||
continue;
|
||||
|
||||
if (fstatat(overlay[i].dirfd, dent->d_name, &my_stat,
|
||||
AT_SYMLINK_NOFOLLOW)) {
|
||||
PDEBUG("stat failed for '%s': %m\n", dent->d_name);
|
||||
rc = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cb(overlay[i].dirfd, dent->d_name, &my_stat, data)) {
|
||||
PDEBUG("dir_for_each callback failed for '%s'\n",
|
||||
dent->d_name);
|
||||
rc = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -475,7 +219,8 @@ int _aa_dirat_for_each(int dirfd, const char *name, void *data,
|
||||
return -1;
|
||||
}
|
||||
|
||||
num_dirs = readdirfd(cb_dirfd, &namelist, NULL);
|
||||
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;
|
||||
|
@@ -17,7 +17,6 @@
|
||||
#ifndef _AA_PRIVATE_H
|
||||
#define _AA_PRIVATE_H 1
|
||||
|
||||
#include <dirent.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/apparmor_private.h>
|
||||
|
||||
@@ -52,7 +51,4 @@ void print_debug(const char *fmt, ...);
|
||||
void atomic_inc(unsigned int *v);
|
||||
bool atomic_dec_and_test(unsigned int *v);
|
||||
|
||||
int _aa_dirat_for_each2(int dirfd, const char *name, void *data,
|
||||
int (* cb)(int, const struct dirent *, void *));
|
||||
|
||||
#endif /* _AA_PRIVATE_H */
|
||||
|
@@ -1,4 +1,4 @@
|
||||
/usr/sbin/cupsd {
|
||||
owner /boot/ r,
|
||||
/boot/ r,
|
||||
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
/home/ubuntu/bzr/apparmor/tests/regression/apparmor/mkdir {
|
||||
owner /tmp/sdtest.7283-14445-r31VAP/tmpdir/ w,
|
||||
/tmp/sdtest.7283-14445-r31VAP/tmpdir/ w,
|
||||
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
/home/ubuntu/bzr/apparmor/tests/regression/apparmor/link {
|
||||
owner /tmp/sdtest.19088-12382-HWH57d/linkfile l,
|
||||
/tmp/sdtest.19088-12382-HWH57d/linkfile l,
|
||||
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
"/home/steve/tmp/my prog.sh" {
|
||||
owner "/home/steve/tmp/my prog.sh" r,
|
||||
"/home/steve/tmp/my prog.sh" r,
|
||||
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
profile "test space" {
|
||||
owner /lib/x86_64-linux-gnu/libdl-2.13.so r,
|
||||
/lib/x86_64-linux-gnu/libdl-2.13.so r,
|
||||
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
/home/ubuntu/bzr/apparmor/tests/regression/apparmor/link {
|
||||
owner /tmp/sdtest.19088-12382-HWH57d/linkfile l,
|
||||
/tmp/sdtest.19088-12382-HWH57d/linkfile l,
|
||||
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
/usr/sbin/vsftpd {
|
||||
owner /home/bane/foo r,
|
||||
/home/bane/foo r,
|
||||
|
||||
}
|
||||
|
@@ -2,8 +2,6 @@
|
||||
# Copyright (c) 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007
|
||||
# NOVELL (All rights reserved)
|
||||
#
|
||||
# Copyright (c) Christian Boltz 2018
|
||||
#
|
||||
# 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.
|
||||
@@ -24,11 +22,10 @@ include $(COMMONDIR)/Make.rules
|
||||
|
||||
DESTDIR=/
|
||||
APPARMOR_BIN_PREFIX=${DESTDIR}/lib/apparmor
|
||||
SYSTEMD_UNIT_DIR=${DESTDIR}/usr/lib/systemd/system
|
||||
CONFDIR=/etc/apparmor
|
||||
INSTALL_CONFDIR=${DESTDIR}${CONFDIR}
|
||||
LOCALEDIR=/usr/share/locale
|
||||
MANPAGES=apparmor.d.5 apparmor.7 apparmor_parser.8 subdomain.conf.5 aa-teardown.8
|
||||
MANPAGES=apparmor.d.5 apparmor.7 apparmor_parser.8 subdomain.conf.5
|
||||
|
||||
YACC := bison
|
||||
YFLAGS := -d
|
||||
@@ -317,9 +314,12 @@ install-redhat:
|
||||
install -m 755 rc.apparmor.$(subst install-,,$@) $(DESTDIR)/etc/init.d/apparmor
|
||||
|
||||
.PHONY: install-suse
|
||||
install-suse: install-systemd
|
||||
install-suse:
|
||||
install -m 755 -d $(DESTDIR)/etc/init.d
|
||||
install -m 755 rc.apparmor.$(subst install-,,$(@)) $(DESTDIR)/etc/init.d/boot.apparmor
|
||||
install -m 755 -d $(DESTDIR)/sbin
|
||||
ln -sf service $(DESTDIR)/sbin/rcapparmor
|
||||
ln -sf /etc/init.d/boot.apparmor $(DESTDIR)/sbin/rcapparmor
|
||||
ln -sf rcapparmor $(DESTDIR)/sbin/rcsubdomain
|
||||
|
||||
.PHONY: install-slackware
|
||||
install-slackware:
|
||||
@@ -379,14 +379,6 @@ install-indep: indep
|
||||
$(MAKE) -C po install NAME=${NAME} DESTDIR=${DESTDIR}
|
||||
$(MAKE) install_manpages DESTDIR=${DESTDIR}
|
||||
|
||||
.PHONY: install-systemd
|
||||
install-systemd:
|
||||
install -m 755 -d $(SYSTEMD_UNIT_DIR)
|
||||
install -m 644 apparmor.service $(SYSTEMD_UNIT_DIR)
|
||||
install -m 755 apparmor.systemd $(APPARMOR_BIN_PREFIX)
|
||||
install -m 755 -d $(DESTDIR)/usr/sbin
|
||||
install -m 755 aa-teardown $(DESTDIR)/usr/sbin
|
||||
|
||||
ifndef VERBOSE
|
||||
.SILENT: clean
|
||||
endif
|
||||
|
@@ -1,10 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
test $# = 0 || {
|
||||
echo "Usage: $0"
|
||||
echo
|
||||
echo "Unloads all AppArmor profiles"
|
||||
exit 1
|
||||
}
|
||||
|
||||
/lib/apparmor/apparmor.systemd stop
|
@@ -1,40 +0,0 @@
|
||||
# ----------------------------------------------------------------------
|
||||
# Copyright (c) 2018 Christian Boltz
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2 of the GNU General Public
|
||||
# License published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, contact Novell, Inc.
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
aa-teardown - unload all AppArmor profiles
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
B<aa-teardown>
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
aa-teardown unloads all AppArmor profiles
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
If you find any bugs, please report them at
|
||||
L<https://bugs.launchpad.net/apparmor/+filebug>.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
apparmor(7), apparmor.d(5), and L<https://wiki.apparmor.net>.
|
||||
|
||||
=cut
|
@@ -143,56 +143,6 @@ messages with the KERN facility. Thus, REJECTING and PERMITTING messages
|
||||
may go to either F</var/log/audit/audit.log> or F</var/log/messages>,
|
||||
depending upon local configuration.
|
||||
|
||||
=head1 DEBUGGING
|
||||
|
||||
AppArmor provides a few facilities to log more information,
|
||||
which can help debugging profiles.
|
||||
|
||||
=head2 Enable debug mode
|
||||
|
||||
When debug mode is enabled, AppArmor will log a few extra messages to
|
||||
dmesg (not via the audit subsystem). For example, the logs will tell
|
||||
whether environment scrubbing has been applied.
|
||||
|
||||
To enable debug mode, run:
|
||||
|
||||
echo 1 > /sys/module/apparmor/parameters/debug
|
||||
|
||||
=head2 Turn off deny audit quieting
|
||||
|
||||
By default, operations that trigger C<deny> rules are not logged.
|
||||
This is called I<deny audit quieting>.
|
||||
|
||||
To turn off deny audit quieting, run:
|
||||
|
||||
echo -n noquiet >/sys/module/apparmor/parameters/audit
|
||||
|
||||
=head2 Force audit mode
|
||||
|
||||
AppArmor can log a message for every operation that triggers a rule
|
||||
configured in the policy. This is called I<force audit mode>.
|
||||
|
||||
B<Warning!> Force audit mode can be extremely noisy even for a single profile,
|
||||
let alone when enabled globally.
|
||||
|
||||
To set a specific profile in force audit mode, add the C<audit> flag:
|
||||
|
||||
profile foo flags=(audit) { ... }
|
||||
|
||||
To enable force audit mode globally, run:
|
||||
|
||||
echo -n all > /sys/module/apparmor/parameters/audit
|
||||
|
||||
If auditd is not running, to avoid losing too many of the extra log
|
||||
messages, you will likely have to turn off rate limiting by doing:
|
||||
|
||||
echo 0 > /proc/sys/kernel/printk_ratelimit
|
||||
|
||||
But even then the kernel ring buffer may overflow and you might
|
||||
lose messages.
|
||||
|
||||
Else, if auditd is running, see auditd(8) and auditd.conf(5).
|
||||
|
||||
=head1 FILES
|
||||
|
||||
=over 4
|
||||
|
@@ -1,26 +0,0 @@
|
||||
[Unit]
|
||||
Description=Load AppArmor profiles
|
||||
DefaultDependencies=no
|
||||
Before=sysinit.target
|
||||
After=systemd-journald-audit.socket
|
||||
# profile cache
|
||||
After=var.mount var-lib.mount
|
||||
ConditionSecurity=apparmor
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/lib/apparmor/apparmor.systemd reload
|
||||
ExecReload=/lib/apparmor/apparmor.systemd reload
|
||||
|
||||
# systemd maps 'restart' to 'stop; start' which means removing AppArmor confinement
|
||||
# from running processes (and not being able to re-apply it later).
|
||||
# Upstream systemd developers refused to implement an option that allows overriding
|
||||
# this behaviour, therefore we have to make ExecStop a no-op to error out on the
|
||||
# safe side.
|
||||
#
|
||||
# If you really want to unload all AppArmor profiles, run aa-teardown
|
||||
ExecStop=/bin/true
|
||||
RemainAfterExit=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@@ -1,100 +0,0 @@
|
||||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------
|
||||
# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
|
||||
#
|
||||
# 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.
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
APPARMOR_FUNCTIONS=/lib/apparmor/rc.apparmor.functions
|
||||
|
||||
aa_action()
|
||||
{
|
||||
echo $1
|
||||
shift
|
||||
"$@"
|
||||
return $?
|
||||
}
|
||||
|
||||
aa_log_warning_msg()
|
||||
{
|
||||
echo "Warning: $@"
|
||||
}
|
||||
|
||||
aa_log_failure_msg()
|
||||
{
|
||||
echo "Error: $@"
|
||||
}
|
||||
|
||||
aa_log_action_start()
|
||||
{
|
||||
echo "$@"
|
||||
}
|
||||
|
||||
aa_log_action_end()
|
||||
{
|
||||
echo -n
|
||||
}
|
||||
|
||||
aa_log_daemon_msg()
|
||||
{
|
||||
echo "$@"
|
||||
}
|
||||
|
||||
aa_log_skipped_msg()
|
||||
{
|
||||
echo "Skipped: $@"
|
||||
}
|
||||
|
||||
aa_log_end_msg()
|
||||
{
|
||||
echo -n
|
||||
}
|
||||
|
||||
# source apparmor function library
|
||||
if [ -f "${APPARMOR_FUNCTIONS}" ]; then
|
||||
. ${APPARMOR_FUNCTIONS}
|
||||
else
|
||||
aa_log_failure_msg "Unable to find AppArmor initscript functions"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
apparmor_start
|
||||
rc=$?
|
||||
;;
|
||||
stop)
|
||||
apparmor_stop
|
||||
rc=$?
|
||||
;;
|
||||
restart|reload|force-reload)
|
||||
apparmor_restart
|
||||
rc=$?
|
||||
;;
|
||||
try-restart)
|
||||
apparmor_try_restart
|
||||
rc=$?
|
||||
;;
|
||||
kill)
|
||||
apparmor_kill
|
||||
rc=$?
|
||||
;;
|
||||
status)
|
||||
apparmor_status
|
||||
rc=$?
|
||||
;;
|
||||
*)
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
exit $rc
|
@@ -46,10 +46,9 @@ program. The B<profiles> may be specified by file name or a directory
|
||||
name containing a set of profiles. If a directory is specified then the
|
||||
B<apparmor_parser> will try to do a profile load for each file in the
|
||||
directory that is not a dot file, or explicitly black listed (*.dpkg-new,
|
||||
*.dpkg-old, *.dpkg-dist, *.dpkg-bak, *.dpkg-remove, *.pacsave, *.pacnew,
|
||||
*.rpmnew, *.rpmsave, *.orig, *.rej, *~).
|
||||
The B<apparmor_parser> will fall back to taking input from standard input if
|
||||
a profile or directory is not supplied.
|
||||
*.dpkg-old, *.dpkg-dist, *-dpkg-bak, *.rpmnew, *.rpmsave, *orig, *.rej,
|
||||
*~). The B<apparmor_parser> will fall back to taking input from standard
|
||||
input if a profile or directory is not supplied.
|
||||
|
||||
The input supplied to B<apparmor_parser> should be in the format described in
|
||||
apparmor.d(5).
|
||||
@@ -233,28 +232,8 @@ inconsistent state
|
||||
|
||||
=item -L, --cache-loc
|
||||
|
||||
Set the location(s) of the cache directory. This option can accept a
|
||||
comma separated list of directories, which will be searched in order
|
||||
to find a matching cache. The first matching cache file found is used
|
||||
even if a directory later in the search order may contain a newer cache
|
||||
file.
|
||||
|
||||
If multiple directories are specified and --write-cache has been specified
|
||||
then cache writes will be made to the first directory in the list, all
|
||||
other directories will be treated as read only.
|
||||
|
||||
If a cache directory name needs to have a comma as part of the name, it
|
||||
can be specified by using a backslash to escape the comma character in
|
||||
the directory name.
|
||||
|
||||
If not specified the cache location defaults to /var/cache/apparmor
|
||||
|
||||
=item --print-cache-dir
|
||||
|
||||
Print the cache directory location. This path will be a subdirectory of the
|
||||
directory specified by --cache-loc. The subdirectory used will be influenced by
|
||||
the features available in the currently running kernel or by the features
|
||||
specified with the --match-string or --features-file options.
|
||||
Set the location of the cache directory. If not specified the cache location
|
||||
defaults to /etc/apparmor.d/cache
|
||||
|
||||
=item -Q, --skip-kernel-load
|
||||
|
||||
@@ -355,17 +334,6 @@ This option tells the parser to not attempt to rebuild the cache on
|
||||
failure, instead the parser continues on with processing the remaining
|
||||
profiles.
|
||||
|
||||
=item --config-file
|
||||
|
||||
Specify the config file to use instead of
|
||||
/etc/apparmor/parser.conf. This option will be processed early before
|
||||
regular options regardless of the order it is specified in.
|
||||
|
||||
=item --print-config-file
|
||||
|
||||
Print the config file location that will be used.
|
||||
|
||||
|
||||
=back
|
||||
|
||||
=head1 CONFIG FILE
|
||||
|
@@ -25,8 +25,6 @@
|
||||
#include "parser.h"
|
||||
#include "profile.h"
|
||||
|
||||
typedef int (*comparison_fn_t)(const void *, const void *);
|
||||
|
||||
struct alias_rule {
|
||||
char *from;
|
||||
char *to;
|
||||
|
@@ -45,7 +45,6 @@
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "lib.h"
|
||||
#include "parser.h"
|
||||
|
@@ -2,7 +2,7 @@
|
||||
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
|
||||
* NOVELL (All rights reserved)
|
||||
*
|
||||
* Copyright (c) 2010 - 2018
|
||||
* Copyright (c) 2010 - 2013
|
||||
* Canonical Ltd. (All rights reserved)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@@ -59,10 +59,8 @@
|
||||
#define PRIVILEGED_OPS (kernel_load)
|
||||
#define UNPRIVILEGED_OPS (!(PRIVILEGED_OPS))
|
||||
|
||||
#define EARLY_ARG_CONFIG_FILE 141
|
||||
|
||||
const char *parser_title = "AppArmor parser";
|
||||
const char *parser_copyright = "Copyright (C) 1999-2008 Novell Inc.\nCopyright 2009-2018 Canonical Ltd.";
|
||||
const char *parser_copyright = "Copyright (C) 1999-2008 Novell Inc.\nCopyright 2009-2012 Canonical Ltd.";
|
||||
|
||||
int opt_force_complain = 0;
|
||||
int binary_input = 0;
|
||||
@@ -99,20 +97,12 @@ long jobs_scale = 0; /* number of chance to resample online
|
||||
*/
|
||||
bool debug_jobs = false;
|
||||
|
||||
#define MAX_CACHE_LOCS 4
|
||||
|
||||
struct timespec cache_tstamp, mru_policy_tstamp;
|
||||
|
||||
static char *apparmorfs = NULL;
|
||||
static const char *cacheloc[MAX_CACHE_LOCS];
|
||||
static int cacheloc_n = 0;
|
||||
static bool print_cache_dir = false;
|
||||
|
||||
static aa_features *compile_features = NULL;
|
||||
static aa_features *kernel_features = NULL;
|
||||
|
||||
static const char *config_file = "/etc/apparmor/parser.conf";
|
||||
static char *cacheloc = NULL;
|
||||
|
||||
static aa_features *features = NULL;
|
||||
|
||||
/* Make sure to update BOTH the short and long_options */
|
||||
static const char *short_options = "ad::f:h::rRVvI:b:BCD:NSm:M:qQn:XKTWkL:O:po:j:";
|
||||
@@ -143,6 +133,9 @@ struct option long_options[] = {
|
||||
{"skip-read-cache", 0, 0, 'T'},
|
||||
{"write-cache", 0, 0, 'W'},
|
||||
{"show-cache", 0, 0, 'k'},
|
||||
{"skip-bad-cache", 0, 0, 129}, /* no short option */
|
||||
{"purge-cache", 0, 0, 130}, /* no short option */
|
||||
{"create-cache-dir", 0, 0, 131}, /* no short option */
|
||||
{"cache-loc", 1, 0, 'L'},
|
||||
{"debug", 2, 0, 'd'},
|
||||
{"dump", 1, 0, 'D'},
|
||||
@@ -150,21 +143,12 @@ struct option long_options[] = {
|
||||
{"optimize", 1, 0, 'O'},
|
||||
{"Optimize", 1, 0, 'O'},
|
||||
{"preprocess", 0, 0, 'p'},
|
||||
{"jobs", 1, 0, 'j'},
|
||||
{"skip-bad-cache", 0, 0, 129}, /* no short option */
|
||||
{"purge-cache", 0, 0, 130}, /* no short option */
|
||||
{"create-cache-dir", 0, 0, 131}, /* no short option */
|
||||
{"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 */
|
||||
{"jobs", 1, 0, 'j'},
|
||||
{"max-jobs", 1, 0, 136}, /* no short option */
|
||||
{"print-cache-dir", 0, 0, 137}, /* no short option */
|
||||
{"kernel-features", 1, 0, 138}, /* no short option */
|
||||
{"compile-features", 1, 0, 139}, /* no short option */
|
||||
{"print-config-file", 0, 0, 140}, /* no short option */
|
||||
{"config-file", 1, 0, EARLY_ARG_CONFIG_FILE}, /* early option, no short option */
|
||||
|
||||
{NULL, 0, 0, 0},
|
||||
};
|
||||
|
||||
@@ -194,9 +178,7 @@ static void display_usage(const char *command)
|
||||
"-I n, --Include n Add n to the search path\n"
|
||||
"-f n, --subdomainfs n Set location of apparmor filesystem\n"
|
||||
"-m n, --match-string n Use only features n\n"
|
||||
"-M n, --features-file n Set compile & kernel features to file n\n"
|
||||
"--compile-features n Compile features set in file n\n"
|
||||
"--kernel-features n Kernel features set in file n\n"
|
||||
"-M n, --features-file n Use only features in file n\n"
|
||||
"-n n, --namespace n Set Namespace for the profile\n"
|
||||
"-X, --readimpliesX Map profile read permissions to mr\n"
|
||||
"-k, --show-cache Report cache hit/miss details\n"
|
||||
@@ -206,8 +188,7 @@ static void display_usage(const char *command)
|
||||
" --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"
|
||||
" --print-cache_dir Print the cache directory path\n"
|
||||
"-L, --cache-loc n Set the location of the profile caches\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"
|
||||
"-Q, --skip-kernel-load Do everything except loading into kernel\n"
|
||||
@@ -221,8 +202,6 @@ static void display_usage(const char *command)
|
||||
"--max-jobs n Hard cap on --jobs. Default 8*cpus\n"
|
||||
"--abort-on-error Abort processing of profiles on first error\n"
|
||||
"--skip-bad-cache-rebuild Do not try rebuilding the cache if it is rejected by the kernel\n"
|
||||
"--config-file n Specify the parser config file location, processed early before other options.\n"
|
||||
"--print-config Print config file location\n"
|
||||
"--warn n Enable warnings (see --help=warn)\n"
|
||||
,command);
|
||||
}
|
||||
@@ -243,55 +222,6 @@ void display_warn(const char *command)
|
||||
print_flag_table(warnflag_table);
|
||||
}
|
||||
|
||||
/* Parse comma separated cachelocations. Commas can be escaped by \, */
|
||||
static int parse_cacheloc(const char *arg, const char **cacheloc, int max_size)
|
||||
{
|
||||
const char *s = arg;
|
||||
const char *p = arg;
|
||||
int n = 0;
|
||||
|
||||
while(*p) {
|
||||
if (*p == '\\') {
|
||||
if (*(p + 1) != 0)
|
||||
p++;
|
||||
} else if (*p == ',') {
|
||||
if (p != s) {
|
||||
char *tmp;
|
||||
if (n == max_size) {
|
||||
errno = E2BIG;
|
||||
return -1;
|
||||
}
|
||||
tmp = (char *) malloc(p - s + 1);
|
||||
if (tmp == NULL)
|
||||
return -1;
|
||||
memcpy(tmp, s, p - s);
|
||||
tmp[p - s] = 0;
|
||||
cacheloc[n] = tmp;
|
||||
n++;
|
||||
}
|
||||
p++;
|
||||
s = p;
|
||||
} else
|
||||
p++;
|
||||
}
|
||||
if (p != s) {
|
||||
char *tmp;
|
||||
if (n == max_size) {
|
||||
errno = E2BIG;
|
||||
return -1;
|
||||
}
|
||||
tmp = (char *) malloc(p - s + 1);
|
||||
if (tmp == NULL)
|
||||
return -1;
|
||||
memcpy(tmp, s, p - s);
|
||||
tmp[p - s] = 0;
|
||||
cacheloc[n] = tmp;
|
||||
n++;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Treat conf file like options passed on command line
|
||||
*/
|
||||
static int getopt_long_file(FILE *f, const struct option *longopts,
|
||||
@@ -389,16 +319,6 @@ static long process_jobs_arg(const char *arg, const char *val) {
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
bool early_arg(int c) {
|
||||
switch(c) {
|
||||
case EARLY_ARG_CONFIG_FILE:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* process a single argment from getopt_long
|
||||
* Returns: 1 if an action arg, else 0
|
||||
*/
|
||||
@@ -409,7 +329,8 @@ static int process_arg(int c, char *optarg)
|
||||
switch (c) {
|
||||
case 0:
|
||||
PERROR("Assert, in getopt_long handling\n");
|
||||
exit(1);
|
||||
display_usage(progname);
|
||||
exit(0);
|
||||
break;
|
||||
case 'a':
|
||||
count++;
|
||||
@@ -528,7 +449,7 @@ static int process_arg(int c, char *optarg)
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
if (aa_features_new_from_string(&compile_features,
|
||||
if (aa_features_new_from_string(&features,
|
||||
optarg, strlen(optarg))) {
|
||||
fprintf(stderr,
|
||||
"Failed to parse features string: %m\n");
|
||||
@@ -536,37 +457,12 @@ static int process_arg(int c, char *optarg)
|
||||
}
|
||||
break;
|
||||
case 'M':
|
||||
if (compile_features)
|
||||
aa_features_unref(compile_features);
|
||||
if (kernel_features)
|
||||
aa_features_unref(kernel_features);
|
||||
if (aa_features_new(&compile_features, AT_FDCWD, optarg)) {
|
||||
if (aa_features_new(&features, AT_FDCWD, optarg)) {
|
||||
fprintf(stderr,
|
||||
"Failed to load features from '%s': %m\n",
|
||||
optarg);
|
||||
exit(1);
|
||||
}
|
||||
kernel_features = aa_features_ref(compile_features);
|
||||
break;
|
||||
case 138:
|
||||
if (kernel_features)
|
||||
aa_features_unref(kernel_features);
|
||||
if (aa_features_new(&kernel_features, AT_FDCWD, optarg)) {
|
||||
fprintf(stderr,
|
||||
"Failed to load kernel features from '%s': %m\n",
|
||||
optarg);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 139:
|
||||
if (compile_features)
|
||||
aa_features_unref(compile_features);
|
||||
if (aa_features_new(&compile_features, AT_FDCWD, optarg)) {
|
||||
fprintf(stderr,
|
||||
"Failed to load compile features from '%s': %m\n",
|
||||
optarg);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'q':
|
||||
conf_verbose = 0;
|
||||
@@ -611,11 +507,7 @@ static int process_arg(int c, char *optarg)
|
||||
skip_bad_cache_rebuild = 1;
|
||||
break;
|
||||
case 'L':
|
||||
cacheloc_n = parse_cacheloc(optarg, cacheloc, MAX_CACHE_LOCS);
|
||||
if (cacheloc_n == -1) {
|
||||
PERROR("%s: Invalid --cacheloc option '%s' %m\n", progname, optarg);
|
||||
exit(1);
|
||||
}
|
||||
cacheloc = strdup(optarg);
|
||||
break;
|
||||
case 'Q':
|
||||
kernel_load = 0;
|
||||
@@ -644,22 +536,8 @@ static int process_arg(int c, char *optarg)
|
||||
case 136:
|
||||
jobs_max = process_jobs_arg("max-jobs", optarg);
|
||||
break;
|
||||
case 137:
|
||||
kernel_load = 0;
|
||||
print_cache_dir = true;
|
||||
break;
|
||||
case EARLY_ARG_CONFIG_FILE:
|
||||
config_file = strdup(optarg);
|
||||
if (!config_file) {
|
||||
PERROR("%s: %m", progname);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 140:
|
||||
printf("%s\n", config_file);
|
||||
break;
|
||||
default:
|
||||
/* 'unrecognized option' error message gets printed by getopt_long() */
|
||||
display_usage(progname);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
@@ -667,36 +545,21 @@ static int process_arg(int c, char *optarg)
|
||||
return count;
|
||||
}
|
||||
|
||||
static void process_early_args(int argc, char *argv[])
|
||||
{
|
||||
int c, o;
|
||||
|
||||
while ((c = getopt_long(argc, argv, short_options, long_options, &o)) != -1)
|
||||
{
|
||||
if (early_arg(c))
|
||||
process_arg(c, optarg);
|
||||
}
|
||||
|
||||
/* reset args, so we are ready for a second pass */
|
||||
optind = 1;
|
||||
}
|
||||
|
||||
static int process_args(int argc, char *argv[])
|
||||
{
|
||||
int c, o;
|
||||
int count = 0;
|
||||
option = OPTION_ADD;
|
||||
|
||||
opterr = 1;
|
||||
while ((c = getopt_long(argc, argv, short_options, long_options, &o)) != -1)
|
||||
{
|
||||
if (!early_arg(c))
|
||||
count += process_arg(c, optarg);
|
||||
count += process_arg(c, optarg);
|
||||
}
|
||||
|
||||
if (count > 1) {
|
||||
PERROR("%s: Too many actions given on the command line.\n",
|
||||
progname);
|
||||
display_usage(progname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -711,10 +574,8 @@ static int process_config_file(const char *name)
|
||||
int c, o;
|
||||
|
||||
f = fopen(name, "r");
|
||||
if (!f) {
|
||||
pwarn("config file '%s' not found\n", name);
|
||||
if (!f)
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ((c = getopt_long_file(f, long_options, &optarg, &o)) != -1)
|
||||
process_arg(c, optarg);
|
||||
@@ -731,6 +592,7 @@ int have_enough_privilege(void)
|
||||
if (uid != 0 && euid != 0) {
|
||||
PERROR(_("%s: Sorry. You need root privileges to run this program.\n\n"),
|
||||
progname);
|
||||
display_usage(progname);
|
||||
return EPERM;
|
||||
}
|
||||
|
||||
@@ -761,37 +623,33 @@ no_match:
|
||||
perms_create = 1;
|
||||
}
|
||||
|
||||
static void set_supported_features(aa_features *kernel_features unused)
|
||||
static void set_supported_features(void)
|
||||
{
|
||||
/* has process_args() already assigned a match string? */
|
||||
if (!compile_features && aa_features_new_from_kernel(&compile_features) == -1) {
|
||||
if (!features && aa_features_new_from_kernel(&features) == -1) {
|
||||
set_features_by_match_file();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: intersect with actual kernel features to get proper
|
||||
* rule down grades for a give kernel
|
||||
*/
|
||||
perms_create = 1;
|
||||
kernel_supports_policydb = aa_features_supports(compile_features, "file");
|
||||
kernel_supports_network = aa_features_supports(compile_features, "network");
|
||||
kernel_supports_unix = aa_features_supports(compile_features,
|
||||
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(compile_features, "mount");
|
||||
kernel_supports_dbus = aa_features_supports(compile_features, "dbus");
|
||||
kernel_supports_signal = aa_features_supports(compile_features, "signal");
|
||||
kernel_supports_ptrace = aa_features_supports(compile_features, "ptrace");
|
||||
kernel_supports_setload = aa_features_supports(compile_features,
|
||||
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(compile_features,
|
||||
kernel_supports_diff_encode = aa_features_supports(features,
|
||||
"policy/diff_encode");
|
||||
kernel_supports_stacking = aa_features_supports(compile_features,
|
||||
kernel_supports_stacking = aa_features_supports(features,
|
||||
"domain/stack");
|
||||
|
||||
if (aa_features_supports(compile_features, "policy/versions/v7"))
|
||||
if (aa_features_supports(features, "policy/versions/v7"))
|
||||
kernel_abi_version = 7;
|
||||
else if (aa_features_supports(compile_features, "policy/versions/v6"))
|
||||
else if (aa_features_supports(features, "policy/versions/v6"))
|
||||
kernel_abi_version = 6;
|
||||
|
||||
if (!kernel_supports_diff_encode)
|
||||
@@ -799,33 +657,6 @@ static void set_supported_features(aa_features *kernel_features unused)
|
||||
dfaflags &= ~DFA_CONTROL_DIFF_ENCODE;
|
||||
}
|
||||
|
||||
static bool do_print_cache_dir(aa_features *features, int dirfd, const char *path)
|
||||
{
|
||||
autofree char *cache_dir = NULL;
|
||||
|
||||
cache_dir = aa_policy_cache_dir_path_preview(features, dirfd, path);
|
||||
if (!cache_dir) {
|
||||
PERROR(_("Unable to print the cache directory: %m\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("%s\n", cache_dir);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool do_print_cache_dirs(aa_features *features, const char **cacheloc,
|
||||
int cacheloc_n)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cacheloc_n; i++) {
|
||||
if (!do_print_cache_dir(features, AT_FDCWD, cacheloc[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int process_binary(int option, aa_kernel_interface *kernel_interface,
|
||||
const char *profilename)
|
||||
{
|
||||
@@ -910,11 +741,10 @@ int test_for_dir_mode(const char *basename, const char *linkdir)
|
||||
}
|
||||
|
||||
int process_profile(int option, aa_kernel_interface *kernel_interface,
|
||||
const char *profilename, aa_policy_cache *pc)
|
||||
const char *profilename, const char *cachedir)
|
||||
{
|
||||
int retval = 0;
|
||||
autofree const char *cachename = NULL;
|
||||
autofree const char *writecachename = NULL;
|
||||
autofree const char *cachetmpname = NULL;
|
||||
autoclose int cachetmp = -1;
|
||||
const char *basename = NULL;
|
||||
@@ -931,7 +761,6 @@ int process_profile(int option, aa_kernel_interface *kernel_interface,
|
||||
} else {
|
||||
if (write_cache)
|
||||
pwarn("%s: cannot use or update cache, disable, or force-complain via stdin\n", progname);
|
||||
skip_cache = write_cache = 0;
|
||||
}
|
||||
|
||||
reset_parser(profilename);
|
||||
@@ -956,17 +785,9 @@ int process_profile(int option, aa_kernel_interface *kernel_interface,
|
||||
}
|
||||
|
||||
/* setup cachename and tstamp */
|
||||
if (!force_complain && pc) {
|
||||
cachename = aa_policy_cache_filename(pc, basename);
|
||||
if (!cachename) {
|
||||
autoclose int fd = aa_policy_cache_open(pc,
|
||||
basename,
|
||||
O_RDONLY);
|
||||
if (fd != -1)
|
||||
pwarn(_("Could not get cachename for '%s'\n"), basename);
|
||||
} else {
|
||||
valid_read_cache(cachename);
|
||||
}
|
||||
if (!force_complain && !skip_cache) {
|
||||
cachename = cache_filename(cachedir, basename);
|
||||
valid_read_cache(cachename);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -998,6 +819,8 @@ int process_profile(int option, aa_kernel_interface *kernel_interface,
|
||||
if (!retval || skip_bad_cache_rebuild)
|
||||
return retval;
|
||||
}
|
||||
|
||||
cachetmp = setup_cache_tmp(&cachetmpname, cachename);
|
||||
}
|
||||
|
||||
if (show_cache)
|
||||
@@ -1033,27 +856,15 @@ int process_profile(int option, aa_kernel_interface *kernel_interface,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (pc && write_cache && !force_complain) {
|
||||
writecachename = cache_filename(pc, 0, basename);
|
||||
if (!writecachename) {
|
||||
pwarn("Cache write disabled: Cannot create cache file name '%s': %m\n", basename);
|
||||
write_cache = 0;
|
||||
}
|
||||
cachetmp = setup_cache_tmp(&cachetmpname, writecachename);
|
||||
if (cachetmp == -1) {
|
||||
pwarn("Cache write disabled: Cannot create setup tmp cache file '%s': %m\n", writecachename);
|
||||
write_cache = 0;
|
||||
}
|
||||
}
|
||||
/* cache file generated by load_policy */
|
||||
retval = load_policy(option, kernel_interface, cachetmp);
|
||||
if (retval == 0 && write_cache) {
|
||||
if (cachetmp == -1) {
|
||||
unlink(cachetmpname);
|
||||
pwarn("Warning failed to create cache: %s\n",
|
||||
PERROR("Warning failed to create cache: %s\n",
|
||||
basename);
|
||||
} else {
|
||||
install_cache(cachetmpname, writecachename);
|
||||
install_cache(cachetmpname, cachename);
|
||||
}
|
||||
}
|
||||
out:
|
||||
@@ -1195,7 +1006,7 @@ static void setup_parallel_compile(void)
|
||||
struct dir_cb_data {
|
||||
aa_kernel_interface *kernel_interface;
|
||||
const char *dirname; /* name of the parent dir */
|
||||
aa_policy_cache *policy_cache; /* policy_cache to use */
|
||||
const char *cachedir; /* path to the cache sub directory */
|
||||
};
|
||||
|
||||
/* data - pointer to a dir_cb_data */
|
||||
@@ -1210,7 +1021,7 @@ static int profile_dir_cb(int dirfd unused, const char *name, struct stat *st,
|
||||
if (asprintf(&path, "%s/%s", cb_data->dirname, name) < 0)
|
||||
PERROR(_("Out of memory"));
|
||||
work_spawn(process_profile(option, cb_data->kernel_interface,
|
||||
path, cb_data->policy_cache),
|
||||
path, cb_data->cachedir),
|
||||
handle_work_result);
|
||||
}
|
||||
return rc;
|
||||
@@ -1236,17 +1047,17 @@ static int binary_dir_cb(int dirfd unused, const char *name, struct stat *st,
|
||||
|
||||
static void setup_flags(void)
|
||||
{
|
||||
/* Get the match string to determine type of regex support needed */
|
||||
set_supported_features();
|
||||
|
||||
/* Gracefully handle AppArmor kernel without compatibility patch */
|
||||
if (!kernel_features && aa_features_new_from_kernel(&kernel_features) == -1) {
|
||||
if (!features) {
|
||||
PERROR("Cache read/write disabled: interface file missing. "
|
||||
"(Kernel needs AppArmor 2.4 compatibility patch.)\n");
|
||||
write_cache = 0;
|
||||
skip_read_cache = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the match string to determine type of regex support needed */
|
||||
set_supported_features(kernel_features);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
@@ -1262,8 +1073,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
init_base_dir();
|
||||
|
||||
process_early_args(argc, argv);
|
||||
process_config_file(config_file);
|
||||
process_config_file("/etc/apparmor/parser.conf");
|
||||
optind = process_args(argc, argv);
|
||||
|
||||
setup_parallel_compile();
|
||||
@@ -1283,7 +1093,7 @@ int main(int argc, char *argv[])
|
||||
setup_flags();
|
||||
|
||||
if (!(UNPRIVILEGED_OPS) &&
|
||||
aa_kernel_interface_new(&kernel_interface, kernel_features, apparmorfs) == -1) {
|
||||
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);
|
||||
@@ -1291,22 +1101,18 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
if ((!skip_cache && (write_cache || !skip_read_cache)) ||
|
||||
print_cache_dir || force_clear_cache) {
|
||||
uint16_t max_caches = write_cache && cond_clear_cache ? (uint16_t) (-1) : 0;
|
||||
force_clear_cache) {
|
||||
uint16_t max_caches = write_cache && cond_clear_cache ? 1 : 0;
|
||||
|
||||
if (!cacheloc[0]) {
|
||||
cacheloc[0] = "/var/cache/apparmor";
|
||||
cacheloc_n = 1;
|
||||
if (!cacheloc && asprintf(&cacheloc, "%s/cache", basedir) == -1) {
|
||||
PERROR(_("Memory allocation error."));
|
||||
return 1;
|
||||
}
|
||||
if (print_cache_dir)
|
||||
return do_print_cache_dirs(kernel_features, cacheloc,
|
||||
cacheloc_n) ? 0 : 1;
|
||||
|
||||
if (force_clear_cache) {
|
||||
/* only ever write to the first cacheloc location */
|
||||
if (aa_policy_cache_remove(AT_FDCWD, cacheloc[0])) {
|
||||
if (aa_policy_cache_remove(AT_FDCWD, cacheloc)) {
|
||||
PERROR(_("Failed to clear cache files (%s): %s\n"),
|
||||
cacheloc[0], strerror(errno));
|
||||
cacheloc, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1315,34 +1121,26 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (create_cache_dir)
|
||||
pwarn(_("The --create-cache-dir option is deprecated. Please use --write-cache.\n"));
|
||||
retval = aa_policy_cache_new(&policy_cache, kernel_features,
|
||||
AT_FDCWD, cacheloc[0], max_caches);
|
||||
|
||||
retval = aa_policy_cache_new(&policy_cache, features,
|
||||
AT_FDCWD, cacheloc, max_caches);
|
||||
if (retval) {
|
||||
if (errno != ENOENT && errno != EEXIST && errno != EROFS) {
|
||||
PERROR(_("Failed setting up policy cache (%s): %s\n"),
|
||||
cacheloc[0], strerror(errno));
|
||||
cacheloc, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (show_cache) {
|
||||
if (max_caches > 0)
|
||||
PERROR("Cache write disabled: Cannot create cache '%s': %m\n",
|
||||
cacheloc[0]);
|
||||
cacheloc);
|
||||
else
|
||||
PERROR("Cache read/write disabled: Policy cache is invalid: %m\n");
|
||||
PERROR("Cache read/write disabled: Policy cache is invalid\n");
|
||||
}
|
||||
|
||||
write_cache = 0;
|
||||
} else {
|
||||
if (show_cache)
|
||||
PERROR("Cache: added primary location '%s'\n", cacheloc[0]);
|
||||
for (i = 1; i < cacheloc_n; i++) {
|
||||
if (aa_policy_cache_add_ro_dir(policy_cache, AT_FDCWD,
|
||||
cacheloc[i])) {
|
||||
pwarn("Cache: failed to add read only location '%s', does not contain valid cache directory for the specified feature set\n", cacheloc[i]);
|
||||
} else if (show_cache)
|
||||
pwarn("Cache: added readonly location '%s'\n", cacheloc[i]);
|
||||
}
|
||||
skip_read_cache = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1373,7 +1171,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
memset(&cb_data, 0, sizeof(struct dir_cb_data));
|
||||
cb_data.dirname = profilename;
|
||||
cb_data.policy_cache = policy_cache;
|
||||
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,
|
||||
@@ -1387,7 +1185,7 @@ int main(int argc, char *argv[])
|
||||
handle_work_result);
|
||||
} else {
|
||||
work_spawn(process_profile(option, kernel_interface,
|
||||
profilename, policy_cache),
|
||||
profilename, cacheloc),
|
||||
handle_work_result);
|
||||
}
|
||||
|
||||
|
@@ -125,9 +125,7 @@ static struct keyword_table rlimit_table[] = {
|
||||
{"core", RLIMIT_CORE},
|
||||
{"rss", RLIMIT_RSS},
|
||||
{"nofile", RLIMIT_NOFILE},
|
||||
#ifdef RLIMIT_OFILE
|
||||
{"ofile", RLIMIT_OFILE},
|
||||
#endif
|
||||
{"as", RLIMIT_AS},
|
||||
{"nproc", RLIMIT_NPROC},
|
||||
{"memlock", RLIMIT_MEMLOCK},
|
||||
|
@@ -25,9 +25,6 @@
|
||||
#include "immunix.h"
|
||||
#include "parser.h"
|
||||
|
||||
typedef int (*comparison_fn_t)(const void *, const void *);
|
||||
typedef void (*__free_fn_t)(void *);
|
||||
|
||||
enum var_type {
|
||||
sd_boolean,
|
||||
sd_set,
|
||||
|
@@ -906,7 +906,6 @@ rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE opt_id TOK_END_OF_RULE
|
||||
pwarn(_("RLIMIT 'cpu' no units specified using default units of seconds\n"));
|
||||
value = tmp;
|
||||
break;
|
||||
#ifdef RLIMIT_RTTIME
|
||||
case RLIMIT_RTTIME:
|
||||
/* RTTIME is measured in microseconds */
|
||||
if (!end || $6 == end || tmp < 0)
|
||||
@@ -918,7 +917,6 @@ rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE opt_id TOK_END_OF_RULE
|
||||
pwarn(_("RLIMIT 'rttime' no units specified using default units of microseconds\n"));
|
||||
value = tmp;
|
||||
break;
|
||||
#endif
|
||||
case RLIMIT_NOFILE:
|
||||
case RLIMIT_NPROC:
|
||||
case RLIMIT_LOCKS:
|
||||
|
@@ -14,8 +14,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"Language: af\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -12,8 +12,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"Language: ar\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -15,8 +15,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"Language: bg\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -9,8 +9,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"Language: bn\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -14,8 +14,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"Language: bs\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -14,8 +14,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"Language: ca\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -14,8 +14,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"Language: ce\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -12,8 +12,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"Language: cs\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -14,8 +14,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"Language: cy\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -16,8 +16,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"Language: da\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
219
parser/po/de.po
219
parser/po/de.po
@@ -6,24 +6,24 @@ msgstr ""
|
||||
"Project-Id-Version: apparmor-parser\n"
|
||||
"Report-Msgid-Bugs-To: <apparmor@lists.ubuntu.com>\n"
|
||||
"POT-Creation-Date: 2014-09-13 00:11-0700\n"
|
||||
"PO-Revision-Date: 2018-04-06 14:39+0000\n"
|
||||
"Last-Translator: Tobias Bannert <tobannert@gmail.com>\n"
|
||||
"PO-Revision-Date: 2015-09-05 20:55+0000\n"
|
||||
"Last-Translator: Tobias Bannert <Unknown>\n"
|
||||
"Language-Team: Novell Language <language@novell.com>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-04-07 05:20+0000\n"
|
||||
"X-Generator: Launchpad (build 18599)\n"
|
||||
"X-Launchpad-Export-Date: 2016-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"Language: de\n"
|
||||
|
||||
#: ../parser_include.c:113 ../parser_include.c:111
|
||||
#: ../parser_include.c:113
|
||||
msgid "Error: Out of memory.\n"
|
||||
msgstr "Fehler: nicht genügend Speicher!\n"
|
||||
msgstr "Fehler: Kein Speicher vorhanden.\n"
|
||||
|
||||
#: ../parser_include.c:123 ../parser_include.c:121
|
||||
#: ../parser_include.c:123
|
||||
#, c-format
|
||||
msgid "Error: basedir %s is not a directory, skipping.\n"
|
||||
msgstr "Fehler: basedir »%s« ist kein Verzeichnis, es wird übersprungen.\n"
|
||||
msgstr "Fehler: basedir »%s« ist kein Verzeichnis - es wird übersprungen.\n"
|
||||
|
||||
#: ../parser_include.c:137
|
||||
#, c-format
|
||||
@@ -31,108 +31,99 @@ msgid "Error: Could not add directory %s to search path.\n"
|
||||
msgstr ""
|
||||
"Fehler: Verzeichnis »%s« konnte nicht zu Suchpfad hinzugefügt werden.\n"
|
||||
|
||||
#: ../parser_include.c:147 ../parser_include.c:151
|
||||
#: ../parser_include.c:147
|
||||
msgid "Error: Could not allocate memory.\n"
|
||||
msgstr "Fehler: Es konnte kein Speicher zugeordnet werden.\n"
|
||||
|
||||
#: ../parser_interface.c:69 ../parser_interface.c:72 ../parser_interface.c:49
|
||||
#: ../parser_interface.c:69 ../parser_interface.c:72
|
||||
msgid "Bad write position\n"
|
||||
msgstr "Ungültige Schreibposition\n"
|
||||
|
||||
#: ../parser_interface.c:72 ../parser_interface.c:75 ../parser_interface.c:52
|
||||
#: ../parser_interface.c:72 ../parser_interface.c:75
|
||||
msgid "Permission denied\n"
|
||||
msgstr "Zugriff verweigert\n"
|
||||
|
||||
#: ../parser_interface.c:75 ../parser_interface.c:78 ../parser_interface.c:55
|
||||
#: ../parser_interface.c:75 ../parser_interface.c:78
|
||||
msgid "Out of memory\n"
|
||||
msgstr "Nicht genügend Speicher!\n"
|
||||
msgstr "Kein Speicher vorhanden\n"
|
||||
|
||||
#: ../parser_interface.c:78 ../parser_interface.c:81 ../parser_interface.c:58
|
||||
#: ../parser_interface.c:78 ../parser_interface.c:81
|
||||
msgid "Couldn't copy profile: Bad memory address\n"
|
||||
msgstr "Profil konnte nicht kopiert werden: falsche Speicheradresse\n"
|
||||
msgstr "Profil konnte nicht kopiert werden: Keine Speicheradresse\n"
|
||||
|
||||
#: ../parser_interface.c:81 ../parser_interface.c:84 ../parser_interface.c:61
|
||||
#: ../parser_interface.c:81 ../parser_interface.c:84
|
||||
msgid "Profile doesn't conform to protocol\n"
|
||||
msgstr "Das Profil entspricht nicht dem Protokoll\n"
|
||||
|
||||
#: ../parser_interface.c:84 ../parser_interface.c:87 ../parser_interface.c:64
|
||||
#: ../parser_interface.c:84 ../parser_interface.c:87
|
||||
msgid "Profile does not match signature\n"
|
||||
msgstr "Das Profil stimmt nicht mit der Signatur überein\n"
|
||||
|
||||
#: ../parser_interface.c:87 ../parser_interface.c:90 ../parser_interface.c:67
|
||||
#: ../parser_interface.c:87 ../parser_interface.c:90
|
||||
msgid "Profile version not supported by Apparmor module\n"
|
||||
msgstr "Profilversion wird vom Apparmor-Modul nicht unterstützt\n"
|
||||
|
||||
#: ../parser_interface.c:90 ../parser_interface.c:93 ../parser_interface.c:70
|
||||
#: ../parser_interface.c:90 ../parser_interface.c:93
|
||||
msgid "Profile already exists\n"
|
||||
msgstr "Profil ist bereits vorhanden\n"
|
||||
|
||||
#: ../parser_interface.c:93 ../parser_interface.c:96 ../parser_interface.c:73
|
||||
#: ../parser_interface.c:93 ../parser_interface.c:96
|
||||
msgid "Profile doesn't exist\n"
|
||||
msgstr "Profil ist nicht vorhanden\n"
|
||||
|
||||
#: ../parser_interface.c:96 ../parser_interface.c:99 ../parser_interface.c:76
|
||||
#: ../parser_interface.c:96 ../parser_interface.c:99
|
||||
msgid "Permission denied; attempted to load a profile while confined?\n"
|
||||
msgstr ""
|
||||
"Zugriff verweigert! Haben Sie versucht ein Profil zu laden, während Sie "
|
||||
"eingeschränkt waren?\n"
|
||||
|
||||
#: ../parser_interface.c:99 ../parser_interface.c:102 ../parser_interface.c:79
|
||||
#: ../parser_interface.c:99 ../parser_interface.c:102
|
||||
#, c-format
|
||||
msgid "Unknown error (%d): %s\n"
|
||||
msgstr "Unbekannter Fehler (%d): %s\n"
|
||||
|
||||
#: ../parser_interface.c:116 ../parser_interface.c:119
|
||||
#: ../parser_interface.c:96
|
||||
#, c-format
|
||||
msgid "%s: Unable to add \"%s\". "
|
||||
msgstr "%s: Hinzufügen von »%s« nicht möglich. "
|
||||
|
||||
#: ../parser_interface.c:121 ../parser_interface.c:124
|
||||
#: ../parser_interface.c:101
|
||||
#, c-format
|
||||
msgid "%s: Unable to replace \"%s\". "
|
||||
msgstr "%s: »%s« kann nicht ersetzt werden. "
|
||||
|
||||
#: ../parser_interface.c:126 ../parser_interface.c:129
|
||||
#: ../parser_interface.c:106
|
||||
#, c-format
|
||||
msgid "%s: Unable to remove \"%s\". "
|
||||
msgstr "%s: »%s« kann nicht entfernt werden. "
|
||||
|
||||
#: ../parser_interface.c:131 ../parser_interface.c:134
|
||||
#: ../parser_interface.c:111
|
||||
#, c-format
|
||||
msgid "%s: Unable to write to stdout\n"
|
||||
msgstr "%s: Schreiben in Standardausgabe nicht möglich\n"
|
||||
|
||||
#: ../parser_interface.c:135 ../parser_interface.c:138
|
||||
#: ../parser_interface.c:115
|
||||
#, c-format
|
||||
msgid "%s: Unable to write to output file\n"
|
||||
msgstr "%s: Schreiben in Ausgabedatei nicht möglich\n"
|
||||
|
||||
#: ../parser_interface.c:138 ../parser_interface.c:162
|
||||
#: ../parser_interface.c:141 ../parser_interface.c:165
|
||||
#: ../parser_interface.c:118 ../parser_interface.c:142
|
||||
#, c-format
|
||||
msgid "%s: ASSERT: Invalid option: %d\n"
|
||||
msgstr "%s: ASSERT: Ungültige Option: %d\n"
|
||||
|
||||
#: ../parser_interface.c:147 ../parser_interface.c:150
|
||||
#: ../parser_interface.c:127
|
||||
#, c-format
|
||||
msgid "Addition succeeded for \"%s\".\n"
|
||||
msgstr "Hinzufügen für »%s« erfolgreich.\n"
|
||||
|
||||
#: ../parser_interface.c:151 ../parser_interface.c:154
|
||||
#: ../parser_interface.c:131
|
||||
#, c-format
|
||||
msgid "Replacement succeeded for \"%s\".\n"
|
||||
msgstr "Ersetzungsvorgang für »%s« erfolgreich.\n"
|
||||
|
||||
#: ../parser_interface.c:155 ../parser_interface.c:158
|
||||
#: ../parser_interface.c:135
|
||||
#, c-format
|
||||
msgid "Removal succeeded for \"%s\".\n"
|
||||
msgstr "Entfernen für »%s« erfolgreich.\n"
|
||||
@@ -145,7 +136,6 @@ msgstr ""
|
||||
"Auflösung %p\n"
|
||||
|
||||
#: ../parser_interface.c:656 ../parser_interface.c:658
|
||||
#: ../parser_interface.c:446
|
||||
#, c-format
|
||||
msgid "profile %s network rules not enforced\n"
|
||||
msgstr "Netzwerkregeln für Profil %s werden nicht erzwungen\n"
|
||||
@@ -156,19 +146,16 @@ msgstr "Unbekannter Mustertyp\n"
|
||||
|
||||
#: ../parser_interface.c:750 ../parser_interface.c:902
|
||||
#: ../parser_interface.c:743 ../parser_interface.c:894
|
||||
#: ../parser_interface.c:518 ../parser_interface.c:669
|
||||
#, c-format
|
||||
msgid "Unable to open %s - %s\n"
|
||||
msgstr "%s kann nicht geöffnet werden – %s\n"
|
||||
msgstr "%s kann nicht geöffnet werden - %s\n"
|
||||
|
||||
#: ../parser_interface.c:776 ../parser_interface.c:768
|
||||
#: ../parser_interface.c:543
|
||||
#, c-format
|
||||
msgid "Memory Allocation Error: Unable to remove ^%s\n"
|
||||
msgstr "Speicherzuordnungsfehler: ^%s kann nicht entfernt werden\n"
|
||||
|
||||
#: ../parser_interface.c:789 ../parser_interface.c:781
|
||||
#: ../parser_interface.c:556
|
||||
#, c-format
|
||||
msgid "Memory Allocation Error: Unable to remove %s:%s."
|
||||
msgstr "Speicherzuordnungsfehler: %s:%s kann nicht entfernt werden."
|
||||
@@ -184,24 +171,22 @@ msgstr "Serialisierung von Profil %s nicht möglich\n"
|
||||
|
||||
#: ../parser_interface.c:829 ../parser_interface.c:916
|
||||
#: ../parser_interface.c:821 ../parser_interface.c:908
|
||||
#: ../parser_interface.c:582
|
||||
#, c-format
|
||||
msgid "%s: Unable to write entire profile entry\n"
|
||||
msgstr "%s: Profileintrag kann nicht geschrieben werden\n"
|
||||
|
||||
#: ../parser_interface.c:839 ../parser_interface.c:831
|
||||
#: ../parser_interface.c:593
|
||||
#, c-format
|
||||
msgid "%s: Unable to write entire profile entry to cache\n"
|
||||
msgstr ""
|
||||
"%s: Schreiben des gesamten Profileintrags in den Puffer nicht möglich\n"
|
||||
|
||||
#: parser_lex.l:100 parser_lex.l:163 parser_lex.l:169
|
||||
#: parser_lex.l:100 parser_lex.l:163
|
||||
#, c-format
|
||||
msgid "Could not open '%s'"
|
||||
msgstr "»%s« konnte nicht geöffnet werden"
|
||||
|
||||
#: parser_lex.l:104 parser_lex.l:167 parser_lex.l:173
|
||||
#: parser_lex.l:104 parser_lex.l:167
|
||||
#, c-format
|
||||
msgid "fstat failed for '%s'"
|
||||
msgstr "fstat fehlgeschlagen für »%s«"
|
||||
@@ -216,18 +201,18 @@ msgstr "opendir fehlgeschlagen für »%s«"
|
||||
msgid "stat failed for '%s'"
|
||||
msgstr "stat fehlgeschlagen für »%s«"
|
||||
|
||||
#: parser_lex.l:155 parser_lex.l:133 parser_lex.l:139
|
||||
#: parser_lex.l:155 parser_lex.l:133
|
||||
#, c-format
|
||||
msgid "Could not open '%s' in '%s'"
|
||||
msgstr "»%s« konnte nicht in »%s« geöffnet werden"
|
||||
|
||||
#: parser_lex.l:284 parser_lex.l:322 parser_lex.l:362 parser_lex.l:399
|
||||
#: parser_lex.l:469 parser_lex.l:655 parser_lex.l:586 parser_lex.l:638
|
||||
#: parser_lex.l:469 parser_lex.l:655 parser_lex.l:586
|
||||
#, c-format
|
||||
msgid "Found unexpected character: '%s'"
|
||||
msgstr "Unerwartetes Zeichen gefunden: »%s«"
|
||||
|
||||
#: parser_lex.l:386 parser_lex.l:418 parser_lex.l:428
|
||||
#: parser_lex.l:386 parser_lex.l:418
|
||||
msgid "Variable declarations do not accept trailing commas"
|
||||
msgstr "Variablendeklarationen dürfen nicht mit Kommata enden"
|
||||
|
||||
@@ -236,7 +221,7 @@ msgstr "Variablendeklarationen dürfen nicht mit Kommata enden"
|
||||
msgid "(network_mode) Found unexpected character: '%s'"
|
||||
msgstr "(network_mode) Unerwartetes Zeichen gefunden: »%s«"
|
||||
|
||||
#: ../parser_main.c:333 ../parser_common.c:61 ../parser_common.c:106
|
||||
#: ../parser_main.c:333 ../parser_common.c:61
|
||||
#, c-format
|
||||
msgid "Warning from %s (%s%sline %d): %s"
|
||||
msgstr "Warnung aus %s (%s%sZeile %d): %s"
|
||||
@@ -248,7 +233,7 @@ msgstr ""
|
||||
"%s: Dem Einhängepunkt der Unterdomänenbasis konnte kein Speicher zugeordnet "
|
||||
"werden\n"
|
||||
|
||||
#: ../parser_main.c:577 ../parser_main.c:616 ../parser_main.c:479
|
||||
#: ../parser_main.c:577 ../parser_main.c:616
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Warning: unable to find a suitable fs in %s, is it mounted?\n"
|
||||
@@ -258,7 +243,7 @@ msgstr ""
|
||||
"es eingehängt?\n"
|
||||
"Verwenden Sie --subdomainfs, um es außer Kraft zu setzen.\n"
|
||||
|
||||
#: ../parser_main.c:597 ../parser_main.c:635 ../parser_main.c:498
|
||||
#: ../parser_main.c:597 ../parser_main.c:635
|
||||
#, c-format
|
||||
msgid ""
|
||||
"%s: Sorry. You need root privileges to run this program.\n"
|
||||
@@ -267,7 +252,7 @@ msgstr ""
|
||||
"»%s«: Sie benötigen Systemverwalterrechte zum Ausführen dieses Programms.\n"
|
||||
"\n"
|
||||
|
||||
#: ../parser_main.c:604 ../parser_main.c:642 ../parser_main.c:505
|
||||
#: ../parser_main.c:604 ../parser_main.c:642
|
||||
#, c-format
|
||||
msgid ""
|
||||
"%s: Warning! You've set this program setuid root.\n"
|
||||
@@ -280,7 +265,7 @@ msgstr ""
|
||||
"\n"
|
||||
|
||||
#: ../parser_main.c:704 ../parser_main.c:813 ../parser_main.c:836
|
||||
#: ../parser_main.c:946 ../parser_main.c:860
|
||||
#: ../parser_main.c:946
|
||||
#, c-format
|
||||
msgid "Error: Could not read profile %s: %s.\n"
|
||||
msgstr "Fehler: Profil »%s« konnte nicht gelesen werden: »%s«.\n"
|
||||
@@ -296,33 +281,26 @@ msgstr "Fehler: Profil »%s« konnte nicht gelesen werden: »%s«.\n"
|
||||
#: parser_yacc.y:1042 parser_yacc.y:1078 parser_yacc.y:1082 parser_yacc.y:1092
|
||||
#: parser_yacc.y:1102 parser_yacc.y:1201 parser_yacc.y:1223 parser_yacc.y:1234
|
||||
#: parser_yacc.y:1309 parser_yacc.y:1327 parser_yacc.y:1334 parser_yacc.y:1385
|
||||
#: ../parser_main.c:735 ../parser_main.c:923 ../parser_main.c:1133
|
||||
#: ../parser_main.c:1187 parser_yacc.y:311 parser_yacc.y:462 parser_yacc.y:472
|
||||
#: parser_yacc.y:583 parser_yacc.y:662 parser_yacc.y:669 parser_yacc.y:1130
|
||||
#: parser_yacc.y:1166 parser_yacc.y:1170 parser_yacc.y:1180 parser_yacc.y:1190
|
||||
#: parser_yacc.y:1298 parser_yacc.y:1376 parser_yacc.y:1479 parser_yacc.y:1490
|
||||
#: parser_yacc.y:1565 parser_yacc.y:1583 parser_yacc.y:1590 parser_yacc.y:1639
|
||||
#: ../network.c:314 ../af_unix.cc:203
|
||||
msgid "Memory allocation error."
|
||||
msgstr "Speicherzuordnungsfehler."
|
||||
|
||||
#: ../parser_main.c:740 ../parser_main.c:872 ../parser_main.c:757
|
||||
#: ../parser_main.c:740 ../parser_main.c:872
|
||||
#, c-format
|
||||
msgid "Cached load succeeded for \"%s\".\n"
|
||||
msgstr "Zwischengespeichertes Laden für »%s« erfolgreich.\n"
|
||||
|
||||
#: ../parser_main.c:744 ../parser_main.c:876 ../parser_main.c:761
|
||||
#: ../parser_main.c:744 ../parser_main.c:876
|
||||
#, c-format
|
||||
msgid "Cached reload succeeded for \"%s\".\n"
|
||||
msgstr "Zwischengespeichertes Neuladen für »%s« erfolgreich.\n"
|
||||
|
||||
#: ../parser_main.c:910 ../parser_main.c:1058 ../parser_main.c:967
|
||||
#: ../parser_main.c:910 ../parser_main.c:1058
|
||||
#, c-format
|
||||
msgid "%s: Errors found in file. Aborting.\n"
|
||||
msgstr ""
|
||||
"%s: In der Datei wurde ein Fehler gefunden. Der Vorgang wird abgebrochen.\n"
|
||||
|
||||
#: ../parser_misc.c:426 ../parser_misc.c:597 ../parser_misc.c:339
|
||||
#: ../parser_misc.c:426 ../parser_misc.c:597
|
||||
msgid ""
|
||||
"Uppercase qualifiers \"RWLIMX\" are deprecated, please convert to lowercase\n"
|
||||
"See the apparmor.d(5) manpage for details.\n"
|
||||
@@ -332,17 +310,17 @@ msgstr ""
|
||||
"Weitere Informationen auf der Handbuchseite apparmor.d(5).\n"
|
||||
|
||||
#: ../parser_misc.c:467 ../parser_misc.c:474 ../parser_misc.c:638
|
||||
#: ../parser_misc.c:645 ../parser_misc.c:380 ../parser_misc.c:387
|
||||
#: ../parser_misc.c:645
|
||||
msgid "Conflict 'a' and 'w' perms are mutually exclusive."
|
||||
msgstr "Die Parameter »a« und »w« schließen sich gegenseitig aus."
|
||||
|
||||
#: ../parser_misc.c:491 ../parser_misc.c:662 ../parser_misc.c:404
|
||||
#: ../parser_misc.c:491 ../parser_misc.c:662
|
||||
msgid "Exec qualifier 'i' invalid, conflicting qualifier already specified"
|
||||
msgstr ""
|
||||
"Ausführungskennzeichner »i« ist ungültig, ein Kennzeichner, mit dem ein "
|
||||
"Konflikt besteht, wurde bereits angegeben."
|
||||
|
||||
#: ../parser_misc.c:502 ../parser_misc.c:673 ../parser_misc.c:415
|
||||
#: ../parser_misc.c:502 ../parser_misc.c:673
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Unconfined exec qualifier (%c%c) allows some dangerous environment variables "
|
||||
@@ -353,7 +331,7 @@ msgstr ""
|
||||
"Einzelheiten mit »man 5 apparmor.d«.\n"
|
||||
|
||||
#: ../parser_misc.c:510 ../parser_misc.c:551 ../parser_misc.c:681
|
||||
#: ../parser_misc.c:722 ../parser_misc.c:423 ../parser_misc.c:464
|
||||
#: ../parser_misc.c:722
|
||||
#, c-format
|
||||
msgid "Exec qualifier '%c' invalid, conflicting qualifier already specified"
|
||||
msgstr ""
|
||||
@@ -361,7 +339,7 @@ msgstr ""
|
||||
"Konflikt besteht, wurde bereits angegeben."
|
||||
|
||||
#: ../parser_misc.c:537 ../parser_misc.c:545 ../parser_misc.c:708
|
||||
#: ../parser_misc.c:716 ../parser_misc.c:450 ../parser_misc.c:458
|
||||
#: ../parser_misc.c:716
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Exec qualifier '%c%c' invalid, conflicting qualifier already specified"
|
||||
@@ -369,12 +347,12 @@ msgstr ""
|
||||
"Ausführungskennzeichner »%c%c« ist ungültig, ein Kennzeichner, mit dem ein "
|
||||
"Konflikt besteht, wurde bereits angegeben."
|
||||
|
||||
#: ../parser_misc.c:593 ../parser_misc.c:764 ../parser_misc.c:506
|
||||
#: ../parser_misc.c:593 ../parser_misc.c:764
|
||||
#, c-format
|
||||
msgid "Internal: unexpected mode character '%c' in input"
|
||||
msgstr "Intern: Unerwartetes Moduszeichen »%c« in der Eingabe"
|
||||
|
||||
#: ../parser_misc.c:615 ../parser_misc.c:786 ../parser_misc.c:528
|
||||
#: ../parser_misc.c:615 ../parser_misc.c:786
|
||||
#, c-format
|
||||
msgid "Internal error generated invalid perm 0x%llx\n"
|
||||
msgstr "Interner Fehler erzeugte ungültige Zugriffsrechte 0x%llx\n"
|
||||
@@ -385,69 +363,68 @@ msgstr "Interner Fehler erzeugte ungültige Zugriffsrechte 0x%llx\n"
|
||||
msgid "AppArmor parser error: %s\n"
|
||||
msgstr "AppArmor-Analysefehler: %s\n"
|
||||
|
||||
#: ../parser_merge.c:92 ../parser_merge.c:91 ../parser_merge.c:83
|
||||
#: ../parser_merge.c:92 ../parser_merge.c:91
|
||||
msgid "Couldn't merge entries. Out of Memory\n"
|
||||
msgstr ""
|
||||
"Einträge konnten nicht zusammengeführt werden. Kein Speicher vorhanden\n"
|
||||
|
||||
#: ../parser_merge.c:111 ../parser_merge.c:113 ../parser_merge.c:105
|
||||
#: ../parser_merge.c:111 ../parser_merge.c:113
|
||||
#, c-format
|
||||
msgid "profile %s: has merged rule %s with conflicting x modifiers\n"
|
||||
msgstr ""
|
||||
"Profil %s: enthält zusammengeführte Regel %s mit in Konflikt stehenden x-"
|
||||
"Modifizierern\n"
|
||||
|
||||
#: parser_yacc.y:236 parser_yacc.y:277 parser_yacc.y:320
|
||||
#: parser_yacc.y:236 parser_yacc.y:277
|
||||
msgid "Profile attachment must begin with a '/'."
|
||||
msgstr "Profilanhang muss mit einem »/« beginnen."
|
||||
|
||||
#: parser_yacc.y:260 parser_yacc.y:302 parser_yacc.y:348
|
||||
#: parser_yacc.y:260 parser_yacc.y:302
|
||||
msgid ""
|
||||
"Profile names must begin with a '/', namespace or keyword 'profile' or 'hat'."
|
||||
msgstr ""
|
||||
"Profilnamen müssen mit einem »/«, Namensraum oder dem Schlüsselwort "
|
||||
"»profile« oder »hat« beginnen."
|
||||
|
||||
#: parser_yacc.y:296 parser_yacc.y:338 parser_yacc.y:384
|
||||
#: parser_yacc.y:296 parser_yacc.y:338
|
||||
#, c-format
|
||||
msgid "Failed to create alias %s -> %s\n"
|
||||
msgstr "Alias %s → %s konnte nicht erstellt werden\n"
|
||||
|
||||
#: parser_yacc.y:417 parser_yacc.y:460 parser_yacc.y:506
|
||||
#: parser_yacc.y:417 parser_yacc.y:460
|
||||
msgid "Profile flag chroot_relative conflicts with namespace_relative"
|
||||
msgstr ""
|
||||
"Profil-Marker chroot_relative steht in Konflikt mit namespace_relative"
|
||||
|
||||
#: parser_yacc.y:421 parser_yacc.y:464 parser_yacc.y:510
|
||||
#: parser_yacc.y:421 parser_yacc.y:464
|
||||
msgid "Profile flag mediate_deleted conflicts with delegate_deleted"
|
||||
msgstr "Profil-Marker mediate_deleted steht in Konflikt mit delegate_deleted"
|
||||
|
||||
#: parser_yacc.y:424 parser_yacc.y:467 parser_yacc.y:513
|
||||
#: parser_yacc.y:424 parser_yacc.y:467
|
||||
msgid ""
|
||||
"Profile flag attach_disconnected conflicts with no_attach_disconnected"
|
||||
msgstr ""
|
||||
"Profil-Marker attach_disconnected steht in Konflikt mit "
|
||||
"no_attach_disconnected"
|
||||
|
||||
#: parser_yacc.y:427 parser_yacc.y:470 parser_yacc.y:516
|
||||
#: parser_yacc.y:427 parser_yacc.y:470
|
||||
msgid "Profile flag chroot_attach conflicts with chroot_no_attach"
|
||||
msgstr "Profil-Marker chroot_attach steht in Konflikt mit chroot_no_attach"
|
||||
|
||||
#: parser_yacc.y:441 parser_yacc.y:484 parser_yacc.y:530
|
||||
#: parser_yacc.y:441 parser_yacc.y:484
|
||||
msgid "Profile flag 'debug' is no longer valid."
|
||||
msgstr "Profil-Marker »debug« ist nicht mehr gültig."
|
||||
|
||||
#: parser_yacc.y:463 parser_yacc.y:506 parser_yacc.y:552
|
||||
#: parser_yacc.y:463 parser_yacc.y:506
|
||||
#, c-format
|
||||
msgid "Invalid profile flag: %s."
|
||||
msgstr "Ungültiger Profil-Marker: %s."
|
||||
|
||||
#: parser_yacc.y:498 parser_yacc.y:520 parser_yacc.y:548 parser_yacc.y:594
|
||||
#: parser_yacc.y:498 parser_yacc.y:520 parser_yacc.y:548
|
||||
msgid "Assert: `rule' returned NULL."
|
||||
msgstr "Assert: Für »rule« wurde NULL zurückgegeben."
|
||||
|
||||
#: parser_yacc.y:501 parser_yacc.y:546 parser_yacc.y:552 parser_yacc.y:584
|
||||
#: parser_yacc.y:598 parser_yacc.y:630
|
||||
msgid ""
|
||||
"Invalid mode, in deny rules 'x' must not be preceded by exec qualifier 'i', "
|
||||
"'p', or 'u'"
|
||||
@@ -455,81 +432,80 @@ msgstr ""
|
||||
"Ungültiger Modus, in den Verweigernregeln darf vor »x« keiner der "
|
||||
"Ausführungskennzeichner »i«, »p« oder »u« stehen"
|
||||
|
||||
#: parser_yacc.y:524 parser_yacc.y:556 parser_yacc.y:602
|
||||
#: parser_yacc.y:524 parser_yacc.y:556
|
||||
msgid ""
|
||||
"Invalid mode, 'x' must be preceded by exec qualifier 'i', 'p', 'c', or 'u'"
|
||||
msgstr ""
|
||||
"Ungültiger Modus, in Verbotsregeln muss vor »x« einer der exec-Qualifier "
|
||||
"»i«, »p«, »c« oder »u« stehen"
|
||||
|
||||
#: parser_yacc.y:549 parser_yacc.y:587 parser_yacc.y:633
|
||||
#: parser_yacc.y:549 parser_yacc.y:587
|
||||
msgid "Invalid mode, 'x' must be preceded by exec qualifier 'i', 'p', or 'u'"
|
||||
msgstr ""
|
||||
"Ungültiger Modus, in Verbotsregeln muss vor »x« einer der exec-Qualifier "
|
||||
"»i«, »p« oder »u« stehen"
|
||||
|
||||
#: parser_yacc.y:574 parser_yacc.y:612 parser_yacc.y:614 parser_yacc.y:660
|
||||
#: parser_yacc.y:574 parser_yacc.y:612 parser_yacc.y:614
|
||||
msgid "Assert: `network_rule' return invalid protocol."
|
||||
msgstr "Assert: Für »network_rule« wurde NULL zurückgegeben."
|
||||
|
||||
#: parser_yacc.y:649 parser_yacc.y:696 parser_yacc.y:786
|
||||
#: parser_yacc.y:649 parser_yacc.y:696
|
||||
msgid "Assert: `change_profile' returned NULL."
|
||||
msgstr "Assert: Für »change_profile« wurde NULL zurückgegeben."
|
||||
|
||||
#: parser_yacc.y:680 parser_yacc.y:720 parser_yacc.y:810
|
||||
#: parser_yacc.y:680 parser_yacc.y:720
|
||||
msgid "Assert: 'hat rule' returned NULL."
|
||||
msgstr "Assert: Für »hat rule« wurde NULL zurückgegeben."
|
||||
|
||||
#: parser_yacc.y:689 parser_yacc.y:729 parser_yacc.y:819
|
||||
#: parser_yacc.y:689 parser_yacc.y:729
|
||||
msgid "Assert: 'local_profile rule' returned NULL."
|
||||
msgstr "Assert: Für »local_profile rule« wurde NULL zurückgegeben."
|
||||
|
||||
#: parser_yacc.y:824 parser_yacc.y:885 parser_yacc.y:992
|
||||
#: parser_yacc.y:824 parser_yacc.y:885
|
||||
#, c-format
|
||||
msgid "Unset boolean variable %s used in if-expression"
|
||||
msgstr "In Bedingungssatz verwendete Boolsche-Variable »%s« deaktivieren"
|
||||
|
||||
#: parser_yacc.y:882 parser_yacc.y:986 parser_yacc.y:1092
|
||||
#: parser_yacc.y:882 parser_yacc.y:986
|
||||
msgid "unsafe rule missing exec permissions"
|
||||
msgstr "Fehlende Ausführungsrechte bei unsicherer Regel"
|
||||
|
||||
#: parser_yacc.y:901 parser_yacc.y:954 parser_yacc.y:1060
|
||||
#: parser_yacc.y:901 parser_yacc.y:954
|
||||
msgid "subset can only be used with link rules."
|
||||
msgstr "subset kann nur mit Link-Regeln verwendet werden."
|
||||
|
||||
#: parser_yacc.y:903 parser_yacc.y:956 parser_yacc.y:1062
|
||||
#: parser_yacc.y:903 parser_yacc.y:956
|
||||
msgid "link and exec perms conflict on a file rule using ->"
|
||||
msgstr ""
|
||||
"Verknüpfungs- und Ausführungsberechtigungen stehen in Konflikt mit einer "
|
||||
"Dateiregel, in der »->« verwendet wird"
|
||||
|
||||
#: parser_yacc.y:905 parser_yacc.y:958 parser_yacc.y:1064
|
||||
#: parser_yacc.y:905 parser_yacc.y:958
|
||||
msgid "link perms are not allowed on a named profile transition.\n"
|
||||
msgstr ""
|
||||
"Verknüpfungsberechtigungen sind bei einem benannten Profilübergang nicht "
|
||||
"erlaubt.\n"
|
||||
|
||||
#: parser_yacc.y:921 parser_yacc.y:1003 parser_yacc.y:1109
|
||||
#: parser_yacc.y:921 parser_yacc.y:1003
|
||||
#, c-format
|
||||
msgid "missing an end of line character? (entry: %s)"
|
||||
msgstr "Fehlt ein Zeilenumbruch? (Eintrag: %s)"
|
||||
|
||||
#: parser_yacc.y:975 parser_yacc.y:985 parser_yacc.y:1057 parser_yacc.y:1067
|
||||
#: parser_yacc.y:1145 parser_yacc.y:1155
|
||||
msgid "Invalid network entry."
|
||||
msgstr "Ungültiger Netzwerkeintrag."
|
||||
|
||||
#: parser_yacc.y:1039 parser_yacc.y:1048 parser_yacc.y:1254 parser_yacc.y:1510
|
||||
#: parser_yacc.y:1039 parser_yacc.y:1048 parser_yacc.y:1254
|
||||
#, c-format
|
||||
msgid "Invalid capability %s."
|
||||
msgstr "Ungültige Fähigkeit %s."
|
||||
|
||||
#: parser_yacc.y:1066 parser_yacc.y:1269 parser_yacc.y:1525
|
||||
#: parser_yacc.y:1066 parser_yacc.y:1269
|
||||
#, c-format
|
||||
msgid "AppArmor parser error for %s%s%s at line %d: %s\n"
|
||||
msgstr "AppArmor-Analysefehler für %s%s%s in Zeile %d: %s\n"
|
||||
|
||||
#: parser_yacc.y:1072 parser_yacc.y:1275 parser_yacc.y:1531
|
||||
#: parser_yacc.y:1072 parser_yacc.y:1275
|
||||
#, c-format
|
||||
msgid "AppArmor parser error,%s%s line %d: %s\n"
|
||||
msgstr "AppArmor-Analysefehler,%s%s Zeile %d: %s\n"
|
||||
@@ -540,13 +516,13 @@ msgid "%s: Illegal open {, nesting groupings not allowed\n"
|
||||
msgstr ""
|
||||
"%s: Öffnen mit { ungültig, verschachtelte Gruppierungen sind nicht zulässig\n"
|
||||
|
||||
#: ../parser_regex.c:265 ../parser_regex.c:274 ../parser_regex.c:278
|
||||
#: ../parser_regex.c:265 ../parser_regex.c:274
|
||||
#, c-format
|
||||
msgid "%s: Regex grouping error: Invalid number of items between {}\n"
|
||||
msgstr ""
|
||||
"%s: Regex-Gruppierungsfehler: Ungültige Anzahl an Einträgen zwischen {}\n"
|
||||
|
||||
#: ../parser_regex.c:271 ../parser_regex.c:280 ../parser_regex.c:284
|
||||
#: ../parser_regex.c:271 ../parser_regex.c:280
|
||||
#, c-format
|
||||
msgid ""
|
||||
"%s: Regex grouping error: Invalid close }, no matching open { detected\n"
|
||||
@@ -554,7 +530,7 @@ msgstr ""
|
||||
"%s: Regex-Gruppierungsfehler: Ungültiges schließendes Zeichen }, kein "
|
||||
"passendes öffnendes Zeichen { gefunden\n"
|
||||
|
||||
#: ../parser_regex.c:337 ../parser_regex.c:343 ../parser_regex.c:361
|
||||
#: ../parser_regex.c:337 ../parser_regex.c:343
|
||||
#, c-format
|
||||
msgid ""
|
||||
"%s: Regex grouping error: Unclosed grouping or character class, expecting "
|
||||
@@ -568,17 +544,17 @@ msgstr ""
|
||||
msgid "%s: Internal buffer overflow detected, %d characters exceeded\n"
|
||||
msgstr "%s: Interner Pufferüberlauf erkannt, %d Zeichen überschritten\n"
|
||||
|
||||
#: ../parser_regex.c:355 ../parser_regex.c:361 ../parser_regex.c:377
|
||||
#: ../parser_regex.c:355 ../parser_regex.c:361
|
||||
#, c-format
|
||||
msgid "%s: Unable to parse input line '%s'\n"
|
||||
msgstr "%s: Eingabezeile »%s« kann nicht analysiert werden\n"
|
||||
|
||||
#: ../parser_regex.c:397 ../parser_regex.c:405 ../parser_regex.c:421
|
||||
#: ../parser_regex.c:397 ../parser_regex.c:405
|
||||
#, c-format
|
||||
msgid "%s: Invalid profile name '%s' - bad regular expression\n"
|
||||
msgstr "%s: Ungültiger Profilname »%s« – Fehlerhafter regulärer Ausdruck\n"
|
||||
|
||||
#: ../parser_policy.c:202 ../parser_policy.c:402 ../parser_policy.c:375
|
||||
#: ../parser_policy.c:202 ../parser_policy.c:402
|
||||
#, c-format
|
||||
msgid "ERROR merging rules for profile %s, failed to load\n"
|
||||
msgstr "FEHLER Vereinigungsregeln für Profil »%s«, Laden gescheitert\n"
|
||||
@@ -596,18 +572,18 @@ msgstr ""
|
||||
"erlaubt.\n"
|
||||
"\t»**« kann am Ende einer Regel verwendet werden.\n"
|
||||
|
||||
#: ../parser_policy.c:279 ../parser_policy.c:359 ../parser_policy.c:332
|
||||
#: ../parser_policy.c:279 ../parser_policy.c:359
|
||||
#, c-format
|
||||
msgid "ERROR processing regexs for profile %s, failed to load\n"
|
||||
msgstr "FEHLER Verarbeitung der Regexs für Profil »%s«, Laden gescheitert\n"
|
||||
|
||||
#: ../parser_policy.c:306 ../parser_policy.c:389 ../parser_policy.c:362
|
||||
#: ../parser_policy.c:306 ../parser_policy.c:389
|
||||
#, c-format
|
||||
msgid "ERROR expanding variables for profile %s, failed to load\n"
|
||||
msgstr ""
|
||||
"FEHLER beim Erweitern der Variablen für Profil »%s«, Laden gescheitert\n"
|
||||
|
||||
#: ../parser_policy.c:390 ../parser_policy.c:382 ../parser_policy.c:355
|
||||
#: ../parser_policy.c:390 ../parser_policy.c:382
|
||||
#, c-format
|
||||
msgid "ERROR adding hat access rule for profile %s\n"
|
||||
msgstr "FEHLER Hinzufügen von »hat«-Zugriffsregel für Profil %s\n"
|
||||
@@ -641,32 +617,31 @@ msgstr ""
|
||||
"%s: Beim Kombinieren von Regeln in der Nachverarbeitung sind Fehler "
|
||||
"aufgetreten. Der Vorgang wird abgebrochen.\n"
|
||||
|
||||
#: parser_lex.l:180 parser_lex.l:186
|
||||
#: parser_lex.l:180
|
||||
#, c-format
|
||||
msgid "Could not process include directory '%s' in '%s'"
|
||||
msgstr ""
|
||||
"Das enthaltene Verzeichnis »%s« in »%s« kann nicht verarbeitet werden"
|
||||
|
||||
#: ../parser_main.c:660 ../parser_main.c:523
|
||||
#: ../parser_main.c:660
|
||||
msgid "Feature buffer full."
|
||||
msgstr "Funktionspuffer ist voll."
|
||||
|
||||
#: ../parser_main.c:1115 ../parser_main.c:1132 ../parser_main.c:1024
|
||||
#: ../parser_main.c:1041
|
||||
#: ../parser_main.c:1115 ../parser_main.c:1132
|
||||
msgid "Out of memory"
|
||||
msgstr "Nicht genügend Speicher!"
|
||||
msgstr "Nicht genug Speicher"
|
||||
|
||||
#: ../parser_main.c:1182 ../parser_main.c:1091
|
||||
#: ../parser_main.c:1182
|
||||
#, c-format
|
||||
msgid "Can't create cache directory: %s\n"
|
||||
msgstr "Pufferverzeichnis kann nicht erstellt werden: %s\n"
|
||||
|
||||
#: ../parser_main.c:1185 ../parser_main.c:1094
|
||||
#: ../parser_main.c:1185
|
||||
#, c-format
|
||||
msgid "File in cache directory location: %s\n"
|
||||
msgstr "Datei im Pufferverzeichnisort: %s\n"
|
||||
|
||||
#: ../parser_main.c:1188 ../parser_main.c:1097
|
||||
#: ../parser_main.c:1188
|
||||
#, c-format
|
||||
msgid "Can't update cache directory: %s\n"
|
||||
msgstr "Pufferverzeichnis kann nicht aktualisiert werden: %s\n"
|
||||
@@ -681,11 +656,11 @@ msgstr "Intern: Unerwartetes D-Bus-Moduszeichen »%c« in der Eingabe"
|
||||
msgid "Internal error generated invalid DBus perm 0x%x\n"
|
||||
msgstr "Interner Fehler hat ungültige D-Bus-Zugriffsrechte 0x%x erstellt\n"
|
||||
|
||||
#: parser_yacc.y:575 parser_yacc.y:621
|
||||
#: parser_yacc.y:575
|
||||
msgid "deny prefix not allowed"
|
||||
msgstr "Verweigernpräfix nicht erlaubt"
|
||||
|
||||
#: parser_yacc.y:612 parser_yacc.y:658
|
||||
#: parser_yacc.y:612
|
||||
msgid "owner prefix not allowed"
|
||||
msgstr "Eigentümerpräfix nicht erlaubt"
|
||||
|
||||
@@ -701,25 +676,25 @@ msgstr "Eigentümerpräfix nicht bei D-Bus-Regeln erlauben"
|
||||
msgid "owner prefix not allow on capability rules"
|
||||
msgstr "Eigentümerpräfix nicht bei Fähigkeitsregeln erlauben"
|
||||
|
||||
#: parser_yacc.y:1357 parser_yacc.y:1613
|
||||
#: parser_yacc.y:1357
|
||||
#, c-format
|
||||
msgid "invalid mount conditional %s%s"
|
||||
msgstr "Ungültige Einhängebedingung %s%s"
|
||||
|
||||
#: parser_yacc.y:1374 parser_yacc.y:1628
|
||||
#: parser_yacc.y:1374
|
||||
msgid "bad mount rule"
|
||||
msgstr "Ungültige Einhängeregel"
|
||||
|
||||
#: parser_yacc.y:1381 parser_yacc.y:1635
|
||||
#: parser_yacc.y:1381
|
||||
msgid "mount point conditions not currently supported"
|
||||
msgstr "Einhängepunktbedingungen werden derzeit nicht unterstützt"
|
||||
|
||||
#: parser_yacc.y:1398 parser_yacc.y:1650
|
||||
#: parser_yacc.y:1398
|
||||
#, c-format
|
||||
msgid "invalid pivotroot conditional '%s'"
|
||||
msgstr "Ungültige pivotroot-Bedingung »%s«"
|
||||
|
||||
#: ../parser_regex.c:241 ../parser_regex.c:236
|
||||
#: ../parser_regex.c:241
|
||||
#, c-format
|
||||
msgid ""
|
||||
"%s: Regex grouping error: Invalid close ], no matching open [ detected\n"
|
||||
@@ -727,20 +702,20 @@ msgstr ""
|
||||
"%s: Regex-Gruppierungsfehler: Ungültiges schließendes Zeichen ], kein "
|
||||
"passendes öffnendes Zeichen [ gefunden\n"
|
||||
|
||||
#: ../parser_regex.c:257 ../parser_regex.c:256
|
||||
#: ../parser_regex.c:257
|
||||
#, c-format
|
||||
msgid "%s: Regex grouping error: Exceeded maximum nesting of {}\n"
|
||||
msgstr ""
|
||||
"%s: Regex-Gruppierungsfehler: maximale Verschachtelung von {} überschritten\n"
|
||||
|
||||
#: ../parser_policy.c:366 ../parser_policy.c:339
|
||||
#: ../parser_policy.c:366
|
||||
#, c-format
|
||||
msgid "ERROR processing policydb rules for profile %s, failed to load\n"
|
||||
msgstr ""
|
||||
"FEHLER beim Verarbeiten der policydb-Regeln für das Profil %s. Das Laden ist "
|
||||
"fehlgeschlagen.\n"
|
||||
|
||||
#: ../parser_policy.c:396 ../parser_policy.c:369
|
||||
#: ../parser_policy.c:396
|
||||
#, c-format
|
||||
msgid "ERROR replacing aliases for profile %s, failed to load\n"
|
||||
msgstr ""
|
||||
|
@@ -18,8 +18,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"Language: el\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -14,8 +14,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-02-02 05:11+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"Language: en_AU\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -14,8 +14,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-02-02 05:11+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"Language: en_CA\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -14,8 +14,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-02-02 05:11+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"Language: en_GB\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -12,8 +12,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"Language: es\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -15,8 +15,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"Language: et\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -22,8 +22,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"Language: fi\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -12,8 +12,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-04-29 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 17995)\n"
|
||||
"Language: fr\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -18,8 +18,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"Language: gl\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -9,8 +9,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"Language: gu\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -13,8 +13,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"X-Launchpad-Export-Date: 2016-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"Language: he\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user