aboutsummaryrefslogtreecommitdiff
path: root/security
diff options
context:
space:
mode:
authorFrederic Weisbecker <fweisbec@gmail.com>2009-12-07 07:28:35 +0100
committerFrederic Weisbecker <fweisbec@gmail.com>2009-12-07 07:29:22 +0100
commit6548698f929814375fa5d62ae1db96959b0418c1 (patch)
tree340924ae82cb0946aa15045b2b72186de52a8146 /security
parent1d2c6cfd40b2dece3bb958cbbc405a2c1536ab75 (diff)
parent22763c5cf3690a681551162c15d34d935308c8d7 (diff)
Merge commit 'v2.6.32' into reiserfs/kill-bkl
Merge-reason: The tree was based 2.6.31. It's better to be up to date with 2.6.32. Although no conflicting changes were made in between, it gives benchmarking results closer to the lastest kernel behaviour.
Diffstat (limited to 'security')
-rw-r--r--security/Kconfig30
-rw-r--r--security/Makefile4
-rw-r--r--security/capability.c63
-rw-r--r--security/commoncap.c4
-rw-r--r--security/device_cgroup.c3
-rw-r--r--security/integrity/ima/ima_fs.c14
-rw-r--r--security/integrity/ima/ima_iint.c4
-rw-r--r--security/keys/Makefile1
-rw-r--r--security/keys/compat.c3
-rw-r--r--security/keys/gc.c218
-rw-r--r--security/keys/internal.h10
-rw-r--r--security/keys/key.c24
-rw-r--r--security/keys/keyctl.c164
-rw-r--r--security/keys/keyring.c99
-rw-r--r--security/keys/proc.c93
-rw-r--r--security/keys/process_keys.c69
-rw-r--r--security/keys/sysctl.c28
-rw-r--r--security/lsm_audit.c4
-rw-r--r--security/min_addr.c4
-rw-r--r--security/security.c62
-rw-r--r--security/selinux/avc.c242
-rw-r--r--security/selinux/exports.c6
-rw-r--r--security/selinux/hooks.c320
-rw-r--r--security/selinux/include/av_inherit.h1
-rw-r--r--security/selinux/include/av_perm_to_string.h1
-rw-r--r--security/selinux/include/av_permissions.h23
-rw-r--r--security/selinux/include/avc.h55
-rw-r--r--security/selinux/include/class_to_string.h1
-rw-r--r--security/selinux/include/flask.h1
-rw-r--r--security/selinux/include/netlabel.h4
-rw-r--r--security/selinux/include/xfrm.h8
-rw-r--r--security/selinux/netlabel.c2
-rw-r--r--security/selinux/ss/services.c142
-rw-r--r--security/selinux/xfrm.c4
-rw-r--r--security/smack/smack.h2
-rw-r--r--security/smack/smack_access.c11
-rw-r--r--security/smack/smack_lsm.c73
-rw-r--r--security/smack/smackfs.c6
-rw-r--r--security/tomoyo/common.c30
-rw-r--r--security/tomoyo/common.h2
-rw-r--r--security/tomoyo/domain.c42
-rw-r--r--security/tomoyo/tomoyo.c27
-rw-r--r--security/tomoyo/tomoyo.h3
43 files changed, 1415 insertions, 492 deletions
diff --git a/security/Kconfig b/security/Kconfig
index 4c865345caa..fb363cd81cf 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -113,6 +113,36 @@ config SECURITY_ROOTPLUG
If you are unsure how to answer this question, answer N.
+config INTEL_TXT
+ bool "Enable Intel(R) Trusted Execution Technology (Intel(R) TXT)"
+ depends on HAVE_INTEL_TXT
+ help
+ This option enables support for booting the kernel with the
+ Trusted Boot (tboot) module. This will utilize
+ Intel(R) Trusted Execution Technology to perform a measured launch
+ of the kernel. If the system does not support Intel(R) TXT, this
+ will have no effect.
+
+ Intel TXT will provide higher assurance of system configuration and
+ initial state as well as data reset protection. This is used to
+ create a robust initial kernel measurement and verification, which
+ helps to ensure that kernel security mechanisms are functioning
+ correctly. This level of protection requires a root of trust outside
+ of the kernel itself.
+
+ Intel TXT also helps solve real end user concerns about having
+ confidence that their hardware is running the VMM or kernel that
+ it was configured with, especially since they may be responsible for
+ providing such assurances to VMs and services running on it.
+
+ See <http://www.intel.com/technology/security/> for more information
+ about Intel(R) TXT.
+ See <http://tboot.sourceforge.net> for more information about tboot.
+ See Documentation/intel_txt.txt for a description of how to enable
+ Intel TXT support in a kernel boot.
+
+ If you are unsure as to whether this is required, answer N.
+
config LSM_MMAP_MIN_ADDR
int "Low address space for LSM to protect from user allocation"
depends on SECURITY && SECURITY_SELINUX
diff --git a/security/Makefile b/security/Makefile
index b56e7f9ecbc..95ecc06392d 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -16,9 +16,7 @@ obj-$(CONFIG_SECURITYFS) += inode.o
# Must precede capability.o in order to stack properly.
obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o
obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o
-ifeq ($(CONFIG_AUDIT),y)
-obj-$(CONFIG_SECURITY_SMACK) += lsm_audit.o
-endif
+obj-$(CONFIG_AUDIT) += lsm_audit.o
obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/built-in.o
obj-$(CONFIG_SECURITY_ROOTPLUG) += root_plug.o
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o
diff --git a/security/capability.c b/security/capability.c
index 88f752e8152..fce07a7bc82 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -373,6 +373,11 @@ static int cap_task_create(unsigned long clone_flags)
return 0;
}
+static int cap_cred_alloc_blank(struct cred *cred, gfp_t gfp)
+{
+ return 0;
+}
+
static void cap_cred_free(struct cred *cred)
{
}
@@ -386,6 +391,10 @@ static void cap_cred_commit(struct cred *new, const struct cred *old)
{
}
+static void cap_cred_transfer(struct cred *new, const struct cred *old)
+{
+}
+
static int cap_kernel_act_as(struct cred *new, u32 secid)
{
return 0;
@@ -396,6 +405,11 @@ static int cap_kernel_create_files_as(struct cred *new, struct inode *inode)
return 0;
}
+static int cap_kernel_module_request(void)
+{
+ return 0;
+}
+
static int cap_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
{
return 0;
@@ -701,10 +715,26 @@ static void cap_inet_conn_established(struct sock *sk, struct sk_buff *skb)
{
}
+
+
static void cap_req_classify_flow(const struct request_sock *req,
struct flowi *fl)
{
}
+
+static int cap_tun_dev_create(void)
+{
+ return 0;
+}
+
+static void cap_tun_dev_post_create(struct sock *sk)
+{
+}
+
+static int cap_tun_dev_attach(struct sock *sk)
+{
+ return 0;
+}
#endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_NETWORK_XFRM
@@ -792,6 +822,20 @@ static void cap_release_secctx(char *secdata, u32 seclen)
{
}
+static int cap_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
+{
+ return 0;
+}
+
+static int cap_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
+{
+ return 0;
+}
+
+static int cap_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
+{
+ return 0;
+}
#ifdef CONFIG_KEYS
static int cap_key_alloc(struct key *key, const struct cred *cred,
unsigned long flags)
@@ -815,6 +859,13 @@ static int cap_key_getsecurity(struct key *key, char **_buffer)
return 0;
}
+static int cap_key_session_to_parent(const struct cred *cred,
+ const struct cred *parent_cred,
+ struct key *key)
+{
+ return 0;
+}
+
#endif /* CONFIG_KEYS */
#ifdef CONFIG_AUDIT
@@ -854,7 +905,7 @@ struct security_operations default_security_ops = {
void security_fixup_ops(struct security_operations *ops)
{
- set_to_cap_if_null(ops, ptrace_may_access);
+ set_to_cap_if_null(ops, ptrace_access_check);
set_to_cap_if_null(ops, ptrace_traceme);
set_to_cap_if_null(ops, capget);
set_to_cap_if_null(ops, capset);
@@ -940,11 +991,14 @@ void security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, file_receive);
set_to_cap_if_null(ops, dentry_open);
set_to_cap_if_null(ops, task_create);
+ set_to_cap_if_null(ops, cred_alloc_blank);
set_to_cap_if_null(ops, cred_free);
set_to_cap_if_null(ops, cred_prepare);
set_to_cap_if_null(ops, cred_commit);
+ set_to_cap_if_null(ops, cred_transfer);
set_to_cap_if_null(ops, kernel_act_as);
set_to_cap_if_null(ops, kernel_create_files_as);
+ set_to_cap_if_null(ops, kernel_module_request);
set_to_cap_if_null(ops, task_setuid);
set_to_cap_if_null(ops, task_fix_setuid);
set_to_cap_if_null(ops, task_setgid);
@@ -992,6 +1046,9 @@ void security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, secid_to_secctx);
set_to_cap_if_null(ops, secctx_to_secid);
set_to_cap_if_null(ops, release_secctx);
+ set_to_cap_if_null(ops, inode_notifysecctx);
+ set_to_cap_if_null(ops, inode_setsecctx);
+ set_to_cap_if_null(ops, inode_getsecctx);
#ifdef CONFIG_SECURITY_NETWORK
set_to_cap_if_null(ops, unix_stream_connect);
set_to_cap_if_null(ops, unix_may_send);
@@ -1020,6 +1077,9 @@ void security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, inet_csk_clone);
set_to_cap_if_null(ops, inet_conn_established);
set_to_cap_if_null(ops, req_classify_flow);
+ set_to_cap_if_null(ops, tun_dev_create);
+ set_to_cap_if_null(ops, tun_dev_post_create);
+ set_to_cap_if_null(ops, tun_dev_attach);
#endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_NETWORK_XFRM
set_to_cap_if_null(ops, xfrm_policy_alloc_security);
@@ -1038,6 +1098,7 @@ void security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, key_free);
set_to_cap_if_null(ops, key_permission);
set_to_cap_if_null(ops, key_getsecurity);
+ set_to_cap_if_null(ops, key_session_to_parent);
#endif /* CONFIG_KEYS */
#ifdef CONFIG_AUDIT
set_to_cap_if_null(ops, audit_rule_init);
diff --git a/security/commoncap.c b/security/commoncap.c
index e3097c0a131..fe30751a6cd 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -101,7 +101,7 @@ int cap_settime(struct timespec *ts, struct timezone *tz)
}
/**
- * cap_ptrace_may_access - Determine whether the current process may access
+ * cap_ptrace_access_check - Determine whether the current process may access
* another
* @child: The process to be accessed
* @mode: The mode of attachment.
@@ -109,7 +109,7 @@ int cap_settime(struct timespec *ts, struct timezone *tz)
* Determine whether a process may access another, returning 0 if permission
* granted, -ve if denied.
*/
-int cap_ptrace_may_access(struct task_struct *child, unsigned int mode)
+int cap_ptrace_access_check(struct task_struct *child, unsigned int mode)
{
int ret = 0;
diff --git a/security/device_cgroup.c b/security/device_cgroup.c
index b8186bac8b7..6cf8fd2b79e 100644
--- a/security/device_cgroup.c
+++ b/security/device_cgroup.c
@@ -61,7 +61,8 @@ static inline struct dev_cgroup *task_devcgroup(struct task_struct *task)
struct cgroup_subsys devices_subsys;
static int devcgroup_can_attach(struct cgroup_subsys *ss,
- struct cgroup *new_cgroup, struct task_struct *task)
+ struct cgroup *new_cgroup, struct task_struct *task,
+ bool threadgroup)
{
if (current != task && !capable(CAP_SYS_ADMIN))
return -EPERM;
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 6bfc7eaebfd..0c72c9c3895 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -43,7 +43,7 @@ static ssize_t ima_show_htable_violations(struct file *filp,
return ima_show_htable_value(buf, count, ppos, &ima_htable.violations);
}
-static struct file_operations ima_htable_violations_ops = {
+static const struct file_operations ima_htable_violations_ops = {
.read = ima_show_htable_violations
};
@@ -55,7 +55,7 @@ static ssize_t ima_show_measurements_count(struct file *filp,
}
-static struct file_operations ima_measurements_count_ops = {
+static const struct file_operations ima_measurements_count_ops = {
.read = ima_show_measurements_count
};
@@ -146,7 +146,7 @@ static int ima_measurements_show(struct seq_file *m, void *v)
return 0;
}
-static struct seq_operations ima_measurments_seqops = {
+static const struct seq_operations ima_measurments_seqops = {
.start = ima_measurements_start,
.next = ima_measurements_next,
.stop = ima_measurements_stop,
@@ -158,7 +158,7 @@ static int ima_measurements_open(struct inode *inode, struct file *file)
return seq_open(file, &ima_measurments_seqops);
}
-static struct file_operations ima_measurements_ops = {
+static const struct file_operations ima_measurements_ops = {
.open = ima_measurements_open,
.read = seq_read,
.llseek = seq_lseek,
@@ -221,7 +221,7 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
return 0;
}
-static struct seq_operations ima_ascii_measurements_seqops = {
+static const struct seq_operations ima_ascii_measurements_seqops = {
.start = ima_measurements_start,
.next = ima_measurements_next,
.stop = ima_measurements_stop,
@@ -233,7 +233,7 @@ static int ima_ascii_measurements_open(struct inode *inode, struct file *file)
return seq_open(file, &ima_ascii_measurements_seqops);
}
-static struct file_operations ima_ascii_measurements_ops = {
+static const struct file_operations ima_ascii_measurements_ops = {
.open = ima_ascii_measurements_open,
.read = seq_read,
.llseek = seq_lseek,
@@ -313,7 +313,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)
return 0;
}
-static struct file_operations ima_measure_policy_ops = {
+static const struct file_operations ima_measure_policy_ops = {
.open = ima_open_policy,
.write = ima_write_policy,
.release = ima_release_policy
diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c
index b8dd693f879..a4e2b1dac94 100644
--- a/security/integrity/ima/ima_iint.c
+++ b/security/integrity/ima/ima_iint.c
@@ -58,11 +58,11 @@ struct ima_iint_cache *ima_iint_insert(struct inode *inode)
if (!ima_initialized)
return iint;
- iint = kmem_cache_alloc(iint_cache, GFP_KERNEL);
+ iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
if (!iint)
return iint;
- rc = radix_tree_preload(GFP_KERNEL);
+ rc = radix_tree_preload(GFP_NOFS);
if (rc < 0)
goto out;
diff --git a/security/keys/Makefile b/security/keys/Makefile
index 747a464943a..74d5447d7df 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -3,6 +3,7 @@
#
obj-y := \
+ gc.o \
key.o \
keyring.o \
keyctl.o \
diff --git a/security/keys/compat.c b/security/keys/compat.c
index c766c68a63b..792c0a611a6 100644
--- a/security/keys/compat.c
+++ b/security/keys/compat.c
@@ -82,6 +82,9 @@ asmlinkage long compat_sys_keyctl(u32 option,
case KEYCTL_GET_SECURITY:
return keyctl_get_security(arg2, compat_ptr(arg3), arg4);
+ case KEYCTL_SESSION_TO_PARENT:
+ return keyctl_session_to_parent();
+
default:
return -EOPNOTSUPP;
}
diff --git a/security/keys/gc.c b/security/keys/gc.c
new file mode 100644
index 00000000000..4770be375ff
--- /dev/null
+++ b/security/keys/gc.c
@@ -0,0 +1,218 @@
+/* Key garbage collector
+ *
+ * Copyright (C) 2009 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <keys/keyring-type.h>
+#include "internal.h"
+
+/*
+ * Delay between key revocation/expiry in seconds
+ */
+unsigned key_gc_delay = 5 * 60;
+
+/*
+ * Reaper
+ */
+static void key_gc_timer_func(unsigned long);
+static void key_garbage_collector(struct work_struct *);
+static DEFINE_TIMER(key_gc_timer, key_gc_timer_func, 0, 0);
+static DECLARE_WORK(key_gc_work, key_garbage_collector);
+static key_serial_t key_gc_cursor; /* the last key the gc considered */
+static bool key_gc_again;
+static unsigned long key_gc_executing;
+static time_t key_gc_next_run = LONG_MAX;
+static time_t key_gc_new_timer;
+
+/*
+ * Schedule a garbage collection run
+ * - precision isn't particularly important
+ */
+void key_schedule_gc(time_t gc_at)
+{
+ unsigned long expires;
+ time_t now = current_kernel_time().tv_sec;
+
+ kenter("%ld", gc_at - now);
+
+ if (gc_at <= now) {
+ schedule_work(&key_gc_work);
+ } else if (gc_at < key_gc_next_run) {
+ expires = jiffies + (gc_at - now) * HZ;
+ mod_timer(&key_gc_timer, expires);
+ }
+}
+
+/*
+ * The garbage collector timer kicked off
+ */
+static void key_gc_timer_func(unsigned long data)
+{
+ kenter("");
+ key_gc_next_run = LONG_MAX;
+ schedule_work(&key_gc_work);
+}
+
+/*
+ * Garbage collect pointers from a keyring
+ * - return true if we altered the keyring
+ */
+static bool key_gc_keyring(struct key *keyring, time_t limit)
+ __releases(key_serial_lock)
+{
+ struct keyring_list *klist;
+ struct key *key;
+ int loop;
+
+ kenter("%x", key_serial(keyring));
+
+ if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
+ goto dont_gc;
+
+ /* scan the keyring looking for dead keys */
+ klist = rcu_dereference(keyring->payload.subscriptions);
+ if (!klist)
+ goto dont_gc;
+
+ for (loop = klist->nkeys - 1; loop >= 0; loop--) {
+ key = klist->keys[loop];
+ if (test_bit(KEY_FLAG_DEAD, &key->flags) ||
+ (key->expiry > 0 && key->expiry <= limit))
+ goto do_gc;
+ }
+
+dont_gc:
+ kleave(" = false");
+ return false;
+
+do_gc:
+ key_gc_cursor = keyring->serial;
+ key_get(keyring);
+ spin_unlock(&key_serial_lock);
+ keyring_gc(keyring, limit);
+ key_put(keyring);
+ kleave(" = true");
+ return true;
+}
+
+/*
+ * Garbage collector for keys
+ * - this involves scanning the keyrings for dead, expired and revoked keys
+ * that have overstayed their welcome
+ */
+static void key_garbage_collector(struct work_struct *work)
+{
+ struct rb_node *rb;
+ key_serial_t cursor;
+ struct key *key, *xkey;
+ time_t new_timer = LONG_MAX, limit, now;
+
+ now = current_kernel_time().tv_sec;
+ kenter("[%x,%ld]", key_gc_cursor, key_gc_new_timer - now);
+
+ if (test_and_set_bit(0, &key_gc_executing)) {
+ key_schedule_gc(current_kernel_time().tv_sec + 1);
+ kleave(" [busy; deferring]");
+ return;
+ }
+
+ limit = now;
+ if (limit > key_gc_delay)
+ limit -= key_gc_delay;
+ else
+ limit = key_gc_delay;
+
+ spin_lock(&key_serial_lock);
+
+ if (unlikely(RB_EMPTY_ROOT(&key_serial_tree))) {
+ spin_unlock(&key_serial_lock);
+ clear_bit(0, &key_gc_executing);
+ return;
+ }
+
+ cursor = key_gc_cursor;
+ if (cursor < 0)
+ cursor = 0;
+ if (cursor > 0)
+ new_timer = key_gc_new_timer;
+ else
+ key_gc_again = false;
+
+ /* find the first key above the cursor */
+ key = NULL;
+ rb = key_serial_tree.rb_node;
+ while (rb) {
+ xkey = rb_entry(rb, struct key, serial_node);
+ if (cursor < xkey->serial) {
+ key = xkey;
+ rb = rb->rb_left;
+ } else if (cursor > xkey->serial) {
+ rb = rb->rb_right;
+ } else {
+ rb = rb_next(rb);
+ if (!rb)
+ goto reached_the_end;
+ key = rb_entry(rb, struct key, serial_node);
+ break;
+ }
+ }
+
+ if (!key)
+ goto reached_the_end;
+
+ /* trawl through the keys looking for keyrings */
+ for (;;) {
+ if (key->expiry > limit && key->expiry < new_timer) {
+ kdebug("will expire %x in %ld",
+ key_serial(key), key->expiry - limit);
+ new_timer = key->expiry;
+ }
+
+ if (key->type == &key_type_keyring &&
+ key_gc_keyring(key, limit))
+ /* the gc had to release our lock so that the keyring
+ * could be modified, so we have to get it again */
+ goto gc_released_our_lock;
+
+ rb = rb_next(&key->serial_node);
+ if (!rb)
+ goto reached_the_end;
+ key = rb_entry(rb, struct key, serial_node);
+ }
+
+gc_released_our_lock:
+ kdebug("gc_released_our_lock");
+ key_gc_new_timer = new_timer;
+ key_gc_again = true;
+ clear_bit(0, &key_gc_executing);
+ schedule_work(&key_gc_work);
+ kleave(" [continue]");
+ return;
+
+ /* when we reach the end of the run, we set the timer for the next one */
+reached_the_end:
+ kdebug("reached_the_end");
+ spin_unlock(&key_serial_lock);
+ key_gc_new_timer = new_timer;
+ key_gc_cursor = 0;
+ clear_bit(0, &key_gc_executing);
+
+ if (key_gc_again) {
+ /* there may have been a key that expired whilst we were
+ * scanning, so if we discarded any links we should do another
+ * scan */
+ new_timer = now + 1;
+ key_schedule_gc(new_timer);
+ } else if (new_timer < LONG_MAX) {
+ new_timer += key_gc_delay;
+ key_schedule_gc(new_timer);
+ }
+ kleave(" [end]");
+}
diff --git a/security/keys/internal.h b/security/keys/internal.h
index 9fb679c66b8..24ba0307b7a 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -124,11 +124,18 @@ extern struct key *request_key_and_link(struct key_type *type,
struct key *dest_keyring,
unsigned long flags);
-extern key_ref_t lookup_user_key(key_serial_t id, int create, int partial,
+extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags,
key_perm_t perm);
+#define KEY_LOOKUP_CREATE 0x01
+#define KEY_LOOKUP_PARTIAL 0x02
+#define KEY_LOOKUP_FOR_UNLINK 0x04
extern long join_session_keyring(const char *name);
+extern unsigned key_gc_delay;
+extern void keyring_gc(struct key *keyring, time_t limit);
+extern void key_schedule_gc(time_t expiry_at);
+
/*
* check to see whether permission is granted to use a key in the desired way
*/
@@ -194,6 +201,7 @@ extern long keyctl_set_timeout(key_serial_t, unsigned);
extern long keyctl_assume_authority(key_serial_t);
extern long keyctl_get_security(key_serial_t keyid, char __user *buffer,
size_t buflen);
+extern long keyctl_session_to_parent(void);
/*
* debugging key validation
diff --git a/security/keys/key.c b/security/keys/key.c
index 4a1297d1ada..e50d264c9ad 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -500,6 +500,7 @@ int key_negate_and_link(struct key *key,
set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
now = current_kernel_time();
key->expiry = now.tv_sec + timeout;
+ key_schedule_gc(key->expiry + key_gc_delay);
if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags))
awaken = 1;
@@ -642,10 +643,8 @@ struct key *key_lookup(key_serial_t id)
goto error;
found:
- /* pretend it doesn't exist if it's dead */
- if (atomic_read(&key->usage) == 0 ||
- test_bit(KEY_FLAG_DEAD, &key->flags) ||
- key->type == &key_type_dead)
+ /* pretend it doesn't exist if it is awaiting deletion */
+ if (atomic_read(&key->usage) == 0)
goto not_found;
/* this races with key_put(), but that doesn't matter since key_put()
@@ -890,6 +889,9 @@ EXPORT_SYMBOL(key_update);
*/
void key_revoke(struct key *key)
{
+ struct timespec now;
+ time_t time;
+
key_check(key);
/* make sure no one's trying to change or use the key when we mark it
@@ -902,6 +904,14 @@ void key_revoke(struct key *key)
key->type->revoke)
key->type->revoke(key);
+ /* set the death time to no more than the expiry time */
+ now = current_kernel_time();
+ time = now.tv_sec;
+ if (key->revoked_at == 0 || key->revoked_at > time) {
+ key->revoked_at = time;
+ key_schedule_gc(key->revoked_at + key_gc_delay);
+ }
+
up_write(&key->sem);
} /* end key_revoke() */
@@ -958,8 +968,10 @@ void unregister_key_type(struct key_type *ktype)
for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) {
key = rb_entry(_n, struct key, serial_node);
- if (key->type == ktype)
+ if (key->type == ktype) {
key->type = &key_type_dead;
+ set_bit(KEY_FLAG_DEAD, &key->flags);
+ }
}
spin_unlock(&key_serial_lock);
@@ -984,6 +996,8 @@ void unregister_key_type(struct key_type *ktype)
spin_unlock(&key_serial_lock);
up_write(&key_types_sem);
+ key_schedule_gc(0);
+
} /* end unregister_key_type() */
EXPORT_SYMBOL(unregister_key_type);
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 7f09fb897d2..06ec722897b 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -103,7 +103,7 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type,
}
/* find the target keyring (which must be writable) */
- keyring_ref = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+ keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE);
if (IS_ERR(keyring_ref)) {
ret = PTR_ERR(keyring_ref);
goto error3;
@@ -185,7 +185,8 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type,
/* get the destination keyring if specified */
dest_ref = NULL;
if (destringid) {
- dest_ref = lookup_user_key(destringid, 1, 0, KEY_WRITE);
+ dest_ref = lookup_user_key(destringid, KEY_LOOKUP_CREATE,
+ KEY_WRITE);
if (IS_ERR(dest_ref)) {
ret = PTR_ERR(dest_ref);
goto error3;
@@ -233,9 +234,11 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type,
long keyctl_get_keyring_ID(key_serial_t id, int create)
{
key_ref_t key_ref;
+ unsigned long lflags;
long ret;
- key_ref = lookup_user_key(id, create, 0, KEY_SEARCH);
+ lflags = create ? KEY_LOOKUP_CREATE : 0;
+ key_ref = lookup_user_key(id, lflags, KEY_SEARCH);
if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref);
goto error;
@@ -309,7 +312,7 @@ long keyctl_update_key(key_serial_t id,
}
/* find the target key (which must be writable) */
- key_ref = lookup_user_key(id, 0, 0, KEY_WRITE);
+ key_ref = lookup_user_key(id, 0, KEY_WRITE);
if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref);
goto error2;
@@ -337,10 +340,16 @@ long keyctl_revoke_key(key_serial_t id)
key_ref_t key_ref;
long ret;
- key_ref = lookup_user_key(id, 0, 0, KEY_WRITE);
+ key_ref = lookup_user_key(id, 0, KEY_WRITE);
if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref);
- goto error;
+ if (ret != -EACCES)
+ goto error;
+ key_ref = lookup_user_key(id, 0, KEY_SETATTR);
+ if (IS_ERR(key_ref)) {
+ ret = PTR_ERR(key_ref);
+ goto error;
+ }
}
key_revoke(key_ref_to_ptr(key_ref));
@@ -363,7 +372,7 @@ long keyctl_keyring_clear(key_serial_t ringid)
key_ref_t keyring_ref;
long ret;
- keyring_ref = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+ keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE);
if (IS_ERR(keyring_ref)) {
ret = PTR_ERR(keyring_ref);
goto error;
@@ -389,13 +398,13 @@ long keyctl_keyring_link(key_serial_t id, key_serial_t ringid)
key_ref_t keyring_ref, key_ref;
long ret;
- keyring_ref = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+ keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE);
if (IS_ERR(keyring_ref)) {
ret = PTR_ERR(keyring_ref);
goto error;
}
- key_ref = lookup_user_key(id, 1, 0, KEY_LINK);
+ key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE, KEY_LINK);
if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref);
goto error2;
@@ -423,13 +432,13 @@ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid)
key_ref_t keyring_ref, key_ref;
long ret;
- keyring_ref = lookup_user_key(ringid, 0, 0, KEY_WRITE);
+ keyring_ref = lookup_user_key(ringid, 0, KEY_WRITE);
if (IS_ERR(keyring_ref)) {
ret = PTR_ERR(keyring_ref);
goto error;
}
- key_ref = lookup_user_key(id, 0, 0, 0);
+ key_ref = lookup_user_key(id, KEY_LOOKUP_FOR_UNLINK, 0);
if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref);
goto error2;
@@ -465,7 +474,7 @@ long keyctl_describe_key(key_serial_t keyid,
char *tmpbuf;
long ret;
- key_ref = lookup_user_key(keyid, 0, 1, KEY_VIEW);
+ key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_VIEW);
if (IS_ERR(key_ref)) {
/* viewing a key under construction is permitted if we have the
* authorisation token handy */
@@ -474,7 +483,8 @@ long keyctl_describe_key(key_serial_t keyid,
if (!IS_ERR(instkey)) {
key_put(instkey);
key_ref = lookup_user_key(keyid,
- 0, 1, 0);
+ KEY_LOOKUP_PARTIAL,
+ 0);
if (!IS_ERR(key_ref))
goto okay;
}
@@ -558,7 +568,7 @@ long keyctl_keyring_search(key_serial_t ringid,
}
/* get the keyring at which to begin the search */
- keyring_ref = lookup_user_key(ringid, 0, 0, KEY_SEARCH);
+ keyring_ref = lookup_user_key(ringid, 0, KEY_SEARCH);
if (IS_ERR(keyring_ref)) {
ret = PTR_ERR(keyring_ref);
goto error2;
@@ -567,7 +577,8 @@ long keyctl_keyring_search(key_serial_t ringid,
/* get the destination keyring if specified */
dest_ref = NULL;
if (destringid) {
- dest_ref = lookup_user_key(destringid, 1, 0, KEY_WRITE);
+ dest_ref = lookup_user_key(destringid, KEY_LOOKUP_CREATE,
+ KEY_WRITE);
if (IS_ERR(dest_ref)) {
ret = PTR_ERR(dest_ref);
goto error3;
@@ -637,7 +648,7 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
long ret;
/* find the key first */
- key_ref = lookup_user_key(keyid, 0, 0, 0);
+ key_ref = lookup_user_key(keyid, 0, 0);
if (IS_ERR(key_ref)) {
ret = -ENOKEY;
goto error;
@@ -700,7 +711,8 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
if (uid == (uid_t) -1 && gid == (gid_t) -1)
goto error;
- key_ref = lookup_user_key(id, 1, 1, KEY_SETATTR);
+ key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL,
+ KEY_SETATTR);
if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref);
goto error;
@@ -805,7 +817,8 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm)
if (perm & ~(KEY_POS_ALL | KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL))
goto error;
- key_ref = lookup_user_key(id, 1, 1, KEY_SETATTR);
+ key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL,
+ KEY_SETATTR);
if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref);
goto error;
@@ -847,7 +860,7 @@ static long get_instantiation_keyring(key_serial_t ringid,
/* if a specific keyring is nominated by ID, then use that */
if (ringid > 0) {
- dkref = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+ dkref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE);
if (IS_ERR(dkref))
return PTR_ERR(dkref);
*_dest_keyring = key_ref_to_ptr(dkref);
@@ -860,7 +873,7 @@ static long get_instantiation_keyring(key_serial_t ringid,
/* otherwise specify the destination keyring recorded in the
* authorisation key (any KEY_SPEC_*_KEYRING) */
if (ringid >= KEY_SPEC_REQUESTOR_KEYRING) {
- *_dest_keyring = rka->dest_keyring;
+ *_dest_keyring = key_get(rka->dest_keyring);
return 0;
}
@@ -1083,7 +1096,8 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout)
time_t expiry;
long ret;
- key_ref = lookup_user_key(id, 1, 1, KEY_SETATTR);
+ key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL,
+ KEY_SETATTR);
if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref);
goto error;
@@ -1101,6 +1115,7 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout)
}
key->expiry = expiry;
+ key_schedule_gc(key->expiry + key_gc_delay);
up_write(&key->sem);
key_put(key);
@@ -1170,7 +1185,7 @@ long keyctl_get_security(key_serial_t keyid,
char *context;
long ret;
- key_ref = lookup_user_key(keyid, 0, 1, KEY_VIEW);
+ key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_VIEW);
if (IS_ERR(key_ref)) {
if (PTR_ERR(key_ref) != -EACCES)
return PTR_ERR(key_ref);
@@ -1182,7 +1197,7 @@ long keyctl_get_security(key_serial_t keyid,
return PTR_ERR(key_ref);
key_put(instkey);
- key_ref = lookup_user_key(keyid, 0, 1, 0);
+ key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, 0);
if (IS_ERR(key_ref))
return PTR_ERR(key_ref);
}
@@ -1213,6 +1228,106 @@ long keyctl_get_security(key_serial_t keyid,
return ret;
}
+/*
+ * attempt to install the calling process's session keyring on the process's
+ * parent process
+ * - the keyring must exist and must grant us LINK permission
+ * - implements keyctl(KEYCTL_SESSION_TO_PARENT)
+ */
+long keyctl_session_to_parent(void)
+{
+ struct task_struct *me, *parent;
+ const struct cred *mycred, *pcred;
+ struct cred *cred, *oldcred;
+ key_ref_t keyring_r;
+ int ret;
+
+ keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0, KEY_LINK);
+ if (IS_ERR(keyring_r))
+ return PTR_ERR(keyring_r);
+
+ /* our parent is going to need a new cred struct, a new tgcred struct
+ * and new security data, so we allocate them here to prevent ENOMEM in
+ * our parent */
+ ret = -ENOMEM;
+ cred = cred_alloc_blank();
+ if (!cred)
+ goto error_keyring;
+
+ cred->tgcred->session_keyring = key_ref_to_ptr(keyring_r);
+ keyring_r = NULL;
+
+ me = current;
+ write_lock_irq(&tasklist_lock);
+
+ parent = me->real_parent;
+ ret = -EPERM;
+
+ /* the parent mustn't be init and mustn't be a kernel thread */
+ if (parent->pid <= 1 || !parent->mm)
+ goto not_permitted;
+
+ /* the parent must be single threaded */
+ if (atomic_read(&parent->signal->count) != 1)
+ goto not_permitted;
+
+ /* the parent and the child must have different session keyrings or
+ * there's no point */
+ mycred = current_cred();
+ pcred = __task_cred(parent);
+ if (mycred == pcred ||
+ mycred->tgcred->session_keyring == pcred->tgcred->session_keyring)
+ goto already_same;
+
+ /* the parent must have the same effective ownership and mustn't be
+ * SUID/SGID */
+ if (pcred-> uid != mycred->euid ||
+ pcred->euid != mycred->euid ||
+ pcred->suid != mycred->euid ||
+ pcred-> gid != mycred->egid ||
+ pcred->egid != mycred->egid ||
+ pcred->sgid != mycred->egid)
+ goto not_permitted;
+
+ /* the keyrings must have the same UID */
+ if (pcred ->tgcred->session_keyring->uid != mycred->euid ||
+ mycred->tgcred->session_keyring->uid != mycred->euid)
+ goto not_permitted;
+
+ /* the LSM must permit the replacement of the parent's keyring with the
+ * keyring from this process */
+ ret = security_key_session_to_parent(mycred, pcred,
+ key_ref_to_ptr(keyring_r));
+ if (ret < 0)
+ goto not_permitted;
+
+ /* if there's an already pending keyring replacement, then we replace
+ * that */
+ oldcred = parent->replacement_session_keyring;
+
+ /* the replacement session keyring is applied just prior to userspace
+ * restarting */
+ parent->replacement_session_keyring = cred;
+ cred = NULL;
+ set_ti_thread_flag(task_thread_info(parent), TIF_NOTIFY_RESUME);
+
+ write_unlock_irq(&tasklist_lock);
+ if (oldcred)
+ put_cred(oldcred);
+ return 0;
+
+already_same:
+ ret = 0;
+not_permitted:
+ write_unlock_irq(&tasklist_lock);
+ put_cred(cred);
+ return ret;
+
+error_keyring:
+ key_ref_put(keyring_r);
+ return ret;
+}
+
/*****************************************************************************/
/*
* the key control system call
@@ -1298,6 +1413,9 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
(char __user *) arg3,
(size_t) arg4);
+ case KEYCTL_SESSION_TO_PARENT:
+ return keyctl_session_to_parent();
+
default:
return -EOPNOTSUPP;
}
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index 3dba81c2eba..8ec02746ca9 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -1000,3 +1000,102 @@ static void keyring_revoke(struct key *keyring)
}
} /* end keyring_revoke() */
+
+/*
+ * Determine whether a key is dead
+ */
+static bool key_is_dead(struct key *key, time_t limit)
+{
+ return test_bit(KEY_FLAG_DEAD, &key->flags) ||
+ (key->expiry > 0 && key->expiry <= limit);
+}
+
+/*
+ * Collect garbage from the contents of a keyring
+ */
+void keyring_gc(struct key *keyring, time_t limit)
+{
+ struct keyring_list *klist, *new;
+ struct key *key;
+ int loop, keep, max;
+
+ kenter("{%x,%s}", key_serial(keyring), keyring->description);
+
+ down_write(&keyring->sem);
+
+ klist = keyring->payload.subscriptions;
+ if (!klist)
+ goto no_klist;
+
+ /* work out how many subscriptions we're keeping */
+ keep = 0;
+ for (loop = klist->nkeys - 1; loop >= 0; loop--)
+ if (!key_is_dead(klist->keys[loop], limit))
+ keep++;
+
+ if (keep == klist->nkeys)
+ goto just_return;
+
+ /* allocate a new keyring payload */
+ max = roundup(keep, 4);
+ new = kmalloc(sizeof(struct keyring_list) + max * sizeof(struct key *),
+ GFP_KERNEL);
+ if (!new)
+ goto nomem;
+ new->maxkeys = max;
+ new->nkeys = 0;
+ new->delkey = 0;
+
+ /* install the live keys
+ * - must take care as expired keys may be updated back to life
+ */
+ keep = 0;
+ for (loop = klist->nkeys - 1; loop >= 0; loop--) {
+ key = klist->keys[loop];
+ if (!key_is_dead(key, limit)) {
+ if (keep >= max)
+ goto discard_new;
+ new->keys[keep++] = key_get(key);
+ }
+ }
+ new->nkeys = keep;
+
+ /* adjust the quota */
+ key_payload_reserve(keyring,
+ sizeof(struct keyring_list) +
+ KEYQUOTA_LINK_BYTES * keep);
+
+ if (keep == 0) {
+ rcu_assign_pointer(keyring->payload.subscriptions, NULL);
+ kfree(new);
+ } else {
+ rcu_assign_pointer(keyring->payload.subscriptions, new);
+ }
+
+ up_write(&keyring->sem);
+
+ call_rcu(&klist->rcu, keyring_clear_rcu_disposal);
+ kleave(" [yes]");
+ return;
+
+discard_new:
+ new->nkeys = keep;
+ keyring_clear_rcu_disposal(&new->rcu);
+ up_write(&keyring->sem);
+ kleave(" [discard]");
+ return;
+
+just_return:
+ up_write(&keyring->sem);
+ kleave(" [no dead]");
+ return;
+
+no_klist:
+ up_write(&keyring->sem);
+ kleave(" [no_klist]");
+ return;
+
+nomem:
+ up_write(&keyring->sem);
+ kleave(" [oom]");
+}
diff --git a/security/keys/proc.c b/security/keys/proc.c
index 769f9bdfd2b..9d01021ca0c 100644
--- a/security/keys/proc.c
+++ b/security/keys/proc.c
@@ -91,59 +91,94 @@ __initcall(key_proc_init);
*/
#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS
-static struct rb_node *__key_serial_next(struct rb_node *n)
+static struct rb_node *key_serial_next(struct rb_node *n)
{
+ struct user_namespace *user_ns = current_user_ns();
+
+ n = rb_next(n);
while (n) {
struct key *key = rb_entry(n, struct key, serial_node);
- if (key->user->user_ns == current_user_ns())
+ if (key->user->user_ns == user_ns)
break;
n = rb_next(n);
}
return n;
}
-static struct rb_node *key_serial_next(struct rb_node *n)
+static int proc_keys_open(struct inode *inode, struct file *file)
{
- return __key_serial_next(rb_next(n));
+ return seq_open(file, &proc_keys_ops);
}
-static struct rb_node *key_serial_first(struct rb_root *r)
+static struct key *find_ge_key(key_serial_t id)
{
- struct rb_node *n = rb_first(r);
- return __key_serial_next(n);
-}
+ struct user_namespace *user_ns = current_user_ns();
+ struct rb_node *n = key_serial_tree.rb_node;
+ struct key *minkey = NULL;
-static int proc_keys_open(struct inode *inode, struct file *file)
-{
- return seq_open(file, &proc_keys_ops);
+ while (n) {
+ struct key *key = rb_entry(n, struct key, serial_node);
+ if (id < key->serial) {
+ if (!minkey || minkey->serial > key->serial)
+ minkey = key;
+ n = n->rb_left;
+ } else if (id > key->serial) {
+ n = n->rb_right;
+ } else {
+ minkey = key;
+ break;
+ }
+ key = NULL;
+ }
+ if (!minkey)
+ return NULL;
+
+ for (;;) {
+ if (minkey->user->user_ns == user_ns)
+ return minkey;
+ n = rb_next(&minkey->serial_node);
+ if (!n)
+ return NULL;
+ minkey = rb_entry(n, struct key, serial_node);
+ }
}
static void *proc_keys_start(struct seq_file *p, loff_t *_pos)
+ __acquires(key_serial_lock)
{
- struct rb_node *_p;
- loff_t pos = *_pos;
+ key_serial_t pos = *_pos;
+ struct key *key;
spin_lock(&key_serial_lock);
- _p = key_serial_first(&key_serial_tree);
- while (pos > 0 && _p) {
- pos--;
- _p = key_serial_next(_p);
- }
-
- return _p;
+ if (*_pos > INT_MAX)
+ return NULL;
+ key = find_ge_key(pos);
+ if (!key)
+ return NULL;
+ *_pos = key->serial;
+ return &key->serial_node;
+}
+static inline key_serial_t key_node_serial(struct rb_node *n)
+{
+ struct key *key = rb_entry(n, struct key, serial_node);
+ return key->serial;
}
static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos)
{
- (*_pos)++;
- return key_serial_next((struct rb_node *) v);
+ struct rb_node *n;
+ n = key_serial_next(v);
+ if (n)
+ *_pos = key_node_serial(n);
+ return n;
}
static void proc_keys_stop(struct seq_file *p, void *v)
+ __releases(key_serial_lock)
{
spin_unlock(&key_serial_lock);
}
@@ -174,11 +209,9 @@ static int proc_keys_show(struct seq_file *m, void *v)
/* come up with a suitable timeout value */
if (key->expiry == 0) {
memcpy(xbuf, "perm", 5);
- }
- else if (now.tv_sec >= key->expiry) {
+ } else if (now.tv_sec >= key->expiry) {
memcpy(xbuf, "expd", 5);
- }
- else {
+ } else {
timo = key->expiry - now.tv_sec;
if (timo < 60)
@@ -218,9 +251,7 @@ static int proc_keys_show(struct seq_file *m, void *v)
seq_putc(m, '\n');
rcu_read_unlock();
-
return 0;
-
}
#endif /* CONFIG_KEYS_DEBUG_PROC_KEYS */
@@ -246,6 +277,7 @@ static struct rb_node *key_user_first(struct rb_root *r)
struct rb_node *n = rb_first(r);
return __key_user_next(n);
}
+
/*****************************************************************************/
/*
* implement "/proc/key-users" to provides a list of the key users
@@ -253,10 +285,10 @@ static struct rb_node *key_user_first(struct rb_root *r)
static int proc_key_users_open(struct inode *inode, struct file *file)
{
return seq_open(file, &proc_key_users_ops);
-
}
static void *proc_key_users_start(struct seq_file *p, loff_t *_pos)
+ __acquires(key_user_lock)
{
struct rb_node *_p;
loff_t pos = *_pos;
@@ -270,17 +302,16 @@ static void *proc_key_users_start(struct seq_file *p, loff_t *_pos)
}
return _p;
-
}
static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos)
{
(*_pos)++;
return key_user_next((struct rb_node *) v);
-
}
static void proc_key_users_stop(struct seq_file *p, void *v)
+ __releases(key_user_lock)
{
spin_unlock(&key_user_lock);
}
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index 276d27882ce..5c23afb31ec 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -17,6 +17,7 @@
#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 "internal.h"
@@ -487,7 +488,7 @@ static int lookup_user_key_possessed(const struct key *key, const void *target)
* - don't create special keyrings unless so requested
* - partially constructed keys aren't found unless requested
*/
-key_ref_t lookup_user_key(key_serial_t id, int create, int partial,
+key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
key_perm_t perm)
{
struct request_key_auth *rka;
@@ -503,7 +504,7 @@ try_again:
switch (id) {
case KEY_SPEC_THREAD_KEYRING:
if (!cred->thread_keyring) {
- if (!create)
+ if (!(lflags & KEY_LOOKUP_CREATE))
goto error;
ret = install_thread_keyring();
@@ -521,7 +522,7 @@ try_again:
case KEY_SPEC_PROCESS_KEYRING:
if (!cred->tgcred->process_keyring) {
- if (!create)
+ if (!(lflags & KEY_LOOKUP_CREATE))
goto error;
ret = install_process_keyring();
@@ -642,7 +643,14 @@ try_again:
break;
}
- if (!partial) {
+ /* unlink does not use the nominated key in any way, so can skip all
+ * the permission checks as it is only concerned with the keyring */
+ if (lflags & KEY_LOOKUP_FOR_UNLINK) {
+ ret = 0;
+ goto error;
+ }
+
+ if (!(lflags & KEY_LOOKUP_PARTIAL)) {
ret = wait_for_key_construction(key, true);
switch (ret) {
case -ERESTARTSYS:
@@ -660,7 +668,8 @@ try_again:
}
ret = -EIO;
- if (!partial && !test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
+ if (!(lflags & KEY_LOOKUP_PARTIAL) &&
+ !test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
goto invalid_key;
/* check the permissions */
@@ -702,7 +711,7 @@ long join_session_keyring(const char *name)
/* only permit this if there's a single thread in the thread group -
* this avoids us having to adjust the creds on all threads and risking
* ENOMEM */
- if (!is_single_threaded(current))
+ if (!current_is_single_threaded())
return -EMLINK;
new = prepare_creds();
@@ -760,3 +769,51 @@ error:
abort_creds(new);
return ret;
}
+
+/*
+ * Replace a process's session keyring when that process resumes userspace on
+ * behalf of one of its children
+ */
+void key_replace_session_keyring(void)
+{
+ const struct cred *old;
+ struct cred *new;
+
+ if (!current->replacement_session_keyring)
+ return;
+
+ write_lock_irq(&tasklist_lock);
+ new = current->replacement_session_keyring;
+ current->replacement_session_keyring = NULL;
+ write_unlock_irq(&tasklist_lock);
+
+ if (!new)
+ return;
+
+ old = current_cred();
+ new-> uid = old-> uid;
+ new-> euid = old-> euid;
+ new-> suid = old-> suid;
+ new->fsuid = old->fsuid;
+ new-> gid = old-> gid;
+ new-> egid = old-> egid;
+ new-> sgid = old-> sgid;
+ new->fsgid = old->fsgid;
+ new->user = get_uid(old->user);
+ new->group_info = get_group_info(old->group_info);
+
+ new->securebits = old->securebits;
+ new->cap_inheritable = old->cap_inheritable;
+ new->cap_permitted = old->cap_permitted;
+ new->cap_effective = old->cap_effective;
+ new->cap_bset = old->cap_bset;
+
+ new->jit_keyring = old->jit_keyring;
+ new->thread_keyring = key_get(old->thread_keyring);
+ new->tgcred->tgid = old->tgcred->tgid;
+ new->tgcred->process_keyring = key_get(old->tgcred->process_keyring);
+
+ security_transfer_creds(new, old);
+
+ commit_creds(new);
+}
diff --git a/security/keys/sysctl.c b/security/keys/sysctl.c
index b611d493c2d..5e05dc09e2d 100644
--- a/security/keys/sysctl.c
+++ b/security/keys/sysctl.c
@@ -13,6 +13,8 @@
#include <linux/sysctl.h>
#include "internal.h"
+static const int zero, one = 1, max = INT_MAX;
+
ctl_table key_sysctls[] = {
{
.ctl_name = CTL_UNNUMBERED,
@@ -20,7 +22,9 @@ ctl_table key_sysctls[] = {
.data = &key_quota_maxkeys,
.maxlen = sizeof(unsigned),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = &proc_dointvec_minmax,
+ .extra1 = (void *) &one,
+ .extra2 = (void *) &max,
},
{
.ctl_name = CTL_UNNUMBERED,
@@ -28,7 +32,9 @@ ctl_table key_sysctls[] = {
.data = &key_quota_maxbytes,
.maxlen = sizeof(unsigned),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = &proc_dointvec_minmax,
+ .extra1 = (void *) &one,
+ .extra2 = (void *) &max,
},
{
.ctl_name = CTL_UNNUMBERED,
@@ -36,7 +42,9 @@ ctl_table key_sysctls[] = {
.data = &key_quota_root_maxkeys,
.maxlen = sizeof(unsigned),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = &proc_dointvec_minmax,
+ .extra1 = (void *) &one,
+ .extra2 = (void *) &max,
},
{
.ctl_name = CTL_UNNUMBERED,
@@ -44,7 +52,19 @@ ctl_table key_sysctls[] = {
.data = &key_quota_root_maxbytes,
.maxlen = sizeof(unsigned),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = &proc_dointvec_minmax,
+ .extra1 = (void *) &one,
+ .extra2 = (void *) &max,
+ },
+ {
+ .ctl_name = CTL_UNNUMBERED,
+ .procname = "gc_delay",
+ .data = &key_gc_delay,
+ .maxlen = sizeof(unsigned),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec_minmax,
+ .extra1 = (void *) &zero,
+ .extra2 = (void *) &max,
},
{ .ctl_name = 0 }
};
diff --git a/security/lsm_audit.c b/security/lsm_audit.c
index 94b868494b3..3bb90b6f1dd 100644
--- a/security/lsm_audit.c
+++ b/security/lsm_audit.c
@@ -187,7 +187,7 @@ static inline void print_ipv6_addr(struct audit_buffer *ab,
char *name1, char *name2)
{
if (!ipv6_addr_any(addr))
- audit_log_format(ab, " %s=%pI6", name1, addr);
+ audit_log_format(ab, " %s=%pI6c", name1, addr);
if (port)
audit_log_format(ab, " %s=%d", name2, ntohs(port));
}
@@ -220,6 +220,8 @@ static void dump_common_audit_data(struct audit_buffer *ab,
}
switch (a->type) {
+ case LSM_AUDIT_NO_AUDIT:
+ return;
case LSM_AUDIT_DATA_IPC:
audit_log_format(ab, " key=%d ", a->u.ipc_id);
break;
diff --git a/security/min_addr.c b/security/min_addr.c
index 14cc7b3b8d0..c844eed7915 100644
--- a/security/min_addr.c
+++ b/security/min_addr.c
@@ -28,12 +28,12 @@ static void update_mmap_min_addr(void)
* sysctl handler which just sets dac_mmap_min_addr = the new value and then
* calls update_mmap_min_addr() so non MAP_FIXED hints get rounded properly
*/
-int mmap_min_addr_handler(struct ctl_table *table, int write, struct file *filp,
+int mmap_min_addr_handler(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
int ret;
- ret = proc_doulongvec_minmax(table, write, filp, buffer, lenp, ppos);
+ ret = proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
update_mmap_min_addr();
diff --git a/security/security.c b/security/security.c
index dc7674fbfc7..c4c673240c1 100644
--- a/security/security.c
+++ b/security/security.c
@@ -124,9 +124,9 @@ int register_security(struct security_operations *ops)
/* Security operations */
-int security_ptrace_may_access(struct task_struct *child, unsigned int mode)
+int security_ptrace_access_check(struct task_struct *child, unsigned int mode)
{
- return security_ops->ptrace_may_access(child, mode);
+ return security_ops->ptrace_access_check(child, mode);
}
int security_ptrace_traceme(struct task_struct *parent)
@@ -684,6 +684,11 @@ int security_task_create(unsigned long clone_flags)
return security_ops->task_create(clone_flags);
}
+int security_cred_alloc_blank(struct cred *cred, gfp_t gfp)
+{
+ return security_ops->cred_alloc_blank(cred, gfp);
+}
+
void security_cred_free(struct cred *cred)
{
security_ops->cred_free(cred);
@@ -699,6 +704,11 @@ void security_commit_creds(struct cred *new, const struct cred *old)
security_ops->cred_commit(new, old);
}
+void security_transfer_creds(struct cred *new, const struct cred *old)
+{
+ security_ops->cred_transfer(new, old);
+}
+
int security_kernel_act_as(struct cred *new, u32 secid)
{
return security_ops->kernel_act_as(new, secid);
@@ -709,6 +719,11 @@ int security_kernel_create_files_as(struct cred *new, struct inode *inode)
return security_ops->kernel_create_files_as(new, inode);
}
+int security_kernel_module_request(void)
+{
+ return security_ops->kernel_module_request();
+}
+
int security_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
{
return security_ops->task_setuid(id0, id1, id2, flags);
@@ -959,6 +974,24 @@ void security_release_secctx(char *secdata, u32 seclen)
}
EXPORT_SYMBOL(security_release_secctx);
+int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
+{
+ return security_ops->inode_notifysecctx(inode, ctx, ctxlen);
+}
+EXPORT_SYMBOL(security_inode_notifysecctx);
+
+int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
+{
+ return security_ops->inode_setsecctx(dentry, ctx, ctxlen);
+}
+EXPORT_SYMBOL(security_inode_setsecctx);
+
+int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
+{
+ return security_ops->inode_getsecctx(inode, ctx, ctxlen);
+}
+EXPORT_SYMBOL(security_inode_getsecctx);
+
#ifdef CONFIG_SECURITY_NETWORK
int security_unix_stream_connect(struct socket *sock, struct socket *other,
@@ -1112,6 +1145,24 @@ void security_inet_conn_established(struct sock *sk,
security_ops->inet_conn_established(sk, skb);
}
+int security_tun_dev_create(void)
+{
+ return security_ops->tun_dev_create();
+}
+EXPORT_SYMBOL(security_tun_dev_create);
+
+void security_tun_dev_post_create(struct sock *sk)
+{
+ return security_ops->tun_dev_post_create(sk);
+}
+EXPORT_SYMBOL(security_tun_dev_post_create);
+
+int security_tun_dev_attach(struct sock *sk)
+{
+ return security_ops->tun_dev_attach(sk);
+}
+EXPORT_SYMBOL(security_tun_dev_attach);
+
#endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_NETWORK_XFRM
@@ -1218,6 +1269,13 @@ int security_key_getsecurity(struct key *key, char **_buffer)
return security_ops->key_getsecurity(key, _buffer);
}
+int security_key_session_to_parent(const struct cred *cred,
+ const struct cred *parent_cred,
+ struct key *key)
+{
+ return security_ops->key_session_to_parent(cred, parent_cred, key);
+}
+
#endif /* CONFIG_KEYS */
#ifdef CONFIG_AUDIT
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index b2ab6085983..b4b5da1c0a4 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -137,7 +137,7 @@ static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass)
* @tclass: target security class
* @av: access vector
*/
-void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av)
+static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av)
{
const char **common_pts = NULL;
u32 common_base = 0;
@@ -492,23 +492,35 @@ out:
return node;
}
-static inline void avc_print_ipv6_addr(struct audit_buffer *ab,
- struct in6_addr *addr, __be16 port,
- char *name1, char *name2)
+/**
+ * avc_audit_pre_callback - SELinux specific information
+ * will be called by generic audit code
+ * @ab: the audit buffer
+ * @a: audit_data
+ */
+static void avc_audit_pre_callback(struct audit_buffer *ab, void *a)
{
- if (!ipv6_addr_any(addr))
- audit_log_format(ab, " %s=%pI6", name1, addr);
- if (port)
- audit_log_format(ab, " %s=%d", name2, ntohs(port));
+ struct common_audit_data *ad = a;
+ audit_log_format(ab, "avc: %s ",
+ ad->selinux_audit_data.denied ? "denied" : "granted");
+ avc_dump_av(ab, ad->selinux_audit_data.tclass,
+ ad->selinux_audit_data.audited);
+ audit_log_format(ab, " for ");
}
-static inline void avc_print_ipv4_addr(struct audit_buffer *ab, __be32 addr,
- __be16 port, char *name1, char *name2)
+/**
+ * avc_audit_post_callback - SELinux specific information
+ * will be called by generic audit code
+ * @ab: the audit buffer
+ * @a: audit_data
+ */
+static void avc_audit_post_callback(struct audit_buffer *ab, void *a)
{
- if (addr)
- audit_log_format(ab, " %s=%pI4", name1, &addr);
- if (port)
- audit_log_format(ab, " %s=%d", name2, ntohs(port));
+ struct common_audit_data *ad = a;
+ audit_log_format(ab, " ");
+ avc_dump_query(ab, ad->selinux_audit_data.ssid,
+ ad->selinux_audit_data.tsid,
+ ad->selinux_audit_data.tclass);
}
/**
@@ -532,13 +544,10 @@ static inline void avc_print_ipv4_addr(struct audit_buffer *ab, __be32 addr,
*/
void avc_audit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
- struct av_decision *avd, int result, struct avc_audit_data *a)
+ struct av_decision *avd, int result, struct common_audit_data *a)
{
- struct task_struct *tsk = current;
- struct inode *inode = NULL;
+ struct common_audit_data stack_data;
u32 denied, audited;
- struct audit_buffer *ab;
-
denied = requested & ~avd->allowed;
if (denied) {
audited = denied;
@@ -551,144 +560,20 @@ void avc_audit(u32 ssid, u32 tsid,
if (!(audited & avd->auditallow))
return;
}
-
- ab = audit_log_start(current->audit_context, GFP_ATOMIC, AUDIT_AVC);
- if (!ab)
- return; /* audit_panic has been called */
- audit_log_format(ab, "avc: %s ", denied ? "denied" : "granted");
- avc_dump_av(ab, tclass, audited);
- audit_log_format(ab, " for ");
- if (a && a->tsk)
- tsk = a->tsk;
- if (tsk && tsk->pid) {
- audit_log_format(ab, " pid=%d comm=", tsk->pid);
- audit_log_untrustedstring(ab, tsk->comm);
+ if (!a) {
+ a = &stack_data;
+ memset(a, 0, sizeof(*a));
+ a->type = LSM_AUDIT_NO_AUDIT;
}
- if (a) {
- switch (a->type) {
- case AVC_AUDIT_DATA_IPC:
- audit_log_format(ab, " key=%d", a->u.ipc_id);
- break;
- case AVC_AUDIT_DATA_CAP:
- audit_log_format(ab, " capability=%d", a->u.cap);
- break;
- case AVC_AUDIT_DATA_FS:
- if (a->u.fs.path.dentry) {
- struct dentry *dentry = a->u.fs.path.dentry;
- if (a->u.fs.path.mnt) {
- audit_log_d_path(ab, "path=",
- &a->u.fs.path);
- } else {
- audit_log_format(ab, " name=");
- audit_log_untrustedstring(ab, dentry->d_name.name);
- }
- inode = dentry->d_inode;
- } else if (a->u.fs.inode) {
- struct dentry *dentry;
- inode = a->u.fs.inode;
- dentry = d_find_alias(inode);
- if (dentry) {
- audit_log_format(ab, " name=");
- audit_log_untrustedstring(ab, dentry->d_name.name);
- dput(dentry);
- }
- }
- if (inode)
- audit_log_format(ab, " dev=%s ino=%lu",
- inode->i_sb->s_id,
- inode->i_ino);
- break;
- case AVC_AUDIT_DATA_NET:
- if (a->u.net.sk) {
- struct sock *sk = a->u.net.sk;
- struct unix_sock *u;
- int len = 0;
- char *p = NULL;
-
- switch (sk->sk_family) {
- case AF_INET: {
- struct inet_sock *inet = inet_sk(sk);
-
- avc_print_ipv4_addr(ab, inet->rcv_saddr,
- inet->sport,
- "laddr", "lport");
- avc_print_ipv4_addr(ab, inet->daddr,
- inet->dport,
- "faddr", "fport");
- break;
- }
- case AF_INET6: {
- struct inet_sock *inet = inet_sk(sk);
- struct ipv6_pinfo *inet6 = inet6_sk(sk);
-
- avc_print_ipv6_addr(ab, &inet6->rcv_saddr,
- inet->sport,
- "laddr", "lport");
- avc_print_ipv6_addr(ab, &inet6->daddr,
- inet->dport,
- "faddr", "fport");
- break;
- }
- case AF_UNIX:
- u = unix_sk(sk);
- if (u->dentry) {
- struct path path = {
- .dentry = u->dentry,
- .mnt = u->mnt
- };
- audit_log_d_path(ab, "path=",
- &path);
- break;
- }
- if (!u->addr)
- break;
- len = u->addr->len-sizeof(short);
- p = &u->addr->name->sun_path[0];
- audit_log_format(ab, " path=");
- if (*p)
- audit_log_untrustedstring(ab, p);
- else
- audit_log_n_hex(ab, p, len);
- break;
- }
- }
-
- switch (a->u.net.family) {
- case AF_INET:
- avc_print_ipv4_addr(ab, a->u.net.v4info.saddr,
- a->u.net.sport,
- "saddr", "src");
- avc_print_ipv4_addr(ab, a->u.net.v4info.daddr,
- a->u.net.dport,
- "daddr", "dest");
- break;
- case AF_INET6:
- avc_print_ipv6_addr(ab, &a->u.net.v6info.saddr,
- a->u.net.sport,
- "saddr", "src");
- avc_print_ipv6_addr(ab, &a->u.net.v6info.daddr,
- a->u.net.dport,
- "daddr", "dest");
- break;
- }
- if (a->u.net.netif > 0) {
- struct net_device *dev;
-
- /* NOTE: we always use init's namespace */
- dev = dev_get_by_index(&init_net,
- a->u.net.netif);
- if (dev) {
- audit_log_format(ab, " netif=%s",
- dev->name);
- dev_put(dev);
- }
- }
- break;
- }
- }
- audit_log_format(ab, " ");
- avc_dump_query(ab, ssid, tsid, tclass);
- audit_log_end(ab);
+ a->selinux_audit_data.tclass = tclass;
+ a->selinux_audit_data.requested = requested;
+ a->selinux_audit_data.ssid = ssid;
+ a->selinux_audit_data.tsid = tsid;
+ a->selinux_audit_data.audited = audited;
+ a->selinux_audit_data.denied = denied;
+ a->lsm_pre_audit = avc_audit_pre_callback;
+ a->lsm_post_audit = avc_audit_post_callback;
+ common_lsm_audit(a);
}
/**
@@ -824,18 +709,16 @@ out:
}
/**
- * avc_ss_reset - Flush the cache and revalidate migrated permissions.
- * @seqno: policy sequence number
+ * avc_flush - Flush the cache
*/
-int avc_ss_reset(u32 seqno)
+static void avc_flush(void)
{
- struct avc_callback_node *c;
- int i, rc = 0, tmprc;
- unsigned long flag;
- struct avc_node *node;
struct hlist_head *head;
struct hlist_node *next;
+ struct avc_node *node;
spinlock_t *lock;
+ unsigned long flag;
+ int i;
for (i = 0; i < AVC_CACHE_SLOTS; i++) {
head = &avc_cache.slots[i];
@@ -852,6 +735,18 @@ int avc_ss_reset(u32 seqno)
rcu_read_unlock();
spin_unlock_irqrestore(lock, flag);
}
+}
+
+/**
+ * avc_ss_reset - Flush the cache and revalidate migrated permissions.
+ * @seqno: policy sequence number
+ */
+int avc_ss_reset(u32 seqno)
+{
+ struct avc_callback_node *c;
+ int rc = 0, tmprc;
+
+ avc_flush();
for (c = avc_callbacks; c; c = c->next) {
if (c->events & AVC_CALLBACK_RESET) {
@@ -956,7 +851,7 @@ out:
* another -errno upon other errors.
*/
int avc_has_perm(u32 ssid, u32 tsid, u16 tclass,
- u32 requested, struct avc_audit_data *auditdata)
+ u32 requested, struct common_audit_data *auditdata)
{
struct av_decision avd;
int rc;
@@ -970,3 +865,22 @@ u32 avc_policy_seqno(void)
{
return avc_cache.latest_notif;
}
+
+void avc_disable(void)
+{
+ /*
+ * If you are looking at this because you have realized that we are
+ * not destroying the avc_node_cachep it might be easy to fix, but
+ * I don't know the memory barrier semantics well enough to know. It's
+ * possible that some other task dereferenced security_ops when
+ * it still pointed to selinux operations. If that is the case it's
+ * possible that it is about to use the avc and is about to need the
+ * avc_node_cachep. I know I could wrap the security.c security_ops call
+ * in an rcu_lock, but seriously, it's not worth it. Instead I just flush
+ * the cache and get that memory back.
+ */
+ if (avc_node_cachep) {
+ avc_flush();
+ /* kmem_cache_destroy(avc_node_cachep); */
+ }
+}
diff --git a/security/selinux/exports.c b/security/selinux/exports.c
index c73aeaa008e..c0a454aee1e 100644
--- a/security/selinux/exports.c
+++ b/security/selinux/exports.c
@@ -63,3 +63,9 @@ void selinux_secmark_refcount_dec(void)
atomic_dec(&selinux_secmark_refcount);
}
EXPORT_SYMBOL_GPL(selinux_secmark_refcount_dec);
+
+bool selinux_is_enabled(void)
+{
+ return selinux_enabled;
+}
+EXPORT_SYMBOL_GPL(selinux_is_enabled);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 8d8b69c5664..bb230d5d708 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -13,8 +13,8 @@
* Eric Paris <eparis@redhat.com>
* Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
* <dgoeddel@trustedcs.com>
- * Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P.
- * Paul Moore <paul.moore@hp.com>
+ * Copyright (C) 2006, 2007, 2009 Hewlett-Packard Development Company, L.P.
+ * Paul Moore <paul.moore@hp.com>
* Copyright (C) 2007 Hitachi Software Engineering Co., Ltd.
* Yuichi Nakamura <ynakam@hitachisoft.jp>
*
@@ -448,6 +448,10 @@ static int sb_finish_set_opts(struct super_block *sb)
sbsec->behavior > ARRAY_SIZE(labeling_behaviors))
sbsec->flags &= ~SE_SBLABELSUPP;
+ /* Special handling for sysfs. Is genfs but also has setxattr handler*/
+ if (strncmp(sb->s_type->name, "sysfs", sizeof("sysfs")) == 0)
+ sbsec->flags |= SE_SBLABELSUPP;
+
/* Initialize the root inode. */
rc = inode_doinit_with_dentry(root_inode, root);
@@ -1479,14 +1483,14 @@ static int task_has_capability(struct task_struct *tsk,
const struct cred *cred,
int cap, int audit)
{
- struct avc_audit_data ad;
+ struct common_audit_data ad;
struct av_decision avd;
u16 sclass;
u32 sid = cred_sid(cred);
u32 av = CAP_TO_MASK(cap);
int rc;
- AVC_AUDIT_DATA_INIT(&ad, CAP);
+ COMMON_AUDIT_DATA_INIT(&ad, CAP);
ad.tsk = tsk;
ad.u.cap = cap;
@@ -1525,12 +1529,14 @@ static int task_has_system(struct task_struct *tsk,
static int inode_has_perm(const struct cred *cred,
struct inode *inode,
u32 perms,
- struct avc_audit_data *adp)
+ struct common_audit_data *adp)
{
struct inode_security_struct *isec;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
u32 sid;
+ validate_creds(cred);
+
if (unlikely(IS_PRIVATE(inode)))
return 0;
@@ -1539,7 +1545,7 @@ static int inode_has_perm(const struct cred *cred,
if (!adp) {
adp = &ad;
- AVC_AUDIT_DATA_INIT(&ad, FS);
+ COMMON_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.inode = inode;
}
@@ -1555,9 +1561,9 @@ static inline int dentry_has_perm(const struct cred *cred,
u32 av)
{
struct inode *inode = dentry->d_inode;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
- AVC_AUDIT_DATA_INIT(&ad, FS);
+ COMMON_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.path.mnt = mnt;
ad.u.fs.path.dentry = dentry;
return inode_has_perm(cred, inode, av, &ad);
@@ -1577,11 +1583,11 @@ static int file_has_perm(const struct cred *cred,
{
struct file_security_struct *fsec = file->f_security;
struct inode *inode = file->f_path.dentry->d_inode;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
u32 sid = cred_sid(cred);
int rc;
- AVC_AUDIT_DATA_INIT(&ad, FS);
+ COMMON_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.path = file->f_path;
if (sid != fsec->sid) {
@@ -1612,7 +1618,7 @@ static int may_create(struct inode *dir,
struct inode_security_struct *dsec;
struct superblock_security_struct *sbsec;
u32 sid, newsid;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
int rc;
dsec = dir->i_security;
@@ -1621,7 +1627,7 @@ static int may_create(struct inode *dir,
sid = tsec->sid;
newsid = tsec->create_sid;
- AVC_AUDIT_DATA_INIT(&ad, FS);
+ COMMON_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.path.dentry = dentry;
rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR,
@@ -1665,7 +1671,7 @@ static int may_link(struct inode *dir,
{
struct inode_security_struct *dsec, *isec;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
u32 sid = current_sid();
u32 av;
int rc;
@@ -1673,7 +1679,7 @@ static int may_link(struct inode *dir,
dsec = dir->i_security;
isec = dentry->d_inode->i_security;
- AVC_AUDIT_DATA_INIT(&ad, FS);
+ COMMON_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.path.dentry = dentry;
av = DIR__SEARCH;
@@ -1708,7 +1714,7 @@ static inline int may_rename(struct inode *old_dir,
struct dentry *new_dentry)
{
struct inode_security_struct *old_dsec, *new_dsec, *old_isec, *new_isec;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
u32 sid = current_sid();
u32 av;
int old_is_dir, new_is_dir;
@@ -1719,7 +1725,7 @@ static inline int may_rename(struct inode *old_dir,
old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
new_dsec = new_dir->i_security;
- AVC_AUDIT_DATA_INIT(&ad, FS);
+ COMMON_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.path.dentry = old_dentry;
rc = avc_has_perm(sid, old_dsec->sid, SECCLASS_DIR,
@@ -1761,7 +1767,7 @@ static inline int may_rename(struct inode *old_dir,
static int superblock_has_perm(const struct cred *cred,
struct super_block *sb,
u32 perms,
- struct avc_audit_data *ad)
+ struct common_audit_data *ad)
{
struct superblock_security_struct *sbsec;
u32 sid = cred_sid(cred);
@@ -1855,12 +1861,12 @@ static inline u32 open_file_to_av(struct file *file)
/* Hook functions begin here. */
-static int selinux_ptrace_may_access(struct task_struct *child,
+static int selinux_ptrace_access_check(struct task_struct *child,
unsigned int mode)
{
int rc;
- rc = cap_ptrace_may_access(child, mode);
+ rc = cap_ptrace_access_check(child, mode);
if (rc)
return rc;
@@ -2101,7 +2107,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
const struct task_security_struct *old_tsec;
struct task_security_struct *new_tsec;
struct inode_security_struct *isec;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
struct inode *inode = bprm->file->f_path.dentry->d_inode;
int rc;
@@ -2139,7 +2145,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
return rc;
}
- AVC_AUDIT_DATA_INIT(&ad, FS);
+ COMMON_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.path = bprm->file->f_path;
if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
@@ -2232,7 +2238,7 @@ extern struct dentry *selinux_null;
static inline void flush_unauthorized_files(const struct cred *cred,
struct files_struct *files)
{
- struct avc_audit_data ad;
+ struct common_audit_data ad;
struct file *file, *devnull = NULL;
struct tty_struct *tty;
struct fdtable *fdt;
@@ -2266,7 +2272,7 @@ static inline void flush_unauthorized_files(const struct cred *cred,
/* Revalidate access to inherited open files. */
- AVC_AUDIT_DATA_INIT(&ad, FS);
+ COMMON_AUDIT_DATA_INIT(&ad, FS);
spin_lock(&files->file_lock);
for (;;) {
@@ -2405,7 +2411,7 @@ static void selinux_bprm_committed_creds(struct linux_binprm *bprm)
/* Wake up the parent if it is waiting so that it can recheck
* wait permission to the new task SID. */
read_lock(&tasklist_lock);
- wake_up_interruptible(&current->real_parent->signal->wait_chldexit);
+ __wake_up_parent(current, current->real_parent);
read_unlock(&tasklist_lock);
}
@@ -2515,7 +2521,7 @@ out:
static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data)
{
const struct cred *cred = current_cred();
- struct avc_audit_data ad;
+ struct common_audit_data ad;
int rc;
rc = superblock_doinit(sb, data);
@@ -2526,7 +2532,7 @@ static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data)
if (flags & MS_KERNMOUNT)
return 0;
- AVC_AUDIT_DATA_INIT(&ad, FS);
+ COMMON_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.path.dentry = sb->s_root;
return superblock_has_perm(cred, sb, FILESYSTEM__MOUNT, &ad);
}
@@ -2534,9 +2540,9 @@ static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data)
static int selinux_sb_statfs(struct dentry *dentry)
{
const struct cred *cred = current_cred();
- struct avc_audit_data ad;
+ struct common_audit_data ad;
- AVC_AUDIT_DATA_INIT(&ad, FS);
+ COMMON_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.path.dentry = dentry->d_sb->s_root;
return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad);
}
@@ -2711,12 +2717,18 @@ static int selinux_inode_permission(struct inode *inode, int mask)
static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
{
const struct cred *cred = current_cred();
+ unsigned int ia_valid = iattr->ia_valid;
+
+ /* ATTR_FORCE is just used for ATTR_KILL_S[UG]ID. */
+ if (ia_valid & ATTR_FORCE) {
+ ia_valid &= ~(ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_MODE |
+ ATTR_FORCE);
+ if (!ia_valid)
+ return 0;
+ }
- if (iattr->ia_valid & ATTR_FORCE)
- return 0;
-
- if (iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID |
- ATTR_ATIME_SET | ATTR_MTIME_SET))
+ if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID |
+ ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_TIMES_SET))
return dentry_has_perm(cred, NULL, dentry, FILE__SETATTR);
return dentry_has_perm(cred, NULL, dentry, FILE__WRITE);
@@ -2756,7 +2768,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
struct inode *inode = dentry->d_inode;
struct inode_security_struct *isec = inode->i_security;
struct superblock_security_struct *sbsec;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
u32 newsid, sid = current_sid();
int rc = 0;
@@ -2770,7 +2782,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
if (!is_owner_or_cap(inode))
return -EPERM;
- AVC_AUDIT_DATA_INIT(&ad, FS);
+ COMMON_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.path.dentry = dentry;
rc = avc_has_perm(sid, isec->sid, isec->sclass,
@@ -2915,6 +2927,7 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name,
return rc;
isec->sid = newsid;
+ isec->initialized = 1;
return 0;
}
@@ -2939,11 +2952,6 @@ static int selinux_revalidate_file_permission(struct file *file, int mask)
const struct cred *cred = current_cred();
struct inode *inode = file->f_path.dentry->d_inode;
- if (!mask) {
- /* No permission to check. Existence test. */
- return 0;
- }
-
/* file_mask_to_av won't add FILE__WRITE if MAY_APPEND is set */
if ((file->f_flags & O_APPEND) && (mask & MAY_WRITE))
mask |= MAY_APPEND;
@@ -2954,10 +2962,20 @@ static int selinux_revalidate_file_permission(struct file *file, int mask)
static int selinux_file_permission(struct file *file, int mask)
{
+ struct inode *inode = file->f_path.dentry->d_inode;
+ struct file_security_struct *fsec = file->f_security;
+ struct inode_security_struct *isec = inode->i_security;
+ u32 sid = current_sid();
+
if (!mask)
/* No permission to check. Existence test. */
return 0;
+ if (sid == fsec->sid && fsec->isid == isec->sid &&
+ fsec->pseqno == avc_policy_seqno())
+ /* No change since dentry_open check. */
+ return 0;
+
return selinux_revalidate_file_permission(file, mask);
}
@@ -3220,12 +3238,29 @@ static int selinux_task_create(unsigned long clone_flags)
}
/*
+ * allocate the SELinux part of blank credentials
+ */
+static int selinux_cred_alloc_blank(struct cred *cred, gfp_t gfp)
+{
+ struct task_security_struct *tsec;
+
+ tsec = kzalloc(sizeof(struct task_security_struct), gfp);
+ if (!tsec)
+ return -ENOMEM;
+
+ cred->security = tsec;
+ return 0;
+}
+
+/*
* detach and free the LSM part of a set of credentials
*/
static void selinux_cred_free(struct cred *cred)
{
struct task_security_struct *tsec = cred->security;
- cred->security = NULL;
+
+ BUG_ON((unsigned long) cred->security < PAGE_SIZE);
+ cred->security = (void *) 0x7UL;
kfree(tsec);
}
@@ -3249,6 +3284,17 @@ static int selinux_cred_prepare(struct cred *new, const struct cred *old,
}
/*
+ * transfer the SELinux data to a blank set of creds
+ */
+static void selinux_cred_transfer(struct cred *new, const struct cred *old)
+{
+ const struct task_security_struct *old_tsec = old->security;
+ struct task_security_struct *tsec = new->security;
+
+ *tsec = *old_tsec;
+}
+
+/*
* set the security data for a kernel service
* - all the creation contexts are set to unlabelled
*/
@@ -3292,6 +3338,11 @@ static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode)
return 0;
}
+static int selinux_kernel_module_request(void)
+{
+ return task_has_system(current, SYSTEM__MODULE_REQUEST);
+}
+
static int selinux_task_setpgid(struct task_struct *p, pid_t pgid)
{
return current_has_perm(p, PROCESS__SETPGID);
@@ -3409,7 +3460,7 @@ static void selinux_task_to_inode(struct task_struct *p,
/* Returns error only if unable to parse addresses */
static int selinux_parse_skb_ipv4(struct sk_buff *skb,
- struct avc_audit_data *ad, u8 *proto)
+ struct common_audit_data *ad, u8 *proto)
{
int offset, ihlen, ret = -EINVAL;
struct iphdr _iph, *ih;
@@ -3490,7 +3541,7 @@ out:
/* Returns error only if unable to parse addresses */
static int selinux_parse_skb_ipv6(struct sk_buff *skb,
- struct avc_audit_data *ad, u8 *proto)
+ struct common_audit_data *ad, u8 *proto)
{
u8 nexthdr;
int ret = -EINVAL, offset;
@@ -3561,7 +3612,7 @@ out:
#endif /* IPV6 */
-static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
+static int selinux_parse_skb(struct sk_buff *skb, struct common_audit_data *ad,
char **_addrp, int src, u8 *proto)
{
char *addrp;
@@ -3643,7 +3694,7 @@ static int socket_has_perm(struct task_struct *task, struct socket *sock,
u32 perms)
{
struct inode_security_struct *isec;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
u32 sid;
int err = 0;
@@ -3653,7 +3704,7 @@ static int socket_has_perm(struct task_struct *task, struct socket *sock,
goto out;
sid = task_sid(task);
- AVC_AUDIT_DATA_INIT(&ad, NET);
+ COMMON_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.sk = sock->sk;
err = avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad);
@@ -3740,7 +3791,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
if (family == PF_INET || family == PF_INET6) {
char *addrp;
struct inode_security_struct *isec;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
struct sockaddr_in *addr4 = NULL;
struct sockaddr_in6 *addr6 = NULL;
unsigned short snum;
@@ -3769,7 +3820,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
snum, &sid);
if (err)
goto out;
- AVC_AUDIT_DATA_INIT(&ad, NET);
+ COMMON_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.sport = htons(snum);
ad.u.net.family = family;
err = avc_has_perm(isec->sid, sid,
@@ -3802,7 +3853,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
if (err)
goto out;
- AVC_AUDIT_DATA_INIT(&ad, NET);
+ COMMON_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.sport = htons(snum);
ad.u.net.family = family;
@@ -3836,7 +3887,7 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
isec = SOCK_INODE(sock)->i_security;
if (isec->sclass == SECCLASS_TCP_SOCKET ||
isec->sclass == SECCLASS_DCCP_SOCKET) {
- struct avc_audit_data ad;
+ struct common_audit_data ad;
struct sockaddr_in *addr4 = NULL;
struct sockaddr_in6 *addr6 = NULL;
unsigned short snum;
@@ -3861,7 +3912,7 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
perm = (isec->sclass == SECCLASS_TCP_SOCKET) ?
TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT;
- AVC_AUDIT_DATA_INIT(&ad, NET);
+ COMMON_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.dport = htons(snum);
ad.u.net.family = sk->sk_family;
err = avc_has_perm(isec->sid, sid, isec->sclass, perm, &ad);
@@ -3951,13 +4002,13 @@ static int selinux_socket_unix_stream_connect(struct socket *sock,
struct sk_security_struct *ssec;
struct inode_security_struct *isec;
struct inode_security_struct *other_isec;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
int err;
isec = SOCK_INODE(sock)->i_security;
other_isec = SOCK_INODE(other)->i_security;
- AVC_AUDIT_DATA_INIT(&ad, NET);
+ COMMON_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.sk = other->sk;
err = avc_has_perm(isec->sid, other_isec->sid,
@@ -3983,13 +4034,13 @@ static int selinux_socket_unix_may_send(struct socket *sock,
{
struct inode_security_struct *isec;
struct inode_security_struct *other_isec;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
int err;
isec = SOCK_INODE(sock)->i_security;
other_isec = SOCK_INODE(other)->i_security;
- AVC_AUDIT_DATA_INIT(&ad, NET);
+ COMMON_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.sk = other->sk;
err = avc_has_perm(isec->sid, other_isec->sid,
@@ -4002,7 +4053,7 @@ static int selinux_socket_unix_may_send(struct socket *sock,
static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family,
u32 peer_sid,
- struct avc_audit_data *ad)
+ struct common_audit_data *ad)
{
int err;
u32 if_sid;
@@ -4030,10 +4081,10 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
struct sk_security_struct *sksec = sk->sk_security;
u32 peer_sid;
u32 sk_sid = sksec->sid;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
char *addrp;
- AVC_AUDIT_DATA_INIT(&ad, NET);
+ COMMON_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.netif = skb->iif;
ad.u.net.family = family;
err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL);
@@ -4071,7 +4122,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
struct sk_security_struct *sksec = sk->sk_security;
u16 family = sk->sk_family;
u32 sk_sid = sksec->sid;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
char *addrp;
u8 secmark_active;
u8 peerlbl_active;
@@ -4095,7 +4146,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
if (!secmark_active && !peerlbl_active)
return 0;
- AVC_AUDIT_DATA_INIT(&ad, NET);
+ COMMON_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.netif = skb->iif;
ad.u.net.family = family;
err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL);
@@ -4309,6 +4360,59 @@ static void selinux_req_classify_flow(const struct request_sock *req,
fl->secid = req->secid;
}
+static int selinux_tun_dev_create(void)
+{
+ u32 sid = current_sid();
+
+ /* we aren't taking into account the "sockcreate" SID since the socket
+ * that is being created here is not a socket in the traditional sense,
+ * instead it is a private sock, accessible only to the kernel, and
+ * representing a wide range of network traffic spanning multiple
+ * connections unlike traditional sockets - check the TUN driver to
+ * get a better understanding of why this socket is special */
+
+ return avc_has_perm(sid, sid, SECCLASS_TUN_SOCKET, TUN_SOCKET__CREATE,
+ NULL);
+}
+
+static void selinux_tun_dev_post_create(struct sock *sk)
+{
+ struct sk_security_struct *sksec = sk->sk_security;
+
+ /* we don't currently perform any NetLabel based labeling here and it
+ * isn't clear that we would want to do so anyway; while we could apply
+ * labeling without the support of the TUN user the resulting labeled
+ * traffic from the other end of the connection would almost certainly
+ * cause confusion to the TUN user that had no idea network labeling
+ * protocols were being used */
+
+ /* see the comments in selinux_tun_dev_create() about why we don't use
+ * the sockcreate SID here */
+
+ sksec->sid = current_sid();
+ sksec->sclass = SECCLASS_TUN_SOCKET;
+}
+
+static int selinux_tun_dev_attach(struct sock *sk)
+{
+ struct sk_security_struct *sksec = sk->sk_security;
+ u32 sid = current_sid();
+ int err;
+
+ err = avc_has_perm(sid, sksec->sid, SECCLASS_TUN_SOCKET,
+ TUN_SOCKET__RELABELFROM, NULL);
+ if (err)
+ return err;
+ err = avc_has_perm(sid, sid, SECCLASS_TUN_SOCKET,
+ TUN_SOCKET__RELABELTO, NULL);
+ if (err)
+ return err;
+
+ sksec->sid = sid;
+
+ return 0;
+}
+
static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
{
int err = 0;
@@ -4353,7 +4457,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,
int err;
char *addrp;
u32 peer_sid;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
u8 secmark_active;
u8 netlbl_active;
u8 peerlbl_active;
@@ -4370,7 +4474,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,
if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0)
return NF_DROP;
- AVC_AUDIT_DATA_INIT(&ad, NET);
+ COMMON_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.netif = ifindex;
ad.u.net.family = family;
if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0)
@@ -4458,7 +4562,7 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
{
struct sock *sk = skb->sk;
struct sk_security_struct *sksec;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
char *addrp;
u8 proto;
@@ -4466,7 +4570,7 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
return NF_ACCEPT;
sksec = sk->sk_security;
- AVC_AUDIT_DATA_INIT(&ad, NET);
+ COMMON_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.netif = ifindex;
ad.u.net.family = family;
if (selinux_parse_skb(skb, &ad, &addrp, 0, &proto))
@@ -4490,7 +4594,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
u32 secmark_perm;
u32 peer_sid;
struct sock *sk;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
char *addrp;
u8 secmark_active;
u8 peerlbl_active;
@@ -4549,7 +4653,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
secmark_perm = PACKET__SEND;
}
- AVC_AUDIT_DATA_INIT(&ad, NET);
+ COMMON_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.netif = ifindex;
ad.u.net.family = family;
if (selinux_parse_skb(skb, &ad, &addrp, 0, NULL))
@@ -4619,13 +4723,13 @@ static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
static int selinux_netlink_recv(struct sk_buff *skb, int capability)
{
int err;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
err = cap_netlink_recv(skb, capability);
if (err)
return err;
- AVC_AUDIT_DATA_INIT(&ad, CAP);
+ COMMON_AUDIT_DATA_INIT(&ad, CAP);
ad.u.cap = capability;
return avc_has_perm(NETLINK_CB(skb).sid, NETLINK_CB(skb).sid,
@@ -4684,12 +4788,12 @@ static int ipc_has_perm(struct kern_ipc_perm *ipc_perms,
u32 perms)
{
struct ipc_security_struct *isec;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
u32 sid = current_sid();
isec = ipc_perms->security;
- AVC_AUDIT_DATA_INIT(&ad, IPC);
+ COMMON_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = ipc_perms->key;
return avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad);
@@ -4709,7 +4813,7 @@ static void selinux_msg_msg_free_security(struct msg_msg *msg)
static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
{
struct ipc_security_struct *isec;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
u32 sid = current_sid();
int rc;
@@ -4719,7 +4823,7 @@ static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
isec = msq->q_perm.security;
- AVC_AUDIT_DATA_INIT(&ad, IPC);
+ COMMON_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = msq->q_perm.key;
rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ,
@@ -4739,12 +4843,12 @@ static void selinux_msg_queue_free_security(struct msg_queue *msq)
static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg)
{
struct ipc_security_struct *isec;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
u32 sid = current_sid();
isec = msq->q_perm.security;
- AVC_AUDIT_DATA_INIT(&ad, IPC);
+ COMMON_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = msq->q_perm.key;
return avc_has_perm(sid, isec->sid, SECCLASS_MSGQ,
@@ -4783,7 +4887,7 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
{
struct ipc_security_struct *isec;
struct msg_security_struct *msec;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
u32 sid = current_sid();
int rc;
@@ -4804,7 +4908,7 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
return rc;
}
- AVC_AUDIT_DATA_INIT(&ad, IPC);
+ COMMON_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = msq->q_perm.key;
/* Can this process write to the queue? */
@@ -4828,14 +4932,14 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
{
struct ipc_security_struct *isec;
struct msg_security_struct *msec;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
u32 sid = task_sid(target);
int rc;
isec = msq->q_perm.security;
msec = msg->security;
- AVC_AUDIT_DATA_INIT(&ad, IPC);
+ COMMON_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = msq->q_perm.key;
rc = avc_has_perm(sid, isec->sid,
@@ -4850,7 +4954,7 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
static int selinux_shm_alloc_security(struct shmid_kernel *shp)
{
struct ipc_security_struct *isec;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
u32 sid = current_sid();
int rc;
@@ -4860,7 +4964,7 @@ static int selinux_shm_alloc_security(struct shmid_kernel *shp)
isec = shp->shm_perm.security;
- AVC_AUDIT_DATA_INIT(&ad, IPC);
+ COMMON_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = shp->shm_perm.key;
rc = avc_has_perm(sid, isec->sid, SECCLASS_SHM,
@@ -4880,12 +4984,12 @@ static void selinux_shm_free_security(struct shmid_kernel *shp)
static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg)
{
struct ipc_security_struct *isec;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
u32 sid = current_sid();
isec = shp->shm_perm.security;
- AVC_AUDIT_DATA_INIT(&ad, IPC);
+ COMMON_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = shp->shm_perm.key;
return avc_has_perm(sid, isec->sid, SECCLASS_SHM,
@@ -4942,7 +5046,7 @@ static int selinux_shm_shmat(struct shmid_kernel *shp,
static int selinux_sem_alloc_security(struct sem_array *sma)
{
struct ipc_security_struct *isec;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
u32 sid = current_sid();
int rc;
@@ -4952,7 +5056,7 @@ static int selinux_sem_alloc_security(struct sem_array *sma)
isec = sma->sem_perm.security;
- AVC_AUDIT_DATA_INIT(&ad, IPC);
+ COMMON_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = sma->sem_perm.key;
rc = avc_has_perm(sid, isec->sid, SECCLASS_SEM,
@@ -4972,12 +5076,12 @@ static void selinux_sem_free_security(struct sem_array *sma)
static int selinux_sem_associate(struct sem_array *sma, int semflg)
{
struct ipc_security_struct *isec;
- struct avc_audit_data ad;
+ struct common_audit_data ad;
u32 sid = current_sid();
isec = sma->sem_perm.security;
- AVC_AUDIT_DATA_INIT(&ad, IPC);
+ COMMON_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = sma->sem_perm.key;
return avc_has_perm(sid, isec->sid, SECCLASS_SEM,
@@ -5195,7 +5299,7 @@ static int selinux_setprocattr(struct task_struct *p,
/* Only allow single threaded processes to change context */
error = -EPERM;
- if (!is_single_threaded(p)) {
+ if (!current_is_single_threaded()) {
error = security_bounded_transition(tsec->sid, sid);
if (error)
goto abort_change;
@@ -5252,6 +5356,32 @@ static void selinux_release_secctx(char *secdata, u32 seclen)
kfree(secdata);
}
+/*
+ * called with inode->i_mutex locked
+ */
+static int selinux_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
+{
+ return selinux_inode_setsecurity(inode, XATTR_SELINUX_SUFFIX, ctx, ctxlen, 0);
+}
+
+/*
+ * called with inode->i_mutex locked
+ */
+static int selinux_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
+{
+ return __vfs_setxattr_noperm(dentry, XATTR_NAME_SELINUX, ctx, ctxlen, 0);
+}
+
+static int selinux_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
+{
+ int len = 0;
+ len = selinux_inode_getsecurity(inode, XATTR_SELINUX_SUFFIX,
+ ctx, true);
+ if (len < 0)
+ return len;
+ *ctxlen = len;
+ return 0;
+}
#ifdef CONFIG_KEYS
static int selinux_key_alloc(struct key *k, const struct cred *cred,
@@ -5323,7 +5453,7 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer)
static struct security_operations selinux_ops = {
.name = "selinux",
- .ptrace_may_access = selinux_ptrace_may_access,
+ .ptrace_access_check = selinux_ptrace_access_check,
.ptrace_traceme = selinux_ptrace_traceme,
.capget = selinux_capget,
.capset = selinux_capset,
@@ -5396,10 +5526,13 @@ static struct security_operations selinux_ops = {
.dentry_open = selinux_dentry_open,
.task_create = selinux_task_create,
+ .cred_alloc_blank = selinux_cred_alloc_blank,
.cred_free = selinux_cred_free,
.cred_prepare = selinux_cred_prepare,
+ .cred_transfer = selinux_cred_transfer,
.kernel_act_as = selinux_kernel_act_as,
.kernel_create_files_as = selinux_kernel_create_files_as,
+ .kernel_module_request = selinux_kernel_module_request,
.task_setpgid = selinux_task_setpgid,
.task_getpgid = selinux_task_getpgid,
.task_getsid = selinux_task_getsid,
@@ -5448,6 +5581,9 @@ static struct security_operations selinux_ops = {
.secid_to_secctx = selinux_secid_to_secctx,
.secctx_to_secid = selinux_secctx_to_secid,
.release_secctx = selinux_release_secctx,
+ .inode_notifysecctx = selinux_inode_notifysecctx,
+ .inode_setsecctx = selinux_inode_setsecctx,
+ .inode_getsecctx = selinux_inode_getsecctx,
.unix_stream_connect = selinux_socket_unix_stream_connect,
.unix_may_send = selinux_socket_unix_may_send,
@@ -5477,6 +5613,9 @@ static struct security_operations selinux_ops = {
.inet_csk_clone = selinux_inet_csk_clone,
.inet_conn_established = selinux_inet_conn_established,
.req_classify_flow = selinux_req_classify_flow,
+ .tun_dev_create = selinux_tun_dev_create,
+ .tun_dev_post_create = selinux_tun_dev_post_create,
+ .tun_dev_attach = selinux_tun_dev_attach,
#ifdef CONFIG_SECURITY_NETWORK_XFRM
.xfrm_policy_alloc_security = selinux_xfrm_policy_alloc,
@@ -5691,6 +5830,9 @@ int selinux_disable(void)
selinux_disabled = 1;
selinux_enabled = 0;
+ /* Try to destroy the avc node cache */
+ avc_disable();
+
/* Reset security_ops to the secondary module, dummy or capability. */
security_ops = secondary_ops;
diff --git a/security/selinux/include/av_inherit.h b/security/selinux/include/av_inherit.h
index 8377a4ba3b9..abedcd704da 100644
--- a/security/selinux/include/av_inherit.h
+++ b/security/selinux/include/av_inherit.h
@@ -15,6 +15,7 @@
S_(SECCLASS_KEY_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_UNIX_STREAM_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_UNIX_DGRAM_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_TUN_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_IPC, ipc, 0x00000200UL)
S_(SECCLASS_SEM, ipc, 0x00000200UL)
S_(SECCLASS_MSGQ, ipc, 0x00000200UL)
diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h
index 31df1d7c1ae..2b683ad83d2 100644
--- a/security/selinux/include/av_perm_to_string.h
+++ b/security/selinux/include/av_perm_to_string.h
@@ -107,6 +107,7 @@
S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, "syslog_read")
S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, "syslog_mod")
S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_CONSOLE, "syslog_console")
+ S_(SECCLASS_SYSTEM, SYSTEM__MODULE_REQUEST, "module_request")
S_(SECCLASS_CAPABILITY, CAPABILITY__CHOWN, "chown")
S_(SECCLASS_CAPABILITY, CAPABILITY__DAC_OVERRIDE, "dac_override")
S_(SECCLASS_CAPABILITY, CAPABILITY__DAC_READ_SEARCH, "dac_read_search")
diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h
index d645192ee95..0546d616cca 100644
--- a/security/selinux/include/av_permissions.h
+++ b/security/selinux/include/av_permissions.h
@@ -423,6 +423,28 @@
#define UNIX_DGRAM_SOCKET__RECV_MSG 0x00080000UL
#define UNIX_DGRAM_SOCKET__SEND_MSG 0x00100000UL
#define UNIX_DGRAM_SOCKET__NAME_BIND 0x00200000UL
+#define TUN_SOCKET__IOCTL 0x00000001UL
+#define TUN_SOCKET__READ 0x00000002UL
+#define TUN_SOCKET__WRITE 0x00000004UL
+#define TUN_SOCKET__CREATE 0x00000008UL
+#define TUN_SOCKET__GETATTR 0x00000010UL
+#define TUN_SOCKET__SETATTR 0x00000020UL
+#define TUN_SOCKET__LOCK 0x00000040UL
+#define TUN_SOCKET__RELABELFROM 0x00000080UL
+#define TUN_SOCKET__RELABELTO 0x00000100UL
+#define TUN_SOCKET__APPEND 0x00000200UL
+#define TUN_SOCKET__BIND 0x00000400UL
+#define TUN_SOCKET__CONNECT 0x00000800UL
+#define TUN_SOCKET__LISTEN 0x00001000UL
+#define TUN_SOCKET__ACCEPT 0x00002000UL
+#define TUN_SOCKET__GETOPT 0x00004000UL
+#define TUN_SOCKET__SETOPT 0x00008000UL
+#define TUN_SOCKET__SHUTDOWN 0x00010000UL
+#define TUN_SOCKET__RECVFROM 0x00020000UL
+#define TUN_SOCKET__SENDTO 0x00040000UL
+#define TUN_SOCKET__RECV_MSG 0x00080000UL
+#define TUN_SOCKET__SEND_MSG 0x00100000UL
+#define TUN_SOCKET__NAME_BIND 0x00200000UL
#define PROCESS__FORK 0x00000001UL
#define PROCESS__TRANSITION 0x00000002UL
#define PROCESS__SIGCHLD 0x00000004UL
@@ -508,6 +530,7 @@
#define SYSTEM__SYSLOG_READ 0x00000002UL
#define SYSTEM__SYSLOG_MOD 0x00000004UL
#define SYSTEM__SYSLOG_CONSOLE 0x00000008UL
+#define SYSTEM__MODULE_REQUEST 0x00000010UL
#define CAPABILITY__CHOWN 0x00000001UL
#define CAPABILITY__DAC_OVERRIDE 0x00000002UL
#define CAPABILITY__DAC_READ_SEARCH 0x00000004UL
diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h
index d12ff1a9c0a..e94e82f7381 100644
--- a/security/selinux/include/avc.h
+++ b/security/selinux/include/avc.h
@@ -13,6 +13,7 @@
#include <linux/spinlock.h>
#include <linux/init.h>
#include <linux/audit.h>
+#include <linux/lsm_audit.h>
#include <linux/in6.h>
#include <linux/path.h>
#include <asm/system.h>
@@ -36,48 +37,6 @@ struct inode;
struct sock;
struct sk_buff;
-/* Auxiliary data to use in generating the audit record. */
-struct avc_audit_data {
- char type;
-#define AVC_AUDIT_DATA_FS 1
-#define AVC_AUDIT_DATA_NET 2
-#define AVC_AUDIT_DATA_CAP 3
-#define AVC_AUDIT_DATA_IPC 4
- struct task_struct *tsk;
- union {
- struct {
- struct path path;
- struct inode *inode;
- } fs;
- struct {
- int netif;
- struct sock *sk;
- u16 family;
- __be16 dport;
- __be16 sport;
- union {
- struct {
- __be32 daddr;
- __be32 saddr;
- } v4;
- struct {
- struct in6_addr daddr;
- struct in6_addr saddr;
- } v6;
- } fam;
- } net;
- int cap;
- int ipc_id;
- } u;
-};
-
-#define v4info fam.v4
-#define v6info fam.v6
-
-/* Initialize an AVC audit data structure. */
-#define AVC_AUDIT_DATA_INIT(_d,_t) \
- { memset((_d), 0, sizeof(struct avc_audit_data)); (_d)->type = AVC_AUDIT_DATA_##_t; }
-
/*
* AVC statistics
*/
@@ -98,7 +57,9 @@ void __init avc_init(void);
void avc_audit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
- struct av_decision *avd, int result, struct avc_audit_data *auditdata);
+ struct av_decision *avd,
+ int result,
+ struct common_audit_data *a);
#define AVC_STRICT 1 /* Ignore permissive mode. */
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
@@ -108,7 +69,7 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
int avc_has_perm(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
- struct avc_audit_data *auditdata);
+ struct common_audit_data *auditdata);
u32 avc_policy_seqno(void);
@@ -127,13 +88,13 @@ int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid,
u32 events, u32 ssid, u32 tsid,
u16 tclass, u32 perms);
-/* Shows permission in human readable form */
-void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av);
-
/* Exported to selinuxfs */
int avc_get_hash_stats(char *page);
extern unsigned int avc_cache_threshold;
+/* Attempt to free avc node cache */
+void avc_disable(void);
+
#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
DECLARE_PER_CPU(struct avc_cache_stats, avc_cache_stats);
#endif
diff --git a/security/selinux/include/class_to_string.h b/security/selinux/include/class_to_string.h
index 21ec786611d..7ab9299bfb6 100644
--- a/security/selinux/include/class_to_string.h
+++ b/security/selinux/include/class_to_string.h
@@ -77,3 +77,4 @@
S_(NULL)
S_(NULL)
S_("kernel_service")
+ S_("tun_socket")
diff --git a/security/selinux/include/flask.h b/security/selinux/include/flask.h
index 882f27d66fa..f248500a1e3 100644
--- a/security/selinux/include/flask.h
+++ b/security/selinux/include/flask.h
@@ -53,6 +53,7 @@
#define SECCLASS_PEER 68
#define SECCLASS_CAPABILITY2 69
#define SECCLASS_KERNEL_SERVICE 74
+#define SECCLASS_TUN_SOCKET 75
/*
* Security identifier indices for initial entities
diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h
index b4b5b9b2f0b..8d7384280a7 100644
--- a/security/selinux/include/netlabel.h
+++ b/security/selinux/include/netlabel.h
@@ -59,7 +59,7 @@ int selinux_netlbl_socket_post_create(struct sock *sk, u16 family);
int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
struct sk_buff *skb,
u16 family,
- struct avc_audit_data *ad);
+ struct common_audit_data *ad);
int selinux_netlbl_socket_setsockopt(struct socket *sock,
int level,
int optname);
@@ -129,7 +129,7 @@ static inline int selinux_netlbl_socket_post_create(struct sock *sk,
static inline int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
struct sk_buff *skb,
u16 family,
- struct avc_audit_data *ad)
+ struct common_audit_data *ad)
{
return 0;
}
diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h
index 289e24b39e3..13128f9a3e5 100644
--- a/security/selinux/include/xfrm.h
+++ b/security/selinux/include/xfrm.h
@@ -41,9 +41,9 @@ static inline int selinux_xfrm_enabled(void)
}
int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb,
- struct avc_audit_data *ad);
+ struct common_audit_data *ad);
int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
- struct avc_audit_data *ad, u8 proto);
+ struct common_audit_data *ad, u8 proto);
int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall);
static inline void selinux_xfrm_notify_policyload(void)
@@ -57,13 +57,13 @@ static inline int selinux_xfrm_enabled(void)
}
static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
- struct avc_audit_data *ad)
+ struct common_audit_data *ad)
{
return 0;
}
static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
- struct avc_audit_data *ad, u8 proto)
+ struct common_audit_data *ad, u8 proto)
{
return 0;
}
diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c
index 2e984413c7b..e68823741ad 100644
--- a/security/selinux/netlabel.c
+++ b/security/selinux/netlabel.c
@@ -342,7 +342,7 @@ int selinux_netlbl_socket_post_create(struct sock *sk, u16 family)
int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
struct sk_buff *skb,
u16 family,
- struct avc_audit_data *ad)
+ struct common_audit_data *ad)
{
int rc;
u32 nlbl_sid;
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 500e6f78e11..ff17820d35e 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -22,6 +22,11 @@
*
* Added validation of kernel classes and permissions
*
+ * Updated: KaiGai Kohei <kaigai@ak.jp.nec.com>
+ *
+ * Added support for bounds domain and audit messaged on masked permissions
+ *
+ * Copyright (C) 2008, 2009 NEC Corporation
* Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P.
* Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
* Copyright (C) 2003 - 2004, 2006 Tresys Technology, LLC
@@ -279,6 +284,95 @@ mls_ops:
}
/*
+ * security_dump_masked_av - dumps masked permissions during
+ * security_compute_av due to RBAC, MLS/Constraint and Type bounds.
+ */
+static int dump_masked_av_helper(void *k, void *d, void *args)
+{
+ struct perm_datum *pdatum = d;
+ char **permission_names = args;
+
+ BUG_ON(pdatum->value < 1 || pdatum->value > 32);
+
+ permission_names[pdatum->value - 1] = (char *)k;
+
+ return 0;
+}
+
+static void security_dump_masked_av(struct context *scontext,
+ struct context *tcontext,
+ u16 tclass,
+ u32 permissions,
+ const char *reason)
+{
+ struct common_datum *common_dat;
+ struct class_datum *tclass_dat;
+ struct audit_buffer *ab;
+ char *tclass_name;
+ char *scontext_name = NULL;
+ char *tcontext_name = NULL;
+ char *permission_names[32];
+ int index, length;
+ bool need_comma = false;
+
+ if (!permissions)
+ return;
+
+ tclass_name = policydb.p_class_val_to_name[tclass - 1];
+ tclass_dat = policydb.class_val_to_struct[tclass - 1];
+ common_dat = tclass_dat->comdatum;
+
+ /* init permission_names */
+ if (common_dat &&
+ hashtab_map(common_dat->permissions.table,
+ dump_masked_av_helper, permission_names) < 0)
+ goto out;
+
+ if (hashtab_map(tclass_dat->permissions.table,
+ dump_masked_av_helper, permission_names) < 0)
+ goto out;
+
+ /* get scontext/tcontext in text form */
+ if (context_struct_to_string(scontext,
+ &scontext_name, &length) < 0)
+ goto out;
+
+ if (context_struct_to_string(tcontext,
+ &tcontext_name, &length) < 0)
+ goto out;
+
+ /* audit a message */
+ ab = audit_log_start(current->audit_context,
+ GFP_ATOMIC, AUDIT_SELINUX_ERR);
+ if (!ab)
+ goto out;
+
+ audit_log_format(ab, "op=security_compute_av reason=%s "
+ "scontext=%s tcontext=%s tclass=%s perms=",
+ reason, scontext_name, tcontext_name, tclass_name);
+
+ for (index = 0; index < 32; index++) {
+ u32 mask = (1 << index);
+
+ if ((mask & permissions) == 0)
+ continue;
+
+ audit_log_format(ab, "%s%s",
+ need_comma ? "," : "",
+ permission_names[index]
+ ? permission_names[index] : "????");
+ need_comma = true;
+ }
+ audit_log_end(ab);
+out:
+ /* release scontext/tcontext */
+ kfree(tcontext_name);
+ kfree(scontext_name);
+
+ return;
+}
+
+/*
* security_boundary_permission - drops violated permissions
* on boundary constraint.
*/
@@ -347,28 +441,12 @@ static void type_attribute_bounds_av(struct context *scontext,
}
if (masked) {
- struct audit_buffer *ab;
- char *stype_name
- = policydb.p_type_val_to_name[source->value - 1];
- char *ttype_name
- = policydb.p_type_val_to_name[target->value - 1];
- char *tclass_name
- = policydb.p_class_val_to_name[tclass - 1];
-
/* mask violated permissions */
avd->allowed &= ~masked;
- /* notice to userspace via audit message */
- ab = audit_log_start(current->audit_context,
- GFP_ATOMIC, AUDIT_SELINUX_ERR);
- if (!ab)
- return;
-
- audit_log_format(ab, "av boundary violation: "
- "source=%s target=%s tclass=%s",
- stype_name, ttype_name, tclass_name);
- avc_dump_av(ab, tclass, masked);
- audit_log_end(ab);
+ /* audit masked permissions */
+ security_dump_masked_av(scontext, tcontext,
+ tclass, masked, "bounds");
}
}
@@ -480,7 +558,7 @@ static int context_struct_compute_av(struct context *scontext,
if ((constraint->permissions & (avd->allowed)) &&
!constraint_expr_eval(scontext, tcontext, NULL,
constraint->expr)) {
- avd->allowed = (avd->allowed) & ~(constraint->permissions);
+ avd->allowed &= ~(constraint->permissions);
}
constraint = constraint->next;
}
@@ -499,8 +577,8 @@ static int context_struct_compute_av(struct context *scontext,
break;
}
if (!ra)
- avd->allowed = (avd->allowed) & ~(PROCESS__TRANSITION |
- PROCESS__DYNTRANSITION);
+ avd->allowed &= ~(PROCESS__TRANSITION |
+ PROCESS__DYNTRANSITION);
}
/*
@@ -687,6 +765,26 @@ int security_bounded_transition(u32 old_sid, u32 new_sid)
}
index = type->bounds;
}
+
+ if (rc) {
+ char *old_name = NULL;
+ char *new_name = NULL;
+ int length;
+
+ if (!context_struct_to_string(old_context,
+ &old_name, &length) &&
+ !context_struct_to_string(new_context,
+ &new_name, &length)) {
+ audit_log(current->audit_context,
+ GFP_ATOMIC, AUDIT_SELINUX_ERR,
+ "op=security_bounded_transition "
+ "result=denied "
+ "oldcontext=%s newcontext=%s",
+ old_name, new_name);
+ }
+ kfree(new_name);
+ kfree(old_name);
+ }
out:
read_unlock(&policy_rwlock);
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
index 72b18452e1a..f3cb9ed731a 100644
--- a/security/selinux/xfrm.c
+++ b/security/selinux/xfrm.c
@@ -401,7 +401,7 @@ int selinux_xfrm_state_delete(struct xfrm_state *x)
* gone thru the IPSec process.
*/
int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
- struct avc_audit_data *ad)
+ struct common_audit_data *ad)
{
int i, rc = 0;
struct sec_path *sp;
@@ -442,7 +442,7 @@ int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
* checked in the selinux_xfrm_state_pol_flow_match hook above.
*/
int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
- struct avc_audit_data *ad, u8 proto)
+ struct common_audit_data *ad, u8 proto)
{
struct dst_entry *dst;
int rc = 0;
diff --git a/security/smack/smack.h b/security/smack/smack.h
index 243bec175be..c6e9acae72e 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -275,7 +275,7 @@ static inline void smk_ad_init(struct smk_audit_info *a, const char *func,
{
memset(a, 0, sizeof(*a));
a->a.type = type;
- a->a.function = func;
+ a->a.smack_audit_data.function = func;
}
static inline void smk_ad_setfield_u_tsk(struct smk_audit_info *a,
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c
index 513dc1aa16d..0f9ac814690 100644
--- a/security/smack/smack_access.c
+++ b/security/smack/smack_access.c
@@ -240,8 +240,9 @@ static inline void smack_str_from_perm(char *string, int access)
static void smack_log_callback(struct audit_buffer *ab, void *a)
{
struct common_audit_data *ad = a;
- struct smack_audit_data *sad = &ad->lsm_priv.smack_audit_data;
- audit_log_format(ab, "lsm=SMACK fn=%s action=%s", ad->function,
+ struct smack_audit_data *sad = &ad->smack_audit_data;
+ audit_log_format(ab, "lsm=SMACK fn=%s action=%s",
+ ad->smack_audit_data.function,
sad->result ? "denied" : "granted");
audit_log_format(ab, " subject=");
audit_log_untrustedstring(ab, sad->subject);
@@ -274,11 +275,11 @@ void smack_log(char *subject_label, char *object_label, int request,
if (result == 0 && (log_policy & SMACK_AUDIT_ACCEPT) == 0)
return;
- if (a->function == NULL)
- a->function = "unknown";
+ if (a->smack_audit_data.function == NULL)
+ a->smack_audit_data.function = "unknown";
/* end preparing the audit data */
- sad = &a->lsm_priv.smack_audit_data;
+ sad = &a->smack_audit_data;
smack_str_from_perm(request_buffer, request);
sad->subject = subject_label;
sad->object = object_label;
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 0023182078c..c33b6bb9b6d 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -30,17 +30,11 @@
#include <net/netlabel.h>
#include <net/cipso_ipv4.h>
#include <linux/audit.h>
+#include <linux/magic.h>
#include "smack.h"
#define task_security(task) (task_cred_xxx((task), security))
-/*
- * I hope these are the hokeyist lines of code in the module. Casey.
- */
-#define DEVPTS_SUPER_MAGIC 0x1cd1
-#define SOCKFS_MAGIC 0x534F434B
-#define TMPFS_MAGIC 0x01021994
-
/**
* smk_fetch - Fetch the smack label from a file.
* @ip: a pointer to the inode
@@ -91,7 +85,7 @@ struct inode_smack *new_inode_smack(char *smack)
*/
/**
- * smack_ptrace_may_access - Smack approval on PTRACE_ATTACH
+ * smack_ptrace_access_check - Smack approval on PTRACE_ATTACH
* @ctp: child task pointer
* @mode: ptrace attachment mode
*
@@ -99,13 +93,13 @@ struct inode_smack *new_inode_smack(char *smack)
*
* Do the capability checks, and require read and write.
*/
-static int smack_ptrace_may_access(struct task_struct *ctp, unsigned int mode)
+static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode)
{
int rc;
struct smk_audit_info ad;
char *sp, *tsp;
- rc = cap_ptrace_may_access(ctp, mode);
+ rc = cap_ptrace_access_check(ctp, mode);
if (rc != 0)
return rc;
@@ -1080,6 +1074,22 @@ static int smack_file_receive(struct file *file)
*/
/**
+ * smack_cred_alloc_blank - "allocate" blank task-level security credentials
+ * @new: the new credentials
+ * @gfp: the atomicity of any memory allocations
+ *
+ * Prepare a blank set of credentials for modification. This must allocate all
+ * the memory the LSM module might require such that cred_transfer() can
+ * complete without error.
+ */
+static int smack_cred_alloc_blank(struct cred *cred, gfp_t gfp)
+{
+ cred->security = NULL;
+ return 0;
+}
+
+
+/**
* smack_cred_free - "free" task-level security credentials
* @cred: the credentials in question
*
@@ -1117,6 +1127,18 @@ static void smack_cred_commit(struct cred *new, const struct cred *old)
}
/**
+ * smack_cred_transfer - Transfer the old credentials to the new credentials
+ * @new: the new credentials
+ * @old: the original credentials
+ *
+ * Fill in a set of blank credentials from another set of credentials.
+ */
+static void smack_cred_transfer(struct cred *new, const struct cred *old)
+{
+ new->security = old->security;
+}
+
+/**
* smack_kernel_act_as - Set the subjective context in a set of credentials
* @new: points to the set of credentials to be modified.
* @secid: specifies the security ID to be set
@@ -1638,6 +1660,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) {
nsp->smk_inode = sp;
+ nsp->smk_flags |= SMK_INODE_INSTANT;
return 0;
}
/*
@@ -2464,7 +2487,7 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
/*
* Perfectly reasonable for this to be NULL
*/
- if (sip == NULL || sip->sin_family != PF_INET)
+ if (sip == NULL || sip->sin_family != AF_INET)
return 0;
return smack_netlabel_send(sock->sk, sip);
@@ -3029,10 +3052,31 @@ static void smack_release_secctx(char *secdata, u32 seclen)
{
}
+static int smack_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
+{
+ return smack_inode_setsecurity(inode, XATTR_SMACK_SUFFIX, ctx, ctxlen, 0);
+}
+
+static int smack_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
+{
+ return __vfs_setxattr_noperm(dentry, XATTR_NAME_SMACK, ctx, ctxlen, 0);
+}
+
+static int smack_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
+{
+ int len = 0;
+ len = smack_inode_getsecurity(inode, XATTR_SMACK_SUFFIX, ctx, true);
+
+ if (len < 0)
+ return len;
+ *ctxlen = len;
+ return 0;
+}
+
struct security_operations smack_ops = {
.name = "smack",
- .ptrace_may_access = smack_ptrace_may_access,
+ .ptrace_access_check = smack_ptrace_access_check,
.ptrace_traceme = smack_ptrace_traceme,
.syslog = smack_syslog,
@@ -3073,9 +3117,11 @@ struct security_operations smack_ops = {
.file_send_sigiotask = smack_file_send_sigiotask,
.file_receive = smack_file_receive,
+ .cred_alloc_blank = smack_cred_alloc_blank,
.cred_free = smack_cred_free,
.cred_prepare = smack_cred_prepare,
.cred_commit = smack_cred_commit,
+ .cred_transfer = smack_cred_transfer,
.kernel_act_as = smack_kernel_act_as,
.kernel_create_files_as = smack_kernel_create_files_as,
.task_setpgid = smack_task_setpgid,
@@ -3155,6 +3201,9 @@ struct security_operations smack_ops = {
.secid_to_secctx = smack_secid_to_secctx,
.secctx_to_secid = smack_secctx_to_secid,
.release_secctx = smack_release_secctx,
+ .inode_notifysecctx = smack_inode_notifysecctx,
+ .inode_setsecctx = smack_inode_setsecctx,
+ .inode_getsecctx = smack_inode_getsecctx,
};
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index f83a8098072..aeead758509 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -187,7 +187,7 @@ static void load_seq_stop(struct seq_file *s, void *v)
/* No-op */
}
-static struct seq_operations load_seq_ops = {
+static const struct seq_operations load_seq_ops = {
.start = load_seq_start,
.next = load_seq_next,
.show = load_seq_show,
@@ -503,7 +503,7 @@ static void cipso_seq_stop(struct seq_file *s, void *v)
/* No-op */
}
-static struct seq_operations cipso_seq_ops = {
+static const struct seq_operations cipso_seq_ops = {
.start = cipso_seq_start,
.stop = cipso_seq_stop,
.next = cipso_seq_next,
@@ -697,7 +697,7 @@ static void netlbladdr_seq_stop(struct seq_file *s, void *v)
/* No-op */
}
-static struct seq_operations netlbladdr_seq_ops = {
+static const struct seq_operations netlbladdr_seq_ops = {
.start = netlbladdr_seq_start,
.stop = netlbladdr_seq_stop,
.next = netlbladdr_seq_next,
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c
index fdd1f4b8c44..3c8bd8ee0b9 100644
--- a/security/tomoyo/common.c
+++ b/security/tomoyo/common.c
@@ -1285,6 +1285,36 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
}
/**
+ * tomoyo_delete_domain - Delete a domain.
+ *
+ * @domainname: The name of domain.
+ *
+ * Returns 0.
+ */
+static int tomoyo_delete_domain(char *domainname)
+{
+ struct tomoyo_domain_info *domain;
+ struct tomoyo_path_info name;
+
+ name.name = domainname;
+ tomoyo_fill_path_info(&name);
+ down_write(&tomoyo_domain_list_lock);
+ /* Is there an active domain? */
+ list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ /* Never delete tomoyo_kernel_domain */
+ if (domain == &tomoyo_kernel_domain)
+ continue;
+ if (domain->is_deleted ||
+ tomoyo_pathcmp(domain->domainname, &name))
+ continue;
+ domain->is_deleted = true;
+ break;
+ }
+ up_write(&tomoyo_domain_list_lock);
+ return 0;
+}
+
+/**
* tomoyo_write_domain_policy - Write domain policy.
*
* @head: Pointer to "struct tomoyo_io_buffer".
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
index 6d6ba09af45..31df541911f 100644
--- a/security/tomoyo/common.h
+++ b/security/tomoyo/common.h
@@ -339,8 +339,6 @@ const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain);
const char *tomoyo_get_msg(const bool is_enforce);
/* Convert single path operation to operation name. */
const char *tomoyo_sp2keyword(const u8 operation);
-/* Delete a domain. */
-int tomoyo_delete_domain(char *data);
/* Create "alias" entry in exception policy. */
int tomoyo_write_alias_policy(char *data, const bool is_delete);
/*
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c
index 1d8b1696057..fcf52accce2 100644
--- a/security/tomoyo/domain.c
+++ b/security/tomoyo/domain.c
@@ -717,38 +717,6 @@ int tomoyo_write_alias_policy(char *data, const bool is_delete)
return tomoyo_update_alias_entry(data, cp, is_delete);
}
-/* Domain create/delete handler. */
-
-/**
- * tomoyo_delete_domain - Delete a domain.
- *
- * @domainname: The name of domain.
- *
- * Returns 0.
- */
-int tomoyo_delete_domain(char *domainname)
-{
- struct tomoyo_domain_info *domain;
- struct tomoyo_path_info name;
-
- name.name = domainname;
- tomoyo_fill_path_info(&name);
- down_write(&tomoyo_domain_list_lock);
- /* Is there an active domain? */
- list_for_each_entry(domain, &tomoyo_domain_list, list) {
- /* Never delete tomoyo_kernel_domain */
- if (domain == &tomoyo_kernel_domain)
- continue;
- if (domain->is_deleted ||
- tomoyo_pathcmp(domain->domainname, &name))
- continue;
- domain->is_deleted = true;
- break;
- }
- up_write(&tomoyo_domain_list_lock);
- return 0;
-}
-
/**
* tomoyo_find_or_assign_new_domain - Create a domain.
*
@@ -818,13 +786,11 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
/**
* tomoyo_find_next_domain - Find a domain.
*
- * @bprm: Pointer to "struct linux_binprm".
- * @next_domain: Pointer to pointer to "struct tomoyo_domain_info".
+ * @bprm: Pointer to "struct linux_binprm".
*
* Returns 0 on success, negative value otherwise.
*/
-int tomoyo_find_next_domain(struct linux_binprm *bprm,
- struct tomoyo_domain_info **next_domain)
+int tomoyo_find_next_domain(struct linux_binprm *bprm)
{
/*
* This function assumes that the size of buffer returned by
@@ -946,9 +912,11 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm,
tomoyo_set_domain_flag(old_domain, false,
TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED);
out:
+ if (!domain)
+ domain = old_domain;
+ bprm->cred->security = domain;
tomoyo_free(real_program_name);
tomoyo_free(symlink_program_name);
- *next_domain = domain ? domain : old_domain;
tomoyo_free(tmp);
return retval;
}
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index 3194d09fe0f..9548a0984cc 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -14,6 +14,12 @@
#include "tomoyo.h"
#include "realpath.h"
+static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)
+{
+ new->security = NULL;
+ return 0;
+}
+
static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
gfp_t gfp)
{
@@ -25,6 +31,15 @@ static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
return 0;
}
+static void tomoyo_cred_transfer(struct cred *new, const struct cred *old)
+{
+ /*
+ * Since "struct tomoyo_domain_info *" is a sharable pointer,
+ * we don't need to duplicate.
+ */
+ new->security = old->security;
+}
+
static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
{
int rc;
@@ -61,14 +76,8 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
* Execute permission is checked against pathname passed to do_execve()
* using current domain.
*/
- if (!domain) {
- struct tomoyo_domain_info *next_domain = NULL;
- int retval = tomoyo_find_next_domain(bprm, &next_domain);
-
- if (!retval)
- bprm->cred->security = next_domain;
- return retval;
- }
+ if (!domain)
+ return tomoyo_find_next_domain(bprm);
/*
* Read permission is checked against interpreters using next domain.
* '1' is the result of open_to_namei_flags(O_RDONLY).
@@ -268,7 +277,9 @@ static int tomoyo_dentry_open(struct file *f, const struct cred *cred)
*/
static struct security_operations tomoyo_security_ops = {
.name = "tomoyo",
+ .cred_alloc_blank = tomoyo_cred_alloc_blank,
.cred_prepare = tomoyo_cred_prepare,
+ .cred_transfer = tomoyo_cred_transfer,
.bprm_set_creds = tomoyo_bprm_set_creds,
.bprm_check_security = tomoyo_bprm_check_security,
#ifdef CONFIG_SYSCTL
diff --git a/security/tomoyo/tomoyo.h b/security/tomoyo/tomoyo.h
index 0fd588a629c..cd6ba0bf706 100644
--- a/security/tomoyo/tomoyo.h
+++ b/security/tomoyo/tomoyo.h
@@ -31,8 +31,7 @@ int tomoyo_check_2path_perm(struct tomoyo_domain_info *domain,
struct path *path2);
int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
struct file *filp);
-int tomoyo_find_next_domain(struct linux_binprm *bprm,
- struct tomoyo_domain_info **next_domain);
+int tomoyo_find_next_domain(struct linux_binprm *bprm);
/* Index numbers for Access Controls. */