summaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/Kconfig35
-rw-r--r--security/apparmor/Kconfig31
-rw-r--r--security/apparmor/Makefile2
-rw-r--r--security/apparmor/apparmorfs.c681
-rw-r--r--security/apparmor/audit.c98
-rw-r--r--security/apparmor/capability.c26
-rw-r--r--security/apparmor/context.c107
-rw-r--r--security/apparmor/crypto.c39
-rw-r--r--security/apparmor/domain.c139
-rw-r--r--security/apparmor/file.c80
-rw-r--r--security/apparmor/include/apparmor.h82
-rw-r--r--security/apparmor/include/apparmorfs.h21
-rw-r--r--security/apparmor/include/audit.h152
-rw-r--r--security/apparmor/include/context.h84
-rw-r--r--security/apparmor/include/crypto.h5
-rw-r--r--security/apparmor/include/domain.h4
-rw-r--r--security/apparmor/include/file.h9
-rw-r--r--security/apparmor/include/lib.h194
-rw-r--r--security/apparmor/include/match.h26
-rw-r--r--security/apparmor/include/path.h53
-rw-r--r--security/apparmor/include/policy.h203
-rw-r--r--security/apparmor/include/policy_ns.h147
-rw-r--r--security/apparmor/include/policy_unpack.h28
-rw-r--r--security/apparmor/include/secid.h (renamed from security/apparmor/include/sid.h)18
-rw-r--r--security/apparmor/ipc.c18
-rw-r--r--security/apparmor/lib.c111
-rw-r--r--security/apparmor/lsm.c327
-rw-r--r--security/apparmor/match.c47
-rw-r--r--security/apparmor/nulldfa.in1
-rw-r--r--security/apparmor/policy.c826
-rw-r--r--security/apparmor/policy_ns.c346
-rw-r--r--security/apparmor/policy_unpack.c257
-rw-r--r--security/apparmor/procattr.c38
-rw-r--r--security/apparmor/resource.c19
-rw-r--r--security/apparmor/secid.c55
-rw-r--r--security/apparmor/sid.c55
-rw-r--r--security/commoncap.c8
-rw-r--r--security/inode.c26
-rw-r--r--security/integrity/digsig.c2
-rw-r--r--security/integrity/evm/evm_crypto.c12
-rw-r--r--security/integrity/evm/evm_main.c6
-rw-r--r--security/integrity/ima/Kconfig12
-rw-r--r--security/integrity/ima/Makefile1
-rw-r--r--security/integrity/ima/ima.h33
-rw-r--r--security/integrity/ima/ima_api.c23
-rw-r--r--security/integrity/ima/ima_appraise.c13
-rw-r--r--security/integrity/ima/ima_crypto.c6
-rw-r--r--security/integrity/ima/ima_fs.c32
-rw-r--r--security/integrity/ima/ima_init.c5
-rw-r--r--security/integrity/ima/ima_kexec.c168
-rw-r--r--security/integrity/ima/ima_main.c15
-rw-r--r--security/integrity/ima/ima_queue.c77
-rw-r--r--security/integrity/ima/ima_template.c297
-rw-r--r--security/integrity/ima/ima_template_lib.c7
-rw-r--r--security/keys/dh.c2
-rw-r--r--security/keys/encrypted-keys/encrypted.c8
-rw-r--r--security/keys/gc.c2
-rw-r--r--security/keys/internal.h1
-rw-r--r--security/keys/keyctl.c26
-rw-r--r--security/keys/persistent.c2
-rw-r--r--security/keys/process_keys.c47
-rw-r--r--security/keys/request_key.c7
-rw-r--r--security/keys/request_key_auth.c2
-rw-r--r--security/keys/trusted.c4
-rw-r--r--security/keys/user_defined.c8
-rw-r--r--security/loadpin/loadpin.c2
-rw-r--r--security/security.c48
-rw-r--r--security/selinux/hooks.c528
-rw-r--r--security/selinux/include/classmap.h70
-rw-r--r--security/selinux/include/objsec.h15
-rw-r--r--security/selinux/include/security.h5
-rw-r--r--security/selinux/selinuxfs.c112
-rw-r--r--security/selinux/ss/ebitmap.c2
-rw-r--r--security/selinux/ss/policydb.c2
-rw-r--r--security/selinux/ss/services.c7
-rw-r--r--security/smack/smack.h4
-rw-r--r--security/smack/smack_access.c7
-rw-r--r--security/smack/smack_lsm.c268
-rw-r--r--security/smack/smackfs.c8
-rw-r--r--security/tomoyo/domain.c4
-rw-r--r--security/tomoyo/group.c2
-rw-r--r--security/tomoyo/tomoyo.c2
-rw-r--r--security/tomoyo/util.c2
-rw-r--r--security/yama/yama_lsm.c18
84 files changed, 4239 insertions, 2083 deletions
diff --git a/security/Kconfig b/security/Kconfig
index 118f454..d900f47 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -158,6 +158,41 @@ config HARDENED_USERCOPY_PAGESPAN
been removed. This config is intended to be used only while
trying to find such users.
+config STATIC_USERMODEHELPER
+ bool "Force all usermode helper calls through a single binary"
+ help
+ By default, the kernel can call many different userspace
+ binary programs through the "usermode helper" kernel
+ interface. Some of these binaries are statically defined
+ either in the kernel code itself, or as a kernel configuration
+ option. However, some of these are dynamically created at
+ runtime, or can be modified after the kernel has started up.
+ To provide an additional layer of security, route all of these
+ calls through a single executable that can not have its name
+ changed.
+
+ Note, it is up to this single binary to then call the relevant
+ "real" usermode helper binary, based on the first argument
+ passed to it. If desired, this program can filter and pick
+ and choose what real programs are called.
+
+ If you wish for all usermode helper programs are to be
+ disabled, choose this option and then set
+ STATIC_USERMODEHELPER_PATH to an empty string.
+
+config STATIC_USERMODEHELPER_PATH
+ string "Path to the static usermode helper binary"
+ depends on STATIC_USERMODEHELPER
+ default "/sbin/usermode-helper"
+ help
+ The binary called by the kernel when any usermode helper
+ program is wish to be run. The "real" application's name will
+ be in the first argument passed to this program on the command
+ line.
+
+ If you wish for all usermode helper programs to be disabled,
+ specify an empty string here (i.e. "").
+
source security/selinux/Kconfig
source security/smack/Kconfig
source security/tomoyo/Kconfig
diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig
index be5e941..b6b68a7 100644
--- a/security/apparmor/Kconfig
+++ b/security/apparmor/Kconfig
@@ -36,7 +36,6 @@ config SECURITY_APPARMOR_HASH
select CRYPTO
select CRYPTO_SHA1
default y
-
help
This option selects whether introspection of loaded policy
is available to userspace via the apparmor filesystem.
@@ -45,7 +44,6 @@ config SECURITY_APPARMOR_HASH_DEFAULT
bool "Enable policy hash introspection by default"
depends on SECURITY_APPARMOR_HASH
default y
-
help
This option selects whether sha1 hashing of loaded policy
is enabled by default. The generation of sha1 hashes for
@@ -54,3 +52,32 @@ config SECURITY_APPARMOR_HASH_DEFAULT
however it can slow down policy load on some devices. In
these cases policy hashing can be disabled by default and
enabled only if needed.
+
+config SECURITY_APPARMOR_DEBUG
+ bool "Build AppArmor with debug code"
+ depends on SECURITY_APPARMOR
+ default n
+ help
+ Build apparmor with debugging logic in apparmor. Not all
+ debugging logic will necessarily be enabled. A submenu will
+ provide fine grained control of the debug options that are
+ available.
+
+config SECURITY_APPARMOR_DEBUG_ASSERTS
+ bool "Build AppArmor with debugging asserts"
+ depends on SECURITY_APPARMOR_DEBUG
+ default y
+ help
+ Enable code assertions made with AA_BUG. These are primarily
+ function entry preconditions but also exist at other key
+ points. If the assert is triggered it will trigger a WARN
+ message.
+
+config SECURITY_APPARMOR_DEBUG_MESSAGES
+ bool "Debug messages enabled by default"
+ depends on SECURITY_APPARMOR_DEBUG
+ default n
+ help
+ Set the default value of the apparmor.debug kernel parameter.
+ When enabled, various debug messages will be logged to
+ the kernel message buffer.
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
index d693df8..ad369a7 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
+ resource.o secid.o file.o policy_ns.o
apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o
clean-files := capability_names.h rlim_names.h
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 5923d56..41073f7 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -18,9 +18,12 @@
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
+#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/capability.h>
#include <linux/rcupdate.h>
+#include <uapi/linux/major.h>
+#include <linux/fs.h>
#include "include/apparmor.h"
#include "include/apparmorfs.h"
@@ -28,7 +31,9 @@
#include "include/context.h"
#include "include/crypto.h"
#include "include/policy.h"
+#include "include/policy_ns.h"
#include "include/resource.h"
+#include "include/policy_unpack.h"
/**
* aa_mangle_name - mangle a profile name to std profile layout form
@@ -37,7 +42,7 @@
*
* Returns: length of mangled name
*/
-static int mangle_name(char *name, char *target)
+static int mangle_name(const char *name, char *target)
{
char *t = target;
@@ -71,7 +76,6 @@ static int mangle_name(char *name, char *target)
/**
* aa_simple_write_to_buffer - common routine for getting policy from user
- * @op: operation doing the user buffer copy
* @userbuf: user buffer to copy data from (NOT NULL)
* @alloc_size: size of user buffer (REQUIRES: @alloc_size >= @copy_size)
* @copy_size: size of data to copy from user buffer
@@ -80,31 +84,29 @@ static int mangle_name(char *name, char *target)
* Returns: kernel buffer containing copy of user buffer data or an
* ERR_PTR on failure.
*/
-static char *aa_simple_write_to_buffer(int op, const char __user *userbuf,
- size_t alloc_size, size_t copy_size,
- loff_t *pos)
+static struct aa_loaddata *aa_simple_write_to_buffer(const char __user *userbuf,
+ size_t alloc_size,
+ size_t copy_size,
+ loff_t *pos)
{
- char *data;
+ struct aa_loaddata *data;
- BUG_ON(copy_size > alloc_size);
+ AA_BUG(copy_size > alloc_size);
if (*pos != 0)
/* only writes from pos 0, that is complete writes */
return ERR_PTR(-ESPIPE);
- /*
- * Don't allow profile load/replace/remove from profiles that don't
- * have CAP_MAC_ADMIN
- */
- if (!aa_may_manage_policy(op))
- return ERR_PTR(-EACCES);
-
/* freed by caller to simple_write_to_buffer */
- data = kvmalloc(alloc_size);
+ data = kvmalloc(sizeof(*data) + alloc_size);
if (data == NULL)
return ERR_PTR(-ENOMEM);
+ kref_init(&data->count);
+ data->size = copy_size;
+ data->hash = NULL;
+ data->abi = 0;
- if (copy_from_user(data, userbuf, copy_size)) {
+ if (copy_from_user(data->data, userbuf, copy_size)) {
kvfree(data);
return ERR_PTR(-EFAULT);
}
@@ -112,25 +114,43 @@ static char *aa_simple_write_to_buffer(int op, const char __user *userbuf,
return data;
}
-
-/* .load file hook fn to load policy */
-static ssize_t profile_load(struct file *f, const char __user *buf, size_t size,
- loff_t *pos)
+static ssize_t policy_update(int binop, const char __user *buf, size_t size,
+ loff_t *pos, struct aa_ns *ns)
{
- char *data;
ssize_t error;
+ struct aa_loaddata *data;
+ struct aa_profile *profile = aa_current_profile();
+ const char *op = binop == PROF_ADD ? OP_PROF_LOAD : OP_PROF_REPL;
+ /* high level check about policy management - fine grained in
+ * below after unpack
+ */
+ error = aa_may_manage_policy(profile, ns, op);
+ if (error)
+ return error;
- data = aa_simple_write_to_buffer(OP_PROF_LOAD, buf, size, size, pos);
-
+ data = aa_simple_write_to_buffer(buf, size, size, pos);
error = PTR_ERR(data);
if (!IS_ERR(data)) {
- error = aa_replace_profiles(data, size, PROF_ADD);
- kvfree(data);
+ error = aa_replace_profiles(ns ? ns : profile->ns, profile,
+ binop, data);
+ aa_put_loaddata(data);
}
return error;
}
+/* .load file hook fn to load policy */
+static ssize_t profile_load(struct file *f, const char __user *buf, size_t size,
+ loff_t *pos)
+{
+ struct aa_ns *ns = aa_get_ns(f->f_inode->i_private);
+ int error = policy_update(PROF_ADD, buf, size, pos, ns);
+
+ aa_put_ns(ns);
+
+ return error;
+}
+
static const struct file_operations aa_fs_profile_load = {
.write = profile_load,
.llseek = default_llseek,
@@ -140,15 +160,10 @@ static const struct file_operations aa_fs_profile_load = {
static ssize_t profile_replace(struct file *f, const char __user *buf,
size_t size, loff_t *pos)
{
- char *data;
- ssize_t error;
+ struct aa_ns *ns = aa_get_ns(f->f_inode->i_private);
+ int error = policy_update(PROF_REPLACE, buf, size, pos, ns);
- data = aa_simple_write_to_buffer(OP_PROF_REPL, buf, size, size, pos);
- error = PTR_ERR(data);
- if (!IS_ERR(data)) {
- error = aa_replace_profiles(data, size, PROF_REPLACE);
- kvfree(data);
- }
+ aa_put_ns(ns);
return error;
}
@@ -162,22 +177,34 @@ static const struct file_operations aa_fs_profile_replace = {
static ssize_t profile_remove(struct file *f, const char __user *buf,
size_t size, loff_t *pos)
{
- char *data;
+ struct aa_loaddata *data;
+ struct aa_profile *profile;
ssize_t error;
+ struct aa_ns *ns = aa_get_ns(f->f_inode->i_private);
+
+ profile = aa_current_profile();
+ /* high level check about policy management - fine grained in
+ * below after unpack
+ */
+ error = aa_may_manage_policy(profile, ns, OP_PROF_RM);
+ if (error)
+ goto out;
/*
* aa_remove_profile needs a null terminated string so 1 extra
* byte is allocated and the copied data is null terminated.
*/
- data = aa_simple_write_to_buffer(OP_PROF_RM, buf, size + 1, size, pos);
+ data = aa_simple_write_to_buffer(buf, size + 1, size, pos);
error = PTR_ERR(data);
if (!IS_ERR(data)) {
- data[size] = 0;
- error = aa_remove_profiles(data, size);
- kvfree(data);
+ data->data[size] = 0;
+ error = aa_remove_profiles(ns ? ns : profile->ns, profile,
+ data->data, size);
+ aa_put_loaddata(data);
}
-
+ out:
+ aa_put_ns(ns);
return error;
}
@@ -186,6 +213,144 @@ static const struct file_operations aa_fs_profile_remove = {
.llseek = default_llseek,
};
+/**
+ * query_data - queries a policy and writes its data to buf
+ * @buf: the resulting data is stored here (NOT NULL)
+ * @buf_len: size of buf
+ * @query: query string used to retrieve data
+ * @query_len: size of query including second NUL byte
+ *
+ * The buffers pointed to by buf and query may overlap. The query buffer is
+ * parsed before buf is written to.
+ *
+ * The query should look like "<LABEL>\0<KEY>\0", where <LABEL> is the name of
+ * the security confinement context and <KEY> is the name of the data to
+ * retrieve. <LABEL> and <KEY> must not be NUL-terminated.
+ *
+ * Don't expect the contents of buf to be preserved on failure.
+ *
+ * Returns: number of characters written to buf or -errno on failure
+ */
+static ssize_t query_data(char *buf, size_t buf_len,
+ char *query, size_t query_len)
+{
+ char *out;
+ const char *key;
+ struct aa_profile *profile;
+ struct aa_data *data;
+ u32 bytes, blocks;
+ __le32 outle32;
+
+ if (!query_len)
+ return -EINVAL; /* need a query */
+
+ key = query + strnlen(query, query_len) + 1;
+ if (key + 1 >= query + query_len)
+ return -EINVAL; /* not enough space for a non-empty key */
+ if (key + strnlen(key, query + query_len - key) >= query + query_len)
+ return -EINVAL; /* must end with NUL */
+
+ if (buf_len < sizeof(bytes) + sizeof(blocks))
+ return -EINVAL; /* not enough space */
+
+ profile = aa_current_profile();
+
+ /* We are going to leave space for two numbers. The first is the total
+ * number of bytes we are writing after the first number. This is so
+ * users can read the full output without reallocation.
+ *
+ * The second number is the number of data blocks we're writing. An
+ * application might be confined by multiple policies having data in
+ * the same key.
+ */
+ memset(buf, 0, sizeof(bytes) + sizeof(blocks));
+ out = buf + sizeof(bytes) + sizeof(blocks);
+
+ blocks = 0;
+ if (profile->data) {
+ data = rhashtable_lookup_fast(profile->data, &key,
+ profile->data->p);
+
+ if (data) {
+ if (out + sizeof(outle32) + data->size > buf + buf_len)
+ return -EINVAL; /* not enough space */
+ outle32 = __cpu_to_le32(data->size);
+ memcpy(out, &outle32, sizeof(outle32));
+ out += sizeof(outle32);
+ memcpy(out, data->data, data->size);
+ out += data->size;
+ blocks++;
+ }
+ }
+
+ outle32 = __cpu_to_le32(out - buf - sizeof(bytes));
+ memcpy(buf, &outle32, sizeof(outle32));
+ outle32 = __cpu_to_le32(blocks);
+ memcpy(buf + sizeof(bytes), &outle32, sizeof(outle32));
+
+ return out - buf;
+}
+
+#define QUERY_CMD_DATA "data\0"
+#define QUERY_CMD_DATA_LEN 5
+
+/**
+ * aa_write_access - generic permissions and data query
+ * @file: pointer to open apparmorfs/access file
+ * @ubuf: user buffer containing the complete query string (NOT NULL)
+ * @count: size of ubuf
+ * @ppos: position in the file (MUST BE ZERO)
+ *
+ * Allows for one permissions or data query per open(), write(), and read()
+ * sequence. The only queries currently supported are label-based queries for
+ * permissions or data.
+ *
+ * For permissions queries, ubuf must begin with "label\0", followed by the
+ * profile query specific format described in the query_label() function
+ * documentation.
+ *
+ * For data queries, ubuf must have the form "data\0<LABEL>\0<KEY>\0", where
+ * <LABEL> is the name of the security confinement context and <KEY> is the
+ * name of the data to retrieve.
+ *
+ * Returns: number of bytes written or -errno on failure
+ */
+static ssize_t aa_write_access(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ char *buf;
+ ssize_t len;
+
+ if (*ppos)
+ return -ESPIPE;
+
+ buf = simple_transaction_get(file, ubuf, count);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ if (count > QUERY_CMD_DATA_LEN &&
+ !memcmp(buf, QUERY_CMD_DATA, QUERY_CMD_DATA_LEN)) {
+ len = query_data(buf, SIMPLE_TRANSACTION_LIMIT,
+ buf + QUERY_CMD_DATA_LEN,
+ count - QUERY_CMD_DATA_LEN);
+ } else
+ len = -EINVAL;
+
+ if (len < 0)
+ return len;
+
+ simple_transaction_set(file, len);
+
+ return count;
+}
+
+static const struct file_operations aa_fs_access = {
+ .write = aa_write_access,
+ .read = simple_transaction_read,
+ .release = simple_transaction_release,
+ .llseek = generic_file_llseek,
+};
+
static int aa_fs_seq_show(struct seq_file *seq, void *v)
{
struct aa_fs_entry *fs_file = seq->private;
@@ -227,12 +392,12 @@ const struct file_operations aa_fs_seq_file_ops = {
static int aa_fs_seq_profile_open(struct inode *inode, struct file *file,
int (*show)(struct seq_file *, void *))
{
- struct aa_replacedby *r = aa_get_replacedby(inode->i_private);
- int error = single_open(file, show, r);
+ struct aa_proxy *proxy = aa_get_proxy(inode->i_private);
+ int error = single_open(file, show, proxy);
if (error) {
file->private_data = NULL;
- aa_put_replacedby(r);
+ aa_put_proxy(proxy);
}
return error;
@@ -242,14 +407,14 @@ static int aa_fs_seq_profile_release(struct inode *inode, struct file *file)
{
struct seq_file *seq = (struct seq_file *) file->private_data;
if (seq)
- aa_put_replacedby(seq->private);
+ aa_put_proxy(seq->private);
return single_release(inode, file);
}
static int aa_fs_seq_profname_show(struct seq_file *seq, void *v)
{
- struct aa_replacedby *r = seq->private;
- struct aa_profile *profile = aa_get_profile_rcu(&r->profile);
+ struct aa_proxy *proxy = seq->private;
+ struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
seq_printf(seq, "%s\n", profile->base.name);
aa_put_profile(profile);
@@ -271,8 +436,8 @@ static const struct file_operations aa_fs_profname_fops = {
static int aa_fs_seq_profmode_show(struct seq_file *seq, void *v)
{
- struct aa_replacedby *r = seq->private;
- struct aa_profile *profile = aa_get_profile_rcu(&r->profile);
+ struct aa_proxy *proxy = seq->private;
+ struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
seq_printf(seq, "%s\n", aa_profile_mode_names[profile->mode]);
aa_put_profile(profile);
@@ -294,8 +459,8 @@ static const struct file_operations aa_fs_profmode_fops = {
static int aa_fs_seq_profattach_show(struct seq_file *seq, void *v)
{
- struct aa_replacedby *r = seq->private;
- struct aa_profile *profile = aa_get_profile_rcu(&r->profile);
+ struct aa_proxy *proxy = seq->private;
+ struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
if (profile->attach)
seq_printf(seq, "%s\n", profile->attach);
else if (profile->xmatch)
@@ -322,8 +487,8 @@ static const struct file_operations aa_fs_profattach_fops = {
static int aa_fs_seq_hash_show(struct seq_file *seq, void *v)
{
- struct aa_replacedby *r = seq->private;
- struct aa_profile *profile = aa_get_profile_rcu(&r->profile);
+ struct aa_proxy *proxy = seq->private;
+ struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
unsigned int i, size = aa_hash_size();
if (profile->hash) {
@@ -349,6 +514,145 @@ static const struct file_operations aa_fs_seq_hash_fops = {
.release = single_release,
};
+
+static int aa_fs_seq_show_ns_level(struct seq_file *seq, void *v)
+{
+ struct aa_ns *ns = aa_current_profile()->ns;
+
+ seq_printf(seq, "%d\n", ns->level);
+
+ return 0;
+}
+
+static int aa_fs_seq_open_ns_level(struct inode *inode, struct file *file)
+{
+ return single_open(file, aa_fs_seq_show_ns_level, inode->i_private);
+}
+
+static const struct file_operations aa_fs_ns_level = {
+ .owner = THIS_MODULE,
+ .open = aa_fs_seq_open_ns_level,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int aa_fs_seq_show_ns_name(struct seq_file *seq, void *v)
+{
+ struct aa_ns *ns = aa_current_profile()->ns;
+
+ seq_printf(seq, "%s\n", ns->base.name);
+
+ return 0;
+}
+
+static int aa_fs_seq_open_ns_name(struct inode *inode, struct file *file)
+{
+ return single_open(file, aa_fs_seq_show_ns_name, inode->i_private);
+}
+
+static const struct file_operations aa_fs_ns_name = {
+ .owner = THIS_MODULE,
+ .open = aa_fs_seq_open_ns_name,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int rawdata_release(struct inode *inode, struct file *file)
+{
+ /* TODO: switch to loaddata when profile switched to symlink */
+ aa_put_loaddata(file->private_data);
+
+ return 0;
+}
+
+static int aa_fs_seq_raw_abi_show(struct seq_file *seq, void *v)
+{
+ struct aa_proxy *proxy = seq->private;
+ struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
+
+ if (profile->rawdata->abi) {
+ seq_printf(seq, "v%d", profile->rawdata->abi);
+ seq_puts(seq, "\n");
+ }
+ aa_put_profile(profile);
+
+ return 0;
+}
+
+static int aa_fs_seq_raw_abi_open(struct inode *inode, struct file *file)
+{
+ return aa_fs_seq_profile_open(inode, file, aa_fs_seq_raw_abi_show);
+}
+
+static const struct file_operations aa_fs_seq_raw_abi_fops = {
+ .owner = THIS_MODULE,
+ .open = aa_fs_seq_raw_abi_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = aa_fs_seq_profile_release,
+};
+
+static int aa_fs_seq_raw_hash_show(struct seq_file *seq, void *v)
+{
+ struct aa_proxy *proxy = seq->private;
+ struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
+ unsigned int i, size = aa_hash_size();
+
+ if (profile->rawdata->hash) {
+ for (i = 0; i < size; i++)
+ seq_printf(seq, "%.2x", profile->rawdata->hash[i]);
+ seq_puts(seq, "\n");
+ }
+ aa_put_profile(profile);
+
+ return 0;
+}
+
+static int aa_fs_seq_raw_hash_open(struct inode *inode, struct file *file)
+{
+ return aa_fs_seq_profile_open(inode, file, aa_fs_seq_raw_hash_show);
+}
+
+static const struct file_operations aa_fs_seq_raw_hash_fops = {
+ .owner = THIS_MODULE,
+ .open = aa_fs_seq_raw_hash_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = aa_fs_seq_profile_release,
+};
+
+static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size,
+ loff_t *ppos)
+{
+ struct aa_loaddata *rawdata = file->private_data;
+
+ return simple_read_from_buffer(buf, size, ppos, rawdata->data,
+ rawdata->size);
+}
+
+static int rawdata_open(struct inode *inode, struct file *file)
+{
+ struct aa_proxy *proxy = inode->i_private;
+ struct aa_profile *profile;
+
+ if (!policy_view_capable(NULL))
+ return -EACCES;
+ profile = aa_get_profile_rcu(&proxy->profile);
+ file->private_data = aa_get_loaddata(profile->rawdata);
+ aa_put_profile(profile);
+
+ return 0;
+}
+
+static const struct file_operations aa_fs_rawdata_fops = {
+ .open = rawdata_open,
+ .read = rawdata_read,
+ .llseek = generic_file_llseek,
+ .release = rawdata_release,
+};
+
/** fns to setup dynamic per profile/namespace files **/
void __aa_fs_profile_rmdir(struct aa_profile *profile)
{
@@ -362,13 +666,13 @@ void __aa_fs_profile_rmdir(struct aa_profile *profile)
__aa_fs_profile_rmdir(child);
for (i = AAFS_PROF_SIZEOF - 1; i >= 0; --i) {
- struct aa_replacedby *r;
+ struct aa_proxy *proxy;
if (!profile->dents[i])
continue;
- r = d_inode(profile->dents[i])->i_private;
+ proxy = d_inode(profile->dents[i])->i_private;
securityfs_remove(profile->dents[i]);
- aa_put_replacedby(r);
+ aa_put_proxy(proxy);
profile->dents[i] = NULL;
}
}
@@ -390,12 +694,12 @@ static struct dentry *create_profile_file(struct dentry *dir, const char *name,
struct aa_profile *profile,
const struct file_operations *fops)
{
- struct aa_replacedby *r = aa_get_replacedby(profile->replacedby);
+ struct aa_proxy *proxy = aa_get_proxy(profile->proxy);
struct dentry *dent;
- dent = securityfs_create_file(name, S_IFREG | 0444, dir, r, fops);
+ dent = securityfs_create_file(name, S_IFREG | 0444, dir, proxy, fops);
if (IS_ERR(dent))
- aa_put_replacedby(r);
+ aa_put_proxy(proxy);
return dent;
}
@@ -460,6 +764,29 @@ int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
profile->dents[AAFS_PROF_HASH] = dent;
}
+ if (profile->rawdata) {
+ dent = create_profile_file(dir, "raw_sha1", profile,
+ &aa_fs_seq_raw_hash_fops);
+ if (IS_ERR(dent))
+ goto fail;
+ profile->dents[AAFS_PROF_RAW_HASH] = dent;
+
+ dent = create_profile_file(dir, "raw_abi", profile,
+ &aa_fs_seq_raw_abi_fops);
+ if (IS_ERR(dent))
+ goto fail;
+ profile->dents[AAFS_PROF_RAW_ABI] = dent;
+
+ dent = securityfs_create_file("raw_data", S_IFREG | 0444, dir,
+ profile->proxy,
+ &aa_fs_rawdata_fops);
+ if (IS_ERR(dent))
+ goto fail;
+ profile->dents[AAFS_PROF_RAW_DATA] = dent;
+ d_inode(dent)->i_size = profile->rawdata->size;
+ aa_get_proxy(profile->proxy);
+ }
+
list_for_each_entry(child, &profile->base.profiles, base.list) {
error = __aa_fs_profile_mkdir(child, prof_child_dir(profile));
if (error)
@@ -477,9 +804,9 @@ fail2:
return error;
}
-void __aa_fs_namespace_rmdir(struct aa_namespace *ns)
+void __aa_fs_ns_rmdir(struct aa_ns *ns)
{
- struct aa_namespace *sub;
+ struct aa_ns *sub;
struct aa_profile *child;
int i;
@@ -491,51 +818,116 @@ void __aa_fs_namespace_rmdir(struct aa_namespace *ns)
list_for_each_entry(sub, &ns->sub_ns, base.list) {
mutex_lock(&sub->lock);
- __aa_fs_namespace_rmdir(sub);
+ __aa_fs_ns_rmdir(sub);
mutex_unlock(&sub->lock);
}
+ if (ns_subns_dir(ns)) {
+ sub = d_inode(ns_subns_dir(ns))->i_private;
+ aa_put_ns(sub);
+ }
+ if (ns_subload(ns)) {
+ sub = d_inode(ns_subload(ns))->i_private;
+ aa_put_ns(sub);
+ }
+ if (ns_subreplace(ns)) {
+ sub = d_inode(ns_subreplace(ns))->i_private;
+ aa_put_ns(sub);
+ }
+ if (ns_subremove(ns)) {
+ sub = d_inode(ns_subremove(ns))->i_private;
+ aa_put_ns(sub);
+ }
+
for (i = AAFS_NS_SIZEOF - 1; i >= 0; --i) {
securityfs_remove(ns->dents[i]);
ns->dents[i] = NULL;
}
}
-int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent,
- const char *name)
+/* assumes cleanup in caller */
+static int __aa_fs_ns_mkdir_entries(struct aa_ns *ns, struct dentry *dir)
+{
+ struct dentry *dent;
+
+ AA_BUG(!ns);
+ AA_BUG(!dir);
+
+ dent = securityfs_create_dir("profiles", dir);
+ if (IS_ERR(dent))
+ return PTR_ERR(dent);
+ ns_subprofs_dir(ns) = dent;
+
+ dent = securityfs_create_dir("raw_data", dir);
+ if (IS_ERR(dent))
+ return PTR_ERR(dent);
+ ns_subdata_dir(ns) = dent;
+
+ dent = securityfs_create_file(".load", 0640, dir, ns,
+ &aa_fs_profile_load);
+ if (IS_ERR(dent))
+ return PTR_ERR(dent);
+ aa_get_ns(ns);
+ ns_subload(ns) = dent;
+
+ dent = securityfs_create_file(".replace", 0640, dir, ns,
+ &aa_fs_profile_replace);
+ if (IS_ERR(dent))
+ return PTR_ERR(dent);
+ aa_get_ns(ns);
+ ns_subreplace(ns) = dent;
+
+ dent = securityfs_create_file(".remove", 0640, dir, ns,
+ &aa_fs_profile_remove);
+ if (IS_ERR(dent))
+ return PTR_ERR(dent);
+ aa_get_ns(ns);
+ ns_subremove(ns) = dent;
+
+ dent = securityfs_create_dir("namespaces", dir);
+ if (IS_ERR(dent))
+ return PTR_ERR(dent);
+ aa_get_ns(ns);
+ ns_subns_dir(ns) = dent;
+
+ return 0;
+}
+
+int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name)
{
- struct aa_namespace *sub;
+ struct aa_ns *sub;
struct aa_profile *child;
struct dentry *dent, *dir;
int error;
+ AA_BUG(!ns);
+ AA_BUG(!parent);
+ AA_BUG(!mutex_is_locked(&ns->lock));
+
if (!name)
name = ns->base.name;
+ /* create ns dir if it doesn't already exist */
dent = securityfs_create_dir(name, parent);
if (IS_ERR(dent))
goto fail;
- ns_dir(ns) = dir = dent;
- dent = securityfs_create_dir("profiles", dir);
- if (IS_ERR(dent))
- goto fail;
- ns_subprofs_dir(ns) = dent;
-
- dent = securityfs_create_dir("namespaces", dir);
- if (IS_ERR(dent))
- goto fail;
- ns_subns_dir(ns) = dent;
+ ns_dir(ns) = dir = dent;
+ error = __aa_fs_ns_mkdir_entries(ns, dir);
+ if (error)
+ goto fail2;
+ /* profiles */
list_for_each_entry(child, &ns->base.profiles, base.list) {
error = __aa_fs_profile_mkdir(child, ns_subprofs_dir(ns));
if (error)
goto fail2;
}
+ /* subnamespaces */
list_for_each_entry(sub, &ns->sub_ns, base.list) {
mutex_lock(&sub->lock);
- error = __aa_fs_namespace_mkdir(sub, ns_subns_dir(ns), NULL);
+ error = __aa_fs_ns_mkdir(sub, ns_subns_dir(ns), NULL);
mutex_unlock(&sub->lock);
if (error)
goto fail2;
@@ -547,7 +939,7 @@ fail:
error = PTR_ERR(dent);
fail2:
- __aa_fs_namespace_rmdir(ns);
+ __aa_fs_ns_rmdir(ns);
return error;
}
@@ -556,7 +948,7 @@ fail2:
#define list_entry_is_head(pos, head, member) (&pos->member == (head))
/**
- * __next_namespace - find the next namespace to list
+ * __next_ns - find the next namespace to list
* @root: root namespace to stop search at (NOT NULL)
* @ns: current ns position (NOT NULL)
*
@@ -567,10 +959,9 @@ fail2:
* Requires: ns->parent->lock to be held
* NOTE: will not unlock root->lock
*/
-static struct aa_namespace *__next_namespace(struct aa_namespace *root,
- struct aa_namespace *ns)
+static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns)
{
- struct aa_namespace *parent, *next;
+ struct aa_ns *parent, *next;
/* is next namespace a child */
if (!list_empty(&ns->sub_ns)) {
@@ -603,10 +994,10 @@ static struct aa_namespace *__next_namespace(struct aa_namespace *root,
* Returns: unrefcounted profile or NULL if no profile
* Requires: profile->ns.lock to be held
*/
-static struct aa_profile *__first_profile(struct aa_namespace *root,
- struct aa_namespace *ns)
+static struct aa_profile *__first_profile(struct aa_ns *root,
+ struct aa_ns *ns)
{
- for (; ns; ns = __next_namespace(root, ns)) {
+ for (; ns; ns = __next_ns(root, ns)) {
if (!list_empty(&ns->base.profiles))
return list_first_entry(&ns->base.profiles,
struct aa_profile, base.list);
@@ -626,7 +1017,7 @@ static struct aa_profile *__first_profile(struct aa_namespace *root,
static struct aa_profile *__next_profile(struct aa_profile *p)
{
struct aa_profile *parent;
- struct aa_namespace *ns = p->ns;
+ struct aa_ns *ns = p->ns;
/* is next profile a child */
if (!list_empty(&p->base.profiles))
@@ -660,7 +1051,7 @@ static struct aa_profile *__next_profile(struct aa_profile *p)
*
* Returns: next profile or NULL if there isn't one
*/
-static struct aa_profile *next_profile(struct aa_namespace *root,
+static struct aa_profile *next_profile(struct aa_ns *root,
struct aa_profile *profile)
{
struct aa_profile *next = __next_profile(profile);
@@ -668,7 +1059,7 @@ static struct aa_profile *next_profile(struct aa_namespace *root,
return next;
/* finished all profiles in namespace move to next namespace */
- return __first_profile(root, __next_namespace(root, profile->ns));
+ return __first_profile(root, __next_ns(root, profile->ns));
}
/**
@@ -683,9 +1074,9 @@ static struct aa_profile *next_profile(struct aa_namespace *root,
static void *p_start(struct seq_file *f, loff_t *pos)
{
struct aa_profile *profile = NULL;
- struct aa_namespace *root = aa_current_profile()->ns;
+ struct aa_ns *root = aa_current_profile()->ns;
loff_t l = *pos;
- f->private = aa_get_namespace(root);
+ f->private = aa_get_ns(root);
/* find the first profile */
@@ -712,7 +1103,7 @@ static void *p_start(struct seq_file *f, loff_t *pos)
static void *p_next(struct seq_file *f, void *p, loff_t *pos)
{
struct aa_profile *profile = p;
- struct aa_namespace *ns = f->private;
+ struct aa_ns *ns = f->private;
(*pos)++;
return next_profile(ns, profile);
@@ -728,14 +1119,14 @@ static void *p_next(struct seq_file *f, void *p, loff_t *pos)
static void p_stop(struct seq_file *f, void *p)
{
struct aa_profile *profile = p;
- struct aa_namespace *root = f->private, *ns;
+ struct aa_ns *root = f->private, *ns;
if (profile) {
for (ns = profile->ns; ns && ns != root; ns = ns->parent)
mutex_unlock(&ns->lock);
}
mutex_unlock(&root->lock);
- aa_put_namespace(root);
+ aa_put_ns(root);
}
/**
@@ -748,10 +1139,10 @@ static void p_stop(struct seq_file *f, void *p)
static int seq_show_profile(struct seq_file *f, void *p)
{
struct aa_profile *profile = (struct aa_profile *)p;
- struct aa_namespace *root = f->private;
+ struct aa_ns *root = f->private;
if (profile->ns != root)
- seq_printf(f, ":%s://", aa_ns_name(root, profile->ns));
+ seq_printf(f, ":%s://", aa_ns_name(root, profile->ns, true));
seq_printf(f, "%s (%s)\n", profile->base.hname,
aa_profile_mode_names[profile->mode]);
@@ -767,6 +1158,9 @@ static const struct seq_operations aa_fs_profiles_op = {
static int profiles_open(struct inode *inode, struct file *file)
{
+ if (!policy_view_capable(NULL))
+ return -EACCES;
+
return seq_open(file, &aa_fs_profiles_op);
}
@@ -795,12 +1189,20 @@ static struct aa_fs_entry aa_fs_entry_domain[] = {
AA_FS_FILE_BOOLEAN("change_hatv", 1),
AA_FS_FILE_BOOLEAN("change_onexec", 1),
AA_FS_FILE_BOOLEAN("change_profile", 1),
+ AA_FS_FILE_BOOLEAN("fix_binfmt_elf_mmap", 1),
+ AA_FS_FILE_STRING("version", "1.2"),
+ { }
+};
+
+static struct aa_fs_entry aa_fs_entry_versions[] = {
+ AA_FS_FILE_BOOLEAN("v5", 1),
{ }
};
static struct aa_fs_entry aa_fs_entry_policy[] = {
- AA_FS_FILE_BOOLEAN("set_load", 1),
- {}
+ AA_FS_DIR("versions", aa_fs_entry_versions),
+ AA_FS_FILE_BOOLEAN("set_load", 1),
+ { }
};
static struct aa_fs_entry aa_fs_entry_features[] = {
@@ -814,10 +1216,10 @@ static struct aa_fs_entry aa_fs_entry_features[] = {
};
static struct aa_fs_entry aa_fs_entry_apparmor[] = {
- AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load),
- AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace),
- AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove),
- AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops),
+ AA_FS_FILE_FOPS(".access", 0640, &aa_fs_access),
+ AA_FS_FILE_FOPS(".ns_level", 0666, &aa_fs_ns_level),
+ AA_FS_FILE_FOPS(".ns_name", 0640, &aa_fs_ns_name),
+ AA_FS_FILE_FOPS("profiles", 0440, &aa_fs_profiles_fops),
AA_FS_DIR("features", aa_fs_entry_features),
{ }
};
@@ -926,6 +1328,52 @@ void __init aa_destroy_aafs(void)
aafs_remove_dir(&aa_fs_entry);
}
+
+#define NULL_FILE_NAME ".null"
+struct path aa_null;
+
+static int aa_mk_null_file(struct dentry *parent)
+{
+ struct vfsmount *mount = NULL;
+ struct dentry *dentry;
+ struct inode *inode;
+ int count = 0;
+ int error = simple_pin_fs(parent->d_sb->s_type, &mount, &count);
+
+ if (error)
+ return error;
+
+ inode_lock(d_inode(parent));
+ dentry = lookup_one_len(NULL_FILE_NAME, parent, strlen(NULL_FILE_NAME));
+ if (IS_ERR(dentry)) {
+ error = PTR_ERR(dentry);
+ goto out;
+ }
+ inode = new_inode(parent->d_inode->i_sb);
+ if (!inode) {
+ error = -ENOMEM;
+ goto out1;
+ }
+
+ inode->i_ino = get_next_ino();
+ inode->i_mode = S_IFCHR | S_IRUGO | S_IWUGO;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO,
+ MKDEV(MEM_MAJOR, 3));
+ d_instantiate(dentry, inode);
+ aa_null.dentry = dget(dentry);
+ aa_null.mnt = mntget(mount);
+
+ error = 0;
+
+out1:
+ dput(dentry);
+out:
+ inode_unlock(d_inode(parent));
+ simple_release_fs(&mount, &count);
+ return error;
+}
+
/**
* aa_create_aafs - create the apparmor security filesystem
*
@@ -935,6 +1383,7 @@ void __init aa_destroy_aafs(void)
*/
static int __init aa_create_aafs(void)
{
+ struct dentry *dent;
int error;
if (!apparmor_initialized)
@@ -950,12 +1399,42 @@ static int __init aa_create_aafs(void)
if (error)
goto error;
- error = __aa_fs_namespace_mkdir(root_ns, aa_fs_entry.dentry,
- "policy");
+ dent = securityfs_create_file(".load", 0666, aa_fs_entry.dentry,
+ NULL, &aa_fs_profile_load);
+ if (IS_ERR(dent)) {
+ error = PTR_ERR(dent);
+ goto error;
+ }
+ ns_subload(root_ns) = dent;
+
+ dent = securityfs_create_file(".replace", 0666, aa_fs_entry.dentry,
+ NULL, &aa_fs_profile_replace);
+ if (IS_ERR(dent)) {
+ error = PTR_ERR(dent);
+ goto error;
+ }
+ ns_subreplace(root_ns) = dent;
+
+ dent = securityfs_create_file(".remove", 0666, aa_fs_entry.dentry,
+ NULL, &aa_fs_profile_remove);
+ if (IS_ERR(dent)) {
+ error = PTR_ERR(dent);
+ goto error;
+ }
+ ns_subremove(root_ns) = dent;
+
+ mutex_lock(&root_ns->lock);
+ error = __aa_fs_ns_mkdir(root_ns, aa_fs_entry.dentry, "policy");
+ mutex_unlock(&root_ns->lock);
+
+ if (error)
+ goto error;
+
+ error = aa_mk_null_file(aa_fs_entry.dentry);
if (error)
goto error;
- /* TODO: add support for apparmorfs_null and apparmorfs_mnt */
+ /* TODO: add default profile to apparmorfs */
/* Report that AppArmor fs is enabled */
aa_info_message("AppArmor Filesystem Enabled");
diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c
index 3a7f1da..87f40fa 100644
--- a/security/apparmor/audit.c
+++ b/security/apparmor/audit.c
@@ -18,60 +18,8 @@
#include "include/apparmor.h"
#include "include/audit.h"
#include "include/policy.h"
+#include "include/policy_ns.h"
-const char *const op_table[] = {
- "null",
-
- "sysctl",
- "capable",
-
- "unlink",
- "mkdir",
- "rmdir",
- "mknod",
- "truncate",
- "link",
- "symlink",
- "rename_src",
- "rename_dest",
- "chmod",
- "chown",
- "getattr",
- "open",
-
- "file_perm",
- "file_lock",
- "file_mmap",
- "file_mprotect",
-
- "create",
- "post_create",
- "bind",
- "connect",
- "listen",
- "accept",
- "sendmsg",
- "recvmsg",
- "getsockname",
- "getpeername",
- "getsockopt",
- "setsockopt",
- "socket_shutdown",
-
- "ptrace",
-
- "exec",
- "change_hat",
- "change_profile",
- "change_onexec",
-
- "setprocattr",
- "setrlimit",
-
- "profile_replace",
- "profile_load",
- "profile_remove"
-};
const char *const audit_mode_names[] = {
"normal",
@@ -114,23 +62,23 @@ static void audit_pre(struct audit_buffer *ab, void *ca)
if (aa_g_audit_header) {
audit_log_format(ab, "apparmor=");
- audit_log_string(ab, aa_audit_type[sa->aad->type]);
+ audit_log_string(ab, aa_audit_type[aad(sa)->type]);
}
- if (sa->aad->op) {
+ if (aad(sa)->op) {
audit_log_format(ab, " operation=");
- audit_log_string(ab, op_table[sa->aad->op]);
+ audit_log_string(ab, aad(sa)->op);
}
- if (sa->aad->info) {
+ if (aad(sa)->info) {
audit_log_format(ab, " info=");
- audit_log_string(ab, sa->aad->info);
- if (sa->aad->error)
- audit_log_format(ab, " error=%d", sa->aad->error);
+ audit_log_string(ab, aad(sa)->info);
+ if (aad(sa)->error)
+ audit_log_format(ab, " error=%d", aad(sa)->error);
}
- if (sa->aad->profile) {
- struct aa_profile *profile = sa->aad->profile;
+ if (aad(sa)->profile) {
+ struct aa_profile *profile = aad(sa)->profile;
if (profile->ns != root_ns) {
audit_log_format(ab, " namespace=");
audit_log_untrustedstring(ab, profile->ns->base.hname);
@@ -139,9 +87,9 @@ static void audit_pre(struct audit_buffer *ab, void *ca)
audit_log_untrustedstring(ab, profile->base.hname);
}
- if (sa->aad->name) {
+ if (aad(sa)->name) {
audit_log_format(ab, " name=");
- audit_log_untrustedstring(ab, sa->aad->name);
+ audit_log_untrustedstring(ab, aad(sa)->name);
}
}
@@ -153,7 +101,7 @@ static void audit_pre(struct audit_buffer *ab, void *ca)
void aa_audit_msg(int type, struct common_audit_data *sa,
void (*cb) (struct audit_buffer *, void *))
{
- sa->aad->type = type;
+ aad(sa)->type = type;
common_lsm_audit(sa, audit_pre, cb);
}
@@ -161,7 +109,6 @@ void aa_audit_msg(int type, struct common_audit_data *sa,
* aa_audit - Log a profile based audit event to the audit subsystem
* @type: audit type for the message
* @profile: profile to check against (NOT NULL)
- * @gfp: allocation flags to use
* @sa: audit event (NOT NULL)
* @cb: optional callback fn for type specific fields (MAYBE NULL)
*
@@ -169,14 +116,13 @@ void aa_audit_msg(int type, struct common_audit_data *sa,
*
* Returns: error on failure
*/
-int aa_audit(int type, struct aa_profile *profile, gfp_t gfp,
- struct common_audit_data *sa,
+int aa_audit(int type, struct aa_profile *profile, struct common_audit_data *sa,
void (*cb) (struct audit_buffer *, void *))
{
- BUG_ON(!profile);
+ AA_BUG(!profile);
if (type == AUDIT_APPARMOR_AUTO) {
- if (likely(!sa->aad->error)) {
+ if (likely(!aad(sa)->error)) {
if (AUDIT_MODE(profile) != AUDIT_ALL)
return 0;
type = AUDIT_APPARMOR_AUDIT;
@@ -188,23 +134,23 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp,
if (AUDIT_MODE(profile) == AUDIT_QUIET ||
(type == AUDIT_APPARMOR_DENIED &&
AUDIT_MODE(profile) == AUDIT_QUIET))
- return sa->aad->error;
+ return aad(sa)->error;
if (KILL_MODE(profile) && type == AUDIT_APPARMOR_DENIED)
type = AUDIT_APPARMOR_KILL;
if (!unconfined(profile))
- sa->aad->profile = profile;
+ aad(sa)->profile = profile;
aa_audit_msg(type, sa, cb);
- if (sa->aad->type == AUDIT_APPARMOR_KILL)
+ if (aad(sa)->type == AUDIT_APPARMOR_KILL)
(void)send_sig_info(SIGKILL, NULL,
sa->type == LSM_AUDIT_DATA_TASK && sa->u.tsk ?
sa->u.tsk : current);
- if (sa->aad->type == AUDIT_APPARMOR_ALLOWED)
- return complain_error(sa->aad->error);
+ if (aad(sa)->type == AUDIT_APPARMOR_ALLOWED)
+ return complain_error(aad(sa)->error);
- return sa->aad->error;
+ return aad(sa)->error;
}
diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c
index 1101c6f..ed0a3e6 100644
--- a/security/apparmor/capability.c
+++ b/security/apparmor/capability.c
@@ -15,6 +15,7 @@
#include <linux/capability.h>
#include <linux/errno.h>
#include <linux/gfp.h>
+#include <linux/security.h>
#include "include/apparmor.h"
#include "include/capability.h"
@@ -55,6 +56,7 @@ static void audit_cb(struct audit_buffer *ab, void *va)
* audit_caps - audit a capability
* @profile: profile being tested for confinement (NOT NULL)
* @cap: capability tested
+ @audit: whether an audit record should be generated
* @error: error code returned by test
*
* Do auditing of capability and handle, audit/complain/kill modes switching
@@ -62,17 +64,16 @@ static void audit_cb(struct audit_buffer *ab, void *va)
*
* Returns: 0 or sa->error on success, error code on failure
*/
-static int audit_caps(struct aa_profile *profile, int cap, int error)
+static int audit_caps(struct aa_profile *profile, int cap, int audit,
+ int error)
{
struct audit_cache *ent;
int type = AUDIT_APPARMOR_AUTO;
- struct common_audit_data sa;
- struct apparmor_audit_data aad = {0,};
- sa.type = LSM_AUDIT_DATA_CAP;
- sa.aad = &aad;
+ DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_CAP, OP_CAPABLE);
sa.u.cap = cap;
- sa.aad->op = OP_CAPABLE;
- sa.aad->error = error;
+ aad(&sa)->error = error;
+ if (audit == SECURITY_CAP_NOAUDIT)
+ aad(&sa)->info = "optional: no audit";
if (likely(!error)) {
/* test if auditing is being forced */
@@ -104,7 +105,7 @@ static int audit_caps(struct aa_profile *profile, int cap, int error)
}
put_cpu_var(audit_cache);
- return aa_audit(type, profile, GFP_ATOMIC, &sa, audit_cb);
+ return aa_audit(type, profile, &sa, audit_cb);
}
/**
@@ -133,11 +134,10 @@ int aa_capable(struct aa_profile *profile, int cap, int audit)
{
int error = profile_capable(profile, cap);
- if (!audit) {
- if (COMPLAIN_MODE(profile))
- return complain_error(error);
- return error;
+ if (audit == SECURITY_CAP_NOAUDIT) {
+ if (!COMPLAIN_MODE(profile))
+ return error;
}
- return audit_caps(profile, cap, error);
+ return audit_caps(profile, cap, audit, error);
}
diff --git a/security/apparmor/context.c b/security/apparmor/context.c
index 3064c6c..1fc16b8 100644
--- a/security/apparmor/context.c
+++ b/security/apparmor/context.c
@@ -13,11 +13,11 @@
* License.
*
*
- * AppArmor sets confinement on every task, via the the aa_task_cxt and
- * the aa_task_cxt.profile, both of which are required and are not allowed
- * to be NULL. The aa_task_cxt is not reference counted and is unique
+ * AppArmor sets confinement on every task, via the the aa_task_ctx and
+ * the aa_task_ctx.profile, both of which are required and are not allowed
+ * to be NULL. The aa_task_ctx is not reference counted and is unique
* to each cred (which is reference count). The profile pointed to by
- * the task_cxt is reference counted.
+ * the task_ctx is reference counted.
*
* TODO
* If a task uses change_hat it currently does not return to the old
@@ -30,28 +30,28 @@
#include "include/policy.h"
/**
- * aa_alloc_task_context - allocate a new task_cxt
+ * aa_alloc_task_context - allocate a new task_ctx
* @flags: gfp flags for allocation
*
* Returns: allocated buffer or NULL on failure
*/
-struct aa_task_cxt *aa_alloc_task_context(gfp_t flags)
+struct aa_task_ctx *aa_alloc_task_context(gfp_t flags)
{
- return kzalloc(sizeof(struct aa_task_cxt), flags);
+ return kzalloc(sizeof(struct aa_task_ctx), flags);
}
/**
- * aa_free_task_context - free a task_cxt
- * @cxt: task_cxt to free (MAYBE NULL)
+ * aa_free_task_context - free a task_ctx
+ * @ctx: task_ctx to free (MAYBE NULL)
*/
-void aa_free_task_context(struct aa_task_cxt *cxt)
+void aa_free_task_context(struct aa_task_ctx *ctx)
{
- if (cxt) {
- aa_put_profile(cxt->profile);
- aa_put_profile(cxt->previous);
- aa_put_profile(cxt->onexec);
+ if (ctx) {
+ aa_put_profile(ctx->profile);
+ aa_put_profile(ctx->previous);
+ aa_put_profile(ctx->onexec);
- kzfree(cxt);
+ kzfree(ctx);
}
}
@@ -60,7 +60,7 @@ void aa_free_task_context(struct aa_task_cxt *cxt)
* @new: a blank task context (NOT NULL)
* @old: the task context to copy (NOT NULL)
*/
-void aa_dup_task_context(struct aa_task_cxt *new, const struct aa_task_cxt *old)
+void aa_dup_task_context(struct aa_task_ctx *new, const struct aa_task_ctx *old)
{
*new = *old;
aa_get_profile(new->profile);
@@ -93,31 +93,36 @@ struct aa_profile *aa_get_task_profile(struct task_struct *task)
*/
int aa_replace_current_profile(struct aa_profile *profile)
{
- struct aa_task_cxt *cxt = current_cxt();
+ struct aa_task_ctx *ctx = current_ctx();
struct cred *new;
- BUG_ON(!profile);
+ AA_BUG(!profile);
- if (cxt->profile == profile)
+ if (ctx->profile == profile)
return 0;
+ if (current_cred() != current_real_cred())
+ return -EBUSY;
+
new = prepare_creds();
if (!new)
return -ENOMEM;
- cxt = cred_cxt(new);
- if (unconfined(profile) || (cxt->profile->ns != profile->ns))
+ ctx = cred_ctx(new);
+ if (unconfined(profile) || (ctx->profile->ns != profile->ns))
/* if switching to unconfined or a different profile namespace
* clear out context state
*/
- aa_clear_task_cxt_trans(cxt);
+ aa_clear_task_ctx_trans(ctx);
- /* be careful switching cxt->profile, when racing replacement it
- * is possible that cxt->profile->replacedby->profile is the reference
+ /*
+ * be careful switching ctx->profile, when racing replacement it
+ * is possible that ctx->profile->proxy->profile is the reference
* keeping @profile valid, so make sure to get its reference before
- * dropping the reference on cxt->profile */
+ * dropping the reference on ctx->profile
+ */
aa_get_profile(profile);
- aa_put_profile(cxt->profile);
- cxt->profile = profile;
+ aa_put_profile(ctx->profile);
+ ctx->profile = profile;
commit_creds(new);
return 0;
@@ -131,15 +136,15 @@ int aa_replace_current_profile(struct aa_profile *profile)
*/
int aa_set_current_onexec(struct aa_profile *profile)
{
- struct aa_task_cxt *cxt;
+ struct aa_task_ctx *ctx;
struct cred *new = prepare_creds();
if (!new)
return -ENOMEM;
- cxt = cred_cxt(new);
+ ctx = cred_ctx(new);
aa_get_profile(profile);
- aa_put_profile(cxt->onexec);
- cxt->onexec = profile;
+ aa_put_profile(ctx->onexec);
+ ctx->onexec = profile;
commit_creds(new);
return 0;
@@ -157,28 +162,28 @@ int aa_set_current_onexec(struct aa_profile *profile)
*/
int aa_set_current_hat(struct aa_profile *profile, u64 token)
{
- struct aa_task_cxt *cxt;
+ struct aa_task_ctx *ctx;
struct cred *new = prepare_creds();
if (!new)
return -ENOMEM;
- BUG_ON(!profile);
+ AA_BUG(!profile);
- cxt = cred_cxt(new);
- if (!cxt->previous) {
+ ctx = cred_ctx(new);
+ if (!ctx->previous) {
/* transfer refcount */
- cxt->previous = cxt->profile;
- cxt->token = token;
- } else if (cxt->token == token) {
- aa_put_profile(cxt->profile);
+ ctx->previous = ctx->profile;
+ ctx->token = token;
+ } else if (ctx->token == token) {
+ aa_put_profile(ctx->profile);
} else {
- /* previous_profile && cxt->token != token */
+ /* previous_profile && ctx->token != token */
abort_creds(new);
return -EACCES;
}
- cxt->profile = aa_get_newest_profile(profile);
+ ctx->profile = aa_get_newest_profile(profile);
/* clear exec on switching context */
- aa_put_profile(cxt->onexec);
- cxt->onexec = NULL;
+ aa_put_profile(ctx->onexec);
+ ctx->onexec = NULL;
commit_creds(new);
return 0;
@@ -195,27 +200,27 @@ int aa_set_current_hat(struct aa_profile *profile, u64 token)
*/
int aa_restore_previous_profile(u64 token)
{
- struct aa_task_cxt *cxt;
+ struct aa_task_ctx *ctx;
struct cred *new = prepare_creds();
if (!new)
return -ENOMEM;
- cxt = cred_cxt(new);
- if (cxt->token != token) {
+ ctx = cred_ctx(new);
+ if (ctx->token != token) {
abort_creds(new);
return -EACCES;
}
/* ignore restores when there is no saved profile */
- if (!cxt->previous) {
+ if (!ctx->previous) {
abort_creds(new);
return 0;
}
- aa_put_profile(cxt->profile);
- cxt->profile = aa_get_newest_profile(cxt->previous);
- BUG_ON(!cxt->profile);
+ aa_put_profile(ctx->profile);
+ ctx->profile = aa_get_newest_profile(ctx->previous);
+ AA_BUG(!ctx->profile);
/* clear exec && prev information when restoring to previous context */
- aa_clear_task_cxt_trans(cxt);
+ aa_clear_task_ctx_trans(ctx);
commit_creds(new);
return 0;
diff --git a/security/apparmor/crypto.c b/security/apparmor/crypto.c
index b75dab0..de8dc78 100644
--- a/security/apparmor/crypto.c
+++ b/security/apparmor/crypto.c
@@ -29,6 +29,43 @@ unsigned int aa_hash_size(void)
return apparmor_hash_size;
}
+char *aa_calc_hash(void *data, size_t len)
+{
+ struct {
+ struct shash_desc shash;
+ char ctx[crypto_shash_descsize(apparmor_tfm)];
+ } desc;
+ char *hash = NULL;
+ int error = -ENOMEM;
+
+ if (!apparmor_tfm)
+ return NULL;
+
+ hash = kzalloc(apparmor_hash_size, GFP_KERNEL);
+ if (!hash)
+ goto fail;
+
+ desc.shash.tfm = apparmor_tfm;
+ desc.shash.flags = 0;
+
+ error = crypto_shash_init(&desc.shash);
+ if (error)
+ goto fail;
+ error = crypto_shash_update(&desc.shash, (u8 *) data, len);
+ if (error)
+ goto fail;
+ error = crypto_shash_final(&desc.shash, hash);
+ if (error)
+ goto fail;
+
+ return hash;
+
+fail:
+ kfree(hash);
+
+ return ERR_PTR(error);
+}
+
int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
size_t len)
{
@@ -37,7 +74,7 @@ int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
char ctx[crypto_shash_descsize(apparmor_tfm)];
} desc;
int error = -ENOMEM;
- u32 le32_version = cpu_to_le32(version);
+ __le32 le32_version = cpu_to_le32(version);
if (!aa_g_hash_policy)
return 0;
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index a4d90aa..001e133 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -29,6 +29,7 @@
#include "include/match.h"
#include "include/path.h"
#include "include/policy.h"
+#include "include/policy_ns.h"
/**
* aa_free_domain_entries - free entries in a domain table
@@ -93,7 +94,7 @@ out:
* Returns: permission set
*/
static struct file_perms change_profile_perms(struct aa_profile *profile,
- struct aa_namespace *ns,
+ struct aa_ns *ns,
const char *name, u32 request,
unsigned int start)
{
@@ -170,7 +171,7 @@ static struct aa_profile *__attach_match(const char *name,
*
* Returns: profile or NULL if no match found
*/
-static struct aa_profile *find_attach(struct aa_namespace *ns,
+static struct aa_profile *find_attach(struct aa_ns *ns,
struct list_head *list, const char *name)
{
struct aa_profile *profile;
@@ -239,7 +240,7 @@ static const char *next_name(int xtype, const char *name)
static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex)
{
struct aa_profile *new_profile = NULL;
- struct aa_namespace *ns = profile->ns;
+ struct aa_ns *ns = profile->ns;
u32 xtype = xindex & AA_X_TYPE_MASK;
int index = xindex & AA_X_INDEX_MASK;
const char *name;
@@ -247,7 +248,7 @@ static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex)
/* index is guaranteed to be in range, validated at load time */
for (name = profile->file.trans.table[index]; !new_profile && name;
name = next_name(xtype, name)) {
- struct aa_namespace *new_ns;
+ struct aa_ns *new_ns;
const char *xname = NULL;
new_ns = NULL;
@@ -267,7 +268,7 @@ static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex)
;
}
/* released below */
- new_ns = aa_find_namespace(ns, ns_name);
+ new_ns = aa_find_ns(ns, ns_name);
if (!new_ns)
continue;
} else if (*name == '@') {
@@ -280,7 +281,7 @@ static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex)
/* released by caller */
new_profile = aa_lookup_profile(new_ns ? new_ns : ns, xname);
- aa_put_namespace(new_ns);
+ aa_put_ns(new_ns);
}
/* released by caller */
@@ -301,7 +302,7 @@ static struct aa_profile *x_to_profile(struct aa_profile *profile,
const char *name, u32 xindex)
{
struct aa_profile *new_profile = NULL;
- struct aa_namespace *ns = profile->ns;
+ struct aa_ns *ns = profile->ns;
u32 xtype = xindex & AA_X_TYPE_MASK;
switch (xtype) {
@@ -336,9 +337,9 @@ static struct aa_profile *x_to_profile(struct aa_profile *profile,
*/
int apparmor_bprm_set_creds(struct linux_binprm *bprm)
{
- struct aa_task_cxt *cxt;
+ struct aa_task_ctx *ctx;
struct aa_profile *profile, *new_profile = NULL;
- struct aa_namespace *ns;
+ struct aa_ns *ns;
char *buffer = NULL;
unsigned int state;
struct file_perms perms = {};
@@ -352,10 +353,10 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
if (bprm->cred_prepared)
return 0;
- cxt = cred_cxt(bprm->cred);
- BUG_ON(!cxt);
+ ctx = cred_ctx(bprm->cred);
+ AA_BUG(!ctx);
- profile = aa_get_newest_profile(cxt->profile);
+ profile = aa_get_newest_profile(ctx->profile);
/*
* get the namespace from the replacement profile as replacement
* can change the namespace
@@ -379,9 +380,9 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
*/
if (unconfined(profile)) {
/* unconfined task */
- if (cxt->onexec)
+ if (ctx->onexec)
/* change_profile on exec already been granted */
- new_profile = aa_get_profile(cxt->onexec);
+ new_profile = aa_get_profile(ctx->onexec);
else
new_profile = find_attach(ns, &ns->base.profiles, name);
if (!new_profile)
@@ -396,10 +397,10 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
/* find exec permissions for name */
state = aa_str_perms(profile->file.dfa, state, name, &cond, &perms);
- if (cxt->onexec) {
+ if (ctx->onexec) {
struct file_perms cp;
info = "change_profile onexec";
- new_profile = aa_get_newest_profile(cxt->onexec);
+ new_profile = aa_get_newest_profile(ctx->onexec);
if (!(perms.allow & AA_MAY_ONEXEC))
goto audit;
@@ -408,8 +409,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
* exec\0change_profile
*/
state = aa_dfa_null_transition(profile->file.dfa, state);
- cp = change_profile_perms(profile, cxt->onexec->ns,
- cxt->onexec->base.name,
+ cp = change_profile_perms(profile, ctx->onexec->ns,
+ ctx->onexec->base.name,
AA_MAY_ONEXEC, state);
if (!(cp.allow & AA_MAY_ONEXEC))
@@ -441,7 +442,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
}
} else if (COMPLAIN_MODE(profile)) {
/* no exec permission - are we in learning mode */
- new_profile = aa_new_null_profile(profile, 0);
+ new_profile = aa_new_null_profile(profile, false, name,
+ GFP_ATOMIC);
if (!new_profile) {
error = -ENOMEM;
info = "could not create null profile";
@@ -469,7 +471,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
;
}
- if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
+ if (bprm->unsafe & LSM_UNSAFE_PTRACE) {
error = may_change_ptraced_domain(new_profile);
if (error)
goto audit;
@@ -497,17 +499,16 @@ apply:
bprm->per_clear |= PER_CLEAR_ON_SETID;
x_clear:
- aa_put_profile(cxt->profile);
- /* transfer new profile reference will be released when cxt is freed */
- cxt->profile = new_profile;
+ aa_put_profile(ctx->profile);
+ /* transfer new profile reference will be released when ctx is freed */
+ ctx->profile = new_profile;
new_profile = NULL;
/* clear out all temporary/transitional state from the context */
- aa_clear_task_cxt_trans(cxt);
+ aa_clear_task_ctx_trans(ctx);
audit:
- error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC,
- name,
+ error = aa_audit_file(profile, &perms, OP_EXEC, MAY_EXEC, name,
new_profile ? new_profile->base.hname : NULL,
cond.uid, info, error);
@@ -543,17 +544,17 @@ int apparmor_bprm_secureexec(struct linux_binprm *bprm)
void apparmor_bprm_committing_creds(struct linux_binprm *bprm)
{
struct aa_profile *profile = __aa_current_profile();
- struct aa_task_cxt *new_cxt = cred_cxt(bprm->cred);
+ struct aa_task_ctx *new_ctx = cred_ctx(bprm->cred);
/* bail out if unconfined or not changing profile */
- if ((new_cxt->profile == profile) ||
- (unconfined(new_cxt->profile)))
+ if ((new_ctx->profile == profile) ||
+ (unconfined(new_ctx->profile)))
return;
current->pdeath_signal = 0;
/* reset soft limits and set hard limits for the new profile */
- __aa_transition_rlimits(profile, new_cxt->profile);
+ __aa_transition_rlimits(profile, new_ctx->profile);
}
/**
@@ -602,7 +603,7 @@ static char *new_compound_name(const char *n1, const char *n2)
int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
{
const struct cred *cred;
- struct aa_task_cxt *cxt;
+ struct aa_task_ctx *ctx;
struct aa_profile *profile, *previous_profile, *hat = NULL;
char *name = NULL;
int i;
@@ -620,9 +621,9 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
/* released below */
cred = get_current_cred();
- cxt = cred_cxt(cred);
+ ctx = cred_ctx(cred);
profile = aa_get_newest_profile(aa_cred_profile(cred));
- previous_profile = aa_get_newest_profile(cxt->previous);
+ previous_profile = aa_get_newest_profile(ctx->previous);
if (unconfined(profile)) {
info = "unconfined";
@@ -666,7 +667,8 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
aa_put_profile(root);
target = name;
/* released below */
- hat = aa_new_null_profile(profile, 1);
+ hat = aa_new_null_profile(profile, true, hats[0],
+ GFP_KERNEL);
if (!hat) {
info = "failed null profile create";
error = -ENOMEM;
@@ -711,9 +713,9 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
audit:
if (!permtest)
- error = aa_audit_file(profile, &perms, GFP_KERNEL,
- OP_CHANGE_HAT, AA_MAY_CHANGEHAT, NULL,
- target, GLOBAL_ROOT_UID, info, error);
+ error = aa_audit_file(profile, &perms, OP_CHANGE_HAT,
+ AA_MAY_CHANGEHAT, NULL, target,
+ GLOBAL_ROOT_UID, info, error);
out:
aa_put_profile(hat);
@@ -727,8 +729,7 @@ out:
/**
* aa_change_profile - perform a one-way profile transition
- * @ns_name: name of the profile namespace to change to (MAYBE NULL)
- * @hname: name of profile to change to (MAYBE NULL)
+ * @fqname: name of profile may include namespace (NOT NULL)
* @onexec: whether this transition is to take place immediately or at exec
* @permtest: true if this is just a permission test
*
@@ -740,19 +741,20 @@ out:
*
* Returns %0 on success, error otherwise.
*/
-int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
- bool permtest)
+int aa_change_profile(const char *fqname, bool onexec,
+ bool permtest, bool stack)
{
const struct cred *cred;
struct aa_profile *profile, *target = NULL;
- struct aa_namespace *ns = NULL;
struct file_perms perms = {};
- const char *name = NULL, *info = NULL;
- int op, error = 0;
+ const char *info = NULL, *op;
+ int error = 0;
u32 request;
- if (!hname && !ns_name)
+ if (!fqname || !*fqname) {
+ AA_DEBUG("no profile name");
return -EINVAL;
+ }
if (onexec) {
request = AA_MAY_ONEXEC;
@@ -777,44 +779,15 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
return -EPERM;
}
- if (ns_name) {
- /* released below */
- ns = aa_find_namespace(profile->ns, ns_name);
- if (!ns) {
- /* we don't create new namespace in complain mode */
- name = ns_name;
- info = "namespace not found";
- error = -ENOENT;
- goto audit;
- }
- } else
- /* released below */
- ns = aa_get_namespace(profile->ns);
-
- /* if the name was not specified, use the name of the current profile */
- if (!hname) {
- if (unconfined(profile))
- hname = ns->unconfined->base.hname;
- else
- hname = profile->base.hname;
- }
-
- perms = change_profile_perms(profile, ns, hname, request,
- profile->file.start);
- if (!(perms.allow & request)) {
- error = -EACCES;
- goto audit;
- }
-
- /* released below */
- target = aa_lookup_profile(ns, hname);
+ target = aa_fqlookupn_profile(profile, fqname, strlen(fqname));
if (!target) {
info = "profile not found";
error = -ENOENT;
if (permtest || !COMPLAIN_MODE(profile))
goto audit;
/* released below */
- target = aa_new_null_profile(profile, 0);
+ target = aa_new_null_profile(profile, false, fqname,
+ GFP_KERNEL);
if (!target) {
info = "failed null profile create";
error = -ENOMEM;
@@ -822,6 +795,13 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
}
}
+ perms = change_profile_perms(profile, target->ns, target->base.hname,
+ request, profile->file.start);
+ if (!(perms.allow & request)) {
+ error = -EACCES;
+ goto audit;
+ }
+
/* check if tracing task is allowed to trace target domain */
error = may_change_ptraced_domain(target);
if (error) {
@@ -839,10 +819,9 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
audit:
if (!permtest)
- error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request,
- name, hname, GLOBAL_ROOT_UID, info, error);
+ error = aa_audit_file(profile, &perms, op, request, NULL,
+ fqname, GLOBAL_ROOT_UID, info, error);
- aa_put_namespace(ns);
aa_put_profile(target);
put_cred(cred);
diff --git a/security/apparmor/file.c b/security/apparmor/file.c
index 4d2af4b..750564c 100644
--- a/security/apparmor/file.c
+++ b/security/apparmor/file.c
@@ -67,24 +67,24 @@ static void file_audit_cb(struct audit_buffer *ab, void *va)
struct common_audit_data *sa = va;
kuid_t fsuid = current_fsuid();
- if (sa->aad->fs.request & AA_AUDIT_FILE_MASK) {
+ if (aad(sa)->fs.request & AA_AUDIT_FILE_MASK) {
audit_log_format(ab, " requested_mask=");
- audit_file_mask(ab, sa->aad->fs.request);
+ audit_file_mask(ab, aad(sa)->fs.request);
}
- if (sa->aad->fs.denied & AA_AUDIT_FILE_MASK) {
+ if (aad(sa)->fs.denied & AA_AUDIT_FILE_MASK) {
audit_log_format(ab, " denied_mask=");
- audit_file_mask(ab, sa->aad->fs.denied);
+ audit_file_mask(ab, aad(sa)->fs.denied);
}
- if (sa->aad->fs.request & AA_AUDIT_FILE_MASK) {
+ if (aad(sa)->fs.request & AA_AUDIT_FILE_MASK) {
audit_log_format(ab, " fsuid=%d",
from_kuid(&init_user_ns, fsuid));
audit_log_format(ab, " ouid=%d",
- from_kuid(&init_user_ns, sa->aad->fs.ouid));
+ from_kuid(&init_user_ns, aad(sa)->fs.ouid));
}
- if (sa->aad->fs.target) {
+ if (aad(sa)->fs.target) {
audit_log_format(ab, " target=");
- audit_log_untrustedstring(ab, sa->aad->fs.target);
+ audit_log_untrustedstring(ab, aad(sa)->fs.target);
}
}
@@ -104,54 +104,53 @@ static void file_audit_cb(struct audit_buffer *ab, void *va)
* Returns: %0 or error on failure
*/
int aa_audit_file(struct aa_profile *profile, struct file_perms *perms,
- gfp_t gfp, int op, u32 request, const char *name,
+ const char *op, u32 request, const char *name,
const char *target, kuid_t ouid, const char *info, int error)
{
int type = AUDIT_APPARMOR_AUTO;
- struct common_audit_data sa;
- struct apparmor_audit_data aad = {0,};
- sa.type = LSM_AUDIT_DATA_TASK;
+ DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_TASK, op);
+
+ sa.u.tsk = NULL;
+ aad(&sa)->fs.request = request;
+ aad(&sa)->name = name;
+ aad(&sa)->fs.target = target;
+ aad(&sa)->fs.ouid = ouid;
+ aad(&sa)->info = info;
+ aad(&sa)->error = error;
sa.u.tsk = NULL;
- sa.aad = &aad;
- aad.op = op,
- aad.fs.request = request;
- aad.name = name;
- aad.fs.target = target;
- aad.fs.ouid = ouid;
- aad.info = info;
- aad.error = error;
-
- if (likely(!sa.aad->error)) {
+
+ if (likely(!aad(&sa)->error)) {
u32 mask = perms->audit;
if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL))
mask = 0xffff;
/* mask off perms that are not being force audited */
- sa.aad->fs.request &= mask;
+ aad(&sa)->fs.request &= mask;
- if (likely(!sa.aad->fs.request))
+ if (likely(!aad(&sa)->fs.request))
return 0;
type = AUDIT_APPARMOR_AUDIT;
} else {
/* only report permissions that were denied */
- sa.aad->fs.request = sa.aad->fs.request & ~perms->allow;
+ aad(&sa)->fs.request = aad(&sa)->fs.request & ~perms->allow;
+ AA_BUG(!aad(&sa)->fs.request);
- if (sa.aad->fs.request & perms->kill)
+ if (aad(&sa)->fs.request & perms->kill)
type = AUDIT_APPARMOR_KILL;
/* quiet known rejects, assumes quiet and kill do not overlap */
- if ((sa.aad->fs.request & perms->quiet) &&
+ if ((aad(&sa)->fs.request & perms->quiet) &&
AUDIT_MODE(profile) != AUDIT_NOQUIET &&
AUDIT_MODE(profile) != AUDIT_ALL)
- sa.aad->fs.request &= ~perms->quiet;
+ aad(&sa)->fs.request &= ~perms->quiet;
- if (!sa.aad->fs.request)
- return COMPLAIN_MODE(profile) ? 0 : sa.aad->error;
+ if (!aad(&sa)->fs.request)
+ return COMPLAIN_MODE(profile) ? 0 : aad(&sa)->error;
}
- sa.aad->fs.denied = sa.aad->fs.request & ~perms->allow;
- return aa_audit(type, profile, gfp, &sa, file_audit_cb);
+ aad(&sa)->fs.denied = aad(&sa)->fs.request & ~perms->allow;
+ return aa_audit(type, profile, &sa, file_audit_cb);
}
/**
@@ -276,8 +275,9 @@ static inline bool is_deleted(struct dentry *dentry)
*
* Returns: %0 else error if access denied or other error
*/
-int aa_path_perm(int op, struct aa_profile *profile, const struct path *path,
- int flags, u32 request, struct path_cond *cond)
+int aa_path_perm(const char *op, struct aa_profile *profile,
+ const struct path *path, int flags, u32 request,
+ struct path_cond *cond)
{
char *buffer = NULL;
struct file_perms perms = {};
@@ -301,8 +301,8 @@ int aa_path_perm(int op, struct aa_profile *profile, const struct path *path,
if (request & ~perms.allow)
error = -EACCES;
}
- error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, name,
- NULL, cond->uid, info, error);
+ error = aa_audit_file(profile, &perms, op, request, name, NULL,
+ cond->uid, info, error);
kfree(buffer);
return error;
@@ -349,8 +349,8 @@ static inline bool xindex_is_subset(u32 link, u32 target)
int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
const struct path *new_dir, struct dentry *new_dentry)
{
- struct path link = { new_dir->mnt, new_dentry };
- struct path target = { new_dir->mnt, old_dentry };
+ struct path link = { .mnt = new_dir->mnt, .dentry = new_dentry };
+ struct path target = { .mnt = new_dir->mnt, .dentry = old_dentry };
struct path_cond cond = {
d_backing_inode(old_dentry)->i_uid,
d_backing_inode(old_dentry)->i_mode
@@ -429,7 +429,7 @@ done_tests:
error = 0;
audit:
- error = aa_audit_file(profile, &lperms, GFP_KERNEL, OP_LINK, request,
+ error = aa_audit_file(profile, &lperms, OP_LINK, request,
lname, tname, cond.uid, info, error);
kfree(buffer);
kfree(buffer2);
@@ -446,7 +446,7 @@ audit:
*
* Returns: %0 if access allowed else error
*/
-int aa_file_perm(int op, struct aa_profile *profile, struct file *file,
+int aa_file_perm(const char *op, struct aa_profile *profile, struct file *file,
u32 request)
{
struct path_cond cond = {
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h
index 5d721e9..1750cc0 100644
--- a/security/apparmor/include/apparmor.h
+++ b/security/apparmor/include/apparmor.h
@@ -1,7 +1,7 @@
/*
* AppArmor security module
*
- * This file contains AppArmor basic global and lib definitions
+ * This file contains AppArmor basic global
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
@@ -15,10 +15,7 @@
#ifndef __APPARMOR_H
#define __APPARMOR_H
-#include <linux/slab.h>
-#include <linux/fs.h>
-
-#include "match.h"
+#include <linux/types.h>
/*
* Class of mediation types in the AppArmor policy db
@@ -43,79 +40,4 @@ extern bool aa_g_logsyscall;
extern bool aa_g_paranoid_load;
extern unsigned int aa_g_path_max;
-/*
- * DEBUG remains global (no per profile flag) since it is mostly used in sysctl
- * which is not related to profile accesses.
- */
-
-#define AA_DEBUG(fmt, args...) \
- do { \
- if (aa_g_debug && printk_ratelimit()) \
- printk(KERN_DEBUG "AppArmor: " fmt, ##args); \
- } while (0)
-
-#define AA_ERROR(fmt, args...) \
- do { \
- if (printk_ratelimit()) \
- printk(KERN_ERR "AppArmor: " fmt, ##args); \
- } while (0)
-
-/* Flag indicating whether initialization completed */
-extern int apparmor_initialized __initdata;
-
-/* fn's in lib */
-char *aa_split_fqname(char *args, char **ns_name);
-void aa_info_message(const char *str);
-void *__aa_kvmalloc(size_t size, gfp_t flags);
-
-static inline void *kvmalloc(size_t size)
-{
- return __aa_kvmalloc(size, 0);
-}
-
-static inline void *kvzalloc(size_t size)
-{
- return __aa_kvmalloc(size, __GFP_ZERO);
-}
-
-/* returns 0 if kref not incremented */
-static inline int kref_get_not0(struct kref *kref)
-{
- return atomic_inc_not_zero(&kref->refcount);
-}
-
-/**
- * aa_strneq - compare null terminated @str to a non null terminated substring
- * @str: a null terminated string
- * @sub: a substring, not necessarily null terminated
- * @len: length of @sub to compare
- *
- * The @str string must be full consumed for this to be considered a match
- */
-static inline bool aa_strneq(const char *str, const char *sub, int len)
-{
- return !strncmp(str, sub, len) && !str[len];
-}
-
-/**
- * aa_dfa_null_transition - step to next state after null character
- * @dfa: the dfa to match against
- * @start: the state of the dfa to start matching in
- *
- * aa_dfa_null_transition transitions to the next state after a null
- * character which is not used in standard matching and is only
- * used to separate pairs.
- */
-static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa,
- unsigned int start)
-{
- /* the null transition only needs the string's null terminator byte */
- return aa_dfa_next(dfa, start, 0);
-}
-
-static inline bool mediated_filesystem(struct dentry *dentry)
-{
- return !(dentry->d_sb->s_flags & MS_NOUSER);
-}
-
#endif /* __APPARMOR_H */
diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h
index 414e568..120a798 100644
--- a/security/apparmor/include/apparmorfs.h
+++ b/security/apparmor/include/apparmorfs.h
@@ -15,6 +15,8 @@
#ifndef __AA_APPARMORFS_H
#define __AA_APPARMORFS_H
+extern struct path aa_null;
+
enum aa_fs_type {
AA_FS_TYPE_BOOLEAN,
AA_FS_TYPE_STRING,
@@ -62,12 +64,16 @@ extern const struct file_operations aa_fs_seq_file_ops;
extern void __init aa_destroy_aafs(void);
struct aa_profile;
-struct aa_namespace;
+struct aa_ns;
enum aafs_ns_type {
AAFS_NS_DIR,
AAFS_NS_PROFS,
AAFS_NS_NS,
+ AAFS_NS_RAW_DATA,
+ AAFS_NS_LOAD,
+ AAFS_NS_REPLACE,
+ AAFS_NS_REMOVE,
AAFS_NS_COUNT,
AAFS_NS_MAX_COUNT,
AAFS_NS_SIZE,
@@ -83,12 +89,19 @@ enum aafs_prof_type {
AAFS_PROF_MODE,
AAFS_PROF_ATTACH,
AAFS_PROF_HASH,
+ AAFS_PROF_RAW_DATA,
+ AAFS_PROF_RAW_HASH,
+ AAFS_PROF_RAW_ABI,
AAFS_PROF_SIZEOF,
};
#define ns_dir(X) ((X)->dents[AAFS_NS_DIR])
#define ns_subns_dir(X) ((X)->dents[AAFS_NS_NS])
#define ns_subprofs_dir(X) ((X)->dents[AAFS_NS_PROFS])
+#define ns_subdata_dir(X) ((X)->dents[AAFS_NS_RAW_DATA])
+#define ns_subload(X) ((X)->dents[AAFS_NS_LOAD])
+#define ns_subreplace(X) ((X)->dents[AAFS_NS_REPLACE])
+#define ns_subremove(X) ((X)->dents[AAFS_NS_REMOVE])
#define prof_dir(X) ((X)->dents[AAFS_PROF_DIR])
#define prof_child_dir(X) ((X)->dents[AAFS_PROF_PROFS])
@@ -97,8 +110,8 @@ void __aa_fs_profile_rmdir(struct aa_profile *profile);
void __aa_fs_profile_migrate_dents(struct aa_profile *old,
struct aa_profile *new);
int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent);
-void __aa_fs_namespace_rmdir(struct aa_namespace *ns);
-int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent,
- const char *name);
+void __aa_fs_ns_rmdir(struct aa_ns *ns);
+int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent,
+ const char *name);
#endif /* __AA_APPARMORFS_H */
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
index ba3dfd1..fdc4774 100644
--- a/security/apparmor/include/audit.h
+++ b/security/apparmor/include/audit.h
@@ -46,97 +46,115 @@ enum audit_type {
AUDIT_APPARMOR_AUTO
};
-extern const char *const op_table[];
-enum aa_ops {
- OP_NULL,
-
- OP_SYSCTL,
- OP_CAPABLE,
-
- OP_UNLINK,
- OP_MKDIR,
- OP_RMDIR,
- OP_MKNOD,
- OP_TRUNC,
- OP_LINK,
- OP_SYMLINK,
- OP_RENAME_SRC,
- OP_RENAME_DEST,
- OP_CHMOD,
- OP_CHOWN,
- OP_GETATTR,
- OP_OPEN,
-
- OP_FPERM,
- OP_FLOCK,
- OP_FMMAP,
- OP_FMPROT,
-
- OP_CREATE,
- OP_POST_CREATE,
- OP_BIND,
- OP_CONNECT,
- OP_LISTEN,
- OP_ACCEPT,
- OP_SENDMSG,
- OP_RECVMSG,
- OP_GETSOCKNAME,
- OP_GETPEERNAME,
- OP_GETSOCKOPT,
- OP_SETSOCKOPT,
- OP_SOCK_SHUTDOWN,
-
- OP_PTRACE,
-
- OP_EXEC,
- OP_CHANGE_HAT,
- OP_CHANGE_PROFILE,
- OP_CHANGE_ONEXEC,
-
- OP_SETPROCATTR,
- OP_SETRLIMIT,
-
- OP_PROF_REPL,
- OP_PROF_LOAD,
- OP_PROF_RM,
-};
+#define OP_NULL NULL
+
+#define OP_SYSCTL "sysctl"
+#define OP_CAPABLE "capable"
+
+#define OP_UNLINK "unlink"
+#define OP_MKDIR "mkdir"
+#define OP_RMDIR "rmdir"
+#define OP_MKNOD "mknod"
+#define OP_TRUNC "truncate"
+#define OP_LINK "link"
+#define OP_SYMLINK "symlink"
+#define OP_RENAME_SRC "rename_src"
+#define OP_RENAME_DEST "rename_dest"
+#define OP_CHMOD "chmod"
+#define OP_CHOWN "chown"
+#define OP_GETATTR "getattr"
+#define OP_OPEN "open"
+
+#define OP_FPERM "file_perm"
+#define OP_FLOCK "file_lock"
+#define OP_FMMAP "file_mmap"
+#define OP_FMPROT "file_mprotect"
+
+#define OP_CREATE "create"
+#define OP_POST_CREATE "post_create"
+#define OP_BIND "bind"
+#define OP_CONNECT "connect"
+#define OP_LISTEN "listen"
+#define OP_ACCEPT "accept"
+#define OP_SENDMSG "sendmsg"
+#define OP_RECVMSG "recvmsg"
+#define OP_GETSOCKNAME "getsockname"
+#define OP_GETPEERNAME "getpeername"
+#define OP_GETSOCKOPT "getsockopt"
+#define OP_SETSOCKOPT "setsockopt"
+#define OP_SHUTDOWN "socket_shutdown"
+
+#define OP_PTRACE "ptrace"
+
+#define OP_EXEC "exec"
+
+#define OP_CHANGE_HAT "change_hat"
+#define OP_CHANGE_PROFILE "change_profile"
+#define OP_CHANGE_ONEXEC "change_onexec"
+
+#define OP_SETPROCATTR "setprocattr"
+#define OP_SETRLIMIT "setrlimit"
+
+#define OP_PROF_REPL "profile_replace"
+#define OP_PROF_LOAD "profile_load"
+#define OP_PROF_RM "profile_remove"
struct apparmor_audit_data {
int error;
- int op;
+ const char *op;
int type;
void *profile;
const char *name;
const char *info;
union {
- void *target;
+ /* these entries require a custom callback fn */
+ struct {
+ struct aa_profile *peer;
+ struct {
+ const char *target;
+ u32 request;
+ u32 denied;
+ kuid_t ouid;
+ } fs;
+ };
struct {
+ const char *name;
long pos;
- void *target;
+ const char *ns;
} iface;
struct {
int rlim;
unsigned long max;
} rlim;
- struct {
- const char *target;
- u32 request;
- u32 denied;
- kuid_t ouid;
- } fs;
};
};
-/* define a short hand for apparmor_audit_data structure */
-#define aad apparmor_audit_data
+/* macros for dealing with apparmor_audit_data structure */
+#define aad(SA) ((SA)->apparmor_audit_data)
+#define DEFINE_AUDIT_DATA(NAME, T, X) \
+ /* TODO: cleanup audit init so we don't need _aad = {0,} */ \
+ struct apparmor_audit_data NAME ## _aad = { .op = (X), }; \
+ struct common_audit_data NAME = \
+ { \
+ .type = (T), \
+ .u.tsk = NULL, \
+ }; \
+ NAME.apparmor_audit_data = &(NAME ## _aad)
void aa_audit_msg(int type, struct common_audit_data *sa,
void (*cb) (struct audit_buffer *, void *));
-int aa_audit(int type, struct aa_profile *profile, gfp_t gfp,
- struct common_audit_data *sa,
+int aa_audit(int type, struct aa_profile *profile, struct common_audit_data *sa,
void (*cb) (struct audit_buffer *, void *));
+#define aa_audit_error(ERROR, SA, CB) \
+({ \
+ aad((SA))->error = (ERROR); \
+ aa_audit_msg(AUDIT_APPARMOR_ERROR, (SA), (CB)); \
+ aad((SA))->error; \
+})
+
+
static inline int complain_error(int error)
{
if (error == -EPERM || error == -EACCES)
diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h
index 6bf6579..5b18fed 100644
--- a/security/apparmor/include/context.h
+++ b/security/apparmor/include/context.h
@@ -20,44 +20,45 @@
#include <linux/sched.h>
#include "policy.h"
+#include "policy_ns.h"
-#define cred_cxt(X) (X)->security
-#define current_cxt() cred_cxt(current_cred())
+#define cred_ctx(X) ((X)->security)
+#define current_ctx() cred_ctx(current_cred())
-/* struct aa_file_cxt - the AppArmor context the file was opened in
+/* struct aa_file_ctx - the AppArmor context the file was opened in
* @perms: the permission the file was opened with
*
- * The file_cxt could currently be directly stored in file->f_security
+ * The file_ctx could currently be directly stored in file->f_security
* as the profile reference is now stored in the f_cred. However the
- * cxt struct will expand in the future so we keep the struct.
+ * ctx struct will expand in the future so we keep the struct.
*/
-struct aa_file_cxt {
+struct aa_file_ctx {
u16 allow;
};
/**
- * aa_alloc_file_context - allocate file_cxt
+ * aa_alloc_file_context - allocate file_ctx
* @gfp: gfp flags for allocation
*
- * Returns: file_cxt or NULL on failure
+ * Returns: file_ctx or NULL on failure
*/
-static inline struct aa_file_cxt *aa_alloc_file_context(gfp_t gfp)
+static inline struct aa_file_ctx *aa_alloc_file_context(gfp_t gfp)
{
- return kzalloc(sizeof(struct aa_file_cxt), gfp);
+ return kzalloc(sizeof(struct aa_file_ctx), gfp);
}
/**
- * aa_free_file_context - free a file_cxt
- * @cxt: file_cxt to free (MAYBE_NULL)
+ * aa_free_file_context - free a file_ctx
+ * @ctx: file_ctx to free (MAYBE_NULL)
*/
-static inline void aa_free_file_context(struct aa_file_cxt *cxt)
+static inline void aa_free_file_context(struct aa_file_ctx *ctx)
{
- if (cxt)
- kzfree(cxt);
+ if (ctx)
+ kzfree(ctx);
}
/**
- * struct aa_task_cxt - primary label for confined tasks
+ * struct aa_task_ctx - primary label for confined tasks
* @profile: the current profile (NOT NULL)
* @exec: profile to transition to on next exec (MAYBE NULL)
* @previous: profile the task may return to (MAYBE NULL)
@@ -68,17 +69,17 @@ static inline void aa_free_file_context(struct aa_file_cxt *cxt)
*
* TODO: make so a task can be confined by a stack of contexts
*/
-struct aa_task_cxt {
+struct aa_task_ctx {
struct aa_profile *profile;
struct aa_profile *onexec;
struct aa_profile *previous;
u64 token;
};
-struct aa_task_cxt *aa_alloc_task_context(gfp_t flags);
-void aa_free_task_context(struct aa_task_cxt *cxt);
-void aa_dup_task_context(struct aa_task_cxt *new,
- const struct aa_task_cxt *old);
+struct aa_task_ctx *aa_alloc_task_context(gfp_t flags);
+void aa_free_task_context(struct aa_task_ctx *ctx);
+void aa_dup_task_context(struct aa_task_ctx *new,
+ const struct aa_task_ctx *old);
int aa_replace_current_profile(struct aa_profile *profile);
int aa_set_current_onexec(struct aa_profile *profile);
int aa_set_current_hat(struct aa_profile *profile, u64 token);
@@ -96,9 +97,10 @@ struct aa_profile *aa_get_task_profile(struct task_struct *task);
*/
static inline struct aa_profile *aa_cred_profile(const struct cred *cred)
{
- struct aa_task_cxt *cxt = cred_cxt(cred);
- BUG_ON(!cxt || !cxt->profile);
- return cxt->profile;
+ struct aa_task_ctx *ctx = cred_ctx(cred);
+
+ AA_BUG(!ctx || !ctx->profile);
+ return ctx->profile;
}
/**
@@ -148,31 +150,37 @@ static inline struct aa_profile *__aa_current_profile(void)
*/
static inline struct aa_profile *aa_current_profile(void)
{
- const struct aa_task_cxt *cxt = current_cxt();
+ const struct aa_task_ctx *ctx = current_ctx();
struct aa_profile *profile;
- BUG_ON(!cxt || !cxt->profile);
- if (PROFILE_INVALID(cxt->profile)) {
- profile = aa_get_newest_profile(cxt->profile);
+ AA_BUG(!ctx || !ctx->profile);
+
+ if (profile_is_stale(ctx->profile)) {
+ profile = aa_get_newest_profile(ctx->profile);
aa_replace_current_profile(profile);
aa_put_profile(profile);
- cxt = current_cxt();
+ ctx = current_ctx();
}
- return cxt->profile;
+ return ctx->profile;
+}
+
+static inline struct aa_ns *aa_get_current_ns(void)
+{
+ return aa_get_ns(__aa_current_profile()->ns);
}
/**
- * aa_clear_task_cxt_trans - clear transition tracking info from the cxt
- * @cxt: task context to clear (NOT NULL)
+ * aa_clear_task_ctx_trans - clear transition tracking info from the ctx
+ * @ctx: task context to clear (NOT NULL)
*/
-static inline void aa_clear_task_cxt_trans(struct aa_task_cxt *cxt)
+static inline void aa_clear_task_ctx_trans(struct aa_task_ctx *ctx)
{
- aa_put_profile(cxt->previous);
- aa_put_profile(cxt->onexec);
- cxt->previous = NULL;
- cxt->onexec = NULL;
- cxt->token = 0;
+ aa_put_profile(ctx->previous);
+ aa_put_profile(ctx->onexec);
+ ctx->previous = NULL;
+ ctx->onexec = NULL;
+ ctx->token = 0;
}
#endif /* __AA_CONTEXT_H */
diff --git a/security/apparmor/include/crypto.h b/security/apparmor/include/crypto.h
index dc418e5..c1469f8 100644
--- a/security/apparmor/include/crypto.h
+++ b/security/apparmor/include/crypto.h
@@ -18,9 +18,14 @@
#ifdef CONFIG_SECURITY_APPARMOR_HASH
unsigned int aa_hash_size(void);
+char *aa_calc_hash(void *data, size_t len);
int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
size_t len);
#else
+static inline char *aa_calc_hash(void *data, size_t len)
+{
+ return NULL;
+}
static inline int aa_calc_profile_hash(struct aa_profile *profile, u32 version,
void *start, size_t len)
{
diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h
index de04464..3054472 100644
--- a/security/apparmor/include/domain.h
+++ b/security/apparmor/include/domain.h
@@ -30,7 +30,7 @@ void apparmor_bprm_committed_creds(struct linux_binprm *bprm);
void aa_free_domain_entries(struct aa_domain *domain);
int aa_change_hat(const char *hats[], int count, u64 token, bool permtest);
-int aa_change_profile(const char *ns_name, const char *name, bool onexec,
- bool permtest);
+int aa_change_profile(const char *fqname, bool onexec, bool permtest,
+ bool stack);
#endif /* __AA_DOMAIN_H */
diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h
index 4803c97..38f821b 100644
--- a/security/apparmor/include/file.h
+++ b/security/apparmor/include/file.h
@@ -145,7 +145,7 @@ static inline u16 dfa_map_xindex(u16 mask)
dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff)
int aa_audit_file(struct aa_profile *profile, struct file_perms *perms,
- gfp_t gfp, int op, u32 request, const char *name,
+ const char *op, u32 request, const char *name,
const char *target, kuid_t ouid, const char *info, int error);
/**
@@ -171,13 +171,14 @@ unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start,
const char *name, struct path_cond *cond,
struct file_perms *perms);
-int aa_path_perm(int op, struct aa_profile *profile, const struct path *path,
- int flags, u32 request, struct path_cond *cond);
+int aa_path_perm(const char *op, struct aa_profile *profile,
+ const struct path *path, int flags, u32 request,
+ struct path_cond *cond);
int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
const struct path *new_dir, struct dentry *new_dentry);
-int aa_file_perm(int op, struct aa_profile *profile, struct file *file,
+int aa_file_perm(const char *op, struct aa_profile *profile, struct file *file,
u32 request);
static inline void aa_free_file_rules(struct aa_file_rules *rules)
diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h
new file mode 100644
index 0000000..65ff492
--- /dev/null
+++ b/security/apparmor/include/lib.h
@@ -0,0 +1,194 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor lib definitions
+ *
+ * 2017 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_LIB_H
+#define __AA_LIB_H
+
+#include <linux/slab.h>
+#include <linux/fs.h>
+
+#include "match.h"
+
+/* Provide our own test for whether a write lock is held for asserts
+ * this is because on none SMP systems write_can_lock will always
+ * resolve to true, which is what you want for code making decisions
+ * based on it, but wrong for asserts checking that the lock is held
+ */
+#ifdef CONFIG_SMP
+#define write_is_locked(X) !write_can_lock(X)
+#else
+#define write_is_locked(X) (1)
+#endif /* CONFIG_SMP */
+
+/*
+ * DEBUG remains global (no per profile flag) since it is mostly used in sysctl
+ * which is not related to profile accesses.
+ */
+
+#define DEBUG_ON (aa_g_debug)
+#define dbg_printk(__fmt, __args...) pr_debug(__fmt, ##__args)
+#define AA_DEBUG(fmt, args...) \
+ do { \
+ if (DEBUG_ON) \
+ pr_debug_ratelimited("AppArmor: " fmt, ##args); \
+ } while (0)
+
+#define AA_WARN(X) WARN((X), "APPARMOR WARN %s: %s\n", __func__, #X)
+
+#define AA_BUG(X, args...) AA_BUG_FMT((X), "" args)
+#ifdef CONFIG_SECURITY_APPARMOR_DEBUG_ASSERTS
+#define AA_BUG_FMT(X, fmt, args...) \
+ WARN((X), "AppArmor WARN %s: (" #X "): " fmt, __func__, ##args)
+#else
+#define AA_BUG_FMT(X, fmt, args...)
+#endif
+
+#define AA_ERROR(fmt, args...) \
+ pr_err_ratelimited("AppArmor: " fmt, ##args)
+
+/* Flag indicating whether initialization completed */
+extern int apparmor_initialized __initdata;
+
+/* fn's in lib */
+char *aa_split_fqname(char *args, char **ns_name);
+const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
+ size_t *ns_len);
+void aa_info_message(const char *str);
+void *__aa_kvmalloc(size_t size, gfp_t flags);
+
+static inline void *kvmalloc(size_t size)
+{
+ return __aa_kvmalloc(size, 0);
+}
+
+static inline void *kvzalloc(size_t size)
+{
+ return __aa_kvmalloc(size, __GFP_ZERO);
+}
+
+/**
+ * aa_strneq - compare null terminated @str to a non null terminated substring
+ * @str: a null terminated string
+ * @sub: a substring, not necessarily null terminated
+ * @len: length of @sub to compare
+ *
+ * The @str string must be full consumed for this to be considered a match
+ */
+static inline bool aa_strneq(const char *str, const char *sub, int len)
+{
+ return !strncmp(str, sub, len) && !str[len];
+}
+
+/**
+ * aa_dfa_null_transition - step to next state after null character
+ * @dfa: the dfa to match against
+ * @start: the state of the dfa to start matching in
+ *
+ * aa_dfa_null_transition transitions to the next state after a null
+ * character which is not used in standard matching and is only
+ * used to separate pairs.
+ */
+static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa,
+ unsigned int start)
+{
+ /* the null transition only needs the string's null terminator byte */
+ return aa_dfa_next(dfa, start, 0);
+}
+
+static inline bool path_mediated_fs(struct dentry *dentry)
+{
+ return !(dentry->d_sb->s_flags & MS_NOUSER);
+}
+
+/* struct aa_policy - common part of both namespaces and profiles
+ * @name: name of the object
+ * @hname - The hierarchical name
+ * @list: list policy object is on
+ * @profiles: head of the profiles list contained in the object
+ */
+struct aa_policy {
+ const char *name;
+ const char *hname;
+ struct list_head list;
+ struct list_head profiles;
+};
+
+/**
+ * basename - find the last component of an hname
+ * @name: hname to find the base profile name component of (NOT NULL)
+ *
+ * Returns: the tail (base profile name) name component of an hname
+ */
+static inline const char *basename(const char *hname)
+{
+ char *split;
+
+ hname = strim((char *)hname);
+ for (split = strstr(hname, "//"); split; split = strstr(hname, "//"))
+ hname = split + 2;
+
+ return hname;
+}
+
+/**
+ * __policy_find - find a policy by @name on a policy list
+ * @head: list to search (NOT NULL)
+ * @name: name to search for (NOT NULL)
+ *
+ * Requires: rcu_read_lock be held
+ *
+ * Returns: unrefcounted policy that match @name or NULL if not found
+ */
+static inline struct aa_policy *__policy_find(struct list_head *head,
+ const char *name)
+{
+ struct aa_policy *policy;
+
+ list_for_each_entry_rcu(policy, head, list) {
+ if (!strcmp(policy->name, name))
+ return policy;
+ }
+ return NULL;
+}
+
+/**
+ * __policy_strn_find - find a policy that's name matches @len chars of @str
+ * @head: list to search (NOT NULL)
+ * @str: string to search for (NOT NULL)
+ * @len: length of match required
+ *
+ * Requires: rcu_read_lock be held
+ *
+ * Returns: unrefcounted policy that match @str or NULL if not found
+ *
+ * if @len == strlen(@strlen) then this is equiv to __policy_find
+ * other wise it allows searching for policy by a partial match of name
+ */
+static inline struct aa_policy *__policy_strn_find(struct list_head *head,
+ const char *str, int len)
+{
+ struct aa_policy *policy;
+
+ list_for_each_entry_rcu(policy, head, list) {
+ if (aa_strneq(policy->name, str, len))
+ return policy;
+ }
+
+ return NULL;
+}
+
+bool aa_policy_init(struct aa_policy *policy, const char *prefix,
+ const char *name, gfp_t gfp);
+void aa_policy_destroy(struct aa_policy *policy);
+
+#endif /* AA_LIB_H */
diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h
index a1c04fe..add4c67 100644
--- a/security/apparmor/include/match.h
+++ b/security/apparmor/include/match.h
@@ -100,13 +100,15 @@ struct aa_dfa {
struct table_header *tables[YYTD_ID_TSIZE];
};
+extern struct aa_dfa *nulldfa;
+
#define byte_to_byte(X) (X)
-#define UNPACK_ARRAY(TABLE, BLOB, LEN, TYPE, NTOHX) \
+#define UNPACK_ARRAY(TABLE, BLOB, LEN, TTYPE, BTYPE, NTOHX) \
do { \
typeof(LEN) __i; \
- TYPE *__t = (TYPE *) TABLE; \
- TYPE *__b = (TYPE *) BLOB; \
+ TTYPE *__t = (TTYPE *) TABLE; \
+ BTYPE *__b = (BTYPE *) BLOB; \
for (__i = 0; __i < LEN; __i++) { \
__t[__i] = NTOHX(__b[__i]); \
} \
@@ -117,6 +119,9 @@ static inline size_t table_size(size_t len, size_t el_size)
return ALIGN(sizeof(struct table_header) + len * el_size, 8);
}
+int aa_setup_dfa_engine(void);
+void aa_teardown_dfa_engine(void);
+
struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags);
unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start,
const char *str, int len);
@@ -128,6 +133,21 @@ unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state,
void aa_dfa_free_kref(struct kref *kref);
/**
+ * aa_get_dfa - increment refcount on dfa @p
+ * @dfa: dfa (MAYBE NULL)
+ *
+ * Returns: pointer to @dfa if @dfa is NULL will return NULL
+ * Requires: @dfa must be held with valid refcount when called
+ */
+static inline struct aa_dfa *aa_get_dfa(struct aa_dfa *dfa)
+{
+ if (dfa)
+ kref_get(&(dfa->count));
+
+ return dfa;
+}
+
+/**
* aa_put_dfa - put a dfa refcount
* @dfa: dfa to put refcount (MAYBE NULL)
*
diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h
index 73560f2..0444fdd 100644
--- a/security/apparmor/include/path.h
+++ b/security/apparmor/include/path.h
@@ -29,4 +29,57 @@ enum path_flags {
int aa_path_name(const struct path *path, int flags, char **buffer,
const char **name, const char **info);
+#define MAX_PATH_BUFFERS 2
+
+/* Per cpu buffers used during mediation */
+/* preallocated buffers to use during path lookups */
+struct aa_buffers {
+ char *buf[MAX_PATH_BUFFERS];
+};
+
+#include <linux/percpu.h>
+#include <linux/preempt.h>
+
+DECLARE_PER_CPU(struct aa_buffers, aa_buffers);
+
+#define COUNT_ARGS(X...) COUNT_ARGS_HELPER(, ##X, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+#define COUNT_ARGS_HELPER(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, n, X...) n
+#define CONCAT(X, Y) X ## Y
+#define CONCAT_AFTER(X, Y) CONCAT(X, Y)
+
+#define ASSIGN(FN, X, N) ((X) = FN(N))
+#define EVAL1(FN, X) ASSIGN(FN, X, 0) /*X = FN(0)*/
+#define EVAL2(FN, X, Y...) do { ASSIGN(FN, X, 1); EVAL1(FN, Y); } while (0)
+#define EVAL(FN, X...) CONCAT_AFTER(EVAL, COUNT_ARGS(X))(FN, X)
+
+#define for_each_cpu_buffer(I) for ((I) = 0; (I) < MAX_PATH_BUFFERS; (I)++)
+
+#ifdef CONFIG_DEBUG_PREEMPT
+#define AA_BUG_PREEMPT_ENABLED(X) AA_BUG(preempt_count() <= 0, X)
+#else
+#define AA_BUG_PREEMPT_ENABLED(X) /* nop */
+#endif
+
+#define __get_buffer(N) ({ \
+ struct aa_buffers *__cpu_var; \
+ AA_BUG_PREEMPT_ENABLED("__get_buffer without preempt disabled"); \
+ __cpu_var = this_cpu_ptr(&aa_buffers); \
+ __cpu_var->buf[(N)]; })
+
+#define __get_buffers(X...) EVAL(__get_buffer, X)
+
+#define __put_buffers(X, Y...) ((void)&(X))
+
+#define get_buffers(X...) \
+do { \
+ preempt_disable(); \
+ __get_buffers(X); \
+} while (0)
+
+#define put_buffers(X, Y...) \
+do { \
+ __put_buffers(X, Y); \
+ preempt_enable(); \
+} while (0)
+
#endif /* __AA_PATH_H */
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
index 52275f0..67bc96a 100644
--- a/security/apparmor/include/policy.h
+++ b/security/apparmor/include/policy.h
@@ -18,6 +18,7 @@
#include <linux/capability.h>
#include <linux/cred.h>
#include <linux/kref.h>
+#include <linux/rhashtable.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/socket.h>
@@ -27,8 +28,14 @@
#include "capability.h"
#include "domain.h"
#include "file.h"
+#include "lib.h"
#include "resource.h"
+
+struct aa_ns;
+
+extern int unprivileged_userns_apparmor_policy;
+
extern const char *const aa_profile_mode_names[];
#define APPARMOR_MODE_NAMES_MAX_INDEX 4
@@ -42,7 +49,7 @@ extern const char *const aa_profile_mode_names[];
#define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT)
-#define PROFILE_INVALID(_profile) ((_profile)->flags & PFLAG_INVALID)
+#define profile_is_stale(_profile) ((_profile)->flags & PFLAG_STALE)
#define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2)
@@ -67,7 +74,7 @@ enum profile_flags {
PFLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */
PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */
PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */
- PFLAG_INVALID = 0x200, /* profile replaced/removed */
+ PFLAG_STALE = 0x200, /* profile replaced/removed */
PFLAG_NS_COUNT = 0x400, /* carries NS ref count */
/* These flags must correspond with PATH_flags */
@@ -76,70 +83,6 @@ enum profile_flags {
struct aa_profile;
-/* struct aa_policy - common part of both namespaces and profiles
- * @name: name of the object
- * @hname - The hierarchical name
- * @list: list policy object is on
- * @profiles: head of the profiles list contained in the object
- */
-struct aa_policy {
- char *name;
- char *hname;
- struct list_head list;
- struct list_head profiles;
-};
-
-/* struct aa_ns_acct - accounting of profiles in namespace
- * @max_size: maximum space allowed for all profiles in namespace
- * @max_count: maximum number of profiles that can be in this namespace
- * @size: current size of profiles
- * @count: current count of profiles (includes null profiles)
- */
-struct aa_ns_acct {
- int max_size;
- int max_count;
- int size;
- int count;
-};
-
-/* struct aa_namespace - namespace for a set of profiles
- * @base: common policy
- * @parent: parent of namespace
- * @lock: lock for modifying the object
- * @acct: accounting for the namespace
- * @unconfined: special unconfined profile for the namespace
- * @sub_ns: list of namespaces under the current namespace.
- * @uniq_null: uniq value used for null learning profiles
- * @uniq_id: a unique id count for the profiles in the namespace
- * @dents: dentries for the namespaces file entries in apparmorfs
- *
- * An aa_namespace defines the set profiles that are searched to determine
- * which profile to attach to a task. Profiles can not be shared between
- * aa_namespaces and profile names within a namespace are guaranteed to be
- * unique. When profiles in separate namespaces have the same name they
- * are NOT considered to be equivalent.
- *
- * Namespaces are hierarchical and only namespaces and profiles below the
- * current namespace are visible.
- *
- * Namespace names must be unique and can not contain the characters :/\0
- *
- * FIXME TODO: add vserver support of namespaces (can it all be done in
- * userspace?)
- */
-struct aa_namespace {
- struct aa_policy base;
- struct aa_namespace *parent;
- struct mutex lock;
- struct aa_ns_acct acct;
- struct aa_profile *unconfined;
- struct list_head sub_ns;
- atomic_t uniq_null;
- long uniq_id;
-
- struct dentry *dents[AAFS_NS_SIZEOF];
-};
-
/* struct aa_policydb - match engine for a policy
* dfa: dfa pattern match
* start: set of start states for the different classes of data
@@ -151,11 +94,24 @@ struct aa_policydb {
};
-struct aa_replacedby {
+struct aa_proxy {
struct kref count;
struct aa_profile __rcu *profile;
};
+/* struct aa_data - generic data structure
+ * key: name for retrieving this data
+ * size: size of data in bytes
+ * data: binary data
+ * head: reserved for rhashtable
+ */
+struct aa_data {
+ char *key;
+ u32 size;
+ char *data;
+ struct rhash_head head;
+};
+
/* struct aa_profile - basic confinement data
* @base - base components of the profile (name, refcount, lists, lock ...)
@@ -163,7 +119,7 @@ struct aa_replacedby {
* @rcu: rcu head used when removing from @list
* @parent: parent of profile
* @ns: namespace the profile is in
- * @replacedby: is set to the profile that replaced this profile
+ * @proxy: is set to the profile that replaced this profile
* @rename: optional profile name that this profile renamed
* @attach: human readable attachment string
* @xmatch: optional extended matching for unconfined executables names
@@ -180,13 +136,14 @@ struct aa_replacedby {
*
* @dents: dentries for the profiles file entries in apparmorfs
* @dirname: name of the profile dir in apparmorfs
+ * @data: hashtable for free-form policy aa_data
*
* The AppArmor profile contains the basic confinement data. Each profile
* has a name, and exists in a namespace. The @name and @exec_match are
* used to determine profile attachment against unconfined tasks. All other
* attachments are determined by profile X transition rules.
*
- * The @replacedby struct is write protected by the profile lock.
+ * The @proxy struct is write protected by the profile lock.
*
* Profiles have a hierarchy where hats and children profiles keep
* a reference to their parent.
@@ -201,8 +158,8 @@ struct aa_profile {
struct rcu_head rcu;
struct aa_profile __rcu *parent;
- struct aa_namespace *ns;
- struct aa_replacedby *replacedby;
+ struct aa_ns *ns;
+ struct aa_proxy *proxy;
const char *rename;
const char *attach;
@@ -219,37 +176,39 @@ struct aa_profile {
struct aa_caps caps;
struct aa_rlimit rlimits;
+ struct aa_loaddata *rawdata;
unsigned char *hash;
char *dirname;
struct dentry *dents[AAFS_PROF_SIZEOF];
+ struct rhashtable *data;
};
-extern struct aa_namespace *root_ns;
extern enum profile_mode aa_g_profile_mode;
-void aa_add_profile(struct aa_policy *common, struct aa_profile *profile);
-
-bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view);
-const char *aa_ns_name(struct aa_namespace *parent, struct aa_namespace *child);
-int aa_alloc_root_ns(void);
-void aa_free_root_ns(void);
-void aa_free_namespace_kref(struct kref *kref);
+void __aa_update_proxy(struct aa_profile *orig, struct aa_profile *new);
-struct aa_namespace *aa_find_namespace(struct aa_namespace *root,
- const char *name);
+void aa_add_profile(struct aa_policy *common, struct aa_profile *profile);
-void aa_free_replacedby_kref(struct kref *kref);
-struct aa_profile *aa_alloc_profile(const char *name);
-struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat);
+void aa_free_proxy_kref(struct kref *kref);
+struct aa_profile *aa_alloc_profile(const char *name, gfp_t gfp);
+struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat,
+ const char *base, gfp_t gfp);
void aa_free_profile(struct aa_profile *profile);
void aa_free_profile_kref(struct kref *kref);
struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name);
-struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *name);
-struct aa_profile *aa_match_profile(struct aa_namespace *ns, const char *name);
-
-ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace);
-ssize_t aa_remove_profiles(char *name, size_t size);
+struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname,
+ size_t n);
+struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *name);
+struct aa_profile *aa_fqlookupn_profile(struct aa_profile *base,
+ const char *fqname, size_t n);
+struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name);
+
+ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_profile *profile,
+ bool noreplace, struct aa_loaddata *udata);
+ssize_t aa_remove_profiles(struct aa_ns *view, struct aa_profile *profile,
+ char *name, size_t size);
+void __aa_profile_list_release(struct list_head *head);
#define PROF_ADD 1
#define PROF_REPLACE 0
@@ -257,12 +216,6 @@ ssize_t aa_remove_profiles(char *name, size_t size);
#define unconfined(X) ((X)->mode == APPARMOR_UNCONFINED)
-static inline struct aa_profile *aa_deref_parent(struct aa_profile *p)
-{
- return rcu_dereference_protected(p->parent,
- mutex_is_locked(&p->ns->lock));
-}
-
/**
* aa_get_profile - increment refcount on profile @p
* @p: profile (MAYBE NULL)
@@ -287,7 +240,7 @@ static inline struct aa_profile *aa_get_profile(struct aa_profile *p)
*/
static inline struct aa_profile *aa_get_profile_not0(struct aa_profile *p)
{
- if (p && kref_get_not0(&p->count))
+ if (p && kref_get_unless_zero(&p->count))
return p;
return NULL;
@@ -307,7 +260,7 @@ static inline struct aa_profile *aa_get_profile_rcu(struct aa_profile __rcu **p)
rcu_read_lock();
do {
c = rcu_dereference(*p);
- } while (c && !kref_get_not0(&c->count));
+ } while (c && !kref_get_unless_zero(&c->count));
rcu_read_unlock();
return c;
@@ -326,8 +279,8 @@ static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p)
if (!p)
return NULL;
- if (PROFILE_INVALID(p))
- return aa_get_profile_rcu(&p->replacedby->profile);
+ if (profile_is_stale(p))
+ return aa_get_profile_rcu(&p->proxy->profile);
return aa_get_profile(p);
}
@@ -342,7 +295,7 @@ static inline void aa_put_profile(struct aa_profile *p)
kref_put(&p->count, aa_free_profile_kref);
}
-static inline struct aa_replacedby *aa_get_replacedby(struct aa_replacedby *p)
+static inline struct aa_proxy *aa_get_proxy(struct aa_proxy *p)
{
if (p)
kref_get(&(p->count));
@@ -350,49 +303,10 @@ static inline struct aa_replacedby *aa_get_replacedby(struct aa_replacedby *p)
return p;
}
-static inline void aa_put_replacedby(struct aa_replacedby *p)
+static inline void aa_put_proxy(struct aa_proxy *p)
{
if (p)
- kref_put(&p->count, aa_free_replacedby_kref);
-}
-
-/* requires profile list write lock held */
-static inline void __aa_update_replacedby(struct aa_profile *orig,
- struct aa_profile *new)
-{
- struct aa_profile *tmp;
- tmp = rcu_dereference_protected(orig->replacedby->profile,
- mutex_is_locked(&orig->ns->lock));
- rcu_assign_pointer(orig->replacedby->profile, aa_get_profile(new));
- orig->flags |= PFLAG_INVALID;
- aa_put_profile(tmp);
-}
-
-/**
- * aa_get_namespace - increment references count on @ns
- * @ns: namespace to increment reference count of (MAYBE NULL)
- *
- * Returns: pointer to @ns, if @ns is NULL returns NULL
- * Requires: @ns must be held with valid refcount when called
- */
-static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns)
-{
- if (ns)
- aa_get_profile(ns->unconfined);
-
- return ns;
-}
-
-/**
- * aa_put_namespace - decrement refcount on @ns
- * @ns: namespace to put reference of
- *
- * Decrement reference count of @ns and if no longer in use free it
- */
-static inline void aa_put_namespace(struct aa_namespace *ns)
-{
- if (ns)
- aa_put_profile(ns->unconfined);
+ kref_put(&p->count, aa_free_proxy_kref);
}
static inline int AUDIT_MODE(struct aa_profile *profile)
@@ -403,8 +317,9 @@ static inline int AUDIT_MODE(struct aa_profile *profile)
return profile->audit;
}
-bool policy_view_capable(void);
-bool policy_admin_capable(void);
-bool aa_may_manage_policy(int op);
+bool policy_view_capable(struct aa_ns *ns);
+bool policy_admin_capable(struct aa_ns *ns);
+int aa_may_manage_policy(struct aa_profile *profile, struct aa_ns *ns,
+ const char *op);
#endif /* __AA_POLICY_H */
diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h
new file mode 100644
index 0000000..89cffdd
--- /dev/null
+++ b/security/apparmor/include/policy_ns.h
@@ -0,0 +1,147 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor policy definitions.
+ *
+ * Copyright (C) 1998-2008 Novell/SUSE
+ * Copyright 2009-2017 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_NAMESPACE_H
+#define __AA_NAMESPACE_H
+
+#include <linux/kref.h>
+
+#include "apparmor.h"
+#include "apparmorfs.h"
+#include "policy.h"
+
+
+/* struct aa_ns_acct - accounting of profiles in namespace
+ * @max_size: maximum space allowed for all profiles in namespace
+ * @max_count: maximum number of profiles that can be in this namespace
+ * @size: current size of profiles
+ * @count: current count of profiles (includes null profiles)
+ */
+struct aa_ns_acct {
+ int max_size;
+ int max_count;
+ int size;
+ int count;
+};
+
+/* struct aa_ns - namespace for a set of profiles
+ * @base: common policy
+ * @parent: parent of namespace
+ * @lock: lock for modifying the object
+ * @acct: accounting for the namespace
+ * @unconfined: special unconfined profile for the namespace
+ * @sub_ns: list of namespaces under the current namespace.
+ * @uniq_null: uniq value used for null learning profiles
+ * @uniq_id: a unique id count for the profiles in the namespace
+ * @level: level of ns within the tree hierarchy
+ * @dents: dentries for the namespaces file entries in apparmorfs
+ *
+ * An aa_ns defines the set profiles that are searched to determine which
+ * profile to attach to a task. Profiles can not be shared between aa_ns
+ * and profile names within a namespace are guaranteed to be unique. When
+ * profiles in separate namespaces have the same name they are NOT considered
+ * to be equivalent.
+ *
+ * Namespaces are hierarchical and only namespaces and profiles below the
+ * current namespace are visible.
+ *
+ * Namespace names must be unique and can not contain the characters :/\0
+ */
+struct aa_ns {
+ struct aa_policy base;
+ struct aa_ns *parent;
+ struct mutex lock;
+ struct aa_ns_acct acct;
+ struct aa_profile *unconfined;
+ struct list_head sub_ns;
+ atomic_t uniq_null;
+ long uniq_id;
+ int level;
+
+ struct dentry *dents[AAFS_NS_SIZEOF];
+};
+
+extern struct aa_ns *root_ns;
+
+extern const char *aa_hidden_ns_name;
+
+bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view, bool subns);
+const char *aa_ns_name(struct aa_ns *parent, struct aa_ns *child, bool subns);
+void aa_free_ns(struct aa_ns *ns);
+int aa_alloc_root_ns(void);
+void aa_free_root_ns(void);
+void aa_free_ns_kref(struct kref *kref);
+
+struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name);
+struct aa_ns *aa_findn_ns(struct aa_ns *root, const char *name, size_t n);
+struct aa_ns *__aa_find_or_create_ns(struct aa_ns *parent, const char *name,
+ struct dentry *dir);
+struct aa_ns *aa_prepare_ns(struct aa_ns *root, const char *name);
+void __aa_remove_ns(struct aa_ns *ns);
+
+static inline struct aa_profile *aa_deref_parent(struct aa_profile *p)
+{
+ return rcu_dereference_protected(p->parent,
+ mutex_is_locked(&p->ns->lock));
+}
+
+/**
+ * aa_get_ns - increment references count on @ns
+ * @ns: namespace to increment reference count of (MAYBE NULL)
+ *
+ * Returns: pointer to @ns, if @ns is NULL returns NULL
+ * Requires: @ns must be held with valid refcount when called
+ */
+static inline struct aa_ns *aa_get_ns(struct aa_ns *ns)
+{
+ if (ns)
+ aa_get_profile(ns->unconfined);
+
+ return ns;
+}
+
+/**
+ * aa_put_ns - decrement refcount on @ns
+ * @ns: namespace to put reference of
+ *
+ * Decrement reference count of @ns and if no longer in use free it
+ */
+static inline void aa_put_ns(struct aa_ns *ns)
+{
+ if (ns)
+ aa_put_profile(ns->unconfined);
+}
+
+/**
+ * __aa_findn_ns - find a namespace on a list by @name
+ * @head: list to search for namespace on (NOT NULL)
+ * @name: name of namespace to look for (NOT NULL)
+ * @n: length of @name
+ * Returns: unrefcounted namespace
+ *
+ * Requires: rcu_read_lock be held
+ */
+static inline struct aa_ns *__aa_findn_ns(struct list_head *head,
+ const char *name, size_t n)
+{
+ return (struct aa_ns *)__policy_strn_find(head, name, n);
+}
+
+static inline struct aa_ns *__aa_find_ns(struct list_head *head,
+ const char *name)
+{
+ return __aa_findn_ns(head, name, strlen(name));
+}
+
+#endif /* AA_NAMESPACE_H */
diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h
index c214fb8..4c1319e 100644
--- a/security/apparmor/include/policy_unpack.h
+++ b/security/apparmor/include/policy_unpack.h
@@ -16,12 +16,14 @@
#define __POLICY_INTERFACE_H
#include <linux/list.h>
+#include <linux/kref.h>
struct aa_load_ent {
struct list_head list;
struct aa_profile *new;
struct aa_profile *old;
struct aa_profile *rename;
+ const char *ns_name;
};
void aa_load_ent_free(struct aa_load_ent *ent);
@@ -34,6 +36,30 @@ struct aa_load_ent *aa_load_ent_alloc(void);
#define PACKED_MODE_KILL 2
#define PACKED_MODE_UNCONFINED 3
-int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns);
+/* struct aa_loaddata - buffer of policy load data set */
+struct aa_loaddata {
+ struct kref count;
+ size_t size;
+ int abi;
+ unsigned char *hash;
+ char data[];
+};
+
+int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, const char **ns);
+
+static inline struct aa_loaddata *
+aa_get_loaddata(struct aa_loaddata *data)
+{
+ if (data)
+ kref_get(&(data->count));
+ return data;
+}
+
+void aa_loaddata_kref(struct kref *kref);
+static inline void aa_put_loaddata(struct aa_loaddata *data)
+{
+ if (data)
+ kref_put(&data->count, aa_loaddata_kref);
+}
#endif /* __POLICY_INTERFACE_H */
diff --git a/security/apparmor/include/sid.h b/security/apparmor/include/secid.h
index 513ca0e..95ed86a 100644
--- a/security/apparmor/include/sid.h
+++ b/security/apparmor/include/secid.h
@@ -1,7 +1,7 @@
/*
* AppArmor security module
*
- * This file contains AppArmor security identifier (sid) definitions
+ * This file contains AppArmor security identifier (secid) definitions
*
* Copyright 2009-2010 Canonical Ltd.
*
@@ -11,16 +11,16 @@
* License.
*/
-#ifndef __AA_SID_H
-#define __AA_SID_H
+#ifndef __AA_SECID_H
+#define __AA_SECID_H
#include <linux/types.h>
-/* sid value that will not be allocated */
-#define AA_SID_INVALID 0
-#define AA_SID_ALLOC AA_SID_INVALID
+/* secid value that will not be allocated */
+#define AA_SECID_INVALID 0
+#define AA_SECID_ALLOC AA_SECID_INVALID
-u32 aa_alloc_sid(void);
-void aa_free_sid(u32 sid);
+u32 aa_alloc_secid(void);
+void aa_free_secid(u32 secid);
-#endif /* __AA_SID_H */
+#endif /* __AA_SECID_H */
diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c
index 777ac1c..edac790 100644
--- a/security/apparmor/ipc.c
+++ b/security/apparmor/ipc.c
@@ -25,8 +25,8 @@
static void audit_cb(struct audit_buffer *ab, void *va)
{
struct common_audit_data *sa = va;
- audit_log_format(ab, " target=");
- audit_log_untrustedstring(ab, sa->aad->target);
+ audit_log_format(ab, " peer=");
+ audit_log_untrustedstring(ab, aad(sa)->peer->base.hname);
}
/**
@@ -40,16 +40,12 @@ static void audit_cb(struct audit_buffer *ab, void *va)
static int aa_audit_ptrace(struct aa_profile *profile,
struct aa_profile *target, int error)
{
- struct common_audit_data sa;
- struct apparmor_audit_data aad = {0,};
- sa.type = LSM_AUDIT_DATA_NONE;
- sa.aad = &aad;
- aad.op = OP_PTRACE;
- aad.target = target;
- aad.error = error;
+ DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_PTRACE);
- return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_ATOMIC, &sa,
- audit_cb);
+ aad(&sa)->peer = target;
+ aad(&sa)->error = error;
+
+ return aa_audit(AUDIT_APPARMOR_AUTO, profile, &sa, audit_cb);
}
/**
diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c
index c1827e0..66475bd 100644
--- a/security/apparmor/lib.c
+++ b/security/apparmor/lib.c
@@ -12,6 +12,7 @@
* License.
*/
+#include <linux/ctype.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/string.h>
@@ -19,7 +20,8 @@
#include "include/audit.h"
#include "include/apparmor.h"
-
+#include "include/lib.h"
+#include "include/policy.h"
/**
* aa_split_fqname - split a fqname into a profile and namespace name
@@ -60,17 +62,67 @@ char *aa_split_fqname(char *fqname, char **ns_name)
}
/**
+ * skipn_spaces - Removes leading whitespace from @str.
+ * @str: The string to be stripped.
+ *
+ * Returns a pointer to the first non-whitespace character in @str.
+ * if all whitespace will return NULL
+ */
+
+static const char *skipn_spaces(const char *str, size_t n)
+{
+ for (; n && isspace(*str); --n)
+ ++str;
+ if (n)
+ return (char *)str;
+ return NULL;
+}
+
+const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
+ size_t *ns_len)
+{
+ const char *end = fqname + n;
+ const char *name = skipn_spaces(fqname, n);
+
+ if (!name)
+ return NULL;
+ *ns_name = NULL;
+ *ns_len = 0;
+ if (name[0] == ':') {
+ char *split = strnchr(&name[1], end - &name[1], ':');
+ *ns_name = skipn_spaces(&name[1], end - &name[1]);
+ if (!*ns_name)
+ return NULL;
+ if (split) {
+ *ns_len = split - *ns_name;
+ if (*ns_len == 0)
+ *ns_name = NULL;
+ split++;
+ if (end - split > 1 && strncmp(split, "//", 2) == 0)
+ split += 2;
+ name = skipn_spaces(split, end - split);
+ } else {
+ /* a ns name without a following profile is allowed */
+ name = NULL;
+ *ns_len = end - *ns_name;
+ }
+ }
+ if (name && *name == 0)
+ name = NULL;
+
+ return name;
+}
+
+/**
* aa_info_message - log a none profile related status message
* @str: message to log
*/
void aa_info_message(const char *str)
{
if (audit_enabled) {
- struct common_audit_data sa;
- struct apparmor_audit_data aad = {0,};
- sa.type = LSM_AUDIT_DATA_NONE;
- sa.aad = &aad;
- aad.info = str;
+ DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL);
+
+ aad(&sa)->info = str;
aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL);
}
printk(KERN_INFO "AppArmor: %s\n", str);
@@ -95,7 +147,8 @@ void *__aa_kvmalloc(size_t size, gfp_t flags)
/* do not attempt kmalloc if we need more than 16 pages at once */
if (size <= (16*PAGE_SIZE))
- buffer = kmalloc(size, flags | GFP_NOIO | __GFP_NOWARN);
+ buffer = kmalloc(size, flags | GFP_KERNEL | __GFP_NORETRY |
+ __GFP_NOWARN);
if (!buffer) {
if (flags & __GFP_ZERO)
buffer = vzalloc(size);
@@ -104,3 +157,47 @@ void *__aa_kvmalloc(size_t size, gfp_t flags)
}
return buffer;
}
+
+/**
+ * aa_policy_init - initialize a policy structure
+ * @policy: policy to initialize (NOT NULL)
+ * @prefix: prefix name if any is required. (MAYBE NULL)
+ * @name: name of the policy, init will make a copy of it (NOT NULL)
+ *
+ * Note: this fn creates a copy of strings passed in
+ *
+ * Returns: true if policy init successful
+ */
+bool aa_policy_init(struct aa_policy *policy, const char *prefix,
+ const char *name, gfp_t gfp)
+{
+ /* freed by policy_free */
+ if (prefix) {
+ policy->hname = kmalloc(strlen(prefix) + strlen(name) + 3,
+ gfp);
+ if (policy->hname)
+ sprintf((char *)policy->hname, "%s//%s", prefix, name);
+ } else
+ policy->hname = kstrdup(name, gfp);
+ if (!policy->hname)
+ return 0;
+ /* base.name is a substring of fqname */
+ policy->name = basename(policy->hname);
+ INIT_LIST_HEAD(&policy->list);
+ INIT_LIST_HEAD(&policy->profiles);
+
+ return 1;
+}
+
+/**
+ * aa_policy_destroy - free the elements referenced by @policy
+ * @policy: policy that is to have its elements freed (NOT NULL)
+ */
+void aa_policy_destroy(struct aa_policy *policy)
+{
+ AA_BUG(on_list_rcu(&policy->profiles));
+ AA_BUG(on_list_rcu(&policy->list));
+
+ /* don't free name as its a subset of hname */
+ kzfree(policy->hname);
+}
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 41b8cb1..709eacd 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -23,6 +23,7 @@
#include <linux/sysctl.h>
#include <linux/audit.h>
#include <linux/user_namespace.h>
+#include <linux/kmemleak.h>
#include <net/sock.h>
#include "include/apparmor.h"
@@ -34,22 +35,26 @@
#include "include/ipc.h"
#include "include/path.h"
#include "include/policy.h"
+#include "include/policy_ns.h"
#include "include/procattr.h"
/* Flag indicating whether initialization completed */
int apparmor_initialized __initdata;
+DEFINE_PER_CPU(struct aa_buffers, aa_buffers);
+
+
/*
* LSM hook functions
*/
/*
- * free the associated aa_task_cxt and put its profiles
+ * free the associated aa_task_ctx and put its profiles
*/
static void apparmor_cred_free(struct cred *cred)
{
- aa_free_task_context(cred_cxt(cred));
- cred_cxt(cred) = NULL;
+ aa_free_task_context(cred_ctx(cred));
+ cred_ctx(cred) = NULL;
}
/*
@@ -58,27 +63,29 @@ static void apparmor_cred_free(struct cred *cred)
static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp)
{
/* freed by apparmor_cred_free */
- struct aa_task_cxt *cxt = aa_alloc_task_context(gfp);
- if (!cxt)
+ struct aa_task_ctx *ctx = aa_alloc_task_context(gfp);
+
+ if (!ctx)
return -ENOMEM;
- cred_cxt(cred) = cxt;
+ cred_ctx(cred) = ctx;
return 0;
}
/*
- * prepare new aa_task_cxt for modification by prepare_cred block
+ * prepare new aa_task_ctx for modification by prepare_cred block
*/
static int apparmor_cred_prepare(struct cred *new, const struct cred *old,
gfp_t gfp)
{
/* freed by apparmor_cred_free */
- struct aa_task_cxt *cxt = aa_alloc_task_context(gfp);
- if (!cxt)
+ struct aa_task_ctx *ctx = aa_alloc_task_context(gfp);
+
+ if (!ctx)
return -ENOMEM;
- aa_dup_task_context(cxt, cred_cxt(old));
- cred_cxt(new) = cxt;
+ aa_dup_task_context(ctx, cred_ctx(old));
+ cred_ctx(new) = ctx;
return 0;
}
@@ -87,10 +94,10 @@ static int apparmor_cred_prepare(struct cred *new, const struct cred *old,
*/
static void apparmor_cred_transfer(struct cred *new, const struct cred *old)
{
- const struct aa_task_cxt *old_cxt = cred_cxt(old);
- struct aa_task_cxt *new_cxt = cred_cxt(new);
+ const struct aa_task_ctx *old_ctx = cred_ctx(old);
+ struct aa_task_ctx *new_ctx = cred_ctx(new);
- aa_dup_task_context(new_cxt, old_cxt);
+ aa_dup_task_context(new_ctx, old_ctx);
}
static int apparmor_ptrace_access_check(struct task_struct *child,
@@ -149,7 +156,7 @@ static int apparmor_capable(const struct cred *cred, struct user_namespace *ns,
*
* Returns: %0 else error code if error or permission denied
*/
-static int common_perm(int op, const struct path *path, u32 mask,
+static int common_perm(const char *op, const struct path *path, u32 mask,
struct path_cond *cond)
{
struct aa_profile *profile;
@@ -163,41 +170,42 @@ static int common_perm(int op, const struct path *path, u32 mask,
}
/**
- * common_perm_dir_dentry - common permission wrapper when path is dir, dentry
+ * common_perm_cond - common permission wrapper around inode cond
* @op: operation being checked
- * @dir: directory of the dentry (NOT NULL)
- * @dentry: dentry to check (NOT NULL)
+ * @path: location to check (NOT NULL)
* @mask: requested permissions mask
- * @cond: conditional info for the permission request (NOT NULL)
*
* Returns: %0 else error code if error or permission denied
*/
-static int common_perm_dir_dentry(int op, const struct path *dir,
- struct dentry *dentry, u32 mask,
- struct path_cond *cond)
+static int common_perm_cond(const char *op, const struct path *path, u32 mask)
{
- struct path path = { dir->mnt, dentry };
+ struct path_cond cond = { d_backing_inode(path->dentry)->i_uid,
+ d_backing_inode(path->dentry)->i_mode
+ };
- return common_perm(op, &path, mask, cond);
+ if (!path_mediated_fs(path->dentry))
+ return 0;
+
+ return common_perm(op, path, mask, &cond);
}
/**
- * common_perm_path - common permission wrapper when mnt, dentry
+ * common_perm_dir_dentry - common permission wrapper when path is dir, dentry
* @op: operation being checked
- * @path: location to check (NOT NULL)
+ * @dir: directory of the dentry (NOT NULL)
+ * @dentry: dentry to check (NOT NULL)
* @mask: requested permissions mask
+ * @cond: conditional info for the permission request (NOT NULL)
*
* Returns: %0 else error code if error or permission denied
*/
-static inline int common_perm_path(int op, const struct path *path, u32 mask)
+static int common_perm_dir_dentry(const char *op, const struct path *dir,
+ struct dentry *dentry, u32 mask,
+ struct path_cond *cond)
{
- struct path_cond cond = { d_backing_inode(path->dentry)->i_uid,
- d_backing_inode(path->dentry)->i_mode
- };
- if (!mediated_filesystem(path->dentry))
- return 0;
+ struct path path = { .mnt = dir->mnt, .dentry = dentry };
- return common_perm(op, path, mask, &cond);
+ return common_perm(op, &path, mask, cond);
}
/**
@@ -209,13 +217,13 @@ static inline int common_perm_path(int op, const struct path *path, u32 mask)
*
* Returns: %0 else error code if error or permission denied
*/
-static int common_perm_rm(int op, const struct path *dir,
+static int common_perm_rm(const char *op, const struct path *dir,
struct dentry *dentry, u32 mask)
{
struct inode *inode = d_backing_inode(dentry);
struct path_cond cond = { };
- if (!inode || !mediated_filesystem(dentry))
+ if (!inode || !path_mediated_fs(dentry))
return 0;
cond.uid = inode->i_uid;
@@ -234,12 +242,12 @@ static int common_perm_rm(int op, const struct path *dir,
*
* Returns: %0 else error code if error or permission denied
*/
-static int common_perm_create(int op, const struct path *dir,
+static int common_perm_create(const char *op, const struct path *dir,
struct dentry *dentry, u32 mask, umode_t mode)
{
struct path_cond cond = { current_fsuid(), mode };
- if (!mediated_filesystem(dir->dentry))
+ if (!path_mediated_fs(dir->dentry))
return 0;
return common_perm_dir_dentry(op, dir, dentry, mask, &cond);
@@ -270,7 +278,7 @@ static int apparmor_path_mknod(const struct path *dir, struct dentry *dentry,
static int apparmor_path_truncate(const struct path *path)
{
- return common_perm_path(OP_TRUNC, path, MAY_WRITE | AA_MAY_META_WRITE);
+ return common_perm_cond(OP_TRUNC, path, MAY_WRITE | AA_MAY_META_WRITE);
}
static int apparmor_path_symlink(const struct path *dir, struct dentry *dentry,
@@ -286,7 +294,7 @@ static int apparmor_path_link(struct dentry *old_dentry, const struct path *new_
struct aa_profile *profile;
int error = 0;
- if (!mediated_filesystem(old_dentry))
+ if (!path_mediated_fs(old_dentry))
return 0;
profile = aa_current_profile();
@@ -301,13 +309,15 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d
struct aa_profile *profile;
int error = 0;
- if (!mediated_filesystem(old_dentry))
+ if (!path_mediated_fs(old_dentry))
return 0;
profile = aa_current_profile();
if (!unconfined(profile)) {
- struct path old_path = { old_dir->mnt, old_dentry };
- struct path new_path = { new_dir->mnt, new_dentry };
+ struct path old_path = { .mnt = old_dir->mnt,
+ .dentry = old_dentry };
+ struct path new_path = { .mnt = new_dir->mnt,
+ .dentry = new_dentry };
struct path_cond cond = { d_backing_inode(old_dentry)->i_uid,
d_backing_inode(old_dentry)->i_mode
};
@@ -327,26 +337,26 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d
static int apparmor_path_chmod(const struct path *path, umode_t mode)
{
- return common_perm_path(OP_CHMOD, path, AA_MAY_CHMOD);
+ return common_perm_cond(OP_CHMOD, path, AA_MAY_CHMOD);
}
static int apparmor_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
{
- return common_perm_path(OP_CHOWN, path, AA_MAY_CHOWN);
+ return common_perm_cond(OP_CHOWN, path, AA_MAY_CHOWN);
}
static int apparmor_inode_getattr(const struct path *path)
{
- return common_perm_path(OP_GETATTR, path, AA_MAY_META_READ);
+ return common_perm_cond(OP_GETATTR, path, AA_MAY_META_READ);
}
static int apparmor_file_open(struct file *file, const struct cred *cred)
{
- struct aa_file_cxt *fcxt = file->f_security;
+ struct aa_file_ctx *fctx = file->f_security;
struct aa_profile *profile;
int error = 0;
- if (!mediated_filesystem(file->f_path.dentry))
+ if (!path_mediated_fs(file->f_path.dentry))
return 0;
/* If in exec, permission is handled by bprm hooks.
@@ -355,7 +365,7 @@ static int apparmor_file_open(struct file *file, const struct cred *cred)
* actually execute the image.
*/
if (current->in_execve) {
- fcxt->allow = MAY_EXEC | MAY_READ | AA_EXEC_MMAP;
+ fctx->allow = MAY_EXEC | MAY_READ | AA_EXEC_MMAP;
return 0;
}
@@ -367,7 +377,7 @@ static int apparmor_file_open(struct file *file, const struct cred *cred)
error = aa_path_perm(OP_OPEN, profile, &file->f_path, 0,
aa_map_file_to_perms(file), &cond);
/* todo cache full allowed permissions set and state */
- fcxt->allow = aa_map_file_to_perms(file);
+ fctx->allow = aa_map_file_to_perms(file);
}
return error;
@@ -385,21 +395,21 @@ static int apparmor_file_alloc_security(struct file *file)
static void apparmor_file_free_security(struct file *file)
{
- struct aa_file_cxt *cxt = file->f_security;
+ struct aa_file_ctx *ctx = file->f_security;
- aa_free_file_context(cxt);
+ aa_free_file_context(ctx);
}
-static int common_file_perm(int op, struct file *file, u32 mask)
+static int common_file_perm(const char *op, struct file *file, u32 mask)
{
- struct aa_file_cxt *fcxt = file->f_security;
+ struct aa_file_ctx *fctx = file->f_security;
struct aa_profile *profile, *fprofile = aa_cred_profile(file->f_cred);
int error = 0;
- BUG_ON(!fprofile);
+ AA_BUG(!fprofile);
if (!file->f_path.mnt ||
- !mediated_filesystem(file->f_path.dentry))
+ !path_mediated_fs(file->f_path.dentry))
return 0;
profile = __aa_current_profile();
@@ -412,7 +422,7 @@ static int common_file_perm(int op, struct file *file, u32 mask)
* delegation from unconfined tasks
*/
if (!unconfined(profile) && !unconfined(fprofile) &&
- ((fprofile != profile) || (mask & ~fcxt->allow)))
+ ((fprofile != profile) || (mask & ~fctx->allow)))
error = aa_file_perm(op, profile, file, mask);
return error;
@@ -433,7 +443,7 @@ static int apparmor_file_lock(struct file *file, unsigned int cmd)
return common_file_perm(OP_FLOCK, file, mask);
}
-static int common_mmap(int op, struct file *file, unsigned long prot,
+static int common_mmap(const char *op, struct file *file, unsigned long prot,
unsigned long flags)
{
int mask = 0;
@@ -474,15 +484,15 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
int error = -ENOENT;
/* released below */
const struct cred *cred = get_task_cred(task);
- struct aa_task_cxt *cxt = cred_cxt(cred);
+ struct aa_task_ctx *ctx = cred_ctx(cred);
struct aa_profile *profile = NULL;
if (strcmp(name, "current") == 0)
- profile = aa_get_newest_profile(cxt->profile);
- else if (strcmp(name, "prev") == 0 && cxt->previous)
- profile = aa_get_newest_profile(cxt->previous);
- else if (strcmp(name, "exec") == 0 && cxt->onexec)
- profile = aa_get_newest_profile(cxt->onexec);
+ profile = aa_get_newest_profile(ctx->profile);
+ else if (strcmp(name, "prev") == 0 && ctx->previous)
+ profile = aa_get_newest_profile(ctx->previous);
+ else if (strcmp(name, "exec") == 0 && ctx->onexec)
+ profile = aa_get_newest_profile(ctx->onexec);
else
error = -EINVAL;
@@ -495,20 +505,16 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
return error;
}
-static int apparmor_setprocattr(struct task_struct *task, char *name,
- void *value, size_t size)
+static int apparmor_setprocattr(const char *name, void *value,
+ size_t size)
{
- struct common_audit_data sa;
- struct apparmor_audit_data aad = {0,};
char *command, *largs = NULL, *args = value;
size_t arg_size;
int error;
+ DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SETPROCATTR);
if (size == 0)
return -EINVAL;
- /* task can only write its own attributes */
- if (current != task)
- return -EACCES;
/* AppArmor requires that the buffer must be null terminated atm */
if (args[size - 1] != '\0') {
@@ -538,17 +544,17 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
error = aa_setprocattr_changehat(args, arg_size,
AA_DO_TEST);
} else if (strcmp(command, "changeprofile") == 0) {
- error = aa_setprocattr_changeprofile(args, !AA_ONEXEC,
- !AA_DO_TEST);
+ error = aa_change_profile(args, !AA_ONEXEC,
+ !AA_DO_TEST, false);
} else if (strcmp(command, "permprofile") == 0) {
- error = aa_setprocattr_changeprofile(args, !AA_ONEXEC,
- AA_DO_TEST);
+ error = aa_change_profile(args, !AA_ONEXEC, AA_DO_TEST,
+ false);
} else
goto fail;
} else if (strcmp(name, "exec") == 0) {
if (strcmp(command, "exec") == 0)
- error = aa_setprocattr_changeprofile(args, AA_ONEXEC,
- !AA_DO_TEST);
+ error = aa_change_profile(args, AA_ONEXEC, !AA_DO_TEST,
+ false);
else
goto fail;
} else
@@ -562,12 +568,9 @@ out:
return error;
fail:
- sa.type = LSM_AUDIT_DATA_NONE;
- sa.aad = &aad;
- aad.profile = aa_current_profile();
- aad.op = OP_SETPROCATTR;
- aad.info = name;
- aad.error = error = -EINVAL;
+ aad(&sa)->profile = aa_current_profile();
+ aad(&sa)->info = name;
+ aad(&sa)->error = error = -EINVAL;
aa_audit_msg(AUDIT_APPARMOR_DENIED, &sa, NULL);
goto out;
}
@@ -671,14 +674,14 @@ enum profile_mode aa_g_profile_mode = APPARMOR_ENFORCE;
module_param_call(mode, param_set_mode, param_get_mode,
&aa_g_profile_mode, S_IRUSR | S_IWUSR);
-#ifdef CONFIG_SECURITY_APPARMOR_HASH
/* whether policy verification hashing is enabled */
bool aa_g_hash_policy = IS_ENABLED(CONFIG_SECURITY_APPARMOR_HASH_DEFAULT);
+#ifdef CONFIG_SECURITY_APPARMOR_HASH
module_param_named(hash_policy, aa_g_hash_policy, aabool, S_IRUSR | S_IWUSR);
#endif
/* Debug mode */
-bool aa_g_debug;
+bool aa_g_debug = IS_ENABLED(CONFIG_SECURITY_DEBUG_MESSAGES);
module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR);
/* Audit mode */
@@ -711,10 +714,11 @@ module_param_named(path_max, aa_g_path_max, aauint, S_IRUSR | S_IWUSR);
/* Determines how paranoid loading of policy is and how much verification
* on the loaded policy is done.
+ * DEPRECATED: read only as strict checking of load is always done now
+ * that none root users (user namespaces) can load policy.
*/
bool aa_g_paranoid_load = 1;
-module_param_named(paranoid_load, aa_g_paranoid_load, aabool,
- S_IRUSR | S_IWUSR);
+module_param_named(paranoid_load, aa_g_paranoid_load, aabool, S_IRUGO);
/* Boot time disable flag */
static bool apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE;
@@ -734,49 +738,59 @@ __setup("apparmor=", apparmor_enabled_setup);
/* set global flag turning off the ability to load policy */
static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp)
{
- if (!policy_admin_capable())
+ if (!policy_admin_capable(NULL))
return -EPERM;
return param_set_bool(val, kp);
}
static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp)
{
- if (!policy_view_capable())
+ if (!policy_view_capable(NULL))
return -EPERM;
+ if (!apparmor_enabled)
+ return -EINVAL;
return param_get_bool(buffer, kp);
}
static int param_set_aabool(const char *val, const struct kernel_param *kp)
{
- if (!policy_admin_capable())
+ if (!policy_admin_capable(NULL))
return -EPERM;
+ if (!apparmor_enabled)
+ return -EINVAL;
return param_set_bool(val, kp);
}
static int param_get_aabool(char *buffer, const struct kernel_param *kp)
{
- if (!policy_view_capable())
+ if (!policy_view_capable(NULL))
return -EPERM;
+ if (!apparmor_enabled)
+ return -EINVAL;
return param_get_bool(buffer, kp);
}
static int param_set_aauint(const char *val, const struct kernel_param *kp)
{
- if (!policy_admin_capable())
+ if (!policy_admin_capable(NULL))
return -EPERM;
+ if (!apparmor_enabled)
+ return -EINVAL;
return param_set_uint(val, kp);
}
static int param_get_aauint(char *buffer, const struct kernel_param *kp)
{
- if (!policy_view_capable())
+ if (!policy_view_capable(NULL))
return -EPERM;
+ if (!apparmor_enabled)
+ return -EINVAL;
return param_get_uint(buffer, kp);
}
static int param_get_audit(char *buffer, struct kernel_param *kp)
{
- if (!policy_view_capable())
+ if (!policy_view_capable(NULL))
return -EPERM;
if (!apparmor_enabled)
@@ -788,7 +802,7 @@ static int param_get_audit(char *buffer, struct kernel_param *kp)
static int param_set_audit(const char *val, struct kernel_param *kp)
{
int i;
- if (!policy_admin_capable())
+ if (!policy_admin_capable(NULL))
return -EPERM;
if (!apparmor_enabled)
@@ -809,7 +823,7 @@ static int param_set_audit(const char *val, struct kernel_param *kp)
static int param_get_mode(char *buffer, struct kernel_param *kp)
{
- if (!policy_admin_capable())
+ if (!policy_view_capable(NULL))
return -EPERM;
if (!apparmor_enabled)
@@ -821,7 +835,7 @@ static int param_get_mode(char *buffer, struct kernel_param *kp)
static int param_set_mode(const char *val, struct kernel_param *kp)
{
int i;
- if (!policy_admin_capable())
+ if (!policy_admin_capable(NULL))
return -EPERM;
if (!apparmor_enabled)
@@ -845,25 +859,102 @@ static int param_set_mode(const char *val, struct kernel_param *kp)
*/
/**
- * set_init_cxt - set a task context and profile on the first task.
+ * set_init_ctx - set a task context and profile on the first task.
*
* TODO: allow setting an alternate profile than unconfined
*/
-static int __init set_init_cxt(void)
+static int __init set_init_ctx(void)
{
struct cred *cred = (struct cred *)current->real_cred;
- struct aa_task_cxt *cxt;
+ struct aa_task_ctx *ctx;
- cxt = aa_alloc_task_context(GFP_KERNEL);
- if (!cxt)
+ ctx = aa_alloc_task_context(GFP_KERNEL);
+ if (!ctx)
return -ENOMEM;
- cxt->profile = aa_get_profile(root_ns->unconfined);
- cred_cxt(cred) = cxt;
+ ctx->profile = aa_get_profile(root_ns->unconfined);
+ cred_ctx(cred) = ctx;
return 0;
}
+static void destroy_buffers(void)
+{
+ u32 i, j;
+
+ for_each_possible_cpu(i) {
+ for_each_cpu_buffer(j) {
+ kfree(per_cpu(aa_buffers, i).buf[j]);
+ per_cpu(aa_buffers, i).buf[j] = NULL;
+ }
+ }
+}
+
+static int __init alloc_buffers(void)
+{
+ u32 i, j;
+
+ for_each_possible_cpu(i) {
+ for_each_cpu_buffer(j) {
+ char *buffer;
+
+ if (cpu_to_node(i) > num_online_nodes())
+ /* fallback to kmalloc for offline nodes */
+ buffer = kmalloc(aa_g_path_max, GFP_KERNEL);
+ else
+ buffer = kmalloc_node(aa_g_path_max, GFP_KERNEL,
+ cpu_to_node(i));
+ if (!buffer) {
+ destroy_buffers();
+ return -ENOMEM;
+ }
+ per_cpu(aa_buffers, i).buf[j] = buffer;
+ }
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_SYSCTL
+static int apparmor_dointvec(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ if (!policy_admin_capable(NULL))
+ return -EPERM;
+ if (!apparmor_enabled)
+ return -EINVAL;
+
+ return proc_dointvec(table, write, buffer, lenp, ppos);
+}
+
+static struct ctl_path apparmor_sysctl_path[] = {
+ { .procname = "kernel", },
+ { }
+};
+
+static struct ctl_table apparmor_sysctl_table[] = {
+ {
+ .procname = "unprivileged_userns_apparmor_policy",
+ .data = &unprivileged_userns_apparmor_policy,
+ .maxlen = sizeof(int),
+ .mode = 0600,
+ .proc_handler = apparmor_dointvec,
+ },
+ { }
+};
+
+static int __init apparmor_init_sysctl(void)
+{
+ return register_sysctl_paths(apparmor_sysctl_path,
+ apparmor_sysctl_table) ? 0 : -ENOMEM;
+}
+#else
+static inline int apparmor_init_sysctl(void)
+{
+ return 0;
+}
+#endif /* CONFIG_SYSCTL */
+
static int __init apparmor_init(void)
{
int error;
@@ -874,19 +965,39 @@ static int __init apparmor_init(void)
return 0;
}
+ error = aa_setup_dfa_engine();
+ if (error) {
+ AA_ERROR("Unable to setup dfa engine\n");
+ goto alloc_out;
+ }
+
error = aa_alloc_root_ns();
if (error) {
AA_ERROR("Unable to allocate default profile namespace\n");
goto alloc_out;
}
- error = set_init_cxt();
+ error = apparmor_init_sysctl();
+ if (error) {
+ AA_ERROR("Unable to register sysctls\n");
+ goto alloc_out;
+
+ }
+
+ error = alloc_buffers();
+ if (error) {
+ AA_ERROR("Unable to allocate work buffers\n");
+ goto buffers_out;
+ }
+
+ error = set_init_ctx();
if (error) {
AA_ERROR("Failed to set context on init task\n");
aa_free_root_ns();
- goto alloc_out;
+ goto buffers_out;
}
- security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks));
+ security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks),
+ "apparmor");
/* Report that AppArmor successfully initialized */
apparmor_initialized = 1;
@@ -899,8 +1010,12 @@ static int __init apparmor_init(void)
return error;
+buffers_out:
+ destroy_buffers();
+
alloc_out:
aa_destroy_aafs();
+ aa_teardown_dfa_engine();
apparmor_enabled = 0;
return error;
diff --git a/security/apparmor/match.c b/security/apparmor/match.c
index 3f900fc..eb0efef 100644
--- a/security/apparmor/match.c
+++ b/security/apparmor/match.c
@@ -20,11 +20,38 @@
#include <linux/err.h>
#include <linux/kref.h>
-#include "include/apparmor.h"
+#include "include/lib.h"
#include "include/match.h"
#define base_idx(X) ((X) & 0xffffff)
+static char nulldfa_src[] = {
+ #include "nulldfa.in"
+};
+struct aa_dfa *nulldfa;
+
+int aa_setup_dfa_engine(void)
+{
+ int error;
+
+ nulldfa = aa_dfa_unpack(nulldfa_src, sizeof(nulldfa_src),
+ TO_ACCEPT1_FLAG(YYTD_DATA32) |
+ TO_ACCEPT2_FLAG(YYTD_DATA32));
+ if (!IS_ERR(nulldfa))
+ return 0;
+
+ error = PTR_ERR(nulldfa);
+ nulldfa = NULL;
+
+ return error;
+}
+
+void aa_teardown_dfa_engine(void)
+{
+ aa_put_dfa(nulldfa);
+ nulldfa = NULL;
+}
+
/**
* unpack_table - unpack a dfa table (one of accept, default, base, next check)
* @blob: data to unpack (NOT NULL)
@@ -46,11 +73,11 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
/* loaded td_id's start at 1, subtract 1 now to avoid doing
* it every time we use td_id as an index
*/
- th.td_id = be16_to_cpu(*(u16 *) (blob)) - 1;
+ th.td_id = be16_to_cpu(*(__be16 *) (blob)) - 1;
if (th.td_id > YYTD_ID_MAX)
goto out;
- th.td_flags = be16_to_cpu(*(u16 *) (blob + 2));
- th.td_lolen = be32_to_cpu(*(u32 *) (blob + 8));
+ th.td_flags = be16_to_cpu(*(__be16 *) (blob + 2));
+ th.td_lolen = be32_to_cpu(*(__be32 *) (blob + 8));
blob += sizeof(struct table_header);
if (!(th.td_flags == YYTD_DATA16 || th.td_flags == YYTD_DATA32 ||
@@ -68,13 +95,13 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
table->td_lolen = th.td_lolen;
if (th.td_flags == YYTD_DATA8)
UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
- u8, byte_to_byte);
+ u8, u8, byte_to_byte);
else if (th.td_flags == YYTD_DATA16)
UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
- u16, be16_to_cpu);
+ u16, __be16, be16_to_cpu);
else if (th.td_flags == YYTD_DATA32)
UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
- u32, be32_to_cpu);
+ u32, __be32, be32_to_cpu);
else
goto fail;
/* if table was vmalloced make sure the page tables are synced
@@ -222,14 +249,14 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags)
if (size < sizeof(struct table_set_header))
goto fail;
- if (ntohl(*(u32 *) data) != YYTH_MAGIC)
+ if (ntohl(*(__be32 *) data) != YYTH_MAGIC)
goto fail;
- hsize = ntohl(*(u32 *) (data + 4));
+ hsize = ntohl(*(__be32 *) (data + 4));
if (size < hsize)
goto fail;
- dfa->flags = ntohs(*(u16 *) (data + 12));
+ dfa->flags = ntohs(*(__be16 *) (data + 12));
data += hsize;
size -= hsize;
diff --git a/security/apparmor/nulldfa.in b/security/apparmor/nulldfa.in
new file mode 100644
index 0000000..3cb3802
--- /dev/null
+++ b/security/apparmor/nulldfa.in
@@ -0,0 +1 @@
+0x1B, 0x5E, 0x78, 0x3D, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x04, 0x90, 0x00, 0x00, 0x6E, 0x6F, 0x74, 0x66, 0x6C, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index 179e68d..def1fbd 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -76,6 +76,9 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
+#include <linux/cred.h>
+#include <linux/rculist.h>
+#include <linux/user_namespace.h>
#include "include/apparmor.h"
#include "include/capability.h"
@@ -85,12 +88,11 @@
#include "include/match.h"
#include "include/path.h"
#include "include/policy.h"
+#include "include/policy_ns.h"
#include "include/policy_unpack.h"
#include "include/resource.h"
-
-/* root profile namespace */
-struct aa_namespace *root_ns;
+int unprivileged_userns_apparmor_policy = 1;
const char *const aa_profile_mode_names[] = {
"enforce",
@@ -99,318 +101,16 @@ const char *const aa_profile_mode_names[] = {
"unconfined",
};
-/**
- * hname_tail - find the last component of an hname
- * @name: hname to find the base profile name component of (NOT NULL)
- *
- * Returns: the tail (base profile name) name component of an hname
- */
-static const char *hname_tail(const char *hname)
-{
- char *split;
- hname = strim((char *)hname);
- for (split = strstr(hname, "//"); split; split = strstr(hname, "//"))
- hname = split + 2;
-
- return hname;
-}
-
-/**
- * policy_init - initialize a policy structure
- * @policy: policy to initialize (NOT NULL)
- * @prefix: prefix name if any is required. (MAYBE NULL)
- * @name: name of the policy, init will make a copy of it (NOT NULL)
- *
- * Note: this fn creates a copy of strings passed in
- *
- * Returns: true if policy init successful
- */
-static bool policy_init(struct aa_policy *policy, const char *prefix,
- const char *name)
-{
- /* freed by policy_free */
- if (prefix) {
- policy->hname = kmalloc(strlen(prefix) + strlen(name) + 3,
- GFP_KERNEL);
- if (policy->hname)
- sprintf(policy->hname, "%s//%s", prefix, name);
- } else
- policy->hname = kstrdup(name, GFP_KERNEL);
- if (!policy->hname)
- return 0;
- /* base.name is a substring of fqname */
- policy->name = (char *)hname_tail(policy->hname);
- INIT_LIST_HEAD(&policy->list);
- INIT_LIST_HEAD(&policy->profiles);
-
- return 1;
-}
-
-/**
- * policy_destroy - free the elements referenced by @policy
- * @policy: policy that is to have its elements freed (NOT NULL)
- */
-static void policy_destroy(struct aa_policy *policy)
-{
- /* still contains profiles -- invalid */
- if (on_list_rcu(&policy->profiles)) {
- AA_ERROR("%s: internal error, "
- "policy '%s' still contains profiles\n",
- __func__, policy->name);
- BUG();
- }
- if (on_list_rcu(&policy->list)) {
- AA_ERROR("%s: internal error, policy '%s' still on list\n",
- __func__, policy->name);
- BUG();
- }
-
- /* don't free name as its a subset of hname */
- kzfree(policy->hname);
-}
-
-/**
- * __policy_find - find a policy by @name on a policy list
- * @head: list to search (NOT NULL)
- * @name: name to search for (NOT NULL)
- *
- * Requires: rcu_read_lock be held
- *
- * Returns: unrefcounted policy that match @name or NULL if not found
- */
-static struct aa_policy *__policy_find(struct list_head *head, const char *name)
-{
- struct aa_policy *policy;
-
- list_for_each_entry_rcu(policy, head, list) {
- if (!strcmp(policy->name, name))
- return policy;
- }
- return NULL;
-}
-
-/**
- * __policy_strn_find - find a policy that's name matches @len chars of @str
- * @head: list to search (NOT NULL)
- * @str: string to search for (NOT NULL)
- * @len: length of match required
- *
- * Requires: rcu_read_lock be held
- *
- * Returns: unrefcounted policy that match @str or NULL if not found
- *
- * if @len == strlen(@strlen) then this is equiv to __policy_find
- * other wise it allows searching for policy by a partial match of name
- */
-static struct aa_policy *__policy_strn_find(struct list_head *head,
- const char *str, int len)
-{
- struct aa_policy *policy;
-
- list_for_each_entry_rcu(policy, head, list) {
- if (aa_strneq(policy->name, str, len))
- return policy;
- }
-
- return NULL;
-}
-
-/*
- * Routines for AppArmor namespaces
- */
-
-static const char *hidden_ns_name = "---";
-/**
- * aa_ns_visible - test if @view is visible from @curr
- * @curr: namespace to treat as the parent (NOT NULL)
- * @view: namespace to test if visible from @curr (NOT NULL)
- *
- * Returns: true if @view is visible from @curr else false
- */
-bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view)
-{
- if (curr == view)
- return true;
-
- for ( ; view; view = view->parent) {
- if (view->parent == curr)
- return true;
- }
- return false;
-}
-
-/**
- * aa_na_name - Find the ns name to display for @view from @curr
- * @curr - current namespace (NOT NULL)
- * @view - namespace attempting to view (NOT NULL)
- *
- * Returns: name of @view visible from @curr
- */
-const char *aa_ns_name(struct aa_namespace *curr, struct aa_namespace *view)
-{
- /* if view == curr then the namespace name isn't displayed */
- if (curr == view)
- return "";
-
- if (aa_ns_visible(curr, view)) {
- /* at this point if a ns is visible it is in a view ns
- * thus the curr ns.hname is a prefix of its name.
- * Only output the virtualized portion of the name
- * Add + 2 to skip over // separating curr hname prefix
- * from the visible tail of the views hname
- */
- return view->base.hname + strlen(curr->base.hname) + 2;
- } else
- return hidden_ns_name;
-}
-
-/**
- * alloc_namespace - allocate, initialize and return a new namespace
- * @prefix: parent namespace name (MAYBE NULL)
- * @name: a preallocated name (NOT NULL)
- *
- * Returns: refcounted namespace or NULL on failure.
- */
-static struct aa_namespace *alloc_namespace(const char *prefix,
- const char *name)
-{
- struct aa_namespace *ns;
-
- ns = kzalloc(sizeof(*ns), GFP_KERNEL);
- AA_DEBUG("%s(%p)\n", __func__, ns);
- if (!ns)
- return NULL;
- if (!policy_init(&ns->base, prefix, name))
- goto fail_ns;
-
- INIT_LIST_HEAD(&ns->sub_ns);
- mutex_init(&ns->lock);
-
- /* released by free_namespace */
- ns->unconfined = aa_alloc_profile("unconfined");
- if (!ns->unconfined)
- goto fail_unconfined;
-
- ns->unconfined->flags = PFLAG_IX_ON_NAME_ERROR |
- PFLAG_IMMUTABLE | PFLAG_NS_COUNT;
- ns->unconfined->mode = APPARMOR_UNCONFINED;
-
- /* ns and ns->unconfined share ns->unconfined refcount */
- ns->unconfined->ns = ns;
-
- atomic_set(&ns->uniq_null, 0);
-
- return ns;
-
-fail_unconfined:
- kzfree(ns->base.hname);
-fail_ns:
- kzfree(ns);
- return NULL;
-}
-
-/**
- * free_namespace - free a profile namespace
- * @ns: the namespace to free (MAYBE NULL)
- *
- * Requires: All references to the namespace must have been put, if the
- * namespace was referenced by a profile confining a task,
- */
-static void free_namespace(struct aa_namespace *ns)
+/* requires profile list write lock held */
+void __aa_update_proxy(struct aa_profile *orig, struct aa_profile *new)
{
- if (!ns)
- return;
-
- policy_destroy(&ns->base);
- aa_put_namespace(ns->parent);
-
- ns->unconfined->ns = NULL;
- aa_free_profile(ns->unconfined);
- kzfree(ns);
-}
-
-/**
- * __aa_find_namespace - find a namespace on a list by @name
- * @head: list to search for namespace on (NOT NULL)
- * @name: name of namespace to look for (NOT NULL)
- *
- * Returns: unrefcounted namespace
- *
- * Requires: rcu_read_lock be held
- */
-static struct aa_namespace *__aa_find_namespace(struct list_head *head,
- const char *name)
-{
- return (struct aa_namespace *)__policy_find(head, name);
-}
-
-/**
- * aa_find_namespace - look up a profile namespace on the namespace list
- * @root: namespace to search in (NOT NULL)
- * @name: name of namespace to find (NOT NULL)
- *
- * Returns: a refcounted namespace on the list, or NULL if no namespace
- * called @name exists.
- *
- * refcount released by caller
- */
-struct aa_namespace *aa_find_namespace(struct aa_namespace *root,
- const char *name)
-{
- struct aa_namespace *ns = NULL;
-
- rcu_read_lock();
- ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name));
- rcu_read_unlock();
-
- return ns;
-}
-
-/**
- * aa_prepare_namespace - find an existing or create a new namespace of @name
- * @name: the namespace to find or add (MAYBE NULL)
- *
- * Returns: refcounted namespace or NULL if failed to create one
- */
-static struct aa_namespace *aa_prepare_namespace(const char *name)
-{
- struct aa_namespace *ns, *root;
-
- root = aa_current_profile()->ns;
+ struct aa_profile *tmp;
- mutex_lock(&root->lock);
-
- /* if name isn't specified the profile is loaded to the current ns */
- if (!name) {
- /* released by caller */
- ns = aa_get_namespace(root);
- goto out;
- }
-
- /* try and find the specified ns and if it doesn't exist create it */
- /* released by caller */
- ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name));
- if (!ns) {
- ns = alloc_namespace(root->base.hname, name);
- if (!ns)
- goto out;
- if (__aa_fs_namespace_mkdir(ns, ns_subns_dir(root), name)) {
- AA_ERROR("Failed to create interface for ns %s\n",
- ns->base.name);
- free_namespace(ns);
- ns = NULL;
- goto out;
- }
- ns->parent = aa_get_namespace(root);
- list_add_rcu(&ns->base.list, &root->sub_ns);
- /* add list ref */
- aa_get_namespace(ns);
- }
-out:
- mutex_unlock(&root->lock);
-
- /* return ref */
- return ns;
+ tmp = rcu_dereference_protected(orig->proxy->profile,
+ mutex_is_locked(&orig->ns->lock));
+ rcu_assign_pointer(orig->proxy->profile, aa_get_profile(new));
+ orig->flags |= PFLAG_STALE;
+ aa_put_profile(tmp);
}
/**
@@ -448,8 +148,6 @@ static void __list_remove_profile(struct aa_profile *profile)
aa_put_profile(profile);
}
-static void __profile_list_release(struct list_head *head);
-
/**
* __remove_profile - remove old profile, and children
* @profile: profile to be replaced (NOT NULL)
@@ -459,122 +157,56 @@ static void __profile_list_release(struct list_head *head);
static void __remove_profile(struct aa_profile *profile)
{
/* release any children lists first */
- __profile_list_release(&profile->base.profiles);
+ __aa_profile_list_release(&profile->base.profiles);
/* released by free_profile */
- __aa_update_replacedby(profile, profile->ns->unconfined);
+ __aa_update_proxy(profile, profile->ns->unconfined);
__aa_fs_profile_rmdir(profile);
__list_remove_profile(profile);
}
/**
- * __profile_list_release - remove all profiles on the list and put refs
+ * __aa_profile_list_release - remove all profiles on the list and put refs
* @head: list of profiles (NOT NULL)
*
* Requires: namespace lock be held
*/
-static void __profile_list_release(struct list_head *head)
+void __aa_profile_list_release(struct list_head *head)
{
struct aa_profile *profile, *tmp;
list_for_each_entry_safe(profile, tmp, head, base.list)
__remove_profile(profile);
}
-static void __ns_list_release(struct list_head *head);
-/**
- * destroy_namespace - remove everything contained by @ns
- * @ns: namespace to have it contents removed (NOT NULL)
- */
-static void destroy_namespace(struct aa_namespace *ns)
+static void free_proxy(struct aa_proxy *p)
{
- if (!ns)
- return;
-
- mutex_lock(&ns->lock);
- /* release all profiles in this namespace */
- __profile_list_release(&ns->base.profiles);
-
- /* release all sub namespaces */
- __ns_list_release(&ns->sub_ns);
-
- if (ns->parent)
- __aa_update_replacedby(ns->unconfined, ns->parent->unconfined);
- __aa_fs_namespace_rmdir(ns);
- mutex_unlock(&ns->lock);
+ if (p) {
+ /* r->profile will not be updated any more as r is dead */
+ aa_put_profile(rcu_dereference_protected(p->profile, true));
+ kzfree(p);
+ }
}
-/**
- * __remove_namespace - remove a namespace and all its children
- * @ns: namespace to be removed (NOT NULL)
- *
- * Requires: ns->parent->lock be held and ns removed from parent.
- */
-static void __remove_namespace(struct aa_namespace *ns)
-{
- /* remove ns from namespace list */
- list_del_rcu(&ns->base.list);
- destroy_namespace(ns);
- aa_put_namespace(ns);
-}
-/**
- * __ns_list_release - remove all profile namespaces on the list put refs
- * @head: list of profile namespaces (NOT NULL)
- *
- * Requires: namespace lock be held
- */
-static void __ns_list_release(struct list_head *head)
+void aa_free_proxy_kref(struct kref *kref)
{
- struct aa_namespace *ns, *tmp;
- list_for_each_entry_safe(ns, tmp, head, base.list)
- __remove_namespace(ns);
+ struct aa_proxy *p = container_of(kref, struct aa_proxy, count);
+ free_proxy(p);
}
/**
- * aa_alloc_root_ns - allocate the root profile namespace
- *
- * Returns: %0 on success else error
- *
+ * aa_free_data - free a data blob
+ * @ptr: data to free
+ * @arg: unused
*/
-int __init aa_alloc_root_ns(void)
+static void aa_free_data(void *ptr, void *arg)
{
- /* released by aa_free_root_ns - used as list ref*/
- root_ns = alloc_namespace(NULL, "root");
- if (!root_ns)
- return -ENOMEM;
-
- return 0;
-}
-
- /**
- * aa_free_root_ns - free the root profile namespace
- */
-void __init aa_free_root_ns(void)
- {
- struct aa_namespace *ns = root_ns;
- root_ns = NULL;
-
- destroy_namespace(ns);
- aa_put_namespace(ns);
-}
-
-
-static void free_replacedby(struct aa_replacedby *r)
-{
- if (r) {
- /* r->profile will not be updated any more as r is dead */
- aa_put_profile(rcu_dereference_protected(r->profile, true));
- kzfree(r);
- }
-}
+ struct aa_data *data = ptr;
-
-void aa_free_replacedby_kref(struct kref *kref)
-{
- struct aa_replacedby *r = container_of(kref, struct aa_replacedby,
- count);
- free_replacedby(r);
+ kzfree(data->data);
+ kzfree(data->key);
+ kzfree(data);
}
/**
@@ -589,16 +221,18 @@ void aa_free_replacedby_kref(struct kref *kref)
*/
void aa_free_profile(struct aa_profile *profile)
{
+ struct rhashtable *rht;
+
AA_DEBUG("%s(%p)\n", __func__, profile);
if (!profile)
return;
/* free children profiles */
- policy_destroy(&profile->base);
+ aa_policy_destroy(&profile->base);
aa_put_profile(rcu_access_pointer(profile->parent));
- aa_put_namespace(profile->ns);
+ aa_put_ns(profile->ns);
kzfree(profile->rename);
aa_free_file_rules(&profile->file);
@@ -608,9 +242,17 @@ void aa_free_profile(struct aa_profile *profile)
kzfree(profile->dirname);
aa_put_dfa(profile->xmatch);
aa_put_dfa(profile->policy.dfa);
- aa_put_replacedby(profile->replacedby);
+ aa_put_proxy(profile->proxy);
+
+ if (profile->data) {
+ rht = profile->data;
+ profile->data = NULL;
+ rhashtable_free_and_destroy(rht, aa_free_data, NULL);
+ kzfree(rht);
+ }
kzfree(profile->hash);
+ aa_put_loaddata(profile->rawdata);
kzfree(profile);
}
@@ -622,7 +264,7 @@ static void aa_free_profile_rcu(struct rcu_head *head)
{
struct aa_profile *p = container_of(head, struct aa_profile, rcu);
if (p->flags & PFLAG_NS_COUNT)
- free_namespace(p->ns);
+ aa_free_ns(p->ns);
else
aa_free_profile(p);
}
@@ -640,24 +282,25 @@ void aa_free_profile_kref(struct kref *kref)
/**
* aa_alloc_profile - allocate, initialize and return a new profile
* @hname: name of the profile (NOT NULL)
+ * @gfp: allocation type
*
* Returns: refcount profile or NULL on failure
*/
-struct aa_profile *aa_alloc_profile(const char *hname)
+struct aa_profile *aa_alloc_profile(const char *hname, gfp_t gfp)
{
struct aa_profile *profile;
/* freed by free_profile - usually through aa_put_profile */
- profile = kzalloc(sizeof(*profile), GFP_KERNEL);
+ profile = kzalloc(sizeof(*profile), gfp);
if (!profile)
return NULL;
- profile->replacedby = kzalloc(sizeof(struct aa_replacedby), GFP_KERNEL);
- if (!profile->replacedby)
+ profile->proxy = kzalloc(sizeof(struct aa_proxy), gfp);
+ if (!profile->proxy)
goto fail;
- kref_init(&profile->replacedby->count);
+ kref_init(&profile->proxy->count);
- if (!policy_init(&profile->base, NULL, hname))
+ if (!aa_policy_init(&profile->base, NULL, hname, gfp))
goto fail;
kref_init(&profile->count);
@@ -665,19 +308,23 @@ struct aa_profile *aa_alloc_profile(const char *hname)
return profile;
fail:
- kzfree(profile->replacedby);
+ kzfree(profile->proxy);
kzfree(profile);
return NULL;
}
/**
- * aa_new_null_profile - create a new null-X learning profile
+ * 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
*
- * Create a null- complain mode profile used in learning mode. The name of
- * the profile is unique and follows the format of parent//null-<uniq>.
+ * 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
@@ -685,40 +332,65 @@ fail:
*
* Returns: new refcounted profile else NULL on failure
*/
-struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat)
+struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat,
+ const char *base, gfp_t gfp)
{
- struct aa_profile *profile = NULL;
+ struct aa_profile *profile;
char *name;
- int uniq = atomic_inc_return(&parent->ns->uniq_null);
- /* freed below */
- name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, GFP_KERNEL);
+ 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)
- goto fail;
- sprintf(name, "%s//null-%x", parent->base.hname, uniq);
+ return NULL;
+ sprintf(name, "%s//null-%x", parent->base.hname,
+ atomic_inc_return(&parent->ns->uniq_null));
- profile = aa_alloc_profile(name);
- kfree(name);
+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, gfp);
if (!profile)
goto fail;
profile->mode = APPARMOR_COMPLAIN;
- profile->flags = PFLAG_NULL;
+ profile->flags |= PFLAG_NULL;
if (hat)
profile->flags |= PFLAG_HAT;
+ profile->path_flags = parent->path_flags;
/* released on free_profile */
rcu_assign_pointer(profile->parent, aa_get_profile(parent));
- profile->ns = aa_get_namespace(parent->ns);
+ 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);
__list_add_profile(&parent->base.profiles, profile);
mutex_unlock(&profile->ns->lock);
/* refcount released by caller */
+out:
+ kfree(name);
+
return profile;
fail:
+ kfree(name);
+ aa_free_profile(profile);
return NULL;
}
@@ -788,7 +460,7 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name)
*
* Returns: unrefcounted policy or NULL if not found
*/
-static struct aa_policy *__lookup_parent(struct aa_namespace *ns,
+static struct aa_policy *__lookup_parent(struct aa_ns *ns,
const char *hname)
{
struct aa_policy *policy;
@@ -812,9 +484,10 @@ static struct aa_policy *__lookup_parent(struct aa_namespace *ns,
}
/**
- * __lookup_profile - lookup the profile matching @hname
+ * __lookupn_profile - lookup the profile matching @hname
* @base: base list to start looking up profile name from (NOT NULL)
* @hname: hierarchical profile name (NOT NULL)
+ * @n: length of @hname
*
* Requires: rcu_read_lock be held
*
@@ -822,53 +495,95 @@ static struct aa_policy *__lookup_parent(struct aa_namespace *ns,
*
* Do a relative name lookup, recursing through profile tree.
*/
-static struct aa_profile *__lookup_profile(struct aa_policy *base,
- const char *hname)
+static struct aa_profile *__lookupn_profile(struct aa_policy *base,
+ const char *hname, size_t n)
{
struct aa_profile *profile = NULL;
- char *split;
+ const char *split;
- for (split = strstr(hname, "//"); split;) {
+ for (split = strnstr(hname, "//", n); split;
+ split = strnstr(hname, "//", n)) {
profile = __strn_find_child(&base->profiles, hname,
split - hname);
if (!profile)
return NULL;
base = &profile->base;
+ n -= split + 2 - hname;
hname = split + 2;
- split = strstr(hname, "//");
}
- profile = __find_child(&base->profiles, hname);
+ if (n)
+ return __strn_find_child(&base->profiles, hname, n);
+ return NULL;
+}
- return profile;
+static struct aa_profile *__lookup_profile(struct aa_policy *base,
+ const char *hname)
+{
+ return __lookupn_profile(base, hname, strlen(hname));
}
/**
* aa_lookup_profile - find a profile by its full or partial name
* @ns: the namespace to start from (NOT NULL)
* @hname: name to do lookup on. Does not contain namespace prefix (NOT NULL)
+ * @n: size of @hname
*
* Returns: refcounted profile or NULL if not found
*/
-struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *hname)
+struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname,
+ size_t n)
{
struct aa_profile *profile;
rcu_read_lock();
do {
- profile = __lookup_profile(&ns->base, hname);
+ profile = __lookupn_profile(&ns->base, hname, n);
} while (profile && !aa_get_profile_not0(profile));
rcu_read_unlock();
/* the unconfined profile is not in the regular profile list */
- if (!profile && strcmp(hname, "unconfined") == 0)
+ if (!profile && strncmp(hname, "unconfined", n) == 0)
profile = aa_get_newest_profile(ns->unconfined);
/* refcount released by caller */
return profile;
}
+struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *hname)
+{
+ return aa_lookupn_profile(ns, hname, strlen(hname));
+}
+
+struct aa_profile *aa_fqlookupn_profile(struct aa_profile *base,
+ const char *fqname, size_t n)
+{
+ struct aa_profile *profile;
+ struct aa_ns *ns;
+ const char *name, *ns_name;
+ size_t ns_len;
+
+ name = aa_splitn_fqname(fqname, n, &ns_name, &ns_len);
+ if (ns_name) {
+ ns = aa_findn_ns(base->ns, ns_name, ns_len);
+ if (!ns)
+ return NULL;
+ } else
+ ns = aa_get_ns(base->ns);
+
+ if (name)
+ profile = aa_lookupn_profile(ns, name, n - (name - fqname));
+ else if (ns)
+ /* default profile for ns, currently unconfined */
+ profile = aa_get_newest_profile(ns->unconfined);
+ else
+ profile = NULL;
+ aa_put_ns(ns);
+
+ return profile;
+}
+
/**
* replacement_allowed - test to see if replacement is allowed
* @profile: profile to test if it can be replaced (MAYBE NULL)
@@ -892,74 +607,109 @@ static int replacement_allowed(struct aa_profile *profile, int noreplace,
return 0;
}
+/* audit callback for net specific fields */
+static void audit_cb(struct audit_buffer *ab, void *va)
+{
+ struct common_audit_data *sa = va;
+
+ if (aad(sa)->iface.ns) {
+ audit_log_format(ab, " ns=");
+ audit_log_untrustedstring(ab, aad(sa)->iface.ns);
+ }
+}
+
/**
* aa_audit_policy - Do auditing of policy changes
+ * @profile: profile to check if it can manage policy
* @op: policy operation being performed
* @gfp: memory allocation flags
+ * @nsname: name of the ns being manipulated (MAY BE NULL)
* @name: name of profile being manipulated (NOT NULL)
* @info: any extra information to be audited (MAYBE NULL)
* @error: error code
*
* Returns: the error to be returned after audit is done
*/
-static int audit_policy(int op, gfp_t gfp, const char *name, const char *info,
- int error)
+static int audit_policy(struct aa_profile *profile, const char *op,
+ const char *nsname, const char *name,
+ const char *info, int error)
{
- struct common_audit_data sa;
- struct apparmor_audit_data aad = {0,};
- sa.type = LSM_AUDIT_DATA_NONE;
- sa.aad = &aad;
- aad.op = op;
- aad.name = name;
- aad.info = info;
- aad.error = error;
-
- return aa_audit(AUDIT_APPARMOR_STATUS, __aa_current_profile(), gfp,
- &sa, NULL);
+ DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op);
+
+ aad(&sa)->iface.ns = nsname;
+ aad(&sa)->name = name;
+ aad(&sa)->info = info;
+ aad(&sa)->error = error;
+
+ return aa_audit(AUDIT_APPARMOR_STATUS, profile, &sa, audit_cb);
}
-bool policy_view_capable(void)
+/**
+ * policy_view_capable - check if viewing policy in at @ns is allowed
+ * ns: namespace being viewed by current task (may be NULL)
+ * Returns: true if viewing policy is allowed
+ *
+ * If @ns is NULL then the namespace being viewed is assumed to be the
+ * tasks current namespace.
+ */
+bool policy_view_capable(struct aa_ns *ns)
{
struct user_namespace *user_ns = current_user_ns();
+ struct aa_ns *view_ns = aa_get_current_ns();
+ bool root_in_user_ns = uid_eq(current_euid(), make_kuid(user_ns, 0)) ||
+ in_egroup_p(make_kgid(user_ns, 0));
bool response = false;
+ if (!ns)
+ ns = view_ns;
- if (ns_capable(user_ns, CAP_MAC_ADMIN))
+ if (root_in_user_ns && aa_ns_visible(view_ns, ns, true) &&
+ (user_ns == &init_user_ns ||
+ (unprivileged_userns_apparmor_policy != 0 &&
+ user_ns->level == view_ns->level)))
response = true;
+ aa_put_ns(view_ns);
return response;
}
-bool policy_admin_capable(void)
+bool policy_admin_capable(struct aa_ns *ns)
{
- return policy_view_capable() && !aa_g_lock_policy;
+ struct user_namespace *user_ns = current_user_ns();
+ bool capable = ns_capable(user_ns, CAP_MAC_ADMIN);
+
+ AA_DEBUG("cap_mac_admin? %d\n", capable);
+ AA_DEBUG("policy locked? %d\n", aa_g_lock_policy);
+
+ return policy_view_capable(ns) && capable && !aa_g_lock_policy;
}
/**
* aa_may_manage_policy - can the current task manage policy
+ * @profile: profile to check if it can manage policy
* @op: the policy manipulation operation being done
*
- * Returns: true if the task is allowed to manipulate policy
+ * Returns: 0 if the task is allowed to manipulate policy else error
*/
-bool aa_may_manage_policy(int op)
+int aa_may_manage_policy(struct aa_profile *profile, struct aa_ns *ns,
+ const char *op)
{
/* check if loading policy is locked out */
- if (aa_g_lock_policy) {
- audit_policy(op, GFP_KERNEL, NULL, "policy_locked", -EACCES);
- return 0;
- }
+ if (aa_g_lock_policy)
+ return audit_policy(profile, op, NULL, NULL,
+ "policy_locked", -EACCES);
- if (!policy_admin_capable()) {
- audit_policy(op, GFP_KERNEL, NULL, "not policy admin", -EACCES);
- return 0;
- }
+ if (!policy_admin_capable(ns))
+ return audit_policy(profile, op, NULL, NULL,
+ "not policy admin", -EACCES);
- return 1;
+ /* TODO: add fine grained mediation of policy loads */
+ return 0;
}
static struct aa_profile *__list_lookup_parent(struct list_head *lh,
struct aa_profile *profile)
{
- const char *base = hname_tail(profile->base.hname);
+ const char *base = basename(profile->base.hname);
long len = base - profile->base.hname;
struct aa_load_ent *ent;
@@ -983,7 +733,7 @@ static struct aa_profile *__list_lookup_parent(struct list_head *lh,
* __replace_profile - replace @old with @new on a list
* @old: profile to be replaced (NOT NULL)
* @new: profile to replace @old with (NOT NULL)
- * @share_replacedby: transfer @old->replacedby to @new
+ * @share_proxy: transfer @old->proxy to @new
*
* Will duplicate and refcount elements that @new inherits from @old
* and will inherit @old children.
@@ -993,7 +743,7 @@ static struct aa_profile *__list_lookup_parent(struct list_head *lh,
* Requires: namespace list lock be held, or list not be shared
*/
static void __replace_profile(struct aa_profile *old, struct aa_profile *new,
- bool share_replacedby)
+ bool share_proxy)
{
struct aa_profile *child, *tmp;
@@ -1008,7 +758,7 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new,
p = __find_child(&new->base.profiles, child->base.name);
if (p) {
/* @p replaces @child */
- __replace_profile(child, p, share_replacedby);
+ __replace_profile(child, p, share_proxy);
continue;
}
@@ -1026,13 +776,13 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new,
struct aa_profile *parent = aa_deref_parent(old);
rcu_assign_pointer(new->parent, aa_get_profile(parent));
}
- __aa_update_replacedby(old, new);
- if (share_replacedby) {
- aa_put_replacedby(new->replacedby);
- new->replacedby = aa_get_replacedby(old->replacedby);
- } else if (!rcu_access_pointer(new->replacedby->profile))
- /* aafs interface uses replacedby */
- rcu_assign_pointer(new->replacedby->profile,
+ __aa_update_proxy(old, new);
+ if (share_proxy) {
+ aa_put_proxy(new->proxy);
+ new->proxy = aa_get_proxy(old->proxy);
+ } else if (!rcu_access_pointer(new->proxy->profile))
+ /* aafs interface uses proxy */
+ rcu_assign_pointer(new->proxy->profile,
aa_get_profile(new));
__aa_fs_profile_migrate_dents(old, new);
@@ -1055,7 +805,7 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new,
*
* Returns: profile to replace (no ref) on success else ptr error
*/
-static int __lookup_replace(struct aa_namespace *ns, const char *hname,
+static int __lookup_replace(struct aa_ns *ns, const char *hname,
bool noreplace, struct aa_profile **p,
const char **info)
{
@@ -1073,42 +823,72 @@ static int __lookup_replace(struct aa_namespace *ns, const char *hname,
/**
* aa_replace_profiles - replace profile(s) on the profile list
- * @udata: serialized data stream (NOT NULL)
- * @size: size of the serialized data stream
+ * @view: namespace load is viewed from
+ * @label: label that is attempting to load/replace policy
* @noreplace: true if only doing addition, no replacement allowed
+ * @udata: serialized data stream (NOT NULL)
*
* unpack and replace a profile on the profile list and uses of that profile
- * by any aa_task_cxt. If the profile does not exist on the profile list
+ * by any aa_task_ctx. If the profile does not exist on the profile list
* it is added.
*
* Returns: size of data consumed else error code on failure.
*/
-ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
+ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_profile *profile,
+ bool noreplace, struct aa_loaddata *udata)
{
const char *ns_name, *info = NULL;
- struct aa_namespace *ns = NULL;
+ struct aa_ns *ns = NULL;
struct aa_load_ent *ent, *tmp;
- int op = OP_PROF_REPL;
- ssize_t error;
+ const char *op = OP_PROF_REPL;
+ ssize_t count, error;
LIST_HEAD(lh);
/* released below */
- error = aa_unpack(udata, size, &lh, &ns_name);
+ error = aa_unpack(udata, &lh, &ns_name);
if (error)
goto out;
- /* released below */
- ns = aa_prepare_namespace(ns_name);
- if (!ns) {
- error = audit_policy(op, GFP_KERNEL, ns_name,
- "failed to prepare namespace", -ENOMEM);
- goto free;
+ /* ensure that profiles are all for the same ns
+ * TODO: update locking to remove this constaint. All profiles in
+ * the load set must succeed as a set or the load will
+ * fail. Sort ent list and take ns locks in hierarchy order
+ */
+ count = 0;
+ list_for_each_entry(ent, &lh, list) {
+ if (ns_name) {
+ if (ent->ns_name &&
+ strcmp(ent->ns_name, ns_name) != 0) {
+ info = "policy load has mixed namespaces";
+ error = -EACCES;
+ goto fail;
+ }
+ } else if (ent->ns_name) {
+ if (count) {
+ info = "policy load has mixed namespaces";
+ error = -EACCES;
+ goto fail;
+ }
+ ns_name = ent->ns_name;
+ } else
+ count++;
}
+ if (ns_name) {
+ ns = aa_prepare_ns(view, ns_name);
+ if (IS_ERR(ns)) {
+ info = "failed to prepare namespace";
+ error = PTR_ERR(ns);
+ ns = NULL;
+ goto fail;
+ }
+ } else
+ ns = aa_get_ns(view);
mutex_lock(&ns->lock);
/* setup parent and ns info */
list_for_each_entry(ent, &lh, list) {
struct aa_policy *policy;
+ ent->new->rawdata = aa_get_loaddata(udata);
error = __lookup_replace(ns, ent->new->base.hname, noreplace,
&ent->old, &info);
if (error)
@@ -1123,7 +903,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
}
/* released when @new is freed */
- ent->new->ns = aa_get_namespace(ns);
+ ent->new->ns = aa_get_ns(ns);
if (ent->old || ent->rename)
continue;
@@ -1177,20 +957,21 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
list_del_init(&ent->list);
op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL;
- audit_policy(op, GFP_ATOMIC, ent->new->base.hname, NULL, error);
+ audit_policy(profile, op, NULL, ent->new->base.hname,
+ NULL, error);
if (ent->old) {
__replace_profile(ent->old, ent->new, 1);
if (ent->rename) {
- /* aafs interface uses replacedby */
- struct aa_replacedby *r = ent->new->replacedby;
+ /* aafs interface uses proxy */
+ struct aa_proxy *r = ent->new->proxy;
rcu_assign_pointer(r->profile,
aa_get_profile(ent->new));
__replace_profile(ent->rename, ent->new, 0);
}
} else if (ent->rename) {
- /* aafs interface uses replacedby */
- rcu_assign_pointer(ent->new->replacedby->profile,
+ /* aafs interface uses proxy */
+ rcu_assign_pointer(ent->new->proxy->profile,
aa_get_profile(ent->new));
__replace_profile(ent->rename, ent->new, 0);
} else if (ent->new->parent) {
@@ -1204,14 +985,14 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
rcu_assign_pointer(ent->new->parent, newest);
aa_put_profile(parent);
}
- /* aafs interface uses replacedby */
- rcu_assign_pointer(ent->new->replacedby->profile,
+ /* aafs interface uses proxy */
+ rcu_assign_pointer(ent->new->proxy->profile,
aa_get_profile(ent->new));
__list_add_profile(&newest->base.profiles, ent->new);
aa_put_profile(newest);
} else {
- /* aafs interface uses replacedby */
- rcu_assign_pointer(ent->new->replacedby->profile,
+ /* aafs interface uses proxy */
+ rcu_assign_pointer(ent->new->proxy->profile,
aa_get_profile(ent->new));
__list_add_profile(&ns->base.profiles, ent->new);
}
@@ -1220,18 +1001,20 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
mutex_unlock(&ns->lock);
out:
- aa_put_namespace(ns);
+ aa_put_ns(ns);
if (error)
return error;
- return size;
+ return udata->size;
fail_lock:
mutex_unlock(&ns->lock);
/* audit cause of failure */
op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL;
- audit_policy(op, GFP_KERNEL, ent->new->base.hname, info, error);
+fail:
+ audit_policy(profile, op, ns_name, ent->new->base.hname,
+ info, error);
/* audit status that rest of profiles in the atomic set failed too */
info = "valid profile in failed atomic policy load";
list_for_each_entry(tmp, &lh, list) {
@@ -1241,9 +1024,9 @@ fail_lock:
continue;
}
op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL;
- audit_policy(op, GFP_KERNEL, tmp->new->base.hname, info, error);
+ audit_policy(profile, op, ns_name,
+ tmp->new->base.hname, info, error);
}
-free:
list_for_each_entry_safe(ent, tmp, &lh, list) {
list_del_init(&ent->list);
aa_load_ent_free(ent);
@@ -1254,6 +1037,8 @@ free:
/**
* aa_remove_profiles - remove profile(s) from the system
+ * @view: namespace the remove is being done from
+ * @subj: profile attempting to remove policy
* @fqname: name of the profile or namespace to remove (NOT NULL)
* @size: size of the name
*
@@ -1264,11 +1049,13 @@ free:
*
* Returns: size of data consume else error code if fails
*/
-ssize_t aa_remove_profiles(char *fqname, size_t size)
+ssize_t aa_remove_profiles(struct aa_ns *view, struct aa_profile *subj,
+ char *fqname, size_t size)
{
- struct aa_namespace *root, *ns = NULL;
+ struct aa_ns *root = NULL, *ns = NULL;
struct aa_profile *profile = NULL;
const char *name = fqname, *info = NULL;
+ char *ns_name = NULL;
ssize_t error = 0;
if (*fqname == 0) {
@@ -1277,13 +1064,12 @@ ssize_t aa_remove_profiles(char *fqname, size_t size)
goto fail;
}
- root = aa_current_profile()->ns;
+ root = view;
if (fqname[0] == ':') {
- char *ns_name;
name = aa_split_fqname(fqname, &ns_name);
/* released below */
- ns = aa_find_namespace(root, ns_name);
+ ns = aa_find_ns(root, ns_name);
if (!ns) {
info = "namespace does not exist";
error = -ENOENT;
@@ -1291,12 +1077,12 @@ ssize_t aa_remove_profiles(char *fqname, size_t size)
}
} else
/* released below */
- ns = aa_get_namespace(root);
+ ns = aa_get_ns(root);
if (!name) {
/* remove namespace - can only happen if fqname[0] == ':' */
mutex_lock(&ns->parent->lock);
- __remove_namespace(ns);
+ __aa_remove_ns(ns);
mutex_unlock(&ns->parent->lock);
} else {
/* remove profile */
@@ -1313,16 +1099,18 @@ ssize_t aa_remove_profiles(char *fqname, size_t size)
}
/* don't fail removal if audit fails */
- (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error);
- aa_put_namespace(ns);
+ (void) audit_policy(subj, OP_PROF_RM, ns_name, name, info,
+ error);
+ aa_put_ns(ns);
aa_put_profile(profile);
return size;
fail_ns_lock:
mutex_unlock(&ns->lock);
- aa_put_namespace(ns);
+ aa_put_ns(ns);
fail:
- (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error);
+ (void) audit_policy(subj, OP_PROF_RM, ns_name, name, info,
+ error);
return error;
}
diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c
new file mode 100644
index 0000000..93d1826
--- /dev/null
+++ b/security/apparmor/policy_ns.c
@@ -0,0 +1,346 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor policy manipulation functions
+ *
+ * Copyright (C) 1998-2008 Novell/SUSE
+ * Copyright 2009-2017 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.
+ *
+ * AppArmor policy namespaces, allow for different sets of policies
+ * to be loaded for tasks within the namespace.
+ */
+
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "include/apparmor.h"
+#include "include/context.h"
+#include "include/policy_ns.h"
+#include "include/policy.h"
+
+/* root profile namespace */
+struct aa_ns *root_ns;
+const char *aa_hidden_ns_name = "---";
+
+/**
+ * aa_ns_visible - test if @view is visible from @curr
+ * @curr: namespace to treat as the parent (NOT NULL)
+ * @view: namespace to test if visible from @curr (NOT NULL)
+ * @subns: whether view of a subns is allowed
+ *
+ * Returns: true if @view is visible from @curr else false
+ */
+bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view, bool subns)
+{
+ if (curr == view)
+ return true;
+
+ if (!subns)
+ return false;
+
+ for ( ; view; view = view->parent) {
+ if (view->parent == curr)
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * aa_na_name - Find the ns name to display for @view from @curr
+ * @curr - current namespace (NOT NULL)
+ * @view - namespace attempting to view (NOT NULL)
+ * @subns - are subns visible
+ *
+ * Returns: name of @view visible from @curr
+ */
+const char *aa_ns_name(struct aa_ns *curr, struct aa_ns *view, bool subns)
+{
+ /* if view == curr then the namespace name isn't displayed */
+ if (curr == view)
+ return "";
+
+ if (aa_ns_visible(curr, view, subns)) {
+ /* at this point if a ns is visible it is in a view ns
+ * thus the curr ns.hname is a prefix of its name.
+ * Only output the virtualized portion of the name
+ * Add + 2 to skip over // separating curr hname prefix
+ * from the visible tail of the views hname
+ */
+ return view->base.hname + strlen(curr->base.hname) + 2;
+ }
+
+ return aa_hidden_ns_name;
+}
+
+/**
+ * alloc_ns - allocate, initialize and return a new namespace
+ * @prefix: parent namespace name (MAYBE NULL)
+ * @name: a preallocated name (NOT NULL)
+ *
+ * Returns: refcounted namespace or NULL on failure.
+ */
+static struct aa_ns *alloc_ns(const char *prefix, const char *name)
+{
+ struct aa_ns *ns;
+
+ ns = kzalloc(sizeof(*ns), GFP_KERNEL);
+ AA_DEBUG("%s(%p)\n", __func__, ns);
+ if (!ns)
+ return NULL;
+ if (!aa_policy_init(&ns->base, prefix, name, GFP_KERNEL))
+ goto fail_ns;
+
+ INIT_LIST_HEAD(&ns->sub_ns);
+ mutex_init(&ns->lock);
+
+ /* released by aa_free_ns() */
+ ns->unconfined = aa_alloc_profile("unconfined", GFP_KERNEL);
+ if (!ns->unconfined)
+ goto fail_unconfined;
+
+ ns->unconfined->flags = PFLAG_IX_ON_NAME_ERROR |
+ PFLAG_IMMUTABLE | PFLAG_NS_COUNT;
+ ns->unconfined->mode = APPARMOR_UNCONFINED;
+
+ /* ns and ns->unconfined share ns->unconfined refcount */
+ ns->unconfined->ns = ns;
+
+ atomic_set(&ns->uniq_null, 0);
+
+ return ns;
+
+fail_unconfined:
+ kzfree(ns->base.hname);
+fail_ns:
+ kzfree(ns);
+ return NULL;
+}
+
+/**
+ * aa_free_ns - free a profile namespace
+ * @ns: the namespace to free (MAYBE NULL)
+ *
+ * Requires: All references to the namespace must have been put, if the
+ * namespace was referenced by a profile confining a task,
+ */
+void aa_free_ns(struct aa_ns *ns)
+{
+ if (!ns)
+ return;
+
+ aa_policy_destroy(&ns->base);
+ aa_put_ns(ns->parent);
+
+ ns->unconfined->ns = NULL;
+ aa_free_profile(ns->unconfined);
+ kzfree(ns);
+}
+
+/**
+ * aa_findn_ns - look up a profile namespace on the namespace list
+ * @root: namespace to search in (NOT NULL)
+ * @name: name of namespace to find (NOT NULL)
+ * @n: length of @name
+ *
+ * Returns: a refcounted namespace on the list, or NULL if no namespace
+ * called @name exists.
+ *
+ * refcount released by caller
+ */
+struct aa_ns *aa_findn_ns(struct aa_ns *root, const char *name, size_t n)
+{
+ struct aa_ns *ns = NULL;
+
+ rcu_read_lock();
+ ns = aa_get_ns(__aa_findn_ns(&root->sub_ns, name, n));
+ rcu_read_unlock();
+
+ return ns;
+}
+
+/**
+ * aa_find_ns - look up a profile namespace on the namespace list
+ * @root: namespace to search in (NOT NULL)
+ * @name: name of namespace to find (NOT NULL)
+ *
+ * Returns: a refcounted namespace on the list, or NULL if no namespace
+ * called @name exists.
+ *
+ * refcount released by caller
+ */
+struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name)
+{
+ return aa_findn_ns(root, name, strlen(name));
+}
+
+static struct aa_ns *__aa_create_ns(struct aa_ns *parent, const char *name,
+ struct dentry *dir)
+{
+ struct aa_ns *ns;
+ int error;
+
+ AA_BUG(!parent);
+ AA_BUG(!name);
+ AA_BUG(!mutex_is_locked(&parent->lock));
+
+ ns = alloc_ns(parent->base.hname, name);
+ if (!ns)
+ return NULL;
+ mutex_lock(&ns->lock);
+ error = __aa_fs_ns_mkdir(ns, ns_subns_dir(parent), name);
+ if (error) {
+ AA_ERROR("Failed to create interface for ns %s\n",
+ ns->base.name);
+ mutex_unlock(&ns->lock);
+ aa_free_ns(ns);
+ return ERR_PTR(error);
+ }
+ ns->parent = aa_get_ns(parent);
+ ns->level = parent->level + 1;
+ list_add_rcu(&ns->base.list, &parent->sub_ns);
+ /* add list ref */
+ aa_get_ns(ns);
+ mutex_unlock(&ns->lock);
+
+ return ns;
+}
+
+/**
+ * aa_create_ns - create an ns, fail if it already exists
+ * @parent: the parent of the namespace being created
+ * @name: the name of the namespace
+ * @dir: if not null the dir to put the ns entries in
+ *
+ * Returns: the a refcounted ns that has been add or an ERR_PTR
+ */
+struct aa_ns *__aa_find_or_create_ns(struct aa_ns *parent, const char *name,
+ struct dentry *dir)
+{
+ struct aa_ns *ns;
+
+ AA_BUG(!mutex_is_locked(&parent->lock));
+
+ /* try and find the specified ns */
+ /* released by caller */
+ ns = aa_get_ns(__aa_find_ns(&parent->sub_ns, name));
+ if (!ns)
+ ns = __aa_create_ns(parent, name, dir);
+ else
+ ns = ERR_PTR(-EEXIST);
+
+ /* return ref */
+ return ns;
+}
+
+/**
+ * aa_prepare_ns - find an existing or create a new namespace of @name
+ * @parent: ns to treat as parent
+ * @name: the namespace to find or add (NOT NULL)
+ *
+ * Returns: refcounted namespace or PTR_ERR if failed to create one
+ */
+struct aa_ns *aa_prepare_ns(struct aa_ns *parent, const char *name)
+{
+ struct aa_ns *ns;
+
+ mutex_lock(&parent->lock);
+ /* try and find the specified ns and if it doesn't exist create it */
+ /* released by caller */
+ ns = aa_get_ns(__aa_find_ns(&parent->sub_ns, name));
+ if (!ns)
+ ns = __aa_create_ns(parent, name, NULL);
+ mutex_unlock(&parent->lock);
+
+ /* return ref */
+ return ns;
+}
+
+static void __ns_list_release(struct list_head *head);
+
+/**
+ * destroy_ns - remove everything contained by @ns
+ * @ns: namespace to have it contents removed (NOT NULL)
+ */
+static void destroy_ns(struct aa_ns *ns)
+{
+ if (!ns)
+ return;
+
+ mutex_lock(&ns->lock);
+ /* release all profiles in this namespace */
+ __aa_profile_list_release(&ns->base.profiles);
+
+ /* release all sub namespaces */
+ __ns_list_release(&ns->sub_ns);
+
+ if (ns->parent)
+ __aa_update_proxy(ns->unconfined, ns->parent->unconfined);
+ __aa_fs_ns_rmdir(ns);
+ mutex_unlock(&ns->lock);
+}
+
+/**
+ * __aa_remove_ns - remove a namespace and all its children
+ * @ns: namespace to be removed (NOT NULL)
+ *
+ * Requires: ns->parent->lock be held and ns removed from parent.
+ */
+void __aa_remove_ns(struct aa_ns *ns)
+{
+ /* remove ns from namespace list */
+ list_del_rcu(&ns->base.list);
+ destroy_ns(ns);
+ aa_put_ns(ns);
+}
+
+/**
+ * __ns_list_release - remove all profile namespaces on the list put refs
+ * @head: list of profile namespaces (NOT NULL)
+ *
+ * Requires: namespace lock be held
+ */
+static void __ns_list_release(struct list_head *head)
+{
+ struct aa_ns *ns, *tmp;
+
+ list_for_each_entry_safe(ns, tmp, head, base.list)
+ __aa_remove_ns(ns);
+
+}
+
+/**
+ * aa_alloc_root_ns - allocate the root profile namespace
+ *
+ * Returns: %0 on success else error
+ *
+ */
+int __init aa_alloc_root_ns(void)
+{
+ /* released by aa_free_root_ns - used as list ref*/
+ root_ns = alloc_ns(NULL, "root");
+ if (!root_ns)
+ return -ENOMEM;
+
+ return 0;
+}
+
+ /**
+ * aa_free_root_ns - free the root profile namespace
+ */
+void __init aa_free_root_ns(void)
+{
+ struct aa_ns *ns = root_ns;
+
+ root_ns = NULL;
+
+ destroy_ns(ns);
+ aa_put_ns(ns);
+}
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
index 1381206..2e37c9c 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -29,6 +29,15 @@
#include "include/policy.h"
#include "include/policy_unpack.h"
+#define K_ABI_MASK 0x3ff
+#define FORCE_COMPLAIN_FLAG 0x800
+#define VERSION_LT(X, Y) (((X) & K_ABI_MASK) < ((Y) & K_ABI_MASK))
+#define VERSION_GT(X, Y) (((X) & K_ABI_MASK) > ((Y) & K_ABI_MASK))
+
+#define v5 5 /* base version */
+#define v6 6 /* per entry policydb mediation check */
+#define v7 7 /* full network masking */
+
/*
* The AppArmor interface treats data as a type byte followed by the
* actual data. The interface has the notion of a a named entry
@@ -70,18 +79,23 @@ struct aa_ext {
static void audit_cb(struct audit_buffer *ab, void *va)
{
struct common_audit_data *sa = va;
- if (sa->aad->iface.target) {
- struct aa_profile *name = sa->aad->iface.target;
+
+ if (aad(sa)->iface.ns) {
+ audit_log_format(ab, " ns=");
+ audit_log_untrustedstring(ab, aad(sa)->iface.ns);
+ }
+ if (aad(sa)->iface.name) {
audit_log_format(ab, " name=");
- audit_log_untrustedstring(ab, name->base.hname);
+ audit_log_untrustedstring(ab, aad(sa)->iface.name);
}
- if (sa->aad->iface.pos)
- audit_log_format(ab, " offset=%ld", sa->aad->iface.pos);
+ if (aad(sa)->iface.pos)
+ audit_log_format(ab, " offset=%ld", aad(sa)->iface.pos);
}
/**
* audit_iface - do audit message for policy unpacking/load/replace/remove
* @new: profile if it has been allocated (MAYBE NULL)
+ * @ns_name: name of the ns the profile is to be loaded to (MAY BE NULL)
* @name: name of the profile being manipulated (MAYBE NULL)
* @info: any extra info about the failure (MAYBE NULL)
* @e: buffer position info
@@ -89,23 +103,33 @@ static void audit_cb(struct audit_buffer *ab, void *va)
*
* Returns: %0 or error
*/
-static int audit_iface(struct aa_profile *new, const char *name,
- const char *info, struct aa_ext *e, int error)
+static int audit_iface(struct aa_profile *new, const char *ns_name,
+ const char *name, const char *info, struct aa_ext *e,
+ int error)
{
struct aa_profile *profile = __aa_current_profile();
- struct common_audit_data sa;
- struct apparmor_audit_data aad = {0,};
- sa.type = LSM_AUDIT_DATA_NONE;
- sa.aad = &aad;
+ DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL);
if (e)
- aad.iface.pos = e->pos - e->start;
- aad.iface.target = new;
- aad.name = name;
- aad.info = info;
- aad.error = error;
-
- return aa_audit(AUDIT_APPARMOR_STATUS, profile, GFP_KERNEL, &sa,
- audit_cb);
+ aad(&sa)->iface.pos = e->pos - e->start;
+ aad(&sa)->iface.ns = ns_name;
+ if (new)
+ aad(&sa)->iface.name = new->base.hname;
+ else
+ aad(&sa)->iface.name = name;
+ aad(&sa)->info = info;
+ aad(&sa)->error = error;
+
+ return aa_audit(AUDIT_APPARMOR_STATUS, profile, &sa, audit_cb);
+}
+
+void aa_loaddata_kref(struct kref *kref)
+{
+ struct aa_loaddata *d = container_of(kref, struct aa_loaddata, count);
+
+ if (d) {
+ kzfree(d->hash);
+ kvfree(d);
+ }
}
/* test if read will be in packed data bounds */
@@ -127,8 +151,8 @@ static size_t unpack_u16_chunk(struct aa_ext *e, char **chunk)
if (!inbounds(e, sizeof(u16)))
return 0;
- size = le16_to_cpu(get_unaligned((u16 *) e->pos));
- e->pos += sizeof(u16);
+ size = le16_to_cpu(get_unaligned((__le16 *) e->pos));
+ e->pos += sizeof(__le16);
if (!inbounds(e, size))
return 0;
*chunk = e->pos;
@@ -199,7 +223,7 @@ static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name)
if (!inbounds(e, sizeof(u32)))
return 0;
if (data)
- *data = le32_to_cpu(get_unaligned((u32 *) e->pos));
+ *data = le32_to_cpu(get_unaligned((__le32 *) e->pos));
e->pos += sizeof(u32);
return 1;
}
@@ -212,7 +236,7 @@ static bool unpack_u64(struct aa_ext *e, u64 *data, const char *name)
if (!inbounds(e, sizeof(u64)))
return 0;
if (data)
- *data = le64_to_cpu(get_unaligned((u64 *) e->pos));
+ *data = le64_to_cpu(get_unaligned((__le64 *) e->pos));
e->pos += sizeof(u64);
return 1;
}
@@ -225,7 +249,7 @@ static size_t unpack_array(struct aa_ext *e, const char *name)
int size;
if (!inbounds(e, sizeof(u16)))
return 0;
- size = (int)le16_to_cpu(get_unaligned((u16 *) e->pos));
+ size = (int)le16_to_cpu(get_unaligned((__le16 *) e->pos));
e->pos += sizeof(u16);
return size;
}
@@ -238,7 +262,7 @@ static size_t unpack_blob(struct aa_ext *e, char **blob, const char *name)
u32 size;
if (!inbounds(e, sizeof(u32)))
return 0;
- size = le32_to_cpu(get_unaligned((u32 *) e->pos));
+ size = le32_to_cpu(get_unaligned((__le32 *) e->pos));
e->pos += sizeof(u32);
if (inbounds(e, (size_t) size)) {
*blob = e->pos;
@@ -340,12 +364,7 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e)
((e->pos - e->start) & 7);
size_t pad = ALIGN(sz, 8) - sz;
int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) |
- TO_ACCEPT2_FLAG(YYTD_DATA32);
-
-
- if (aa_g_paranoid_load)
- flags |= DFA_FLAG_VERIFY_STATES;
-
+ TO_ACCEPT2_FLAG(YYTD_DATA32) | DFA_FLAG_VERIFY_STATES;
dfa = aa_dfa_unpack(blob + pad, size - pad, flags);
if (IS_ERR(dfa))
@@ -466,27 +485,67 @@ fail:
return 0;
}
+static void *kvmemdup(const void *src, size_t len)
+{
+ void *p = kvmalloc(len);
+
+ if (p)
+ memcpy(p, src, len);
+ return p;
+}
+
+static u32 strhash(const void *data, u32 len, u32 seed)
+{
+ const char * const *key = data;
+
+ return jhash(*key, strlen(*key), seed);
+}
+
+static int datacmp(struct rhashtable_compare_arg *arg, const void *obj)
+{
+ const struct aa_data *data = obj;
+ const char * const *key = arg->key;
+
+ return strcmp(data->key, *key);
+}
+
/**
* unpack_profile - unpack a serialized profile
* @e: serialized data extent information (NOT NULL)
*
* NOTE: unpack profile sets audit struct if there is a failure
*/
-static struct aa_profile *unpack_profile(struct aa_ext *e)
+static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
{
struct aa_profile *profile = NULL;
- const char *name = NULL;
+ const char *tmpname, *tmpns = NULL, *name = NULL;
+ size_t ns_len;
+ struct rhashtable_params params = { 0 };
+ char *key = NULL;
+ struct aa_data *data;
int i, error = -EPROTO;
kernel_cap_t tmpcap;
u32 tmp;
+ *ns_name = NULL;
+
/* check that we have the right struct being passed */
if (!unpack_nameX(e, AA_STRUCT, "profile"))
goto fail;
if (!unpack_str(e, &name, NULL))
goto fail;
+ if (*name == '\0')
+ goto fail;
+
+ tmpname = aa_splitn_fqname(name, strlen(name), &tmpns, &ns_len);
+ if (tmpns) {
+ *ns_name = kstrndup(tmpns, ns_len, GFP_KERNEL);
+ if (!*ns_name)
+ goto fail;
+ name = tmpname;
+ }
- profile = aa_alloc_profile(name);
+ profile = aa_alloc_profile(name, GFP_KERNEL);
if (!profile)
return ERR_PTR(-ENOMEM);
@@ -519,7 +578,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
profile->flags |= PFLAG_HAT;
if (!unpack_u32(e, &tmp, NULL))
goto fail;
- if (tmp == PACKED_MODE_COMPLAIN)
+ if (tmp == PACKED_MODE_COMPLAIN || (e->version & FORCE_COMPLAIN_FLAG))
profile->mode = APPARMOR_COMPLAIN;
else if (tmp == PACKED_MODE_KILL)
profile->mode = APPARMOR_KILL;
@@ -599,7 +658,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
}
if (!unpack_nameX(e, AA_STRUCTEND, NULL))
goto fail;
- }
+ } else
+ profile->policy.dfa = aa_get_dfa(nulldfa);
/* get file rules */
profile->file.dfa = unpack_dfa(e);
@@ -607,15 +667,59 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
error = PTR_ERR(profile->file.dfa);
profile->file.dfa = NULL;
goto fail;
- }
-
- if (!unpack_u32(e, &profile->file.start, "dfa_start"))
- /* default start state */
- profile->file.start = DFA_START;
+ } else if (profile->file.dfa) {
+ if (!unpack_u32(e, &profile->file.start, "dfa_start"))
+ /* default start state */
+ profile->file.start = DFA_START;
+ } else if (profile->policy.dfa &&
+ profile->policy.start[AA_CLASS_FILE]) {
+ profile->file.dfa = aa_get_dfa(profile->policy.dfa);
+ profile->file.start = profile->policy.start[AA_CLASS_FILE];
+ } else
+ profile->file.dfa = aa_get_dfa(nulldfa);
if (!unpack_trans_table(e, profile))
goto fail;
+ if (unpack_nameX(e, AA_STRUCT, "data")) {
+ profile->data = kzalloc(sizeof(*profile->data), GFP_KERNEL);
+ if (!profile->data)
+ goto fail;
+
+ params.nelem_hint = 3;
+ params.key_len = sizeof(void *);
+ params.key_offset = offsetof(struct aa_data, key);
+ params.head_offset = offsetof(struct aa_data, head);
+ params.hashfn = strhash;
+ params.obj_cmpfn = datacmp;
+
+ if (rhashtable_init(profile->data, &params))
+ goto fail;
+
+ while (unpack_strdup(e, &key, NULL)) {
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ kzfree(key);
+ goto fail;
+ }
+
+ data->key = key;
+ data->size = unpack_blob(e, &data->data, NULL);
+ data->data = kvmemdup(data->data, data->size);
+ if (data->size && !data->data) {
+ kzfree(data->key);
+ kzfree(data);
+ goto fail;
+ }
+
+ rhashtable_insert_fast(profile->data, &data->head,
+ profile->data->p);
+ }
+
+ if (!unpack_nameX(e, AA_STRUCTEND, NULL))
+ goto fail;
+ }
+
if (!unpack_nameX(e, AA_STRUCTEND, NULL))
goto fail;
@@ -626,7 +730,8 @@ fail:
name = NULL;
else if (!name)
name = "unknown";
- audit_iface(profile, name, "failed to unpack profile", e, error);
+ audit_iface(profile, NULL, name, "failed to unpack profile", e,
+ error);
aa_free_profile(profile);
return ERR_PTR(error);
@@ -649,24 +754,32 @@ static int verify_header(struct aa_ext *e, int required, const char **ns)
/* get the interface version */
if (!unpack_u32(e, &e->version, "version")) {
if (required) {
- audit_iface(NULL, NULL, "invalid profile format", e,
- error);
- return error;
- }
-
- /* check that the interface version is currently supported */
- if (e->version != 5) {
- audit_iface(NULL, NULL, "unsupported interface version",
+ audit_iface(NULL, NULL, NULL, "invalid profile format",
e, error);
return error;
}
}
+ /* Check that the interface version is currently supported.
+ * 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)) {
+ audit_iface(NULL, NULL, NULL, "unsupported interface version",
+ e, error);
+ return error;
+ }
/* read the namespace if present */
if (unpack_str(e, &name, "namespace")) {
+ if (*name == '\0') {
+ audit_iface(NULL, NULL, NULL, "invalid namespace name",
+ e, error);
+ return error;
+ }
if (*ns && strcmp(*ns, name))
- audit_iface(NULL, NULL, "invalid ns change", e, error);
+ audit_iface(NULL, NULL, NULL, "invalid ns change", e,
+ error);
else if (!*ns)
*ns = name;
}
@@ -705,14 +818,12 @@ static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size)
*/
static int verify_profile(struct aa_profile *profile)
{
- if (aa_g_paranoid_load) {
- if (profile->file.dfa &&
- !verify_dfa_xindex(profile->file.dfa,
- profile->file.trans.size)) {
- audit_iface(profile, NULL, "Invalid named transition",
- NULL, -EPROTO);
- return -EPROTO;
- }
+ if (profile->file.dfa &&
+ !verify_dfa_xindex(profile->file.dfa,
+ profile->file.trans.size)) {
+ audit_iface(profile, NULL, NULL, "Invalid named transition",
+ NULL, -EPROTO);
+ return -EPROTO;
}
return 0;
@@ -724,6 +835,7 @@ void aa_load_ent_free(struct aa_load_ent *ent)
aa_put_profile(ent->rename);
aa_put_profile(ent->old);
aa_put_profile(ent->new);
+ kfree(ent->ns_name);
kzfree(ent);
}
}
@@ -739,7 +851,6 @@ struct aa_load_ent *aa_load_ent_alloc(void)
/**
* aa_unpack - unpack packed binary profile(s) data loaded from user space
* @udata: user data copied to kmem (NOT NULL)
- * @size: the size of the user data
* @lh: list to place unpacked profiles in a aa_repl_ws
* @ns: Returns namespace profile is in if specified else NULL (NOT NULL)
*
@@ -749,26 +860,28 @@ struct aa_load_ent *aa_load_ent_alloc(void)
*
* Returns: profile(s) on @lh else error pointer if fails to unpack
*/
-int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns)
+int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
+ const char **ns)
{
struct aa_load_ent *tmp, *ent;
struct aa_profile *profile = NULL;
int error;
struct aa_ext e = {
- .start = udata,
- .end = udata + size,
- .pos = udata,
+ .start = udata->data,
+ .end = udata->data + udata->size,
+ .pos = udata->data,
};
*ns = NULL;
while (e.pos < e.end) {
+ char *ns_name = NULL;
void *start;
error = verify_header(&e, e.pos == e.start, ns);
if (error)
goto fail;
start = e.pos;
- profile = unpack_profile(&e);
+ profile = unpack_profile(&e, &ns_name);
if (IS_ERR(profile)) {
error = PTR_ERR(profile);
goto fail;
@@ -778,7 +891,8 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns)
if (error)
goto fail_profile;
- error = aa_calc_profile_hash(profile, e.version, start,
+ if (aa_g_hash_policy)
+ error = aa_calc_profile_hash(profile, e.version, start,
e.pos - start);
if (error)
goto fail_profile;
@@ -790,9 +904,18 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns)
}
ent->new = profile;
+ ent->ns_name = ns_name;
list_add_tail(&ent->list, lh);
}
-
+ udata->abi = e.version & K_ABI_MASK;
+ if (aa_g_hash_policy) {
+ udata->hash = aa_calc_hash(udata->data, udata->size);
+ if (IS_ERR(udata->hash)) {
+ error = PTR_ERR(udata->hash);
+ udata->hash = NULL;
+ goto fail;
+ }
+ }
return 0;
fail_profile:
diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c
index b125acc..3466a27 100644
--- a/security/apparmor/procattr.c
+++ b/security/apparmor/procattr.c
@@ -15,6 +15,7 @@
#include "include/apparmor.h"
#include "include/context.h"
#include "include/policy.h"
+#include "include/policy_ns.h"
#include "include/domain.h"
#include "include/procattr.h"
@@ -39,14 +40,14 @@ int aa_getprocattr(struct aa_profile *profile, char **string)
int len = 0, mode_len = 0, ns_len = 0, name_len;
const char *mode_str = aa_profile_mode_names[profile->mode];
const char *ns_name = NULL;
- struct aa_namespace *ns = profile->ns;
- struct aa_namespace *current_ns = __aa_current_profile()->ns;
+ struct aa_ns *ns = profile->ns;
+ struct aa_ns *current_ns = __aa_current_profile()->ns;
char *s;
- if (!aa_ns_visible(current_ns, ns))
+ if (!aa_ns_visible(current_ns, ns, true))
return -EACCES;
- ns_name = aa_ns_name(current_ns, ns);
+ ns_name = aa_ns_name(current_ns, ns, true);
ns_len = strlen(ns_name);
/* if the visible ns_name is > 0 increase size for : :// seperator */
@@ -87,13 +88,13 @@ int aa_getprocattr(struct aa_profile *profile, char **string)
*
* Returns: start position of name after token else NULL on failure
*/
-static char *split_token_from_name(int op, char *args, u64 * token)
+static char *split_token_from_name(const char *op, char *args, u64 *token)
{
char *name;
*token = simple_strtoull(args, &name, 16);
if ((name == args) || *name != '^') {
- AA_ERROR("%s: Invalid input '%s'", op_table[op], args);
+ AA_ERROR("%s: Invalid input '%s'", op, args);
return ERR_PTR(-EINVAL);
}
@@ -138,28 +139,13 @@ int aa_setprocattr_changehat(char *args, size_t size, int test)
for (count = 0; (hat < end) && count < 16; ++count) {
char *next = hat + strlen(hat) + 1;
hats[count] = hat;
+ AA_DEBUG("%s: (pid %d) Magic 0x%llx count %d hat '%s'\n"
+ , __func__, current->pid, token, count, hat);
hat = next;
}
- }
-
- AA_DEBUG("%s: Magic 0x%llx Hat '%s'\n",
- __func__, token, hat ? hat : NULL);
+ } else
+ AA_DEBUG("%s: (pid %d) Magic 0x%llx count %d Hat '%s'\n",
+ __func__, current->pid, token, count, "<NULL>");
return aa_change_hat(hats, count, token, test);
}
-
-/**
- * aa_setprocattr_changeprofile - handle procattr interface to changeprofile
- * @fqname: args received from writting to /proc/<pid>/attr/current (NOT NULL)
- * @onexec: true if change_profile should be delayed until exec
- * @test: true if this is a test of change_profile permissions
- *
- * Returns: %0 or error code if change_profile fails
- */
-int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test)
-{
- char *name, *ns_name;
-
- name = aa_split_fqname(fqname, &ns_name);
- return aa_change_profile(ns_name, name, onexec, test);
-}
diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c
index 67a6072..86a941a 100644
--- a/security/apparmor/resource.c
+++ b/security/apparmor/resource.c
@@ -35,7 +35,7 @@ static void audit_cb(struct audit_buffer *ab, void *va)
struct common_audit_data *sa = va;
audit_log_format(ab, " rlimit=%s value=%lu",
- rlim_names[sa->aad->rlim.rlim], sa->aad->rlim.max);
+ rlim_names[aad(sa)->rlim.rlim], aad(sa)->rlim.max);
}
/**
@@ -50,17 +50,12 @@ static void audit_cb(struct audit_buffer *ab, void *va)
static int audit_resource(struct aa_profile *profile, unsigned int resource,
unsigned long value, int error)
{
- struct common_audit_data sa;
- struct apparmor_audit_data aad = {0,};
-
- sa.type = LSM_AUDIT_DATA_NONE;
- sa.aad = &aad;
- aad.op = OP_SETRLIMIT,
- aad.rlim.rlim = resource;
- aad.rlim.max = value;
- aad.error = error;
- return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_KERNEL, &sa,
- audit_cb);
+ DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SETRLIMIT);
+
+ aad(&sa)->rlim.rlim = resource;
+ aad(&sa)->rlim.max = value;
+ aad(&sa)->error = error;
+ return aa_audit(AUDIT_APPARMOR_AUTO, profile, &sa, audit_cb);
}
/**
diff --git a/security/apparmor/secid.c b/security/apparmor/secid.c
new file mode 100644
index 0000000..3a3edba
--- /dev/null
+++ b/security/apparmor/secid.c
@@ -0,0 +1,55 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor security identifier (secid) manipulation fns
+ *
+ * Copyright 2009-2010 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.
+ *
+ *
+ * AppArmor allocates a unique secid for every profile loaded. If a profile
+ * is replaced it receives the secid of the profile it is replacing.
+ *
+ * The secid value of 0 is invalid.
+ */
+
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+
+#include "include/secid.h"
+
+/* global counter from which secids are allocated */
+static u32 global_secid;
+static DEFINE_SPINLOCK(secid_lock);
+
+/* TODO FIXME: add secid to profile mapping, and secid recycling */
+
+/**
+ * aa_alloc_secid - allocate a new secid for a profile
+ */
+u32 aa_alloc_secid(void)
+{
+ u32 secid;
+
+ /*
+ * TODO FIXME: secid recycling - part of profile mapping table
+ */
+ spin_lock(&secid_lock);
+ secid = (++global_secid);
+ spin_unlock(&secid_lock);
+ return secid;
+}
+
+/**
+ * aa_free_secid - free a secid
+ * @secid: secid to free
+ */
+void aa_free_secid(u32 secid)
+{
+ ; /* NOP ATM */
+}
diff --git a/security/apparmor/sid.c b/security/apparmor/sid.c
deleted file mode 100644
index f0b34f7..0000000
--- a/security/apparmor/sid.c
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * AppArmor security module
- *
- * This file contains AppArmor security identifier (sid) manipulation fns
- *
- * Copyright 2009-2010 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.
- *
- *
- * AppArmor allocates a unique sid for every profile loaded. If a profile
- * is replaced it receives the sid of the profile it is replacing.
- *
- * The sid value of 0 is invalid.
- */
-
-#include <linux/spinlock.h>
-#include <linux/errno.h>
-#include <linux/err.h>
-
-#include "include/sid.h"
-
-/* global counter from which sids are allocated */
-static u32 global_sid;
-static DEFINE_SPINLOCK(sid_lock);
-
-/* TODO FIXME: add sid to profile mapping, and sid recycling */
-
-/**
- * aa_alloc_sid - allocate a new sid for a profile
- */
-u32 aa_alloc_sid(void)
-{
- u32 sid;
-
- /*
- * TODO FIXME: sid recycling - part of profile mapping table
- */
- spin_lock(&sid_lock);
- sid = (++global_sid);
- spin_unlock(&sid_lock);
- return sid;
-}
-
-/**
- * aa_free_sid - free a sid
- * @sid: sid to free
- */
-void aa_free_sid(u32 sid)
-{
- ; /* NOP ATM */
-}
diff --git a/security/commoncap.c b/security/commoncap.c
index 8df676f..78b3783 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -548,9 +548,10 @@ skip:
if ((is_setid ||
!cap_issubset(new->cap_permitted, old->cap_permitted)) &&
- bprm->unsafe & ~LSM_UNSAFE_PTRACE_CAP) {
+ ((bprm->unsafe & ~LSM_UNSAFE_PTRACE) ||
+ !ptracer_capable(current, new->user_ns))) {
/* downgrade; they get no more than they had, and maybe less */
- if (!capable(CAP_SETUID) ||
+ if (!ns_capable(new->user_ns, CAP_SETUID) ||
(bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)) {
new->euid = new->uid;
new->egid = new->gid;
@@ -1093,7 +1094,8 @@ struct security_hook_list capability_hooks[] = {
void __init capability_add_hooks(void)
{
- security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks));
+ security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks),
+ "capability");
}
#endif /* CONFIG_SECURITY */
diff --git a/security/inode.c b/security/inode.c
index c83db05..2cb1416 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -20,6 +20,7 @@
#include <linux/init.h>
#include <linux/namei.h>
#include <linux/security.h>
+#include <linux/lsm_hooks.h>
#include <linux/magic.h>
static struct vfsmount *mount;
@@ -204,6 +205,21 @@ void securityfs_remove(struct dentry *dentry)
}
EXPORT_SYMBOL_GPL(securityfs_remove);
+#ifdef CONFIG_SECURITY
+static struct dentry *lsm_dentry;
+static ssize_t lsm_read(struct file *filp, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ return simple_read_from_buffer(buf, count, ppos, lsm_names,
+ strlen(lsm_names));
+}
+
+static const struct file_operations lsm_ops = {
+ .read = lsm_read,
+ .llseek = generic_file_llseek,
+};
+#endif
+
static int __init securityfs_init(void)
{
int retval;
@@ -213,9 +229,15 @@ static int __init securityfs_init(void)
return retval;
retval = register_filesystem(&fs_type);
- if (retval)
+ if (retval) {
sysfs_remove_mount_point(kernel_kobj, "security");
- return retval;
+ return retval;
+ }
+#ifdef CONFIG_SECURITY
+ lsm_dentry = securityfs_create_file("lsm", 0444, NULL, NULL,
+ &lsm_ops);
+#endif
+ return 0;
}
core_initcall(securityfs_init);
diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c
index 4304372..106e855 100644
--- a/security/integrity/digsig.c
+++ b/security/integrity/digsig.c
@@ -51,7 +51,7 @@ static bool init_keyring __initdata;
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
const char *digest, int digestlen)
{
- if (id >= INTEGRITY_KEYRING_MAX)
+ if (id >= INTEGRITY_KEYRING_MAX || siglen < 2)
return -EINVAL;
if (!keyring[id]) {
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
index bf66391..d7f282d 100644
--- a/security/integrity/evm/evm_crypto.c
+++ b/security/integrity/evm/evm_crypto.c
@@ -151,8 +151,16 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
memset(&hmac_misc, 0, sizeof(hmac_misc));
hmac_misc.ino = inode->i_ino;
hmac_misc.generation = inode->i_generation;
- hmac_misc.uid = from_kuid(inode->i_sb->s_user_ns, inode->i_uid);
- hmac_misc.gid = from_kgid(inode->i_sb->s_user_ns, inode->i_gid);
+ /* The hmac uid and gid must be encoded in the initial user
+ * namespace (not the filesystems user namespace) as encoding
+ * them in the filesystems user namespace allows an attack
+ * where first they are written in an unprivileged fuse mount
+ * of a filesystem and then the system is tricked to mount the
+ * filesystem for real on next boot and trust it because
+ * everything is signed.
+ */
+ hmac_misc.uid = from_kuid(&init_user_ns, inode->i_uid);
+ hmac_misc.gid = from_kgid(&init_user_ns, inode->i_gid);
hmac_misc.mode = inode->i_mode;
crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc));
if (evm_hmac_attrs & EVM_ATTR_FSUUID)
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index ba86155..063d38a 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -22,6 +22,8 @@
#include <linux/xattr.h>
#include <linux/integrity.h>
#include <linux/evm.h>
+#include <linux/magic.h>
+
#include <crypto/hash.h>
#include <crypto/algapi.h>
#include "evm.h"
@@ -145,6 +147,10 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
/* check value type */
switch (xattr_data->type) {
case EVM_XATTR_HMAC:
+ if (xattr_len != sizeof(struct evm_ima_xattr_data)) {
+ evm_status = INTEGRITY_FAIL;
+ goto out;
+ }
rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
xattr_value_len, calc.digest);
if (rc)
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index 5487827..370eb2f 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -27,6 +27,18 @@ config IMA
to learn more about IMA.
If unsure, say N.
+config IMA_KEXEC
+ bool "Enable carrying the IMA measurement list across a soft boot"
+ depends on IMA && TCG_TPM && HAVE_IMA_KEXEC
+ default n
+ help
+ TPM PCRs are only reset on a hard reboot. In order to validate
+ a TPM's quote after a soft boot, the IMA measurement list of the
+ running kernel must be saved and restored on boot.
+
+ Depending on the IMA policy, the measurement list can grow to
+ be very large.
+
config IMA_MEASURE_PCR_IDX
int
depends on IMA
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
index 9aeaeda..29f198b 100644
--- a/security/integrity/ima/Makefile
+++ b/security/integrity/ima/Makefile
@@ -8,4 +8,5 @@ obj-$(CONFIG_IMA) += ima.o
ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
ima_policy.o ima_template.o ima_template_lib.o
ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o
+ima-$(CONFIG_HAVE_IMA_KEXEC) += ima_kexec.o
obj-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index db25f54..b563fbd 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -28,6 +28,10 @@
#include "../integrity.h"
+#ifdef CONFIG_HAVE_IMA_KEXEC
+#include <asm/ima.h>
+#endif
+
enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_BINARY_NO_FIELD_LEN,
IMA_SHOW_BINARY_OLD_STRING_FMT, IMA_SHOW_ASCII };
enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
@@ -81,6 +85,7 @@ struct ima_template_field {
/* IMA template descriptor definition */
struct ima_template_desc {
+ struct list_head list;
char *name;
char *fmt;
int num_fields;
@@ -102,6 +107,27 @@ struct ima_queue_entry {
};
extern struct list_head ima_measurements; /* list of all measurements */
+/* Some details preceding the binary serialized measurement list */
+struct ima_kexec_hdr {
+ u16 version;
+ u16 _reserved0;
+ u32 _reserved1;
+ u64 buffer_size;
+ u64 count;
+};
+
+#ifdef CONFIG_HAVE_IMA_KEXEC
+void ima_load_kexec_buffer(void);
+#else
+static inline void ima_load_kexec_buffer(void) {}
+#endif /* CONFIG_HAVE_IMA_KEXEC */
+
+/*
+ * The default binary_runtime_measurements list format is defined as the
+ * platform native format. The canonical format is defined as little-endian.
+ */
+extern bool ima_canonical_fmt;
+
/* Internal IMA function definitions */
int ima_init(void);
int ima_fs_init(void);
@@ -122,7 +148,12 @@ int ima_init_crypto(void);
void ima_putc(struct seq_file *m, void *data, int datalen);
void ima_print_digest(struct seq_file *m, u8 *digest, u32 size);
struct ima_template_desc *ima_template_desc_current(void);
+int ima_restore_measurement_entry(struct ima_template_entry *entry);
+int ima_restore_measurement_list(loff_t bufsize, void *buf);
+int ima_measurements_show(struct seq_file *m, void *v);
+unsigned long ima_get_binary_runtime_size(void);
int ima_init_template(void);
+void ima_init_template_list(void);
/*
* used to protect h_table and sha_table
@@ -173,7 +204,7 @@ int ima_store_template(struct ima_template_entry *entry, int violation,
struct inode *inode,
const unsigned char *filename, int pcr);
void ima_free_template_entry(struct ima_template_entry *entry);
-const char *ima_d_path(const struct path *path, char **pathbuf);
+const char *ima_d_path(const struct path *path, char **pathbuf, char *filename);
/* IMA policy related functions */
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 9df26a2..c2edba8 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -157,7 +157,8 @@ err_out:
/**
* ima_get_action - appraise & measure decision based on policy.
* @inode: pointer to inode to measure
- * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE)
+ * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXEC,
+ * MAY_APPEND)
* @func: caller identifier
* @pcr: pointer filled in if matched measure policy sets pcr=
*
@@ -318,7 +319,17 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
iint->flags |= IMA_AUDITED;
}
-const char *ima_d_path(const struct path *path, char **pathbuf)
+/*
+ * ima_d_path - return a pointer to the full pathname
+ *
+ * Attempt to return a pointer to the full pathname for use in the
+ * IMA measurement list, IMA audit records, and auditing logs.
+ *
+ * On failure, return a pointer to a copy of the filename, not dname.
+ * Returning a pointer to dname, could result in using the pointer
+ * after the memory has been freed.
+ */
+const char *ima_d_path(const struct path *path, char **pathbuf, char *namebuf)
{
char *pathname = NULL;
@@ -331,5 +342,11 @@ const char *ima_d_path(const struct path *path, char **pathbuf)
pathname = NULL;
}
}
- return pathname ?: (const char *)path->dentry->d_name.name;
+
+ if (!pathname) {
+ strlcpy(namebuf, path->dentry->d_name.name, NAME_MAX);
+ pathname = namebuf;
+ }
+
+ return pathname;
}
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 389325a..1fd9539 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -130,6 +130,7 @@ enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
int xattr_len)
{
struct signature_v2_hdr *sig;
+ enum hash_algo ret;
if (!xattr_value || xattr_len < 2)
/* return default hash algo */
@@ -143,7 +144,9 @@ enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
return sig->hash_algo;
break;
case IMA_XATTR_DIGEST_NG:
- return xattr_value->digest[0];
+ ret = xattr_value->digest[0];
+ if (ret < HASH_ALGO__LAST)
+ return ret;
break;
case IMA_XATTR_DIGEST:
/* this is for backward compatibility */
@@ -384,14 +387,10 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
result = ima_protect_xattr(dentry, xattr_name, xattr_value,
xattr_value_len);
if (result == 1) {
- bool digsig;
-
if (!xattr_value_len || (xvalue->type >= IMA_XATTR_LAST))
return -EINVAL;
- digsig = (xvalue->type == EVM_IMA_XATTR_DIGSIG);
- if (!digsig && (ima_appraise & IMA_APPRAISE_ENFORCE))
- return -EPERM;
- ima_reset_appraise_flags(d_backing_inode(dentry), digsig);
+ ima_reset_appraise_flags(d_backing_inode(dentry),
+ (xvalue->type == EVM_IMA_XATTR_DIGSIG) ? 1 : 0);
result = 0;
}
return result;
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
index 38f2ed8..802d5d2 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -477,11 +477,13 @@ static int ima_calc_field_array_hash_tfm(struct ima_field_data *field_data,
u8 buffer[IMA_EVENT_NAME_LEN_MAX + 1] = { 0 };
u8 *data_to_hash = field_data[i].data;
u32 datalen = field_data[i].len;
+ u32 datalen_to_hash =
+ !ima_canonical_fmt ? datalen : cpu_to_le32(datalen);
if (strcmp(td->name, IMA_TEMPLATE_IMA_NAME) != 0) {
rc = crypto_shash_update(shash,
- (const u8 *) &field_data[i].len,
- sizeof(field_data[i].len));
+ (const u8 *) &datalen_to_hash,
+ sizeof(datalen_to_hash));
if (rc)
break;
} else if (strcmp(td->fields[i]->field_id, "n") == 0) {
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index c07a384..ca303e5 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -28,6 +28,16 @@
static DEFINE_MUTEX(ima_write_mutex);
+bool ima_canonical_fmt;
+static int __init default_canonical_fmt_setup(char *str)
+{
+#ifdef __BIG_ENDIAN
+ ima_canonical_fmt = 1;
+#endif
+ return 1;
+}
+__setup("ima_canonical_fmt", default_canonical_fmt_setup);
+
static int valid_policy = 1;
#define TMPBUFLEN 12
static ssize_t ima_show_htable_value(char __user *buf, size_t count,
@@ -116,13 +126,13 @@ void ima_putc(struct seq_file *m, void *data, int datalen)
* [eventdata length]
* eventdata[n]=template specific data
*/
-static int ima_measurements_show(struct seq_file *m, void *v)
+int ima_measurements_show(struct seq_file *m, void *v)
{
/* the list never shrinks, so we don't need a lock here */
struct ima_queue_entry *qe = v;
struct ima_template_entry *e;
char *template_name;
- int namelen;
+ u32 pcr, namelen, template_data_len; /* temporary fields */
bool is_ima_template = false;
int i;
@@ -139,25 +149,29 @@ static int ima_measurements_show(struct seq_file *m, void *v)
* PCR used defaults to the same (config option) in
* little-endian format, unless set in policy
*/
- ima_putc(m, &e->pcr, sizeof(e->pcr));
+ pcr = !ima_canonical_fmt ? e->pcr : cpu_to_le32(e->pcr);
+ ima_putc(m, &pcr, sizeof(e->pcr));
/* 2nd: template digest */
ima_putc(m, e->digest, TPM_DIGEST_SIZE);
/* 3rd: template name size */
- namelen = strlen(template_name);
+ namelen = !ima_canonical_fmt ? strlen(template_name) :
+ cpu_to_le32(strlen(template_name));
ima_putc(m, &namelen, sizeof(namelen));
/* 4th: template name */
- ima_putc(m, template_name, namelen);
+ ima_putc(m, template_name, strlen(template_name));
/* 5th: template length (except for 'ima' template) */
if (strcmp(template_name, IMA_TEMPLATE_IMA_NAME) == 0)
is_ima_template = true;
- if (!is_ima_template)
- ima_putc(m, &e->template_data_len,
- sizeof(e->template_data_len));
+ if (!is_ima_template) {
+ template_data_len = !ima_canonical_fmt ? e->template_data_len :
+ cpu_to_le32(e->template_data_len);
+ ima_putc(m, &template_data_len, sizeof(e->template_data_len));
+ }
/* 6th: template specific data */
for (i = 0; i < e->template_desc->num_fields; i++) {
@@ -401,7 +415,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)
const char *cause = valid_policy ? "completed" : "failed";
if ((file->f_flags & O_ACCMODE) == O_RDONLY)
- return 0;
+ return seq_release(inode, file);
if (valid_policy && ima_check_policy() < 0) {
cause = "failed";
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 32912bd..2967d49 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -115,7 +115,8 @@ int __init ima_init(void)
ima_used_chip = 1;
if (!ima_used_chip)
- pr_info("No TPM chip found, activating TPM-bypass!\n");
+ pr_info("No TPM chip found, activating TPM-bypass! (rc=%d)\n",
+ rc);
rc = integrity_init_keyring(INTEGRITY_KEYRING_IMA);
if (rc)
@@ -128,6 +129,8 @@ int __init ima_init(void)
if (rc != 0)
return rc;
+ ima_load_kexec_buffer();
+
rc = ima_add_boot_aggregate(); /* boot aggregate must be first entry */
if (rc != 0)
return rc;
diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
new file mode 100644
index 0000000..e473eee
--- /dev/null
+++ b/security/integrity/ima/ima_kexec.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2016 IBM Corporation
+ *
+ * Authors:
+ * Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
+ * Mimi Zohar <zohar@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/seq_file.h>
+#include <linux/vmalloc.h>
+#include <linux/kexec.h>
+#include "ima.h"
+
+#ifdef CONFIG_IMA_KEXEC
+static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer,
+ unsigned long segment_size)
+{
+ struct ima_queue_entry *qe;
+ struct seq_file file;
+ struct ima_kexec_hdr khdr;
+ int ret = 0;
+
+ /* segment size can't change between kexec load and execute */
+ file.buf = vmalloc(segment_size);
+ if (!file.buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ file.size = segment_size;
+ file.read_pos = 0;
+ file.count = sizeof(khdr); /* reserved space */
+
+ memset(&khdr, 0, sizeof(khdr));
+ khdr.version = 1;
+ list_for_each_entry_rcu(qe, &ima_measurements, later) {
+ if (file.count < file.size) {
+ khdr.count++;
+ ima_measurements_show(&file, qe);
+ } else {
+ ret = -EINVAL;
+ break;
+ }
+ }
+
+ if (ret < 0)
+ goto out;
+
+ /*
+ * fill in reserved space with some buffer details
+ * (eg. version, buffer size, number of measurements)
+ */
+ khdr.buffer_size = file.count;
+ if (ima_canonical_fmt) {
+ khdr.version = cpu_to_le16(khdr.version);
+ khdr.count = cpu_to_le64(khdr.count);
+ khdr.buffer_size = cpu_to_le64(khdr.buffer_size);
+ }
+ memcpy(file.buf, &khdr, sizeof(khdr));
+
+ print_hex_dump(KERN_DEBUG, "ima dump: ", DUMP_PREFIX_NONE,
+ 16, 1, file.buf,
+ file.count < 100 ? file.count : 100, true);
+
+ *buffer_size = file.count;
+ *buffer = file.buf;
+out:
+ if (ret == -EINVAL)
+ vfree(file.buf);
+ return ret;
+}
+
+/*
+ * Called during kexec_file_load so that IMA can add a segment to the kexec
+ * image for the measurement list for the next kernel.
+ *
+ * This function assumes that kexec_mutex is held.
+ */
+void ima_add_kexec_buffer(struct kimage *image)
+{
+ struct kexec_buf kbuf = { .image = image, .buf_align = PAGE_SIZE,
+ .buf_min = 0, .buf_max = ULONG_MAX,
+ .top_down = true };
+ unsigned long binary_runtime_size;
+
+ /* use more understandable variable names than defined in kbuf */
+ void *kexec_buffer = NULL;
+ size_t kexec_buffer_size;
+ size_t kexec_segment_size;
+ int ret;
+
+ /*
+ * Reserve an extra half page of memory for additional measurements
+ * added during the kexec load.
+ */
+ binary_runtime_size = ima_get_binary_runtime_size();
+ if (binary_runtime_size >= ULONG_MAX - PAGE_SIZE)
+ kexec_segment_size = ULONG_MAX;
+ else
+ kexec_segment_size = ALIGN(ima_get_binary_runtime_size() +
+ PAGE_SIZE / 2, PAGE_SIZE);
+ if ((kexec_segment_size == ULONG_MAX) ||
+ ((kexec_segment_size >> PAGE_SHIFT) > totalram_pages / 2)) {
+ pr_err("Binary measurement list too large.\n");
+ return;
+ }
+
+ ima_dump_measurement_list(&kexec_buffer_size, &kexec_buffer,
+ kexec_segment_size);
+ if (!kexec_buffer) {
+ pr_err("Not enough memory for the kexec measurement buffer.\n");
+ return;
+ }
+
+ kbuf.buffer = kexec_buffer;
+ kbuf.bufsz = kexec_buffer_size;
+ kbuf.memsz = kexec_segment_size;
+ ret = kexec_add_buffer(&kbuf);
+ if (ret) {
+ pr_err("Error passing over kexec measurement buffer.\n");
+ return;
+ }
+
+ ret = arch_ima_add_kexec_buffer(image, kbuf.mem, kexec_segment_size);
+ if (ret) {
+ pr_err("Error passing over kexec measurement buffer.\n");
+ return;
+ }
+
+ pr_debug("kexec measurement buffer for the loaded kernel at 0x%lx.\n",
+ kbuf.mem);
+}
+#endif /* IMA_KEXEC */
+
+/*
+ * Restore the measurement list from the previous kernel.
+ */
+void ima_load_kexec_buffer(void)
+{
+ void *kexec_buffer = NULL;
+ size_t kexec_buffer_size = 0;
+ int rc;
+
+ rc = ima_get_kexec_buffer(&kexec_buffer, &kexec_buffer_size);
+ switch (rc) {
+ case 0:
+ rc = ima_restore_measurement_list(kexec_buffer_size,
+ kexec_buffer);
+ if (rc != 0)
+ pr_err("Failed to restore the measurement list: %d\n",
+ rc);
+
+ ima_free_kexec_buffer();
+ break;
+ case -ENOTSUPP:
+ pr_debug("Restoring the measurement list not supported\n");
+ break;
+ case -ENOENT:
+ pr_debug("No measurement list to restore\n");
+ break;
+ default:
+ pr_debug("Error restoring the measurement list: %d\n", rc);
+ }
+}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 423d111..2aebb79 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -83,6 +83,7 @@ static void ima_rdwr_violation_check(struct file *file,
const char **pathname)
{
struct inode *inode = file_inode(file);
+ char filename[NAME_MAX];
fmode_t mode = file->f_mode;
bool send_tomtou = false, send_writers = false;
@@ -102,7 +103,7 @@ static void ima_rdwr_violation_check(struct file *file,
if (!send_tomtou && !send_writers)
return;
- *pathname = ima_d_path(&file->f_path, pathbuf);
+ *pathname = ima_d_path(&file->f_path, pathbuf, filename);
if (send_tomtou)
ima_add_violation(file, *pathname, iint,
@@ -161,6 +162,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
struct integrity_iint_cache *iint = NULL;
struct ima_template_desc *template_desc;
char *pathbuf = NULL;
+ char filename[NAME_MAX];
const char *pathname = NULL;
int rc = -ENOMEM, action, must_appraise;
int pcr = CONFIG_IMA_MEASURE_PCR_IDX;
@@ -239,8 +241,8 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
goto out_digsig;
}
- if (!pathname) /* ima_rdwr_violation possibly pre-fetched */
- pathname = ima_d_path(&file->f_path, &pathbuf);
+ if (!pathbuf) /* ima_rdwr_violation possibly pre-fetched */
+ pathname = ima_d_path(&file->f_path, &pathbuf, filename);
if (action & IMA_MEASURE)
ima_store_measurement(iint, file, pathname,
@@ -307,7 +309,7 @@ int ima_bprm_check(struct linux_binprm *bprm)
/**
* ima_path_check - based on policy, collect/store measurement.
* @file: pointer to the file to be measured
- * @mask: contains MAY_READ, MAY_WRITE or MAY_EXECUTE
+ * @mask: contains MAY_READ, MAY_WRITE, MAY_EXEC or MAY_APPEND
*
* Measure files based on the ima_must_measure() policy decision.
*
@@ -317,8 +319,8 @@ int ima_bprm_check(struct linux_binprm *bprm)
int ima_file_check(struct file *file, int mask, int opened)
{
return process_measurement(file, NULL, 0,
- mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
- FILE_CHECK, opened);
+ mask & (MAY_READ | MAY_WRITE | MAY_EXEC |
+ MAY_APPEND), FILE_CHECK, opened);
}
EXPORT_SYMBOL_GPL(ima_file_check);
@@ -418,6 +420,7 @@ static int __init init_ima(void)
{
int error;
+ ima_init_template_list();
hash_setup(CONFIG_IMA_DEFAULT_HASH);
error = ima_init();
if (!error) {
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index 32f6ac0..d9aa5ab 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -29,6 +29,11 @@
#define AUDIT_CAUSE_LEN_MAX 32
LIST_HEAD(ima_measurements); /* list of all measurements */
+#ifdef CONFIG_IMA_KEXEC
+static unsigned long binary_runtime_size;
+#else
+static unsigned long binary_runtime_size = ULONG_MAX;
+#endif
/* key: inode (before secure-hashing a file) */
struct ima_h_table ima_htable = {
@@ -64,12 +69,32 @@ static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value,
return ret;
}
+/*
+ * Calculate the memory required for serializing a single
+ * binary_runtime_measurement list entry, which contains a
+ * couple of variable length fields (e.g template name and data).
+ */
+static int get_binary_runtime_size(struct ima_template_entry *entry)
+{
+ int size = 0;
+
+ size += sizeof(u32); /* pcr */
+ size += sizeof(entry->digest);
+ size += sizeof(int); /* template name size field */
+ size += strlen(entry->template_desc->name) + 1;
+ size += sizeof(entry->template_data_len);
+ size += entry->template_data_len;
+ return size;
+}
+
/* ima_add_template_entry helper function:
- * - Add template entry to measurement list and hash table.
+ * - Add template entry to the measurement list and hash table, for
+ * all entries except those carried across kexec.
*
* (Called with ima_extend_list_mutex held.)
*/
-static int ima_add_digest_entry(struct ima_template_entry *entry)
+static int ima_add_digest_entry(struct ima_template_entry *entry,
+ bool update_htable)
{
struct ima_queue_entry *qe;
unsigned int key;
@@ -85,11 +110,34 @@ static int ima_add_digest_entry(struct ima_template_entry *entry)
list_add_tail_rcu(&qe->later, &ima_measurements);
atomic_long_inc(&ima_htable.len);
- key = ima_hash_key(entry->digest);
- hlist_add_head_rcu(&qe->hnext, &ima_htable.queue[key]);
+ if (update_htable) {
+ key = ima_hash_key(entry->digest);
+ hlist_add_head_rcu(&qe->hnext, &ima_htable.queue[key]);
+ }
+
+ if (binary_runtime_size != ULONG_MAX) {
+ int size;
+
+ size = get_binary_runtime_size(entry);
+ binary_runtime_size = (binary_runtime_size < ULONG_MAX - size) ?
+ binary_runtime_size + size : ULONG_MAX;
+ }
return 0;
}
+/*
+ * Return the amount of memory required for serializing the
+ * entire binary_runtime_measurement list, including the ima_kexec_hdr
+ * structure.
+ */
+unsigned long ima_get_binary_runtime_size(void)
+{
+ if (binary_runtime_size >= (ULONG_MAX - sizeof(struct ima_kexec_hdr)))
+ return ULONG_MAX;
+ else
+ return binary_runtime_size + sizeof(struct ima_kexec_hdr);
+};
+
static int ima_pcr_extend(const u8 *hash, int pcr)
{
int result = 0;
@@ -103,8 +151,13 @@ static int ima_pcr_extend(const u8 *hash, int pcr)
return result;
}
-/* Add template entry to the measurement list and hash table,
- * and extend the pcr.
+/*
+ * Add template entry to the measurement list and hash table, and
+ * extend the pcr.
+ *
+ * On systems which support carrying the IMA measurement list across
+ * kexec, maintain the total memory size required for serializing the
+ * binary_runtime_measurements.
*/
int ima_add_template_entry(struct ima_template_entry *entry, int violation,
const char *op, struct inode *inode,
@@ -126,7 +179,7 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
}
}
- result = ima_add_digest_entry(entry);
+ result = ima_add_digest_entry(entry, 1);
if (result < 0) {
audit_cause = "ENOMEM";
audit_info = 0;
@@ -149,3 +202,13 @@ out:
op, audit_cause, result, audit_info);
return result;
}
+
+int ima_restore_measurement_entry(struct ima_template_entry *entry)
+{
+ int result = 0;
+
+ mutex_lock(&ima_extend_list_mutex);
+ result = ima_add_digest_entry(entry, 0);
+ mutex_unlock(&ima_extend_list_mutex);
+ return result;
+}
diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c
index febd12e..cebb37c 100644
--- a/security/integrity/ima/ima_template.c
+++ b/security/integrity/ima/ima_template.c
@@ -15,16 +15,20 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/rculist.h>
#include "ima.h"
#include "ima_template_lib.h"
-static struct ima_template_desc defined_templates[] = {
+static struct ima_template_desc builtin_templates[] = {
{.name = IMA_TEMPLATE_IMA_NAME, .fmt = IMA_TEMPLATE_IMA_FMT},
{.name = "ima-ng", .fmt = "d-ng|n-ng"},
{.name = "ima-sig", .fmt = "d-ng|n-ng|sig"},
{.name = "", .fmt = ""}, /* placeholder for a custom format */
};
+static LIST_HEAD(defined_templates);
+static DEFINE_SPINLOCK(template_list);
+
static struct ima_template_field supported_fields[] = {
{.field_id = "d", .field_init = ima_eventdigest_init,
.field_show = ima_show_template_digest},
@@ -37,6 +41,7 @@ static struct ima_template_field supported_fields[] = {
{.field_id = "sig", .field_init = ima_eventsig_init,
.field_show = ima_show_template_sig},
};
+#define MAX_TEMPLATE_NAME_LEN 15
static struct ima_template_desc *ima_template;
static struct ima_template_desc *lookup_template_desc(const char *name);
@@ -52,6 +57,8 @@ static int __init ima_template_setup(char *str)
if (ima_template)
return 1;
+ ima_init_template_list();
+
/*
* Verify that a template with the supplied name exists.
* If not, use CONFIG_IMA_DEFAULT_TEMPLATE.
@@ -80,7 +87,7 @@ __setup("ima_template=", ima_template_setup);
static int __init ima_template_fmt_setup(char *str)
{
- int num_templates = ARRAY_SIZE(defined_templates);
+ int num_templates = ARRAY_SIZE(builtin_templates);
if (ima_template)
return 1;
@@ -91,22 +98,28 @@ static int __init ima_template_fmt_setup(char *str)
return 1;
}
- defined_templates[num_templates - 1].fmt = str;
- ima_template = defined_templates + num_templates - 1;
+ builtin_templates[num_templates - 1].fmt = str;
+ ima_template = builtin_templates + num_templates - 1;
+
return 1;
}
__setup("ima_template_fmt=", ima_template_fmt_setup);
static struct ima_template_desc *lookup_template_desc(const char *name)
{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(defined_templates); i++) {
- if (strcmp(defined_templates[i].name, name) == 0)
- return defined_templates + i;
+ struct ima_template_desc *template_desc;
+ int found = 0;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(template_desc, &defined_templates, list) {
+ if ((strcmp(template_desc->name, name) == 0) ||
+ (strcmp(template_desc->fmt, name) == 0)) {
+ found = 1;
+ break;
+ }
}
-
- return NULL;
+ rcu_read_unlock();
+ return found ? template_desc : NULL;
}
static struct ima_template_field *lookup_template_field(const char *field_id)
@@ -142,9 +155,14 @@ static int template_desc_init_fields(const char *template_fmt,
{
const char *template_fmt_ptr;
struct ima_template_field *found_fields[IMA_TEMPLATE_NUM_FIELDS_MAX];
- int template_num_fields = template_fmt_size(template_fmt);
+ int template_num_fields;
int i, len;
+ if (num_fields && *num_fields > 0) /* already initialized? */
+ return 0;
+
+ template_num_fields = template_fmt_size(template_fmt);
+
if (template_num_fields > IMA_TEMPLATE_NUM_FIELDS_MAX) {
pr_err("format string '%s' contains too many fields\n",
template_fmt);
@@ -182,11 +200,28 @@ static int template_desc_init_fields(const char *template_fmt,
return 0;
}
+void ima_init_template_list(void)
+{
+ int i;
+
+ if (!list_empty(&defined_templates))
+ return;
+
+ spin_lock(&template_list);
+ for (i = 0; i < ARRAY_SIZE(builtin_templates); i++) {
+ list_add_tail_rcu(&builtin_templates[i].list,
+ &defined_templates);
+ }
+ spin_unlock(&template_list);
+}
+
struct ima_template_desc *ima_template_desc_current(void)
{
- if (!ima_template)
+ if (!ima_template) {
+ ima_init_template_list();
ima_template =
lookup_template_desc(CONFIG_IMA_DEFAULT_TEMPLATE);
+ }
return ima_template;
}
@@ -205,3 +240,239 @@ int __init ima_init_template(void)
return result;
}
+
+static struct ima_template_desc *restore_template_fmt(char *template_name)
+{
+ struct ima_template_desc *template_desc = NULL;
+ int ret;
+
+ ret = template_desc_init_fields(template_name, NULL, NULL);
+ if (ret < 0) {
+ pr_err("attempting to initialize the template \"%s\" failed\n",
+ template_name);
+ goto out;
+ }
+
+ template_desc = kzalloc(sizeof(*template_desc), GFP_KERNEL);
+ if (!template_desc)
+ goto out;
+
+ template_desc->name = "";
+ template_desc->fmt = kstrdup(template_name, GFP_KERNEL);
+ if (!template_desc->fmt)
+ goto out;
+
+ spin_lock(&template_list);
+ list_add_tail_rcu(&template_desc->list, &defined_templates);
+ spin_unlock(&template_list);
+out:
+ return template_desc;
+}
+
+static int ima_restore_template_data(struct ima_template_desc *template_desc,
+ void *template_data,
+ int template_data_size,
+ struct ima_template_entry **entry)
+{
+ struct binary_field_data {
+ u32 len;
+ u8 data[0];
+ } __packed;
+
+ struct binary_field_data *field_data;
+ int offset = 0;
+ int ret = 0;
+ int i;
+
+ *entry = kzalloc(sizeof(**entry) +
+ template_desc->num_fields * sizeof(struct ima_field_data),
+ GFP_NOFS);
+ if (!*entry)
+ return -ENOMEM;
+
+ (*entry)->template_desc = template_desc;
+ for (i = 0; i < template_desc->num_fields; i++) {
+ field_data = template_data + offset;
+
+ /* Each field of the template data is prefixed with a length. */
+ if (offset > (template_data_size - sizeof(*field_data))) {
+ pr_err("Restoring the template field failed\n");
+ ret = -EINVAL;
+ break;
+ }
+ offset += sizeof(*field_data);
+
+ if (ima_canonical_fmt)
+ field_data->len = le32_to_cpu(field_data->len);
+
+ if (offset > (template_data_size - field_data->len)) {
+ pr_err("Restoring the template field data failed\n");
+ ret = -EINVAL;
+ break;
+ }
+ offset += field_data->len;
+
+ (*entry)->template_data[i].len = field_data->len;
+ (*entry)->template_data_len += sizeof(field_data->len);
+
+ (*entry)->template_data[i].data =
+ kzalloc(field_data->len + 1, GFP_KERNEL);
+ if (!(*entry)->template_data[i].data) {
+ ret = -ENOMEM;
+ break;
+ }
+ memcpy((*entry)->template_data[i].data, field_data->data,
+ field_data->len);
+ (*entry)->template_data_len += field_data->len;
+ }
+
+ if (ret < 0) {
+ ima_free_template_entry(*entry);
+ *entry = NULL;
+ }
+
+ return ret;
+}
+
+/* Restore the serialized binary measurement list without extending PCRs. */
+int ima_restore_measurement_list(loff_t size, void *buf)
+{
+ struct binary_hdr_v1 {
+ u32 pcr;
+ u8 digest[TPM_DIGEST_SIZE];
+ u32 template_name_len;
+ char template_name[0];
+ } __packed;
+ char template_name[MAX_TEMPLATE_NAME_LEN];
+
+ struct binary_data_v1 {
+ u32 template_data_size;
+ char template_data[0];
+ } __packed;
+
+ struct ima_kexec_hdr *khdr = buf;
+ struct binary_hdr_v1 *hdr_v1;
+ struct binary_data_v1 *data_v1;
+
+ void *bufp = buf + sizeof(*khdr);
+ void *bufendp;
+ struct ima_template_entry *entry;
+ struct ima_template_desc *template_desc;
+ unsigned long count = 0;
+ int ret = 0;
+
+ if (!buf || size < sizeof(*khdr))
+ return 0;
+
+ if (ima_canonical_fmt) {
+ khdr->version = le16_to_cpu(khdr->version);
+ khdr->count = le64_to_cpu(khdr->count);
+ khdr->buffer_size = le64_to_cpu(khdr->buffer_size);
+ }
+
+ if (khdr->version != 1) {
+ pr_err("attempting to restore a incompatible measurement list");
+ return -EINVAL;
+ }
+
+ if (khdr->count > ULONG_MAX - 1) {
+ pr_err("attempting to restore too many measurements");
+ return -EINVAL;
+ }
+
+ /*
+ * ima kexec buffer prefix: version, buffer size, count
+ * v1 format: pcr, digest, template-name-len, template-name,
+ * template-data-size, template-data
+ */
+ bufendp = buf + khdr->buffer_size;
+ while ((bufp < bufendp) && (count++ < khdr->count)) {
+ hdr_v1 = bufp;
+ if (bufp > (bufendp - sizeof(*hdr_v1))) {
+ pr_err("attempting to restore partial measurement\n");
+ ret = -EINVAL;
+ break;
+ }
+ bufp += sizeof(*hdr_v1);
+
+ if (ima_canonical_fmt)
+ hdr_v1->template_name_len =
+ le32_to_cpu(hdr_v1->template_name_len);
+
+ if ((hdr_v1->template_name_len >= MAX_TEMPLATE_NAME_LEN) ||
+ (bufp > (bufendp - hdr_v1->template_name_len))) {
+ pr_err("attempting to restore a template name \
+ that is too long\n");
+ ret = -EINVAL;
+ break;
+ }
+ data_v1 = bufp += (u_int8_t)hdr_v1->template_name_len;
+
+ /* template name is not null terminated */
+ memcpy(template_name, hdr_v1->template_name,
+ hdr_v1->template_name_len);
+ template_name[hdr_v1->template_name_len] = 0;
+
+ if (strcmp(template_name, "ima") == 0) {
+ pr_err("attempting to restore an unsupported \
+ template \"%s\" failed\n", template_name);
+ ret = -EINVAL;
+ break;
+ }
+
+ template_desc = lookup_template_desc(template_name);
+ if (!template_desc) {
+ template_desc = restore_template_fmt(template_name);
+ if (!template_desc)
+ break;
+ }
+
+ /*
+ * Only the running system's template format is initialized
+ * on boot. As needed, initialize the other template formats.
+ */
+ ret = template_desc_init_fields(template_desc->fmt,
+ &(template_desc->fields),
+ &(template_desc->num_fields));
+ if (ret < 0) {
+ pr_err("attempting to restore the template fmt \"%s\" \
+ failed\n", template_desc->fmt);
+ ret = -EINVAL;
+ break;
+ }
+
+ if (bufp > (bufendp - sizeof(data_v1->template_data_size))) {
+ pr_err("restoring the template data size failed\n");
+ ret = -EINVAL;
+ break;
+ }
+ bufp += (u_int8_t) sizeof(data_v1->template_data_size);
+
+ if (ima_canonical_fmt)
+ data_v1->template_data_size =
+ le32_to_cpu(data_v1->template_data_size);
+
+ if (bufp > (bufendp - data_v1->template_data_size)) {
+ pr_err("restoring the template data failed\n");
+ ret = -EINVAL;
+ break;
+ }
+ bufp += data_v1->template_data_size;
+
+ ret = ima_restore_template_data(template_desc,
+ data_v1->template_data,
+ data_v1->template_data_size,
+ &entry);
+ if (ret < 0)
+ break;
+
+ memcpy(entry->digest, hdr_v1->digest, TPM_DIGEST_SIZE);
+ entry->pcr =
+ !ima_canonical_fmt ? hdr_v1->pcr : le32_to_cpu(hdr_v1->pcr);
+ ret = ima_restore_measurement_entry(entry);
+ if (ret < 0)
+ break;
+
+ }
+ return ret;
+}
diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
index f9bae04..f9ba37b 100644
--- a/security/integrity/ima/ima_template_lib.c
+++ b/security/integrity/ima/ima_template_lib.c
@@ -103,8 +103,11 @@ static void ima_show_template_data_binary(struct seq_file *m,
u32 len = (show == IMA_SHOW_BINARY_OLD_STRING_FMT) ?
strlen(field_data->data) : field_data->len;
- if (show != IMA_SHOW_BINARY_NO_FIELD_LEN)
- ima_putc(m, &len, sizeof(len));
+ if (show != IMA_SHOW_BINARY_NO_FIELD_LEN) {
+ u32 field_len = !ima_canonical_fmt ? len : cpu_to_le32(len);
+
+ ima_putc(m, &field_len, sizeof(field_len));
+ }
if (!len)
return;
diff --git a/security/keys/dh.c b/security/keys/dh.c
index 531ed2e..893af4c 100644
--- a/security/keys/dh.c
+++ b/security/keys/dh.c
@@ -55,7 +55,7 @@ static ssize_t mpi_from_key(key_serial_t keyid, size_t maxlen, MPI *mpi)
if (status == 0) {
const struct user_key_payload *payload;
- payload = user_key_payload(key);
+ payload = user_key_payload_locked(key);
if (maxlen == 0) {
*mpi = NULL;
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
index 17a0610..0010955 100644
--- a/security/keys/encrypted-keys/encrypted.c
+++ b/security/keys/encrypted-keys/encrypted.c
@@ -314,7 +314,7 @@ static struct key *request_user_key(const char *master_desc, const u8 **master_k
goto error;
down_read(&ukey->sem);
- upayload = user_key_payload(ukey);
+ upayload = user_key_payload_locked(ukey);
*master_key = upayload->data;
*master_keylen = upayload->datalen;
error:
@@ -437,7 +437,7 @@ static struct skcipher_request *init_skcipher_req(const u8 *key,
static struct key *request_master_key(struct encrypted_key_payload *epayload,
const u8 **master_key, size_t *master_keylen)
{
- struct key *mkey = NULL;
+ struct key *mkey = ERR_PTR(-EINVAL);
if (!strncmp(epayload->master_desc, KEY_TRUSTED_PREFIX,
KEY_TRUSTED_PREFIX_LEN)) {
@@ -926,7 +926,7 @@ static long encrypted_read(const struct key *key, char __user *buffer,
size_t asciiblob_len;
int ret;
- epayload = rcu_dereference_key(key);
+ epayload = dereference_key_locked(key);
/* returns the hex encoded iv, encrypted-data, and hmac as ascii */
asciiblob_len = epayload->datablob_len + ivsize + 1
@@ -985,7 +985,7 @@ static void encrypted_destroy(struct key *key)
if (!epayload)
return;
- memset(epayload->decrypted_data, 0, epayload->decrypted_datalen);
+ memzero_explicit(epayload->decrypted_data, epayload->decrypted_datalen);
kfree(key->payload.data[0]);
}
diff --git a/security/keys/gc.c b/security/keys/gc.c
index addf060..9cb4fe4 100644
--- a/security/keys/gc.c
+++ b/security/keys/gc.c
@@ -46,7 +46,7 @@ static unsigned long key_gc_flags;
* immediately unlinked.
*/
struct key_type key_type_dead = {
- .name = "dead",
+ .name = ".dead",
};
/*
diff --git a/security/keys/internal.h b/security/keys/internal.h
index a705a7d..a2f4c0a 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -13,6 +13,7 @@
#define _INTERNAL_H
#include <linux/sched.h>
+#include <linux/cred.h>
#include <linux/key-type.h>
#include <linux/task_work.h>
#include <linux/keyctl.h>
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index d580ad0..4ad3212 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -12,18 +12,20 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
+#include <linux/sched/task.h>
#include <linux/slab.h>
#include <linux/syscalls.h>
#include <linux/key.h>
#include <linux/keyctl.h>
#include <linux/fs.h>
#include <linux/capability.h>
+#include <linux/cred.h>
#include <linux/string.h>
#include <linux/err.h>
#include <linux/vmalloc.h>
#include <linux/security.h>
#include <linux/uio.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "internal.h"
#define KEY_MAX_DESC_SIZE 4096
@@ -271,7 +273,8 @@ error:
* Create and join an anonymous session keyring or join a named session
* keyring, creating it if necessary. A named session keyring must have Search
* permission for it to be joined. Session keyrings without this permit will
- * be skipped over.
+ * be skipped over. It is not permitted for userspace to create or join
+ * keyrings whose name begin with a dot.
*
* If successful, the ID of the joined session keyring will be returned.
*/
@@ -288,12 +291,16 @@ long keyctl_join_session_keyring(const char __user *_name)
ret = PTR_ERR(name);
goto error;
}
+
+ ret = -EPERM;
+ if (name[0] == '.')
+ goto error_name;
}
/* join the session */
ret = join_session_keyring(name);
+error_name:
kfree(name);
-
error:
return ret;
}
@@ -1074,7 +1081,7 @@ long keyctl_instantiate_key_common(key_serial_t id,
}
ret = -EFAULT;
- if (copy_from_iter(payload, plen, from) != plen)
+ if (!copy_from_iter_full(payload, plen, from))
goto error2;
}
@@ -1251,8 +1258,8 @@ error:
* Read or set the default keyring in which request_key() will cache keys and
* return the old setting.
*
- * If a process keyring is specified then this will be created if it doesn't
- * yet exist. The old setting will be returned if successful.
+ * If a thread or process keyring is specified then it will be created if it
+ * doesn't yet exist. The old setting will be returned if successful.
*/
long keyctl_set_reqkey_keyring(int reqkey_defl)
{
@@ -1277,11 +1284,8 @@ long keyctl_set_reqkey_keyring(int reqkey_defl)
case KEY_REQKEY_DEFL_PROCESS_KEYRING:
ret = install_process_keyring_to_cred(new);
- if (ret < 0) {
- if (ret != -EEXIST)
- goto error;
- ret = 0;
- }
+ if (ret < 0)
+ goto error;
goto set;
case KEY_REQKEY_DEFL_DEFAULT:
diff --git a/security/keys/persistent.c b/security/keys/persistent.c
index 1edc1f0..d0cb5b3 100644
--- a/security/keys/persistent.c
+++ b/security/keys/persistent.c
@@ -10,6 +10,8 @@
*/
#include <linux/user_namespace.h>
+#include <linux/cred.h>
+
#include "internal.h"
unsigned persistent_keyring_expiry = 3 * 24 * 3600; /* Expire after 3 days of non-use */
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index 40a8852..9139b18 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -12,13 +12,14 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
+#include <linux/sched/user.h>
#include <linux/keyctl.h>
#include <linux/fs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/security.h>
#include <linux/user_namespace.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "internal.h"
/* Session keyring create vs join semaphore */
@@ -127,13 +128,18 @@ error:
}
/*
- * Install a fresh thread keyring directly to new credentials. This keyring is
- * allowed to overrun the quota.
+ * Install a thread keyring to the given credentials struct if it didn't have
+ * one already. This is allowed to overrun the quota.
+ *
+ * Return: 0 if a thread keyring is now present; -errno on failure.
*/
int install_thread_keyring_to_cred(struct cred *new)
{
struct key *keyring;
+ if (new->thread_keyring)
+ return 0;
+
keyring = keyring_alloc("_tid", new->uid, new->gid, new,
KEY_POS_ALL | KEY_USR_VIEW,
KEY_ALLOC_QUOTA_OVERRUN,
@@ -146,7 +152,9 @@ int install_thread_keyring_to_cred(struct cred *new)
}
/*
- * Install a fresh thread keyring, discarding the old one.
+ * Install a thread keyring to the current task if it didn't have one already.
+ *
+ * Return: 0 if a thread keyring is now present; -errno on failure.
*/
static int install_thread_keyring(void)
{
@@ -157,8 +165,6 @@ static int install_thread_keyring(void)
if (!new)
return -ENOMEM;
- BUG_ON(new->thread_keyring);
-
ret = install_thread_keyring_to_cred(new);
if (ret < 0) {
abort_creds(new);
@@ -169,17 +175,17 @@ static int install_thread_keyring(void)
}
/*
- * Install a process keyring directly to a credentials struct.
+ * Install a process keyring to the given credentials struct if it didn't have
+ * one already. This is allowed to overrun the quota.
*
- * Returns -EEXIST if there was already a process keyring, 0 if one installed,
- * and other value on any other error
+ * Return: 0 if a process keyring is now present; -errno on failure.
*/
int install_process_keyring_to_cred(struct cred *new)
{
struct key *keyring;
if (new->process_keyring)
- return -EEXIST;
+ return 0;
keyring = keyring_alloc("_pid", new->uid, new->gid, new,
KEY_POS_ALL | KEY_USR_VIEW,
@@ -193,11 +199,9 @@ int install_process_keyring_to_cred(struct cred *new)
}
/*
- * Make sure a process keyring is installed for the current process. The
- * existing process keyring is not replaced.
+ * Install a process keyring to the current task if it didn't have one already.
*
- * Returns 0 if there is a process keyring by the end of this function, some
- * error otherwise.
+ * Return: 0 if a process keyring is now present; -errno on failure.
*/
static int install_process_keyring(void)
{
@@ -211,14 +215,18 @@ static int install_process_keyring(void)
ret = install_process_keyring_to_cred(new);
if (ret < 0) {
abort_creds(new);
- return ret != -EEXIST ? ret : 0;
+ return ret;
}
return commit_creds(new);
}
/*
- * Install a session keyring directly to a credentials struct.
+ * Install the given keyring as the session keyring of the given credentials
+ * struct, replacing the existing one if any. If the given keyring is NULL,
+ * then install a new anonymous session keyring.
+ *
+ * Return: 0 on success; -errno on failure.
*/
int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)
{
@@ -253,8 +261,11 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)
}
/*
- * Install a session keyring, discarding the old one. If a keyring is not
- * supplied, an empty one is invented.
+ * Install the given keyring as the session keyring of the current task,
+ * replacing the existing one if any. If the given keyring is NULL, then
+ * install a new anonymous session keyring.
+ *
+ * Return: 0 on success; -errno on failure.
*/
static int install_session_keyring(struct key *keyring)
{
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
index 43affcf..9822e50 100644
--- a/security/keys/request_key.c
+++ b/security/keys/request_key.c
@@ -72,7 +72,7 @@ static void umh_keys_cleanup(struct subprocess_info *info)
/*
* Call a usermode helper with a specific session keyring.
*/
-static int call_usermodehelper_keys(char *path, char **argv, char **envp,
+static int call_usermodehelper_keys(const char *path, char **argv, char **envp,
struct key *session_keyring, int wait)
{
struct subprocess_info *info;
@@ -95,6 +95,7 @@ static int call_sbin_request_key(struct key_construction *cons,
const char *op,
void *aux)
{
+ static char const request_key[] = "/sbin/request-key";
const struct cred *cred = current_cred();
key_serial_t prkey, sskey;
struct key *key = cons->key, *authkey = cons->authkey, *keyring,
@@ -161,7 +162,7 @@ static int call_sbin_request_key(struct key_construction *cons,
/* set up the argument list */
i = 0;
- argv[i++] = "/sbin/request-key";
+ argv[i++] = (char *)request_key;
argv[i++] = (char *) op;
argv[i++] = key_str;
argv[i++] = uid_str;
@@ -172,7 +173,7 @@ static int call_sbin_request_key(struct key_construction *cons,
argv[i] = NULL;
/* do it */
- ret = call_usermodehelper_keys(argv[0], argv, envp, keyring,
+ ret = call_usermodehelper_keys(request_key, argv, envp, keyring,
UMH_WAIT_PROC);
kdebug("usermode -> 0x%x", ret);
if (ret >= 0) {
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
index 9db8b4a..6bbe2f5 100644
--- a/security/keys/request_key_auth.c
+++ b/security/keys/request_key_auth.c
@@ -16,7 +16,7 @@
#include <linux/err.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "internal.h"
#include <keys/user-type.h>
diff --git a/security/keys/trusted.c b/security/keys/trusted.c
index 90d6175..2ae31c5 100644
--- a/security/keys/trusted.c
+++ b/security/keys/trusted.c
@@ -1140,12 +1140,12 @@ out:
static long trusted_read(const struct key *key, char __user *buffer,
size_t buflen)
{
- struct trusted_key_payload *p;
+ const struct trusted_key_payload *p;
char *ascii_buf;
char *bufp;
int i;
- p = rcu_dereference_key(key);
+ p = dereference_key_locked(key);
if (!p)
return -EINVAL;
if (!buffer || buflen <= 0)
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c
index 66b1840..2660513 100644
--- a/security/keys/user_defined.c
+++ b/security/keys/user_defined.c
@@ -15,7 +15,7 @@
#include <linux/seq_file.h>
#include <linux/err.h>
#include <keys/user-type.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "internal.h"
static int logon_vet_description(const char *desc);
@@ -107,7 +107,7 @@ int user_update(struct key *key, struct key_preparsed_payload *prep)
/* attach the new data, displacing the old */
key->expiry = prep->expiry;
if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags))
- zap = rcu_dereference_key(key);
+ zap = dereference_key_locked(key);
rcu_assign_keypointer(key, prep->payload.data[0]);
prep->payload.data[0] = NULL;
@@ -123,7 +123,7 @@ EXPORT_SYMBOL_GPL(user_update);
*/
void user_revoke(struct key *key)
{
- struct user_key_payload *upayload = key->payload.data[0];
+ struct user_key_payload *upayload = user_key_payload_locked(key);
/* clear the quota */
key_payload_reserve(key, 0);
@@ -169,7 +169,7 @@ long user_read(const struct key *key, char __user *buffer, size_t buflen)
const struct user_key_payload *upayload;
long ret;
- upayload = user_key_payload(key);
+ upayload = user_key_payload_locked(key);
ret = upayload->datalen;
/* we can return the data as is */
diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c
index 89a46f1..1d82eae 100644
--- a/security/loadpin/loadpin.c
+++ b/security/loadpin/loadpin.c
@@ -182,7 +182,7 @@ static struct security_hook_list loadpin_hooks[] = {
void __init loadpin_add_hooks(void)
{
pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis");
- security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks));
+ security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks), "loadpin");
}
/* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
diff --git a/security/security.c b/security/security.c
index f825304..d0e07f2 100644
--- a/security/security.c
+++ b/security/security.c
@@ -32,6 +32,7 @@
/* Maximum number of letters for an LSM name string */
#define SECURITY_NAME_MAX 10
+char *lsm_names;
/* Boot-time LSM user choice */
static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
CONFIG_DEFAULT_SECURITY;
@@ -78,6 +79,22 @@ static int __init choose_lsm(char *str)
}
__setup("security=", choose_lsm);
+static int lsm_append(char *new, char **result)
+{
+ char *cp;
+
+ if (*result == NULL) {
+ *result = kstrdup(new, GFP_KERNEL);
+ } else {
+ cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new);
+ if (cp == NULL)
+ return -ENOMEM;
+ kfree(*result);
+ *result = cp;
+ }
+ return 0;
+}
+
/**
* security_module_enable - Load given security module on boot ?
* @module: the name of the module
@@ -97,6 +114,27 @@ int __init security_module_enable(const char *module)
return !strcmp(module, chosen_lsm);
}
+/**
+ * security_add_hooks - Add a modules hooks to the hook lists.
+ * @hooks: the hooks to add
+ * @count: the number of hooks to add
+ * @lsm: the name of the security module
+ *
+ * Each LSM has to register its hooks with the infrastructure.
+ */
+void __init security_add_hooks(struct security_hook_list *hooks, int count,
+ char *lsm)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ hooks[i].lsm = lsm;
+ list_add_tail_rcu(&hooks[i].list, hooks[i].head);
+ }
+ if (lsm_append(lsm, &lsm_names) < 0)
+ panic("%s - Cannot get early memory.\n", __func__);
+}
+
/*
* Hook list operation macros.
*
@@ -1025,11 +1063,6 @@ int security_task_kill(struct task_struct *p, struct siginfo *info,
return call_int_hook(task_kill, 0, p, info, sig, secid);
}
-int security_task_wait(struct task_struct *p)
-{
- return call_int_hook(task_wait, 0, p);
-}
-
int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5)
{
@@ -1170,9 +1203,9 @@ int security_getprocattr(struct task_struct *p, char *name, char **value)
return call_int_hook(getprocattr, -EINVAL, p, name, value);
}
-int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size)
+int security_setprocattr(const char *name, void *value, size_t size)
{
- return call_int_hook(setprocattr, -EINVAL, p, name, value, size);
+ return call_int_hook(setprocattr, -EINVAL, name, value, size);
}
int security_netlink_send(struct sock *sk, struct sk_buff *skb)
@@ -1769,7 +1802,6 @@ struct security_hook_heads security_hook_heads = {
.task_movememory =
LIST_HEAD_INIT(security_hook_heads.task_movememory),
.task_kill = LIST_HEAD_INIT(security_hook_heads.task_kill),
- .task_wait = LIST_HEAD_INIT(security_hook_heads.task_wait),
.task_prctl = LIST_HEAD_INIT(security_hook_heads.task_prctl),
.task_to_inode =
LIST_HEAD_INIT(security_hook_heads.task_to_inode),
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 09fd610..0c2ac31 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -28,7 +28,8 @@
#include <linux/kernel.h>
#include <linux/tracehook.h>
#include <linux/errno.h>
-#include <linux/sched.h>
+#include <linux/sched/signal.h>
+#include <linux/sched/task.h>
#include <linux/lsm_hooks.h>
#include <linux/xattr.h>
#include <linux/capability.h>
@@ -210,16 +211,6 @@ static inline u32 task_sid(const struct task_struct *task)
return sid;
}
-/*
- * get the subjective security ID of the current task
- */
-static inline u32 current_sid(void)
-{
- const struct task_security_struct *tsec = current_security();
-
- return tsec->sid;
-}
-
/* Allocate and free functions for each kind of security blob. */
static int inode_alloc_security(struct inode *inode)
@@ -231,12 +222,13 @@ static int inode_alloc_security(struct inode *inode)
if (!isec)
return -ENOMEM;
- mutex_init(&isec->lock);
+ spin_lock_init(&isec->lock);
INIT_LIST_HEAD(&isec->list);
isec->inode = inode;
isec->sid = SECINITSID_UNLABELED;
isec->sclass = SECCLASS_FILE;
isec->task_sid = sid;
+ isec->initialized = LABEL_INVALID;
inode->i_security = isec;
return 0;
@@ -247,7 +239,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
/*
* Try reloading inode security labels that have been marked as invalid. The
* @may_sleep parameter indicates when sleeping and thus reloading labels is
- * allowed; when set to false, returns ERR_PTR(-ECHILD) when the label is
+ * allowed; when set to false, returns -ECHILD when the label is
* invalid. The @opt_dentry parameter should be set to a dentry of the inode;
* when no dentry is available, set it to NULL instead.
*/
@@ -491,7 +483,11 @@ static int selinux_is_sblabel_mnt(struct super_block *sb)
!strcmp(sb->s_type->name, "sysfs") ||
!strcmp(sb->s_type->name, "pstore") ||
!strcmp(sb->s_type->name, "debugfs") ||
- !strcmp(sb->s_type->name, "rootfs");
+ !strcmp(sb->s_type->name, "tracefs") ||
+ !strcmp(sb->s_type->name, "rootfs") ||
+ (selinux_policycap_cgroupseclabel &&
+ (!strcmp(sb->s_type->name, "cgroup") ||
+ !strcmp(sb->s_type->name, "cgroup2")));
}
static int sb_finish_set_opts(struct super_block *sb)
@@ -832,10 +828,14 @@ static int selinux_set_mnt_opts(struct super_block *sb,
}
/*
- * If this is a user namespace mount, no contexts are allowed
- * on the command line and security labels must be ignored.
+ * If this is a user namespace mount and the filesystem type is not
+ * explicitly whitelisted, then no contexts are allowed on the command
+ * line and security labels must be ignored.
*/
- if (sb->s_user_ns != &init_user_ns) {
+ if (sb->s_user_ns != &init_user_ns &&
+ strcmp(sb->s_type->name, "tmpfs") &&
+ strcmp(sb->s_type->name, "ramfs") &&
+ strcmp(sb->s_type->name, "devpts")) {
if (context_sid || fscontext_sid || rootcontext_sid ||
defcontext_sid) {
rc = -EACCES;
@@ -1100,11 +1100,12 @@ static int selinux_parse_opts_str(char *options,
}
rc = -ENOMEM;
- opts->mnt_opts = kcalloc(NUM_SEL_MNT_OPTS, sizeof(char *), GFP_ATOMIC);
+ opts->mnt_opts = kcalloc(NUM_SEL_MNT_OPTS, sizeof(char *), GFP_KERNEL);
if (!opts->mnt_opts)
goto out_err;
- opts->mnt_opts_flags = kcalloc(NUM_SEL_MNT_OPTS, sizeof(int), GFP_ATOMIC);
+ opts->mnt_opts_flags = kcalloc(NUM_SEL_MNT_OPTS, sizeof(int),
+ GFP_KERNEL);
if (!opts->mnt_opts_flags) {
kfree(opts->mnt_opts);
goto out_err;
@@ -1266,6 +1267,8 @@ static inline int default_protocol_dgram(int protocol)
static inline u16 socket_type_to_security_class(int family, int type, int protocol)
{
+ int extsockclass = selinux_policycap_extsockclass;
+
switch (family) {
case PF_UNIX:
switch (type) {
@@ -1280,13 +1283,19 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc
case PF_INET6:
switch (type) {
case SOCK_STREAM:
+ case SOCK_SEQPACKET:
if (default_protocol_stream(protocol))
return SECCLASS_TCP_SOCKET;
+ else if (extsockclass && protocol == IPPROTO_SCTP)
+ return SECCLASS_SCTP_SOCKET;
else
return SECCLASS_RAWIP_SOCKET;
case SOCK_DGRAM:
if (default_protocol_dgram(protocol))
return SECCLASS_UDP_SOCKET;
+ else if (extsockclass && (protocol == IPPROTO_ICMP ||
+ protocol == IPPROTO_ICMPV6))
+ return SECCLASS_ICMP_SOCKET;
else
return SECCLASS_RAWIP_SOCKET;
case SOCK_DCCP:
@@ -1340,6 +1349,68 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc
return SECCLASS_APPLETALK_SOCKET;
}
+ if (extsockclass) {
+ switch (family) {
+ case PF_AX25:
+ return SECCLASS_AX25_SOCKET;
+ case PF_IPX:
+ return SECCLASS_IPX_SOCKET;
+ case PF_NETROM:
+ return SECCLASS_NETROM_SOCKET;
+ case PF_ATMPVC:
+ return SECCLASS_ATMPVC_SOCKET;
+ case PF_X25:
+ return SECCLASS_X25_SOCKET;
+ case PF_ROSE:
+ return SECCLASS_ROSE_SOCKET;
+ case PF_DECnet:
+ return SECCLASS_DECNET_SOCKET;
+ case PF_ATMSVC:
+ return SECCLASS_ATMSVC_SOCKET;
+ case PF_RDS:
+ return SECCLASS_RDS_SOCKET;
+ case PF_IRDA:
+ return SECCLASS_IRDA_SOCKET;
+ case PF_PPPOX:
+ return SECCLASS_PPPOX_SOCKET;
+ case PF_LLC:
+ return SECCLASS_LLC_SOCKET;
+ case PF_CAN:
+ return SECCLASS_CAN_SOCKET;
+ case PF_TIPC:
+ return SECCLASS_TIPC_SOCKET;
+ case PF_BLUETOOTH:
+ return SECCLASS_BLUETOOTH_SOCKET;
+ case PF_IUCV:
+ return SECCLASS_IUCV_SOCKET;
+ case PF_RXRPC:
+ return SECCLASS_RXRPC_SOCKET;
+ case PF_ISDN:
+ return SECCLASS_ISDN_SOCKET;
+ case PF_PHONET:
+ return SECCLASS_PHONET_SOCKET;
+ case PF_IEEE802154:
+ return SECCLASS_IEEE802154_SOCKET;
+ case PF_CAIF:
+ return SECCLASS_CAIF_SOCKET;
+ case PF_ALG:
+ return SECCLASS_ALG_SOCKET;
+ case PF_NFC:
+ return SECCLASS_NFC_SOCKET;
+ case PF_VSOCK:
+ return SECCLASS_VSOCK_SOCKET;
+ case PF_KCM:
+ return SECCLASS_KCM_SOCKET;
+ case PF_QIPCRTR:
+ return SECCLASS_QIPCRTR_SOCKET;
+ case PF_SMC:
+ return SECCLASS_SMC_SOCKET;
+#if PF_MAX > 44
+#error New address family defined, please update this function.
+#endif
+ }
+ }
+
return SECCLASS_SOCKET;
}
@@ -1380,7 +1451,8 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
{
struct superblock_security_struct *sbsec = NULL;
struct inode_security_struct *isec = inode->i_security;
- u32 sid;
+ u32 task_sid, sid = 0;
+ u16 sclass;
struct dentry *dentry;
#define INITCONTEXTLEN 255
char *context = NULL;
@@ -1388,12 +1460,15 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
int rc = 0;
if (isec->initialized == LABEL_INITIALIZED)
- goto out;
+ return 0;
- mutex_lock(&isec->lock);
+ spin_lock(&isec->lock);
if (isec->initialized == LABEL_INITIALIZED)
goto out_unlock;
+ if (isec->sclass == SECCLASS_FILE)
+ isec->sclass = inode_mode_to_security_class(inode->i_mode);
+
sbsec = inode->i_sb->s_security;
if (!(sbsec->flags & SE_SBINITIALIZED)) {
/* Defer initialization until selinux_complete_init,
@@ -1406,12 +1481,18 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
goto out_unlock;
}
+ sclass = isec->sclass;
+ task_sid = isec->task_sid;
+ sid = isec->sid;
+ isec->initialized = LABEL_PENDING;
+ spin_unlock(&isec->lock);
+
switch (sbsec->behavior) {
case SECURITY_FS_USE_NATIVE:
break;
case SECURITY_FS_USE_XATTR:
if (!(inode->i_opflags & IOP_XATTR)) {
- isec->sid = sbsec->def_sid;
+ sid = sbsec->def_sid;
break;
}
/* Need a dentry, since the xattr API requires one.
@@ -1433,7 +1514,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
* inode_doinit with a dentry, before these inodes could
* be used again by userspace.
*/
- goto out_unlock;
+ goto out;
}
len = INITCONTEXTLEN;
@@ -1441,7 +1522,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
if (!context) {
rc = -ENOMEM;
dput(dentry);
- goto out_unlock;
+ goto out;
}
context[len] = '\0';
rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len);
@@ -1452,14 +1533,14 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, NULL, 0);
if (rc < 0) {
dput(dentry);
- goto out_unlock;
+ goto out;
}
len = rc;
context = kmalloc(len+1, GFP_NOFS);
if (!context) {
rc = -ENOMEM;
dput(dentry);
- goto out_unlock;
+ goto out;
}
context[len] = '\0';
rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len);
@@ -1471,7 +1552,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
"%d for dev=%s ino=%ld\n", __func__,
-rc, inode->i_sb->s_id, inode->i_ino);
kfree(context);
- goto out_unlock;
+ goto out;
}
/* Map ENODATA to the default file SID */
sid = sbsec->def_sid;
@@ -1501,29 +1582,25 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
}
}
kfree(context);
- isec->sid = sid;
break;
case SECURITY_FS_USE_TASK:
- isec->sid = isec->task_sid;
+ sid = task_sid;
break;
case SECURITY_FS_USE_TRANS:
/* Default to the fs SID. */
- isec->sid = sbsec->sid;
+ sid = sbsec->sid;
/* Try to obtain a transition SID. */
- isec->sclass = inode_mode_to_security_class(inode->i_mode);
- rc = security_transition_sid(isec->task_sid, sbsec->sid,
- isec->sclass, NULL, &sid);
+ rc = security_transition_sid(task_sid, sid, sclass, NULL, &sid);
if (rc)
- goto out_unlock;
- isec->sid = sid;
+ goto out;
break;
case SECURITY_FS_USE_MNTPOINT:
- isec->sid = sbsec->mntpoint_sid;
+ sid = sbsec->mntpoint_sid;
break;
default:
/* Default to the fs superblock SID. */
- isec->sid = sbsec->sid;
+ sid = sbsec->sid;
if ((sbsec->flags & SE_SBGENFS) && !S_ISLNK(inode->i_mode)) {
/* We must have a dentry to determine the label on
@@ -1546,25 +1623,30 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
* could be used again by userspace.
*/
if (!dentry)
- goto out_unlock;
- isec->sclass = inode_mode_to_security_class(inode->i_mode);
- rc = selinux_genfs_get_sid(dentry, isec->sclass,
+ goto out;
+ rc = selinux_genfs_get_sid(dentry, sclass,
sbsec->flags, &sid);
dput(dentry);
if (rc)
- goto out_unlock;
- isec->sid = sid;
+ goto out;
}
break;
}
- isec->initialized = LABEL_INITIALIZED;
+out:
+ spin_lock(&isec->lock);
+ if (isec->initialized == LABEL_PENDING) {
+ if (!sid || rc) {
+ isec->initialized = LABEL_INVALID;
+ goto out_unlock;
+ }
+
+ isec->initialized = LABEL_INITIALIZED;
+ isec->sid = sid;
+ }
out_unlock:
- mutex_unlock(&isec->lock);
-out:
- if (isec->sclass == SECCLASS_FILE)
- isec->sclass = inode_mode_to_security_class(inode->i_mode);
+ spin_unlock(&isec->lock);
return rc;
}
@@ -1595,55 +1677,6 @@ static inline u32 signal_to_av(int sig)
return perm;
}
-/*
- * Check permission between a pair of credentials
- * fork check, ptrace check, etc.
- */
-static int cred_has_perm(const struct cred *actor,
- const struct cred *target,
- u32 perms)
-{
- u32 asid = cred_sid(actor), tsid = cred_sid(target);
-
- return avc_has_perm(asid, tsid, SECCLASS_PROCESS, perms, NULL);
-}
-
-/*
- * Check permission between a pair of tasks, e.g. signal checks,
- * fork check, ptrace check, etc.
- * tsk1 is the actor and tsk2 is the target
- * - this uses the default subjective creds of tsk1
- */
-static int task_has_perm(const struct task_struct *tsk1,
- const struct task_struct *tsk2,
- u32 perms)
-{
- const struct task_security_struct *__tsec1, *__tsec2;
- u32 sid1, sid2;
-
- rcu_read_lock();
- __tsec1 = __task_cred(tsk1)->security; sid1 = __tsec1->sid;
- __tsec2 = __task_cred(tsk2)->security; sid2 = __tsec2->sid;
- rcu_read_unlock();
- return avc_has_perm(sid1, sid2, SECCLASS_PROCESS, perms, NULL);
-}
-
-/*
- * Check permission between current and another task, e.g. signal checks,
- * fork check, ptrace check, etc.
- * current is the actor and tsk2 is the target
- * - this uses current's subjective creds
- */
-static int current_has_perm(const struct task_struct *tsk,
- u32 perms)
-{
- u32 sid, tsid;
-
- sid = current_sid();
- tsid = task_sid(tsk);
- return avc_has_perm(sid, tsid, SECCLASS_PROCESS, perms, NULL);
-}
-
#if CAP_LAST_CAP > 63
#error Fix SELinux to handle capabilities > 63.
#endif
@@ -1685,16 +1718,6 @@ static int cred_has_capability(const struct cred *cred,
return rc;
}
-/* Check whether a task is allowed to use a system operation. */
-static int task_has_system(struct task_struct *tsk,
- u32 perms)
-{
- u32 sid = task_sid(tsk);
-
- return avc_has_perm(sid, SECINITSID_KERNEL,
- SECCLASS_SYSTEM, perms, NULL);
-}
-
/* Check whether a task has a particular permission to an inode.
The 'adp' parameter is optional and allows other audit
data to be passed (e.g. the dentry). */
@@ -1866,15 +1889,6 @@ static int may_create(struct inode *dir,
FILESYSTEM__ASSOCIATE, &ad);
}
-/* Check whether a task can create a key. */
-static int may_create_key(u32 ksid,
- struct task_struct *ctx)
-{
- u32 sid = task_sid(ctx);
-
- return avc_has_perm(sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL);
-}
-
#define MAY_LINK 0
#define MAY_UNLINK 1
#define MAY_RMDIR 2
@@ -2130,24 +2144,26 @@ static int selinux_binder_transfer_file(struct task_struct *from,
static int selinux_ptrace_access_check(struct task_struct *child,
unsigned int mode)
{
- if (mode & PTRACE_MODE_READ) {
- u32 sid = current_sid();
- u32 csid = task_sid(child);
+ u32 sid = current_sid();
+ u32 csid = task_sid(child);
+
+ if (mode & PTRACE_MODE_READ)
return avc_has_perm(sid, csid, SECCLASS_FILE, FILE__READ, NULL);
- }
- return current_has_perm(child, PROCESS__PTRACE);
+ return avc_has_perm(sid, csid, SECCLASS_PROCESS, PROCESS__PTRACE, NULL);
}
static int selinux_ptrace_traceme(struct task_struct *parent)
{
- return task_has_perm(parent, current, PROCESS__PTRACE);
+ return avc_has_perm(task_sid(parent), current_sid(), SECCLASS_PROCESS,
+ PROCESS__PTRACE, NULL);
}
static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
- return current_has_perm(target, PROCESS__GETCAP);
+ return avc_has_perm(current_sid(), task_sid(target), SECCLASS_PROCESS,
+ PROCESS__GETCAP, NULL);
}
static int selinux_capset(struct cred *new, const struct cred *old,
@@ -2155,7 +2171,8 @@ static int selinux_capset(struct cred *new, const struct cred *old,
const kernel_cap_t *inheritable,
const kernel_cap_t *permitted)
{
- return cred_has_perm(old, new, PROCESS__SETCAP);
+ return avc_has_perm(cred_sid(old), cred_sid(new), SECCLASS_PROCESS,
+ PROCESS__SETCAP, NULL);
}
/*
@@ -2211,29 +2228,22 @@ static int selinux_quota_on(struct dentry *dentry)
static int selinux_syslog(int type)
{
- int rc;
-
switch (type) {
case SYSLOG_ACTION_READ_ALL: /* Read last kernel messages */
case SYSLOG_ACTION_SIZE_BUFFER: /* Return size of the log buffer */
- rc = task_has_system(current, SYSTEM__SYSLOG_READ);
- break;
+ return avc_has_perm(current_sid(), SECINITSID_KERNEL,
+ SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, NULL);
case SYSLOG_ACTION_CONSOLE_OFF: /* Disable logging to console */
case SYSLOG_ACTION_CONSOLE_ON: /* Enable logging to console */
/* Set level of messages printed to console */
case SYSLOG_ACTION_CONSOLE_LEVEL:
- rc = task_has_system(current, SYSTEM__SYSLOG_CONSOLE);
- break;
- case SYSLOG_ACTION_CLOSE: /* Close log */
- case SYSLOG_ACTION_OPEN: /* Open log */
- case SYSLOG_ACTION_READ: /* Read from log */
- case SYSLOG_ACTION_READ_CLEAR: /* Read/clear last kernel messages */
- case SYSLOG_ACTION_CLEAR: /* Clear ring buffer */
- default:
- rc = task_has_system(current, SYSTEM__SYSLOG_MOD);
- break;
+ return avc_has_perm(current_sid(), SECINITSID_KERNEL,
+ SECCLASS_SYSTEM, SYSTEM__SYSLOG_CONSOLE,
+ NULL);
}
- return rc;
+ /* All other syslog types */
+ return avc_has_perm(current_sid(), SECINITSID_KERNEL,
+ SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, NULL);
}
/*
@@ -2258,13 +2268,13 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
/* binprm security operations */
-static u32 ptrace_parent_sid(struct task_struct *task)
+static u32 ptrace_parent_sid(void)
{
u32 sid = 0;
struct task_struct *tracer;
rcu_read_lock();
- tracer = ptrace_parent(task);
+ tracer = ptrace_parent(current);
if (tracer)
sid = task_sid(tracer);
rcu_read_unlock();
@@ -2391,9 +2401,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
/* Make sure that anyone attempting to ptrace over a task that
* changes its SID has the appropriate permit */
- if (bprm->unsafe &
- (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
- u32 ptsid = ptrace_parent_sid(current);
+ if (bprm->unsafe & LSM_UNSAFE_PTRACE) {
+ u32 ptsid = ptrace_parent_sid();
if (ptsid != 0) {
rc = avc_has_perm(ptsid, new_tsec->sid,
SECCLASS_PROCESS,
@@ -2525,7 +2534,8 @@ static void selinux_bprm_committing_creds(struct linux_binprm *bprm)
rlim->rlim_cur = min(rlim->rlim_max, initrlim->rlim_cur);
}
task_unlock(current);
- update_rlimit_cpu(current, rlimit(RLIMIT_CPU));
+ if (IS_ENABLED(CONFIG_POSIX_TIMERS))
+ update_rlimit_cpu(current, rlimit(RLIMIT_CPU));
}
}
@@ -2555,9 +2565,11 @@ static void selinux_bprm_committed_creds(struct linux_binprm *bprm)
*/
rc = avc_has_perm(osid, sid, SECCLASS_PROCESS, PROCESS__SIGINH, NULL);
if (rc) {
- memset(&itimer, 0, sizeof itimer);
- for (i = 0; i < 3; i++)
- do_setitimer(i, &itimer, NULL);
+ if (IS_ENABLED(CONFIG_POSIX_TIMERS)) {
+ memset(&itimer, 0, sizeof itimer);
+ for (i = 0; i < 3; i++)
+ do_setitimer(i, &itimer, NULL);
+ }
spin_lock_irq(&current->sighand->siglock);
if (!fatal_signal_pending(current)) {
flush_sigqueue(&current->pending);
@@ -3195,9 +3207,11 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
}
isec = backing_inode_security(dentry);
+ spin_lock(&isec->lock);
isec->sclass = inode_mode_to_security_class(inode->i_mode);
isec->sid = newsid;
isec->initialized = LABEL_INITIALIZED;
+ spin_unlock(&isec->lock);
return;
}
@@ -3290,9 +3304,11 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name,
if (rc)
return rc;
+ spin_lock(&isec->lock);
isec->sclass = inode_mode_to_security_class(inode->i_mode);
isec->sid = newsid;
isec->initialized = LABEL_INITIALIZED;
+ spin_unlock(&isec->lock);
return 0;
}
@@ -3483,6 +3499,7 @@ static int default_noexec;
static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
{
const struct cred *cred = current_cred();
+ u32 sid = cred_sid(cred);
int rc = 0;
if (default_noexec &&
@@ -3493,7 +3510,8 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared
* private file mapping that will also be writable.
* This has an additional check.
*/
- rc = cred_has_perm(cred, cred, PROCESS__EXECMEM);
+ rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
+ PROCESS__EXECMEM, NULL);
if (rc)
goto error;
}
@@ -3544,6 +3562,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
unsigned long prot)
{
const struct cred *cred = current_cred();
+ u32 sid = cred_sid(cred);
if (selinux_checkreqprot)
prot = reqprot;
@@ -3553,12 +3572,14 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
int rc = 0;
if (vma->vm_start >= vma->vm_mm->start_brk &&
vma->vm_end <= vma->vm_mm->brk) {
- rc = cred_has_perm(cred, cred, PROCESS__EXECHEAP);
+ rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
+ PROCESS__EXECHEAP, NULL);
} else if (!vma->vm_file &&
((vma->vm_start <= vma->vm_mm->start_stack &&
vma->vm_end >= vma->vm_mm->start_stack) ||
vma_is_stack_for_current(vma))) {
- rc = current_has_perm(current, PROCESS__EXECSTACK);
+ rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
+ PROCESS__EXECSTACK, NULL);
} else if (vma->vm_file && vma->anon_vma) {
/*
* We are making executable a file mapping that has
@@ -3691,7 +3712,9 @@ static int selinux_file_open(struct file *file, const struct cred *cred)
static int selinux_task_create(unsigned long clone_flags)
{
- return current_has_perm(current, PROCESS__FORK);
+ u32 sid = current_sid();
+
+ return avc_has_perm(sid, sid, SECCLASS_PROCESS, PROCESS__FORK, NULL);
}
/*
@@ -3801,15 +3824,12 @@ static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode)
static int selinux_kernel_module_request(char *kmod_name)
{
- u32 sid;
struct common_audit_data ad;
- sid = task_sid(current);
-
ad.type = LSM_AUDIT_DATA_KMOD;
ad.u.kmod_name = kmod_name;
- return avc_has_perm(sid, SECINITSID_KERNEL, SECCLASS_SYSTEM,
+ return avc_has_perm(current_sid(), SECINITSID_KERNEL, SECCLASS_SYSTEM,
SYSTEM__MODULE_REQUEST, &ad);
}
@@ -3861,17 +3881,20 @@ static int selinux_kernel_read_file(struct file *file,
static int selinux_task_setpgid(struct task_struct *p, pid_t pgid)
{
- return current_has_perm(p, PROCESS__SETPGID);
+ return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
+ PROCESS__SETPGID, NULL);
}
static int selinux_task_getpgid(struct task_struct *p)
{
- return current_has_perm(p, PROCESS__GETPGID);
+ return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
+ PROCESS__GETPGID, NULL);
}
static int selinux_task_getsid(struct task_struct *p)
{
- return current_has_perm(p, PROCESS__GETSESSION);
+ return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
+ PROCESS__GETSESSION, NULL);
}
static void selinux_task_getsecid(struct task_struct *p, u32 *secid)
@@ -3881,17 +3904,20 @@ static void selinux_task_getsecid(struct task_struct *p, u32 *secid)
static int selinux_task_setnice(struct task_struct *p, int nice)
{
- return current_has_perm(p, PROCESS__SETSCHED);
+ return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
+ PROCESS__SETSCHED, NULL);
}
static int selinux_task_setioprio(struct task_struct *p, int ioprio)
{
- return current_has_perm(p, PROCESS__SETSCHED);
+ return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
+ PROCESS__SETSCHED, NULL);
}
static int selinux_task_getioprio(struct task_struct *p)
{
- return current_has_perm(p, PROCESS__GETSCHED);
+ return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
+ PROCESS__GETSCHED, NULL);
}
static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource,
@@ -3904,47 +3930,42 @@ static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource,
later be used as a safe reset point for the soft limit
upon context transitions. See selinux_bprm_committing_creds. */
if (old_rlim->rlim_max != new_rlim->rlim_max)
- return current_has_perm(p, PROCESS__SETRLIMIT);
+ return avc_has_perm(current_sid(), task_sid(p),
+ SECCLASS_PROCESS, PROCESS__SETRLIMIT, NULL);
return 0;
}
static int selinux_task_setscheduler(struct task_struct *p)
{
- return current_has_perm(p, PROCESS__SETSCHED);
+ return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
+ PROCESS__SETSCHED, NULL);
}
static int selinux_task_getscheduler(struct task_struct *p)
{
- return current_has_perm(p, PROCESS__GETSCHED);
+ return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
+ PROCESS__GETSCHED, NULL);
}
static int selinux_task_movememory(struct task_struct *p)
{
- return current_has_perm(p, PROCESS__SETSCHED);
+ return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
+ PROCESS__SETSCHED, NULL);
}
static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
int sig, u32 secid)
{
u32 perm;
- int rc;
if (!sig)
perm = PROCESS__SIGNULL; /* null signal; existence test */
else
perm = signal_to_av(sig);
- if (secid)
- rc = avc_has_perm(secid, task_sid(p),
- SECCLASS_PROCESS, perm, NULL);
- else
- rc = current_has_perm(p, perm);
- return rc;
-}
-
-static int selinux_task_wait(struct task_struct *p)
-{
- return task_has_perm(p, current, PROCESS__SIGCHLD);
+ if (!secid)
+ secid = current_sid();
+ return avc_has_perm(secid, task_sid(p), SECCLASS_PROCESS, perm, NULL);
}
static void selinux_task_to_inode(struct task_struct *p,
@@ -3953,8 +3974,11 @@ static void selinux_task_to_inode(struct task_struct *p,
struct inode_security_struct *isec = inode->i_security;
u32 sid = task_sid(p);
+ spin_lock(&isec->lock);
+ isec->sclass = inode_mode_to_security_class(inode->i_mode);
isec->sid = sid;
isec->initialized = LABEL_INITIALIZED;
+ spin_unlock(&isec->lock);
}
/* Returns error only if unable to parse addresses */
@@ -4231,12 +4255,11 @@ static int socket_sockcreate_sid(const struct task_security_struct *tsec,
socksid);
}
-static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms)
+static int sock_has_perm(struct sock *sk, u32 perms)
{
struct sk_security_struct *sksec = sk->sk_security;
struct common_audit_data ad;
struct lsm_network_audit net = {0,};
- u32 tsid = task_sid(task);
if (sksec->sid == SECINITSID_KERNEL)
return 0;
@@ -4245,7 +4268,8 @@ static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms)
ad.u.net = &net;
ad.u.net->sk = sk;
- return avc_has_perm(tsid, sksec->sid, sksec->sclass, perms, &ad);
+ return avc_has_perm(current_sid(), sksec->sid, sksec->sclass, perms,
+ &ad);
}
static int selinux_socket_create(int family, int type,
@@ -4273,24 +4297,24 @@ static int selinux_socket_post_create(struct socket *sock, int family,
const struct task_security_struct *tsec = current_security();
struct inode_security_struct *isec = inode_security_novalidate(SOCK_INODE(sock));
struct sk_security_struct *sksec;
+ u16 sclass = socket_type_to_security_class(family, type, protocol);
+ u32 sid = SECINITSID_KERNEL;
int err = 0;
- isec->sclass = socket_type_to_security_class(family, type, protocol);
-
- if (kern)
- isec->sid = SECINITSID_KERNEL;
- else {
- err = socket_sockcreate_sid(tsec, isec->sclass, &(isec->sid));
+ if (!kern) {
+ err = socket_sockcreate_sid(tsec, sclass, &sid);
if (err)
return err;
}
+ isec->sclass = sclass;
+ isec->sid = sid;
isec->initialized = LABEL_INITIALIZED;
if (sock->sk) {
sksec = sock->sk->sk_security;
- sksec->sid = isec->sid;
- sksec->sclass = isec->sclass;
+ sksec->sclass = sclass;
+ sksec->sid = sid;
err = selinux_netlbl_socket_post_create(sock->sk, family);
}
@@ -4307,7 +4331,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
u16 family;
int err;
- err = sock_has_perm(current, sk, SOCKET__BIND);
+ err = sock_has_perm(sk, SOCKET__BIND);
if (err)
goto out;
@@ -4342,7 +4366,8 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
inet_get_local_port_range(sock_net(sk), &low, &high);
- if (snum < max(PROT_SOCK, low) || snum > high) {
+ if (snum < max(inet_prot_sock(sock_net(sk)), low) ||
+ snum > high) {
err = sel_netport_sid(sk->sk_protocol,
snum, &sid);
if (err)
@@ -4406,7 +4431,7 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
struct sk_security_struct *sksec = sk->sk_security;
int err;
- err = sock_has_perm(current, sk, SOCKET__CONNECT);
+ err = sock_has_perm(sk, SOCKET__CONNECT);
if (err)
return err;
@@ -4458,7 +4483,7 @@ out:
static int selinux_socket_listen(struct socket *sock, int backlog)
{
- return sock_has_perm(current, sock->sk, SOCKET__LISTEN);
+ return sock_has_perm(sock->sk, SOCKET__LISTEN);
}
static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
@@ -4466,16 +4491,22 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
int err;
struct inode_security_struct *isec;
struct inode_security_struct *newisec;
+ u16 sclass;
+ u32 sid;
- err = sock_has_perm(current, sock->sk, SOCKET__ACCEPT);
+ err = sock_has_perm(sock->sk, SOCKET__ACCEPT);
if (err)
return err;
- newisec = inode_security_novalidate(SOCK_INODE(newsock));
-
isec = inode_security_novalidate(SOCK_INODE(sock));
- newisec->sclass = isec->sclass;
- newisec->sid = isec->sid;
+ spin_lock(&isec->lock);
+ sclass = isec->sclass;
+ sid = isec->sid;
+ spin_unlock(&isec->lock);
+
+ newisec = inode_security_novalidate(SOCK_INODE(newsock));
+ newisec->sclass = sclass;
+ newisec->sid = sid;
newisec->initialized = LABEL_INITIALIZED;
return 0;
@@ -4484,30 +4515,30 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg,
int size)
{
- return sock_has_perm(current, sock->sk, SOCKET__WRITE);
+ return sock_has_perm(sock->sk, SOCKET__WRITE);
}
static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg,
int size, int flags)
{
- return sock_has_perm(current, sock->sk, SOCKET__READ);
+ return sock_has_perm(sock->sk, SOCKET__READ);
}
static int selinux_socket_getsockname(struct socket *sock)
{
- return sock_has_perm(current, sock->sk, SOCKET__GETATTR);
+ return sock_has_perm(sock->sk, SOCKET__GETATTR);
}
static int selinux_socket_getpeername(struct socket *sock)
{
- return sock_has_perm(current, sock->sk, SOCKET__GETATTR);
+ return sock_has_perm(sock->sk, SOCKET__GETATTR);
}
static int selinux_socket_setsockopt(struct socket *sock, int level, int optname)
{
int err;
- err = sock_has_perm(current, sock->sk, SOCKET__SETOPT);
+ err = sock_has_perm(sock->sk, SOCKET__SETOPT);
if (err)
return err;
@@ -4517,12 +4548,12 @@ static int selinux_socket_setsockopt(struct socket *sock, int level, int optname
static int selinux_socket_getsockopt(struct socket *sock, int level,
int optname)
{
- return sock_has_perm(current, sock->sk, SOCKET__GETOPT);
+ return sock_has_perm(sock->sk, SOCKET__GETOPT);
}
static int selinux_socket_shutdown(struct socket *sock, int how)
{
- return sock_has_perm(current, sock->sk, SOCKET__SHUTDOWN);
+ return sock_has_perm(sock->sk, SOCKET__SHUTDOWN);
}
static int selinux_socket_unix_stream_connect(struct sock *sock,
@@ -5010,7 +5041,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
goto out;
}
- err = sock_has_perm(current, sk, perm);
+ err = sock_has_perm(sk, perm);
out:
return err;
}
@@ -5341,20 +5372,17 @@ static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
return selinux_nlmsg_perm(sk, skb);
}
-static int ipc_alloc_security(struct task_struct *task,
- struct kern_ipc_perm *perm,
+static int ipc_alloc_security(struct kern_ipc_perm *perm,
u16 sclass)
{
struct ipc_security_struct *isec;
- u32 sid;
isec = kzalloc(sizeof(struct ipc_security_struct), GFP_KERNEL);
if (!isec)
return -ENOMEM;
- sid = task_sid(task);
isec->sclass = sclass;
- isec->sid = sid;
+ isec->sid = current_sid();
perm->security = isec;
return 0;
@@ -5422,7 +5450,7 @@ static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
u32 sid = current_sid();
int rc;
- rc = ipc_alloc_security(current, &msq->q_perm, SECCLASS_MSGQ);
+ rc = ipc_alloc_security(&msq->q_perm, SECCLASS_MSGQ);
if (rc)
return rc;
@@ -5469,7 +5497,8 @@ static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd)
case IPC_INFO:
case MSG_INFO:
/* No specific object, just general system-wide information. */
- return task_has_system(current, SYSTEM__IPC_INFO);
+ return avc_has_perm(current_sid(), SECINITSID_KERNEL,
+ SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL);
case IPC_STAT:
case MSG_STAT:
perms = MSGQ__GETATTR | MSGQ__ASSOCIATE;
@@ -5563,7 +5592,7 @@ static int selinux_shm_alloc_security(struct shmid_kernel *shp)
u32 sid = current_sid();
int rc;
- rc = ipc_alloc_security(current, &shp->shm_perm, SECCLASS_SHM);
+ rc = ipc_alloc_security(&shp->shm_perm, SECCLASS_SHM);
if (rc)
return rc;
@@ -5611,7 +5640,8 @@ static int selinux_shm_shmctl(struct shmid_kernel *shp, int cmd)
case IPC_INFO:
case SHM_INFO:
/* No specific object, just general system-wide information. */
- return task_has_system(current, SYSTEM__IPC_INFO);
+ return avc_has_perm(current_sid(), SECINITSID_KERNEL,
+ SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL);
case IPC_STAT:
case SHM_STAT:
perms = SHM__GETATTR | SHM__ASSOCIATE;
@@ -5655,7 +5685,7 @@ static int selinux_sem_alloc_security(struct sem_array *sma)
u32 sid = current_sid();
int rc;
- rc = ipc_alloc_security(current, &sma->sem_perm, SECCLASS_SEM);
+ rc = ipc_alloc_security(&sma->sem_perm, SECCLASS_SEM);
if (rc)
return rc;
@@ -5703,7 +5733,8 @@ static int selinux_sem_semctl(struct sem_array *sma, int cmd)
case IPC_INFO:
case SEM_INFO:
/* No specific object, just general system-wide information. */
- return task_has_system(current, SYSTEM__IPC_INFO);
+ return avc_has_perm(current_sid(), SECINITSID_KERNEL,
+ SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL);
case GETPID:
case GETNCNT:
case GETZCNT:
@@ -5784,15 +5815,16 @@ static int selinux_getprocattr(struct task_struct *p,
int error;
unsigned len;
+ rcu_read_lock();
+ __tsec = __task_cred(p)->security;
+
if (current != p) {
- error = current_has_perm(p, PROCESS__GETATTR);
+ error = avc_has_perm(current_sid(), __tsec->sid,
+ SECCLASS_PROCESS, PROCESS__GETATTR, NULL);
if (error)
- return error;
+ goto bad;
}
- rcu_read_lock();
- __tsec = __task_cred(p)->security;
-
if (!strcmp(name, "current"))
sid = __tsec->sid;
else if (!strcmp(name, "prev"))
@@ -5805,8 +5837,10 @@ static int selinux_getprocattr(struct task_struct *p,
sid = __tsec->keycreate_sid;
else if (!strcmp(name, "sockcreate"))
sid = __tsec->sockcreate_sid;
- else
- goto invalid;
+ else {
+ error = -EINVAL;
+ goto bad;
+ }
rcu_read_unlock();
if (!sid)
@@ -5817,48 +5851,44 @@ static int selinux_getprocattr(struct task_struct *p,
return error;
return len;
-invalid:
+bad:
rcu_read_unlock();
- return -EINVAL;
+ return error;
}
-static int selinux_setprocattr(struct task_struct *p,
- char *name, void *value, size_t size)
+static int selinux_setprocattr(const char *name, void *value, size_t size)
{
struct task_security_struct *tsec;
struct cred *new;
- u32 sid = 0, ptsid;
+ u32 mysid = current_sid(), sid = 0, ptsid;
int error;
char *str = value;
- if (current != p) {
- /* SELinux only allows a process to change its own
- security attributes. */
- return -EACCES;
- }
-
/*
* Basic control over ability to set these attributes at all.
- * current == p, but we'll pass them separately in case the
- * above restriction is ever removed.
*/
if (!strcmp(name, "exec"))
- error = current_has_perm(p, PROCESS__SETEXEC);
+ error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS,
+ PROCESS__SETEXEC, NULL);
else if (!strcmp(name, "fscreate"))
- error = current_has_perm(p, PROCESS__SETFSCREATE);
+ error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS,
+ PROCESS__SETFSCREATE, NULL);
else if (!strcmp(name, "keycreate"))
- error = current_has_perm(p, PROCESS__SETKEYCREATE);
+ error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS,
+ PROCESS__SETKEYCREATE, NULL);
else if (!strcmp(name, "sockcreate"))
- error = current_has_perm(p, PROCESS__SETSOCKCREATE);
+ error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS,
+ PROCESS__SETSOCKCREATE, NULL);
else if (!strcmp(name, "current"))
- error = current_has_perm(p, PROCESS__SETCURRENT);
+ error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS,
+ PROCESS__SETCURRENT, NULL);
else
error = -EINVAL;
if (error)
return error;
/* Obtain a SID for the context, if one was specified. */
- if (size && str[1] && str[1] != '\n') {
+ if (size && str[0] && str[0] != '\n') {
if (str[size-1] == '\n') {
str[size-1] = 0;
size--;
@@ -5905,7 +5935,8 @@ static int selinux_setprocattr(struct task_struct *p,
} else if (!strcmp(name, "fscreate")) {
tsec->create_sid = sid;
} else if (!strcmp(name, "keycreate")) {
- error = may_create_key(sid, p);
+ error = avc_has_perm(mysid, sid, SECCLASS_KEY, KEY__CREATE,
+ NULL);
if (error)
goto abort_change;
tsec->keycreate_sid = sid;
@@ -5932,7 +5963,7 @@ static int selinux_setprocattr(struct task_struct *p,
/* Check for ptracing, and update the task SID if ok.
Otherwise, leave SID unchanged and fail. */
- ptsid = ptrace_parent_sid(p);
+ ptsid = ptrace_parent_sid();
if (ptsid != 0) {
error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS,
PROCESS__PTRACE, NULL);
@@ -5978,9 +6009,9 @@ static void selinux_inode_invalidate_secctx(struct inode *inode)
{
struct inode_security_struct *isec = inode->i_security;
- mutex_lock(&isec->lock);
+ spin_lock(&isec->lock);
isec->initialized = LABEL_INVALID;
- mutex_unlock(&isec->lock);
+ spin_unlock(&isec->lock);
}
/*
@@ -6180,7 +6211,6 @@ static struct security_hook_list selinux_hooks[] = {
LSM_HOOK_INIT(task_getscheduler, selinux_task_getscheduler),
LSM_HOOK_INIT(task_movememory, selinux_task_movememory),
LSM_HOOK_INIT(task_kill, selinux_task_kill),
- LSM_HOOK_INIT(task_wait, selinux_task_wait),
LSM_HOOK_INIT(task_to_inode, selinux_task_to_inode),
LSM_HOOK_INIT(ipc_permission, selinux_ipc_permission),
@@ -6320,7 +6350,7 @@ static __init int selinux_init(void)
0, SLAB_PANIC, NULL);
avc_init();
- security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks));
+ security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks), "selinux");
if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET))
panic("SELinux: Unable to register AVC netcache callback\n");
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index 1f1f4b2..d429c4a 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -1,3 +1,5 @@
+#include <linux/capability.h>
+
#define COMMON_FILE_SOCK_PERMS "ioctl", "read", "write", "create", \
"getattr", "setattr", "lock", "relabelfrom", "relabelto", "append"
@@ -24,6 +26,10 @@
#define COMMON_CAP2_PERMS "mac_override", "mac_admin", "syslog", \
"wake_alarm", "block_suspend", "audit_read"
+#if CAP_LAST_CAP > CAP_AUDIT_READ
+#error New capability defined, please update COMMON_CAP2_PERMS.
+#endif
+
/*
* Note: The name for any socket class should be suffixed by "socket",
* and doesn't contain more than one substr of "socket".
@@ -165,5 +171,69 @@ struct security_class_mapping secclass_map[] = {
{ COMMON_CAP_PERMS, NULL } },
{ "cap2_userns",
{ COMMON_CAP2_PERMS, NULL } },
+ { "sctp_socket",
+ { COMMON_SOCK_PERMS,
+ "node_bind", NULL } },
+ { "icmp_socket",
+ { COMMON_SOCK_PERMS,
+ "node_bind", NULL } },
+ { "ax25_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "ipx_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "netrom_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "atmpvc_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "x25_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "rose_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "decnet_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "atmsvc_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "rds_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "irda_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "pppox_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "llc_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "can_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "tipc_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "bluetooth_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "iucv_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "rxrpc_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "isdn_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "phonet_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "ieee802154_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "caif_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "alg_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "nfc_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "vsock_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "kcm_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "qipcrtr_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "smc_socket",
+ { COMMON_SOCK_PERMS, NULL } },
{ NULL }
};
+
+#if PF_MAX > 44
+#error New address family defined, please update secclass_map.
+#endif
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index c21e135..c03cdcd 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -37,9 +37,20 @@ struct task_security_struct {
u32 sockcreate_sid; /* fscreate SID */
};
+/*
+ * get the subjective security ID of the current task
+ */
+static inline u32 current_sid(void)
+{
+ const struct task_security_struct *tsec = current_security();
+
+ return tsec->sid;
+}
+
enum label_initialized {
LABEL_INVALID, /* invalid or not initialized */
- LABEL_INITIALIZED /* initialized */
+ LABEL_INITIALIZED, /* initialized */
+ LABEL_PENDING
};
struct inode_security_struct {
@@ -52,7 +63,7 @@ struct inode_security_struct {
u32 sid; /* SID of this object */
u16 sclass; /* security class of this object */
unsigned char initialized; /* initialization flag */
- struct mutex lock;
+ spinlock_t lock;
};
struct file_security_struct {
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 308a286..f979c35 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -69,15 +69,18 @@ extern int selinux_enabled;
enum {
POLICYDB_CAPABILITY_NETPEER,
POLICYDB_CAPABILITY_OPENPERM,
- POLICYDB_CAPABILITY_REDHAT1,
+ POLICYDB_CAPABILITY_EXTSOCKCLASS,
POLICYDB_CAPABILITY_ALWAYSNETWORK,
+ POLICYDB_CAPABILITY_CGROUPSECLABEL,
__POLICYDB_CAPABILITY_MAX
};
#define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1)
extern int selinux_policycap_netpeer;
extern int selinux_policycap_openperm;
+extern int selinux_policycap_extsockclass;
extern int selinux_policycap_alwaysnetwork;
+extern int selinux_policycap_cgroupseclabel;
/*
* type_datum properties
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 72c145d..cb3fd98 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -45,8 +45,9 @@
static char *policycap_names[] = {
"network_peer_controls",
"open_perms",
- "redhat1",
- "always_check_network"
+ "extended_socket_class",
+ "always_check_network",
+ "cgroup_seclabel"
};
unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE;
@@ -77,25 +78,6 @@ static char policy_opened;
/* global data for policy capabilities */
static struct dentry *policycap_dir;
-/* Check whether a task is allowed to use a security operation. */
-static int task_has_security(struct task_struct *tsk,
- u32 perms)
-{
- const struct task_security_struct *tsec;
- u32 sid = 0;
-
- rcu_read_lock();
- tsec = __task_cred(tsk)->security;
- if (tsec)
- sid = tsec->sid;
- rcu_read_unlock();
- if (!tsec)
- return -EACCES;
-
- return avc_has_perm(sid, SECINITSID_SECURITY,
- SECCLASS_SECURITY, perms, NULL);
-}
-
enum sel_inos {
SEL_ROOT_INO = 2,
SEL_LOAD, /* load policy */
@@ -163,8 +145,12 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
if (sscanf(page, "%d", &new_value) != 1)
goto out;
+ new_value = !!new_value;
+
if (new_value != selinux_enforcing) {
- length = task_has_security(current, SECURITY__SETENFORCE);
+ length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ SECCLASS_SECURITY, SECURITY__SETENFORCE,
+ NULL);
if (length)
goto out;
audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS,
@@ -366,7 +352,8 @@ static int sel_open_policy(struct inode *inode, struct file *filp)
mutex_lock(&sel_mutex);
- rc = task_has_security(current, SECURITY__READ_POLICY);
+ rc = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ SECCLASS_SECURITY, SECURITY__READ_POLICY, NULL);
if (rc)
goto err;
@@ -427,7 +414,8 @@ static ssize_t sel_read_policy(struct file *filp, char __user *buf,
mutex_lock(&sel_mutex);
- ret = task_has_security(current, SECURITY__READ_POLICY);
+ ret = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ SECCLASS_SECURITY, SECURITY__READ_POLICY, NULL);
if (ret)
goto out;
@@ -437,10 +425,9 @@ out:
return ret;
}
-static int sel_mmap_policy_fault(struct vm_area_struct *vma,
- struct vm_fault *vmf)
+static int sel_mmap_policy_fault(struct vm_fault *vmf)
{
- struct policy_load_memory *plm = vma->vm_file->private_data;
+ struct policy_load_memory *plm = vmf->vma->vm_file->private_data;
unsigned long offset;
struct page *page;
@@ -497,7 +484,8 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf,
mutex_lock(&sel_mutex);
- length = task_has_security(current, SECURITY__LOAD_POLICY);
+ length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ SECCLASS_SECURITY, SECURITY__LOAD_POLICY, NULL);
if (length)
goto out;
@@ -520,20 +508,28 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf,
goto out;
length = security_load_policy(data, count);
- if (length)
+ if (length) {
+ pr_warn_ratelimited("SELinux: failed to load policy\n");
goto out;
+ }
length = sel_make_bools();
- if (length)
+ if (length) {
+ pr_err("SELinux: failed to load policy booleans\n");
goto out1;
+ }
length = sel_make_classes();
- if (length)
+ if (length) {
+ pr_err("SELinux: failed to load policy classes\n");
goto out1;
+ }
length = sel_make_policycap();
- if (length)
+ if (length) {
+ pr_err("SELinux: failed to load policy capabilities\n");
goto out1;
+ }
length = count;
@@ -559,7 +555,8 @@ static ssize_t sel_write_context(struct file *file, char *buf, size_t size)
u32 sid, len;
ssize_t length;
- length = task_has_security(current, SECURITY__CHECK_CONTEXT);
+ length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ SECCLASS_SECURITY, SECURITY__CHECK_CONTEXT, NULL);
if (length)
goto out;
@@ -602,7 +599,9 @@ static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf,
ssize_t length;
unsigned int new_value;
- length = task_has_security(current, SECURITY__SETCHECKREQPROT);
+ length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ SECCLASS_SECURITY, SECURITY__SETCHECKREQPROT,
+ NULL);
if (length)
return length;
@@ -643,7 +642,8 @@ static ssize_t sel_write_validatetrans(struct file *file,
u16 tclass;
int rc;
- rc = task_has_security(current, SECURITY__VALIDATE_TRANS);
+ rc = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ SECCLASS_SECURITY, SECURITY__VALIDATE_TRANS, NULL);
if (rc)
goto out;
@@ -770,7 +770,8 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
struct av_decision avd;
ssize_t length;
- length = task_has_security(current, SECURITY__COMPUTE_AV);
+ length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ SECCLASS_SECURITY, SECURITY__COMPUTE_AV, NULL);
if (length)
goto out;
@@ -820,7 +821,9 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size)
u32 len;
int nargs;
- length = task_has_security(current, SECURITY__COMPUTE_CREATE);
+ length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ SECCLASS_SECURITY, SECURITY__COMPUTE_CREATE,
+ NULL);
if (length)
goto out;
@@ -917,7 +920,9 @@ static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size)
char *newcon = NULL;
u32 len;
- length = task_has_security(current, SECURITY__COMPUTE_RELABEL);
+ length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ SECCLASS_SECURITY, SECURITY__COMPUTE_RELABEL,
+ NULL);
if (length)
goto out;
@@ -973,7 +978,9 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size)
int i, rc;
u32 len, nsids;
- length = task_has_security(current, SECURITY__COMPUTE_USER);
+ length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ SECCLASS_SECURITY, SECURITY__COMPUTE_USER,
+ NULL);
if (length)
goto out;
@@ -1033,7 +1040,9 @@ static ssize_t sel_write_member(struct file *file, char *buf, size_t size)
char *newcon = NULL;
u32 len;
- length = task_has_security(current, SECURITY__COMPUTE_MEMBER);
+ length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ SECCLASS_SECURITY, SECURITY__COMPUTE_MEMBER,
+ NULL);
if (length)
goto out;
@@ -1140,7 +1149,9 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
mutex_lock(&sel_mutex);
- length = task_has_security(current, SECURITY__SETBOOL);
+ length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ SECCLASS_SECURITY, SECURITY__SETBOOL,
+ NULL);
if (length)
goto out;
@@ -1196,7 +1207,9 @@ static ssize_t sel_commit_bools_write(struct file *filep,
mutex_lock(&sel_mutex);
- length = task_has_security(current, SECURITY__SETBOOL);
+ length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ SECCLASS_SECURITY, SECURITY__SETBOOL,
+ NULL);
if (length)
goto out;
@@ -1297,11 +1310,14 @@ static int sel_make_bools(void)
isec = (struct inode_security_struct *)inode->i_security;
ret = security_genfs_sid("selinuxfs", page, SECCLASS_FILE, &sid);
- if (ret)
- goto out;
+ if (ret) {
+ pr_warn_ratelimited("SELinux: no sid found, defaulting to security isid for %s\n",
+ page);
+ sid = SECINITSID_SECURITY;
+ }
isec->sid = sid;
- isec->initialized = 1;
+ isec->initialized = LABEL_INITIALIZED;
inode->i_fop = &sel_bool_ops;
inode->i_ino = i|SEL_BOOL_INO_OFFSET;
d_add(dentry, inode);
@@ -1349,7 +1365,9 @@ static ssize_t sel_write_avc_cache_threshold(struct file *file,
ssize_t ret;
unsigned int new_value;
- ret = task_has_security(current, SECURITY__SETSECPARAM);
+ ret = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ SECCLASS_SECURITY, SECURITY__SETSECPARAM,
+ NULL);
if (ret)
return ret;
@@ -1834,7 +1852,7 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent)
isec = (struct inode_security_struct *)inode->i_security;
isec->sid = SECINITSID_DEVNULL;
isec->sclass = SECCLASS_CHR_FILE;
- isec->initialized = 1;
+ isec->initialized = LABEL_INITIALIZED;
init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, MKDEV(MEM_MAJOR, 3));
d_add(dentry, inode);
diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c
index 7d10e5d..9db4709a 100644
--- a/security/selinux/ss/ebitmap.c
+++ b/security/selinux/ss/ebitmap.c
@@ -360,7 +360,7 @@ int ebitmap_read(struct ebitmap *e, void *fp)
if (mapunit != BITS_PER_U64) {
printk(KERN_ERR "SELinux: ebitmap: map size %u does not "
- "match my size %Zd (high bit was %d)\n",
+ "match my size %zd (high bit was %d)\n",
mapunit, BITS_PER_U64, e->highbit);
goto bad;
}
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index d719db4..9c92f29 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -2266,7 +2266,7 @@ int policydb_read(struct policydb *p, void *fp)
len = le32_to_cpu(buf[1]);
if (len != strlen(POLICYDB_STRING)) {
printk(KERN_ERR "SELinux: policydb string length %d does not "
- "match expected length %Zu\n",
+ "match expected length %zu\n",
len, strlen(POLICYDB_STRING));
goto bad;
}
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 082b20c..b4aa491 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -72,7 +72,9 @@
int selinux_policycap_netpeer;
int selinux_policycap_openperm;
+int selinux_policycap_extsockclass;
int selinux_policycap_alwaysnetwork;
+int selinux_policycap_cgroupseclabel;
static DEFINE_RWLOCK(policy_rwlock);
@@ -1988,8 +1990,13 @@ static void security_load_policycaps(void)
POLICYDB_CAPABILITY_NETPEER);
selinux_policycap_openperm = ebitmap_get_bit(&policydb.policycaps,
POLICYDB_CAPABILITY_OPENPERM);
+ selinux_policycap_extsockclass = ebitmap_get_bit(&policydb.policycaps,
+ POLICYDB_CAPABILITY_EXTSOCKCLASS);
selinux_policycap_alwaysnetwork = ebitmap_get_bit(&policydb.policycaps,
POLICYDB_CAPABILITY_ALWAYSNETWORK);
+ selinux_policycap_cgroupseclabel =
+ ebitmap_get_bit(&policydb.policycaps,
+ POLICYDB_CAPABILITY_CGROUPSECLABEL);
}
static int security_preserve_bools(struct policydb *p);
diff --git a/security/smack/smack.h b/security/smack/smack.h
index 51fd301..612b810 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -114,6 +114,7 @@ struct inode_smack {
struct smack_known *smk_mmap; /* label of the mmap domain */
struct mutex smk_lock; /* initialization lock */
int smk_flags; /* smack inode flags */
+ struct rcu_head smk_rcu; /* for freeing inode_smack */
};
struct task_smack {
@@ -173,6 +174,8 @@ struct smk_port_label {
unsigned short smk_port; /* the port number */
struct smack_known *smk_in; /* inbound label */
struct smack_known *smk_out; /* outgoing label */
+ short smk_sock_type; /* Socket type */
+ short smk_can_reuse;
};
#endif /* SMACK_IPV6_PORT_LABELING */
@@ -336,7 +339,6 @@ extern int smack_ptrace_rule;
extern struct smack_known smack_known_floor;
extern struct smack_known smack_known_hat;
extern struct smack_known smack_known_huh;
-extern struct smack_known smack_known_invalid;
extern struct smack_known smack_known_star;
extern struct smack_known smack_known_web;
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c
index 23e5808..356e376 100644
--- a/security/smack/smack_access.c
+++ b/security/smack/smack_access.c
@@ -36,11 +36,6 @@ struct smack_known smack_known_floor = {
.smk_secid = 5,
};
-struct smack_known smack_known_invalid = {
- .smk_known = "",
- .smk_secid = 6,
-};
-
struct smack_known smack_known_web = {
.smk_known = "@",
.smk_secid = 7,
@@ -615,7 +610,7 @@ struct smack_known *smack_from_secid(const u32 secid)
* of a secid that is not on the list.
*/
rcu_read_unlock();
- return &smack_known_invalid;
+ return &smack_known_huh;
}
/*
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 1cb0602..fc8fb31 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -52,6 +52,7 @@
#define SMK_SENDING 2
#ifdef SMACK_IPV6_PORT_LABELING
+DEFINE_MUTEX(smack_ipv6_lock);
static LIST_HEAD(smk_ipv6_port_list);
#endif
static struct kmem_cache *smack_inode_cache;
@@ -225,7 +226,7 @@ static int smk_bu_credfile(const struct cred *cred, struct file *file,
{
struct task_smack *tsp = cred->security;
struct smack_known *sskp = tsp->smk_task;
- struct inode *inode = file->f_inode;
+ struct inode *inode = file_inode(file);
struct inode_smack *isp = inode->i_security;
char acc[SMK_NUM_ACCESS_TYPE + 1];
@@ -347,8 +348,6 @@ static int smk_copy_rules(struct list_head *nhead, struct list_head *ohead,
struct smack_rule *orp;
int rc = 0;
- INIT_LIST_HEAD(nhead);
-
list_for_each_entry_rcu(orp, ohead, list) {
nrp = kzalloc(sizeof(struct smack_rule), gfp);
if (nrp == NULL) {
@@ -375,8 +374,6 @@ static int smk_copy_relabel(struct list_head *nhead, struct list_head *ohead,
struct smack_known_list_elem *nklep;
struct smack_known_list_elem *oklep;
- INIT_LIST_HEAD(nhead);
-
list_for_each_entry(oklep, ohead, list) {
nklep = kzalloc(sizeof(struct smack_known_list_elem), gfp);
if (nklep == NULL) {
@@ -692,12 +689,12 @@ static int smack_parse_opts_str(char *options,
}
}
- opts->mnt_opts = kcalloc(NUM_SMK_MNT_OPTS, sizeof(char *), GFP_ATOMIC);
+ opts->mnt_opts = kcalloc(NUM_SMK_MNT_OPTS, sizeof(char *), GFP_KERNEL);
if (!opts->mnt_opts)
goto out_err;
opts->mnt_opts_flags = kcalloc(NUM_SMK_MNT_OPTS, sizeof(int),
- GFP_ATOMIC);
+ GFP_KERNEL);
if (!opts->mnt_opts_flags) {
kfree(opts->mnt_opts);
goto out_err;
@@ -769,6 +766,31 @@ static int smack_set_mnt_opts(struct super_block *sb,
if (sp->smk_flags & SMK_SB_INITIALIZED)
return 0;
+ if (!smack_privileged(CAP_MAC_ADMIN)) {
+ /*
+ * Unprivileged mounts don't get to specify Smack values.
+ */
+ if (num_opts)
+ return -EPERM;
+ /*
+ * Unprivileged mounts get root and default from the caller.
+ */
+ skp = smk_of_current();
+ sp->smk_root = skp;
+ sp->smk_default = skp;
+ /*
+ * For a handful of fs types with no user-controlled
+ * backing store it's okay to trust security labels
+ * in the filesystem. The rest are untrusted.
+ */
+ if (sb->s_user_ns != &init_user_ns &&
+ sb->s_magic != SYSFS_MAGIC && sb->s_magic != TMPFS_MAGIC &&
+ sb->s_magic != RAMFS_MAGIC) {
+ transmute = 1;
+ sp->smk_flags |= SMK_SB_UNTRUSTED;
+ }
+ }
+
sp->smk_flags |= SMK_SB_INITIALIZED;
for (i = 0; i < num_opts; i++) {
@@ -809,31 +831,6 @@ static int smack_set_mnt_opts(struct super_block *sb,
}
}
- if (!smack_privileged(CAP_MAC_ADMIN)) {
- /*
- * Unprivileged mounts don't get to specify Smack values.
- */
- if (num_opts)
- return -EPERM;
- /*
- * Unprivileged mounts get root and default from the caller.
- */
- skp = smk_of_current();
- sp->smk_root = skp;
- sp->smk_default = skp;
- /*
- * For a handful of fs types with no user-controlled
- * backing store it's okay to trust security labels
- * in the filesystem. The rest are untrusted.
- */
- if (sb->s_user_ns != &init_user_ns &&
- sb->s_magic != SYSFS_MAGIC && sb->s_magic != TMPFS_MAGIC &&
- sb->s_magic != RAMFS_MAGIC) {
- transmute = 1;
- sp->smk_flags |= SMK_SB_UNTRUSTED;
- }
- }
-
/*
* Initialize the root inode.
*/
@@ -934,7 +931,7 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm)
isp->smk_task != sbsp->smk_root)
return 0;
- if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
+ if (bprm->unsafe & LSM_UNSAFE_PTRACE) {
struct task_struct *tracer;
rc = 0;
@@ -1009,15 +1006,39 @@ static int smack_inode_alloc_security(struct inode *inode)
}
/**
- * smack_inode_free_security - free an inode blob
+ * smack_inode_free_rcu - Free inode_smack blob from cache
+ * @head: the rcu_head for getting inode_smack pointer
+ *
+ * Call back function called from call_rcu() to free
+ * the i_security blob pointer in inode
+ */
+static void smack_inode_free_rcu(struct rcu_head *head)
+{
+ struct inode_smack *issp;
+
+ issp = container_of(head, struct inode_smack, smk_rcu);
+ kmem_cache_free(smack_inode_cache, issp);
+}
+
+/**
+ * smack_inode_free_security - free an inode blob using call_rcu()
* @inode: the inode with a blob
*
- * Clears the blob pointer in inode
+ * Clears the blob pointer in inode using RCU
*/
static void smack_inode_free_security(struct inode *inode)
{
- kmem_cache_free(smack_inode_cache, inode->i_security);
- inode->i_security = NULL;
+ struct inode_smack *issp = inode->i_security;
+
+ /*
+ * The inode may still be referenced in a path walk and
+ * a call to smack_inode_permission() can be made
+ * after smack_inode_free_security() is called.
+ * To avoid race condition free the i_security via RCU
+ * and leave the current inode->i_security pointer intact.
+ * The inode will be freed after the RCU grace period too.
+ */
+ call_rcu(&issp->smk_rcu, smack_inode_free_rcu);
}
/**
@@ -1384,20 +1405,14 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
skp = smk_import_entry(value, size);
if (!IS_ERR(skp))
isp->smk_inode = skp;
- else
- isp->smk_inode = &smack_known_invalid;
} else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) {
skp = smk_import_entry(value, size);
if (!IS_ERR(skp))
isp->smk_task = skp;
- else
- isp->smk_task = &smack_known_invalid;
} else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0) {
skp = smk_import_entry(value, size);
if (!IS_ERR(skp))
isp->smk_mmap = skp;
- else
- isp->smk_mmap = &smack_known_invalid;
}
return;
@@ -1632,6 +1647,9 @@ static int smack_file_ioctl(struct file *file, unsigned int cmd,
struct smk_audit_info ad;
struct inode *inode = file_inode(file);
+ if (unlikely(IS_PRIVATE(inode)))
+ return 0;
+
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
smk_ad_setfield_u_fs_path(&ad, file->f_path);
@@ -1661,6 +1679,9 @@ static int smack_file_lock(struct file *file, unsigned int cmd)
int rc;
struct inode *inode = file_inode(file);
+ if (unlikely(IS_PRIVATE(inode)))
+ return 0;
+
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
smk_ad_setfield_u_fs_path(&ad, file->f_path);
rc = smk_curacc(smk_of_inode(inode), MAY_LOCK, &ad);
@@ -1687,6 +1708,9 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd,
int rc = 0;
struct inode *inode = file_inode(file);
+ if (unlikely(IS_PRIVATE(inode)))
+ return 0;
+
switch (cmd) {
case F_GETLK:
break;
@@ -1740,6 +1764,9 @@ static int smack_mmap_file(struct file *file,
if (file == NULL)
return 0;
+ if (unlikely(IS_PRIVATE(file_inode(file))))
+ return 0;
+
isp = file_inode(file)->i_security;
if (isp->smk_mmap == NULL)
return 0;
@@ -1940,12 +1967,9 @@ static int smack_file_open(struct file *file, const struct cred *cred)
struct smk_audit_info ad;
int rc;
- if (smack_privileged(CAP_MAC_OVERRIDE))
- return 0;
-
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
smk_ad_setfield_u_fs_path(&ad, file->f_path);
- rc = smk_access(tsp->smk_task, smk_of_inode(inode), MAY_READ, &ad);
+ rc = smk_tskacc(tsp, smk_of_inode(inode), MAY_READ, &ad);
rc = smk_bu_credfile(cred, file, MAY_READ, rc);
return rc;
@@ -2023,6 +2047,8 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
if (new_tsp == NULL)
return -ENOMEM;
+ new->security = new_tsp;
+
rc = smk_copy_rules(&new_tsp->smk_rules, &old_tsp->smk_rules, gfp);
if (rc != 0)
return rc;
@@ -2032,7 +2058,6 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
if (rc != 0)
return rc;
- new->security = new_tsp;
return 0;
}
@@ -2067,12 +2092,8 @@ static void smack_cred_transfer(struct cred *new, const struct cred *old)
static int smack_kernel_act_as(struct cred *new, u32 secid)
{
struct task_smack *new_tsp = new->security;
- struct smack_known *skp = smack_from_secid(secid);
- if (skp == NULL)
- return -EINVAL;
-
- new_tsp->smk_task = skp;
+ new_tsp->smk_task = smack_from_secid(secid);
return 0;
}
@@ -2281,25 +2302,6 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info,
}
/**
- * smack_task_wait - Smack access check for waiting
- * @p: task to wait for
- *
- * Returns 0
- */
-static int smack_task_wait(struct task_struct *p)
-{
- /*
- * Allow the operation to succeed.
- * Zombies are bad.
- * In userless environments (e.g. phones) programs
- * get marked with SMACK64EXEC and even if the parent
- * and child shouldn't be talking the parent still
- * may expect to know when the child exits.
- */
- return 0;
-}
-
-/**
* smack_task_to_inode - copy task smack into the inode blob
* @p: task to copy from
* @inode: inode to copy to
@@ -2337,8 +2339,16 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
if (ssp == NULL)
return -ENOMEM;
- ssp->smk_in = skp;
- ssp->smk_out = skp;
+ /*
+ * Sockets created by kernel threads receive web label.
+ */
+ if (unlikely(current->flags & PF_KTHREAD)) {
+ ssp->smk_in = &smack_known_web;
+ ssp->smk_out = &smack_known_web;
+ } else {
+ ssp->smk_in = skp;
+ ssp->smk_out = skp;
+ }
ssp->smk_packet = NULL;
sk->sk_security = ssp;
@@ -2354,6 +2364,20 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
*/
static void smack_sk_free_security(struct sock *sk)
{
+#ifdef SMACK_IPV6_PORT_LABELING
+ struct smk_port_label *spp;
+
+ if (sk->sk_family == PF_INET6) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(spp, &smk_ipv6_port_list, list) {
+ if (spp->smk_sock != sk)
+ continue;
+ spp->smk_can_reuse = 1;
+ break;
+ }
+ rcu_read_unlock();
+ }
+#endif
kfree(sk->sk_security);
}
@@ -2435,17 +2459,17 @@ static struct smack_known *smack_ipv6host_label(struct sockaddr_in6 *sip)
list_for_each_entry_rcu(snp, &smk_net6addr_list, list) {
/*
+ * If the label is NULL the entry has
+ * been renounced. Ignore it.
+ */
+ if (snp->smk_label == NULL)
+ continue;
+ /*
* we break after finding the first match because
* the list is sorted from longest to shortest mask
* so we have found the most specific match
*/
for (found = 1, i = 0; i < 8; i++) {
- /*
- * If the label is NULL the entry has
- * been renounced. Ignore it.
- */
- if (snp->smk_label == NULL)
- continue;
if ((sap->s6_addr16[i] & snp->smk_mask.s6_addr16[i]) !=
snp->smk_host.s6_addr16[i]) {
found = 0;
@@ -2604,17 +2628,20 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address)
* on the bound socket. Take the changes to the port
* as well.
*/
- list_for_each_entry(spp, &smk_ipv6_port_list, list) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(spp, &smk_ipv6_port_list, list) {
if (sk != spp->smk_sock)
continue;
spp->smk_in = ssp->smk_in;
spp->smk_out = ssp->smk_out;
+ rcu_read_unlock();
return;
}
/*
* A NULL address is only used for updating existing
* bound entries. If there isn't one, it's OK.
*/
+ rcu_read_unlock();
return;
}
@@ -2630,16 +2657,23 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address)
* Look for an existing port list entry.
* This is an indication that a port is getting reused.
*/
- list_for_each_entry(spp, &smk_ipv6_port_list, list) {
- if (spp->smk_port != port)
+ rcu_read_lock();
+ list_for_each_entry_rcu(spp, &smk_ipv6_port_list, list) {
+ if (spp->smk_port != port || spp->smk_sock_type != sock->type)
continue;
+ if (spp->smk_can_reuse != 1) {
+ rcu_read_unlock();
+ return;
+ }
spp->smk_port = port;
spp->smk_sock = sk;
spp->smk_in = ssp->smk_in;
spp->smk_out = ssp->smk_out;
+ spp->smk_can_reuse = 0;
+ rcu_read_unlock();
return;
}
-
+ rcu_read_unlock();
/*
* A new port entry is required.
*/
@@ -2651,8 +2685,12 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address)
spp->smk_sock = sk;
spp->smk_in = ssp->smk_in;
spp->smk_out = ssp->smk_out;
+ spp->smk_sock_type = sock->type;
+ spp->smk_can_reuse = 0;
- list_add(&spp->list, &smk_ipv6_port_list);
+ mutex_lock(&smack_ipv6_lock);
+ list_add_rcu(&spp->list, &smk_ipv6_port_list);
+ mutex_unlock(&smack_ipv6_lock);
return;
}
@@ -2703,14 +2741,16 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address,
return 0;
port = ntohs(address->sin6_port);
- list_for_each_entry(spp, &smk_ipv6_port_list, list) {
- if (spp->smk_port != port)
+ rcu_read_lock();
+ list_for_each_entry_rcu(spp, &smk_ipv6_port_list, list) {
+ if (spp->smk_port != port || spp->smk_sock_type != sk->sk_type)
continue;
object = spp->smk_in;
if (act == SMK_CONNECTING)
ssp->smk_packet = spp->smk_out;
break;
}
+ rcu_read_unlock();
return smk_ipv6_check(skp, object, address, act);
}
@@ -3439,6 +3479,13 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
case PIPEFS_MAGIC:
isp->smk_inode = smk_of_current();
break;
+ case SOCKFS_MAGIC:
+ /*
+ * Socket access is controlled by the socket
+ * structures associated with the task involved.
+ */
+ isp->smk_inode = &smack_known_star;
+ break;
default:
isp->smk_inode = sbsp->smk_root;
break;
@@ -3455,19 +3502,12 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
*/
switch (sbp->s_magic) {
case SMACK_MAGIC:
- case PIPEFS_MAGIC:
- case SOCKFS_MAGIC:
case CGROUP_SUPER_MAGIC:
/*
* Casey says that it's a little embarrassing
* that the smack file system doesn't do
* extended attributes.
*
- * Casey says pipes are easy (?)
- *
- * Socket access is controlled by the socket
- * structures associated with the task involved.
- *
* Cgroupfs is special
*/
final = &smack_known_star;
@@ -3621,7 +3661,6 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
/**
* smack_setprocattr - Smack process attribute setting
- * @p: the object task
* @name: the name of the attribute in /proc/.../attr
* @value: the value to set
* @size: the size of the value
@@ -3631,8 +3670,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
*
* Returns the length of the smack label or an error code
*/
-static int smack_setprocattr(struct task_struct *p, char *name,
- void *value, size_t size)
+static int smack_setprocattr(const char *name, void *value, size_t size)
{
struct task_smack *tsp = current_security();
struct cred *new;
@@ -3640,13 +3678,6 @@ static int smack_setprocattr(struct task_struct *p, char *name,
struct smack_known_list_elem *sklep;
int rc;
- /*
- * Changing another process' Smack value is too dangerous
- * and supports no sane use case.
- */
- if (p != current)
- return -EPERM;
-
if (!smack_privileged(CAP_MAC_ADMIN) && list_empty(&tsp->smk_relabel))
return -EPERM;
@@ -3661,10 +3692,11 @@ static int smack_setprocattr(struct task_struct *p, char *name,
return PTR_ERR(skp);
/*
- * No process is ever allowed the web ("@") label.
+ * No process is ever allowed the web ("@") label
+ * and the star ("*") label.
*/
- if (skp == &smack_known_web)
- return -EPERM;
+ if (skp == &smack_known_web || skp == &smack_known_star)
+ return -EINVAL;
if (!smack_privileged(CAP_MAC_ADMIN)) {
rc = -EPERM;
@@ -3849,7 +3881,7 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap,
* ambient value.
*/
rcu_read_lock();
- list_for_each_entry(skp, &smack_known_list, list) {
+ list_for_each_entry_rcu(skp, &smack_known_list, list) {
if (sap->attr.mls.lvl != skp->smk_netlabel.attr.mls.lvl)
continue;
/*
@@ -3884,21 +3916,11 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap,
return &smack_known_web;
return &smack_known_star;
}
- if ((sap->flags & NETLBL_SECATTR_SECID) != 0) {
+ if ((sap->flags & NETLBL_SECATTR_SECID) != 0)
/*
* Looks like a fallback, which gives us a secid.
*/
- skp = smack_from_secid(sap->attr.secid);
- /*
- * This has got to be a bug because it is
- * impossible to specify a fallback without
- * specifying the label, which will ensure
- * it has a secid, and the only way to get a
- * secid is from a fallback.
- */
- BUG_ON(skp == NULL);
- return skp;
- }
+ return smack_from_secid(sap->attr.secid);
/*
* Without guidance regarding the smack value
* for the packet fall back on the network
@@ -4677,7 +4699,6 @@ static struct security_hook_list smack_hooks[] = {
LSM_HOOK_INIT(task_getscheduler, smack_task_getscheduler),
LSM_HOOK_INIT(task_movememory, smack_task_movememory),
LSM_HOOK_INIT(task_kill, smack_task_kill),
- LSM_HOOK_INIT(task_wait, smack_task_wait),
LSM_HOOK_INIT(task_to_inode, smack_task_to_inode),
LSM_HOOK_INIT(ipc_permission, smack_ipc_permission),
@@ -4761,7 +4782,6 @@ static __init void init_smack_known_list(void)
mutex_init(&smack_known_hat.smk_rules_lock);
mutex_init(&smack_known_floor.smk_rules_lock);
mutex_init(&smack_known_star.smk_rules_lock);
- mutex_init(&smack_known_invalid.smk_rules_lock);
mutex_init(&smack_known_web.smk_rules_lock);
/*
* Initialize rule lists
@@ -4770,7 +4790,6 @@ static __init void init_smack_known_list(void)
INIT_LIST_HEAD(&smack_known_hat.smk_rules);
INIT_LIST_HEAD(&smack_known_star.smk_rules);
INIT_LIST_HEAD(&smack_known_floor.smk_rules);
- INIT_LIST_HEAD(&smack_known_invalid.smk_rules);
INIT_LIST_HEAD(&smack_known_web.smk_rules);
/*
* Create the known labels list
@@ -4779,7 +4798,6 @@ static __init void init_smack_known_list(void)
smk_insert_entry(&smack_known_hat);
smk_insert_entry(&smack_known_star);
smk_insert_entry(&smack_known_floor);
- smk_insert_entry(&smack_known_invalid);
smk_insert_entry(&smack_known_web);
}
@@ -4832,7 +4850,7 @@ static __init int smack_init(void)
/*
* Register with LSM
*/
- security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks));
+ security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack");
return 0;
}
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index 6492fe9..366b835 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -67,6 +67,7 @@ enum smk_inos {
/*
* List locks
*/
+static DEFINE_MUTEX(smack_master_list_lock);
static DEFINE_MUTEX(smack_cipso_lock);
static DEFINE_MUTEX(smack_ambient_lock);
static DEFINE_MUTEX(smk_net4addr_lock);
@@ -262,12 +263,16 @@ static int smk_set_access(struct smack_parsed_rule *srp,
* it needs to get added for reporting.
*/
if (global) {
+ mutex_unlock(rule_lock);
smlp = kzalloc(sizeof(*smlp), GFP_KERNEL);
if (smlp != NULL) {
smlp->smk_rule = sp;
+ mutex_lock(&smack_master_list_lock);
list_add_rcu(&smlp->list, &smack_rule_list);
+ mutex_unlock(&smack_master_list_lock);
} else
rc = -ENOMEM;
+ return rc;
}
}
@@ -2998,9 +3003,6 @@ static int __init init_smk_fs(void)
rc = smk_preset_netlabel(&smack_known_huh);
if (err == 0 && rc < 0)
err = rc;
- rc = smk_preset_netlabel(&smack_known_invalid);
- if (err == 0 && rc < 0)
- err = rc;
rc = smk_preset_netlabel(&smack_known_star);
if (err == 0 && rc < 0)
err = rc;
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c
index 682b73a..00d223e 100644
--- a/security/tomoyo/domain.c
+++ b/security/tomoyo/domain.c
@@ -5,8 +5,10 @@
*/
#include "common.h"
+
#include <linux/binfmts.h>
#include <linux/slab.h>
+#include <linux/rculist.h>
/* Variables definitions.*/
@@ -881,7 +883,7 @@ bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos,
* the execve().
*/
if (get_user_pages_remote(current, bprm->mm, pos, 1,
- FOLL_FORCE, &page, NULL) <= 0)
+ FOLL_FORCE, &page, NULL, NULL) <= 0)
return false;
#else
page = bprm->page[pos / PAGE_SIZE];
diff --git a/security/tomoyo/group.c b/security/tomoyo/group.c
index 5009253..944ad77 100644
--- a/security/tomoyo/group.c
+++ b/security/tomoyo/group.c
@@ -5,6 +5,8 @@
*/
#include <linux/slab.h>
+#include <linux/rculist.h>
+
#include "common.h"
/**
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index 75c9987..edc52d6 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -542,7 +542,7 @@ static int __init tomoyo_init(void)
if (!security_module_enable("tomoyo"))
return 0;
/* register ourselves with the security framework */
- security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks));
+ security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo");
printk(KERN_INFO "TOMOYO Linux initialized\n");
cred->security = &tomoyo_kernel_domain;
tomoyo_mm_init();
diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c
index 5fe3679..848317f 100644
--- a/security/tomoyo/util.c
+++ b/security/tomoyo/util.c
@@ -5,6 +5,8 @@
*/
#include <linux/slab.h>
+#include <linux/rculist.h>
+
#include "common.h"
/* Lock for protecting policy. */
diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c
index 0309f21..88271a3 100644
--- a/security/yama/yama_lsm.c
+++ b/security/yama/yama_lsm.c
@@ -309,7 +309,7 @@ static int task_is_descendant(struct task_struct *parent,
* @tracer: the task_struct of the process attempting ptrace
* @tracee: the task_struct of the process to be ptraced
*
- * Returns 1 if tracer has is ptracer exception ancestor for tracee.
+ * Returns 1 if tracer has a ptracer exception ancestor for tracee.
*/
static int ptracer_exception_found(struct task_struct *tracer,
struct task_struct *tracee)
@@ -320,6 +320,18 @@ static int ptracer_exception_found(struct task_struct *tracer,
bool found = false;
rcu_read_lock();
+
+ /*
+ * If there's already an active tracing relationship, then make an
+ * exception for the sake of other accesses, like process_vm_rw().
+ */
+ parent = ptrace_parent(tracee);
+ if (parent != NULL && same_thread_group(parent, tracer)) {
+ rc = 1;
+ goto unlock;
+ }
+
+ /* Look for a PR_SET_PTRACER relationship. */
if (!thread_group_leader(tracee))
tracee = rcu_dereference(tracee->group_leader);
list_for_each_entry_rcu(relation, &ptracer_relations, node) {
@@ -334,6 +346,8 @@ static int ptracer_exception_found(struct task_struct *tracer,
if (found && (parent == NULL || task_is_descendant(parent, tracer)))
rc = 1;
+
+unlock:
rcu_read_unlock();
return rc;
@@ -471,6 +485,6 @@ static inline void yama_init_sysctl(void) { }
void __init yama_add_hooks(void)
{
pr_info("Yama: becoming mindful.\n");
- security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks));
+ security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks), "yama");
yama_init_sysctl();
}
OpenPOWER on IntegriCloud