mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-09-01 06:45:38 +00:00
Compare commits
119 Commits
v2.11.3
...
git-conver
Author | SHA1 | Date | |
---|---|---|---|
|
df0f20f32b | ||
|
c4a4e5bb82 | ||
|
d2f7f21b04 | ||
|
eab153a7e9 | ||
|
f737cc3444 | ||
|
7aeebcbb60 | ||
|
62e91696ee | ||
|
ffeb6c02a5 | ||
|
ad94da321b | ||
|
9a69ec2c48 | ||
|
6ce2768115 | ||
|
4961fcb6ba | ||
|
881cf7bddc | ||
|
74178418ad | ||
|
a32a9aa6d1 | ||
|
12fc3f0d4d | ||
|
1d896e014c | ||
|
76a8923ce4 | ||
|
fce8d81d7f | ||
|
7ed6ad7254 | ||
|
49ec85b4fa | ||
|
860c880454 | ||
|
ec39158ce8 | ||
|
b926125618 | ||
|
112af14dd5 | ||
|
f2831c875e | ||
|
630cb2a981 | ||
|
dd852138d6 | ||
|
ca42518c1d | ||
|
41c1e30e9b | ||
|
b973c9b473 | ||
|
c79dd88edb | ||
|
40bf479666 | ||
|
59660c4650 | ||
|
6260deec9a | ||
|
26a12fd9ac | ||
|
b64edfc92b | ||
|
ec6faab94b | ||
|
031792c400 | ||
|
0d6a2fa9e7 | ||
|
0983de0692 | ||
|
ec6f185787 | ||
|
4eece9d5ee | ||
|
347715da30 | ||
|
27654be0b5 | ||
|
84cd523d8c | ||
|
ae692bfb3b | ||
|
a016042a9a | ||
|
d53a4f80bf | ||
|
9480a83ddf | ||
|
db8d865193 | ||
|
237fc59ba8 | ||
|
cfe2854740 | ||
|
77c2e27e6c | ||
|
3abb79b0ae | ||
|
cc5a23d4c1 | ||
|
ff66ca9039 | ||
|
0e6a9c54f2 | ||
|
c519a1a9c1 | ||
|
c086d280b9 | ||
|
2d6fa07fd5 | ||
|
12cfc5ecf4 | ||
|
1d0790c0c6 | ||
|
5e535b246a | ||
|
ad4ac5780f | ||
|
3927a5a54a | ||
|
e038b0fcff | ||
|
9f7eab039a | ||
|
233a011db0 | ||
|
f23675a6e4 | ||
|
8cb6ded867 | ||
|
6bab27b56d | ||
|
325ff61910 | ||
|
cf86c9252d | ||
|
5b4001e810 | ||
|
52d41feeaf | ||
|
5b5da2b010 | ||
|
89c0051304 | ||
|
713b0d2b80 | ||
|
5d516bb4a9 | ||
|
7360781a8f | ||
|
16f16486ee | ||
|
ce33d5af9a | ||
|
673be01cdc | ||
|
e475e2d0b1 | ||
|
b289f0928e | ||
|
1f44387665 | ||
|
598625fdaa | ||
|
c6386bb654 | ||
|
5a1ae88561 | ||
|
0ce0fa45aa | ||
|
49420c51f3 | ||
|
25dde3dcf7 | ||
|
4dbc7e0f4b | ||
|
fe612e771b | ||
|
13567b2ae0 | ||
|
7dce13ab4a | ||
|
4b3888751a | ||
|
13750dfa41 | ||
|
0699034db4 | ||
|
2264ca7e14 | ||
|
285ee63ec3 | ||
|
d658acc402 | ||
|
00bec36bed | ||
|
4b364b82f0 | ||
|
80627b9d52 | ||
|
1b15425ea3 | ||
|
e199951a57 | ||
|
ab64476ae5 | ||
|
45dedc4338 | ||
|
860d4a3841 | ||
|
230a9eea39 | ||
|
17efc775cd | ||
|
c33b2bea09 | ||
|
7c50b9f2eb | ||
|
79f41156e8 | ||
|
abc3c22551 | ||
|
bb981d54f0 | ||
|
c8f5c9e406 |
@@ -2,6 +2,7 @@ apparmor-*
|
||||
parser/po/*.mo
|
||||
parser/af_names.h
|
||||
parser/cap_names.h
|
||||
parser/tst_lib
|
||||
parser/tst_misc
|
||||
parser/tst_regex
|
||||
parser/tst_symtab
|
||||
@@ -19,6 +20,7 @@ parser/*.7.html
|
||||
parser/*.5.html
|
||||
parser/*.8.html
|
||||
parser/apparmor_parser
|
||||
parser/libapparmor_re/parse.cc
|
||||
parser/libapparmor_re/regexp.cc
|
||||
parser/techdoc.aux
|
||||
parser/techdoc.log
|
||||
|
@@ -36,7 +36,7 @@ CFLAGS = -g -pg -fprofile-arcs -ftest-coverage
|
||||
endif
|
||||
endif #CFLAGS
|
||||
|
||||
EXTRA_CFLAGS = ${EXTRA_CXXFLAGS} ${CPP_WARNINGS}
|
||||
EXTRA_CFLAGS = ${CFLAGS} ${CPPFLAGS} ${EXTRA_CXXFLAGS} ${CPP_WARNINGS}
|
||||
|
||||
#INCLUDEDIR = /usr/src/linux/include
|
||||
INCLUDEDIR =
|
||||
|
@@ -56,27 +56,27 @@ Upon exiting, B<aa-enabled> will set its exit status to the following values:
|
||||
|
||||
=over 4
|
||||
|
||||
=item 0:
|
||||
=item B<0>
|
||||
|
||||
if AppArmor is enabled.
|
||||
|
||||
=item 1:
|
||||
=item B<1>
|
||||
|
||||
if AppArmor is not enabled/loaded.
|
||||
|
||||
=item 2:
|
||||
=item B<2>
|
||||
|
||||
intentionally not used as an B<aa-enabled> exit status.
|
||||
|
||||
=item 3:
|
||||
=item B<3>
|
||||
|
||||
if the AppArmor control files aren't available under /sys/kernel/security/.
|
||||
|
||||
=item 4:
|
||||
=item B<4>
|
||||
|
||||
if B<aa-enabled> doesn't have enough privileges to read the apparmor control files.
|
||||
|
||||
=item 64:
|
||||
=item B<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: 2016-03-20 01:58+0000\n"
|
||||
"Last-Translator: Tobias Bannert <Unknown>\n"
|
||||
"PO-Revision-Date: 2017-03-31 10:44+0000\n"
|
||||
"Last-Translator: Tobias Bannert <tobannert@gmail.com>\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: 2016-03-21 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 17947)\n"
|
||||
"X-Launchpad-Export-Date: 2017-04-05 05:23+0000\n"
|
||||
"X-Generator: Launchpad (build 18335)\n"
|
||||
"Language: de\n"
|
||||
|
||||
#: ../aa_enabled.c:26
|
||||
@@ -30,12 +30,12 @@ msgstr ""
|
||||
#: ../aa_enabled.c:45
|
||||
#, c-format
|
||||
msgid "unknown or incompatible options\n"
|
||||
msgstr ""
|
||||
msgstr "unbekannte oder nicht kompatible Optionen\n"
|
||||
|
||||
#: ../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
|
||||
|
@@ -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-02-19 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17925)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\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-02-02 05:11+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\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 <ivofernandes12@gmail.com>\n"
|
||||
"Last-Translator: Ivo Xavier <ivoxavier.8@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-03-04 04:35+0000\n"
|
||||
"X-Generator: Launchpad (build 17936)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\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-03-30 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 17967)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"Language: ru\n"
|
||||
|
||||
#: ../aa_enabled.c:26
|
||||
|
@@ -1 +1 @@
|
||||
2.11.0
|
||||
2.11.95
|
||||
|
@@ -0,0 +1,605 @@
|
||||
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
|
||||
|
@@ -0,0 +1,38 @@
|
||||
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
|
||||
|
@@ -0,0 +1,938 @@
|
||||
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
|
||||
|
@@ -0,0 +1,605 @@
|
||||
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
|
||||
|
@@ -0,0 +1,38 @@
|
||||
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
|
||||
|
@@ -0,0 +1,938 @@
|
||||
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
|
||||
|
@@ -0,0 +1,41 @@
|
||||
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
|
||||
|
@@ -0,0 +1,51 @@
|
||||
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
|
||||
|
@@ -0,0 +1,32 @@
|
||||
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
|
||||
|
@@ -0,0 +1,37 @@
|
||||
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
|
||||
|
@@ -0,0 +1,29 @@
|
||||
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
|
||||
|
@@ -0,0 +1,397 @@
|
||||
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
|
||||
|
1051
kernel-patches/v4.13/0007-apparmor-add-mount-mediation.patch
Normal file
1051
kernel-patches/v4.13/0007-apparmor-add-mount-mediation.patch
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,71 @@
|
||||
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
|
||||
|
@@ -0,0 +1,63 @@
|
||||
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
|
||||
|
@@ -0,0 +1,219 @@
|
||||
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
|
||||
|
@@ -0,0 +1,78 @@
|
||||
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
@@ -0,0 +1,194 @@
|
||||
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
|
||||
|
@@ -0,0 +1,60 @@
|
||||
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
|
||||
|
@@ -0,0 +1,36 @@
|
||||
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
|
||||
|
@@ -0,0 +1,39 @@
|
||||
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
5
kernel-patches/v4.13/README
Normal file
5
kernel-patches/v4.13/README
Normal file
@@ -0,0 +1,5 @@
|
||||
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
4
kernel-patches/v4.14/README
Normal file
4
kernel-patches/v4.14/README
Normal file
@@ -0,0 +1,4 @@
|
||||
This series is based on what is currently in linux-next scheduled for
|
||||
inclusion in 4.14
|
||||
|
||||
af_unix-mediation is the last remaining patch that is out of tree
|
@@ -0,0 +1,603 @@
|
||||
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
|
||||
|
@@ -0,0 +1,38 @@
|
||||
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
|
||||
|
@@ -0,0 +1,962 @@
|
||||
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
|
||||
|
@@ -22,7 +22,9 @@
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Class of public mediation types in the AppArmor policy db
|
||||
@@ -191,6 +193,8 @@ extern int aa_policy_cache_remove(int dirfd, const char *path);
|
||||
extern int aa_policy_cache_replace_all(aa_policy_cache *policy_cache,
|
||||
aa_kernel_interface *kernel_interface);
|
||||
|
||||
__END_DECLS
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* sys/apparmor.h */
|
||||
|
@@ -20,7 +20,9 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int _aa_is_blacklisted(const char *name);
|
||||
|
||||
@@ -33,6 +35,8 @@ int _aa_asprintf(char **strp, const char *fmt, ...);
|
||||
int _aa_dirat_for_each(int dirfd, const char *name, void *data,
|
||||
int (* cb)(int, const char *, struct stat *, void *));
|
||||
|
||||
__END_DECLS
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* sys/apparmor_private.h */
|
||||
|
@@ -121,7 +121,7 @@ class AAPythonBindingsTests(unittest.TestCase):
|
||||
continue
|
||||
else:
|
||||
new_record[key] = str(value)
|
||||
elif record.__getattr__(key):
|
||||
elif value or value == '':
|
||||
new_record[key] = str(value)
|
||||
|
||||
return new_record
|
||||
|
@@ -17,8 +17,8 @@ clean-local:
|
||||
rm -rf tmp.err.* tmp.out.* site.exp site.bak test_multi/out
|
||||
rm -f libaalogparse.log libaalogparse.sum
|
||||
|
||||
check-local:
|
||||
if ! test -f libaalogparse.log ; then echo '*** libaalogparse.log not found - is dejagnu installed? ***'; exit 1; fi
|
||||
if grep ERROR libaalogparse.log ; then exit 1 ; fi
|
||||
check-local: check-DEJAGNU
|
||||
@if ! test -f libaalogparse.log ; then echo '*** libaalogparse.log not found - is dejagnu installed? ***'; exit 1; fi
|
||||
@if grep ERROR libaalogparse.log ; then exit 1 ; fi
|
||||
|
||||
EXTRA_DIST = test_multi/*.in test_multi/*.out test_multi/*.err
|
||||
|
@@ -0,0 +1 @@
|
||||
type=AVC msg=audit(1494272099.261:3455): apparmor="DENIED" operation="ptrace" profile="/bin/netstat" pid=1962 comm="netstat" target=""
|
@@ -0,0 +1,11 @@
|
||||
START
|
||||
File: ptrace_garbage_lp1689667_1.in
|
||||
Event type: AA_RECORD_DENIED
|
||||
Audit ID: 1494272099.261:3455
|
||||
Operation: ptrace
|
||||
Profile: /bin/netstat
|
||||
Command: netstat
|
||||
Name2:
|
||||
PID: 1962
|
||||
Epoch: 1494272099
|
||||
Audit subid: 3455
|
@@ -0,0 +1,2 @@
|
||||
/bin/netstat {
|
||||
}
|
@@ -0,0 +1 @@
|
||||
type=AVC msg=audit(1494272099.261:3455): apparmor="DENIED" operation="ptrace" profile="/bin/netstat" pid=1962 comm="netstat" target=8022C0FF81A0FFFF8022C0FF81A0FFFF1080CBFF81A0FFFF1080CBFF81A0FFFF2080CBFF81A0FFFF2080CBFF81A0FFFF9E03
|
@@ -0,0 +1,10 @@
|
||||
START
|
||||
File: ptrace_garbage_lp1689667_2.in
|
||||
Event type: AA_RECORD_INVALID
|
||||
Audit ID: 1494272099.261:3455
|
||||
Operation: ptrace
|
||||
Profile: /bin/netstat
|
||||
Command: netstat
|
||||
PID: 1962
|
||||
Epoch: 1494272099
|
||||
Audit subid: 3455
|
@@ -0,0 +1 @@
|
||||
type=AVC msg=audit(1495217772.047:4471): apparmor="DENIED" operation="ptrace" profile="/usr/bin/pidgin" pid=21704 comm="pidgin" peer="unconfined"
|
@@ -0,0 +1,11 @@
|
||||
START
|
||||
File: ptrace_no_denied_mask.in
|
||||
Event type: AA_RECORD_DENIED
|
||||
Audit ID: 1495217772.047:4471
|
||||
Operation: ptrace
|
||||
Profile: /usr/bin/pidgin
|
||||
Peer: unconfined
|
||||
Command: pidgin
|
||||
PID: 21704
|
||||
Epoch: 1495217772
|
||||
Audit subid: 4471
|
@@ -0,0 +1,2 @@
|
||||
/usr/bin/pidgin {
|
||||
}
|
@@ -196,16 +196,20 @@ static void writeu16(std::ostringstream &o, int v)
|
||||
#define CMD_OPT 4
|
||||
|
||||
void unix_rule::downgrade_rule(Profile &prof) {
|
||||
unsigned int mask = (unsigned int) -1;
|
||||
|
||||
if (!prof.net.allow && !prof.alloc_net_table())
|
||||
yyerror(_("Memory allocation error."));
|
||||
if (sock_type_n != -1)
|
||||
mask = 1 << sock_type_n;
|
||||
if (deny) {
|
||||
prof.net.deny[AF_UNIX] |= 1 << sock_type_n;
|
||||
prof.net.deny[AF_UNIX] |= mask;
|
||||
if (!audit)
|
||||
prof.net.quiet[AF_UNIX] |= 1 << sock_type_n;
|
||||
prof.net.quiet[AF_UNIX] |= mask;
|
||||
} else {
|
||||
prof.net.allow[AF_UNIX] |= 1 << sock_type_n;
|
||||
prof.net.allow[AF_UNIX] |= mask;
|
||||
if (audit)
|
||||
prof.net.audit[AF_UNIX] |= 1 << sock_type_n;
|
||||
prof.net.audit[AF_UNIX] |= mask;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -111,7 +111,7 @@ capabilities(7))
|
||||
|
||||
B<NETWORK RULE> = [ I<QUALIFIERS> ] 'network' [ I<DOMAIN> ] [ I<TYPE> | I<PROTOCOL> ]
|
||||
|
||||
B<DOMAIN> = ( 'inet' | 'ax25' | 'ipx' | 'appletalk' | 'netrom' | 'bridge' | 'atmpvc' | 'x25' | 'inet6' | 'rose' | 'netbeui' | 'security' | 'key' | 'packet' | 'ash' | 'econet' | 'atmsvc' | 'sna' | 'irda' | 'pppox' | 'wanpipe' | 'bluetooth' | 'netlink' | 'unix' | 'rds' | 'llc' | 'can' | 'tipc' | 'iucv' | 'rxrpc' | 'isdn' | 'phonet' | 'ieee802154' | 'caif' | 'alg' | 'nfc' | 'vsock' | 'mpls' | 'ib' | 'kcm' ) ','
|
||||
B<DOMAIN> = ( 'inet' | 'ax25' | 'ipx' | 'appletalk' | 'netrom' | 'bridge' | 'atmpvc' | 'x25' | 'inet6' | 'rose' | 'netbeui' | 'security' | 'key' | 'packet' | 'ash' | 'econet' | 'atmsvc' | 'sna' | 'irda' | 'pppox' | 'wanpipe' | 'bluetooth' | 'netlink' | 'unix' | 'rds' | 'llc' | 'can' | 'tipc' | 'iucv' | 'rxrpc' | 'isdn' | 'phonet' | 'ieee802154' | 'caif' | 'alg' | 'nfc' | 'vsock' | 'mpls' | 'ib' | 'kcm' | 'smc' ) ','
|
||||
|
||||
B<TYPE> = ( 'stream' | 'dgram' | 'seqpacket' | 'rdm' | 'raw' | 'packet' )
|
||||
|
||||
|
@@ -45,6 +45,7 @@
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "lib.h"
|
||||
#include "parser.h"
|
||||
|
@@ -124,7 +124,9 @@ 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},
|
||||
|
@@ -902,6 +902,7 @@ 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)
|
||||
@@ -913,6 +914,7 @@ 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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"Language: da\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Report-Msgid-Bugs-To: <apparmor@lists.ubuntu.com>\n"
|
||||
"POT-Creation-Date: 2014-09-13 00:11-0700\n"
|
||||
"PO-Revision-Date: 2015-09-05 20:55+0000\n"
|
||||
"Last-Translator: Tobias Bannert <Unknown>\n"
|
||||
"Last-Translator: Tobias Bannert <tobannert@gmail.com>\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: 2016-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"Language: de\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\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-02-02 05:11+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\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-02-02 05:11+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\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-02-02 05:11+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\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-04-29 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 17995)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"Language: he\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -11,8 +11,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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"Language: hi\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -17,8 +17,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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"Language: hr\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -24,8 +24,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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"Language: hu\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-03-30 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 17967)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"Language: id\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"Language: it\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"Language: ja\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"Language: ka\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"Language: km\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"Language: ko\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"Language: lo\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"Language: lt\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"Language: mk\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"Language: mr\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"Language: ms\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -19,8 +19,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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"Language: nb\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"Language: nl\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-03-11 05:18+0000\n"
|
||||
"X-Generator: Launchpad (build 17939)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"Language: oc\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"Language: pa\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-02-02 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 17908)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"Language: pl\n"
|
||||
|
||||
#: ../parser_include.c:113
|
||||
|
@@ -12,13 +12,13 @@ msgstr ""
|
||||
"Report-Msgid-Bugs-To: <apparmor@lists.ubuntu.com>\n"
|
||||
"POT-Creation-Date: 2014-09-13 00:11-0700\n"
|
||||
"PO-Revision-Date: 2016-03-03 08:40+0000\n"
|
||||
"Last-Translator: Ivo Xavier <ivofernandes12@gmail.com>\n"
|
||||
"Last-Translator: Ivo Xavier <ivoxavier.8@gmail.com>\n"
|
||||
"Language-Team: Portuguese <opensuse-pt@opensuse.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-03-04 04:35+0000\n"
|
||||
"X-Generator: Launchpad (build 17936)\n"
|
||||
"X-Launchpad-Export-Date: 2016-06-01 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 18053)\n"
|
||||
"Language: pt\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