summaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-03-16 09:15:43 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2011-03-16 09:15:43 -0700
commit0f6e0e8448a16d8d22119ce91d8dd24b44865b51 (patch)
tree7c295c02db035fc6a0b867465911a2bc9dc6b1ef /security
parent0d2ecee2bdb2a19d04bc5cefac0f86e790f1aad4 (diff)
parenta002951c97ff8da49938c982a4c236bf2fafdc9f (diff)
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6: (33 commits) AppArmor: kill unused macros in lsm.c AppArmor: cleanup generated files correctly KEYS: Add an iovec version of KEYCTL_INSTANTIATE KEYS: Add a new keyctl op to reject a key with a specified error code KEYS: Add a key type op to permit the key description to be vetted KEYS: Add an RCU payload dereference macro AppArmor: Cleanup make file to remove cruft and make it easier to read SELinux: implement the new sb_remount LSM hook LSM: Pass -o remount options to the LSM SELinux: Compute SID for the newly created socket SELinux: Socket retains creator role and MLS attribute SELinux: Auto-generate security_is_socket_class TOMOYO: Fix memory leak upon file open. Revert "selinux: simplify ioctl checking" selinux: drop unused packet flow permissions selinux: Fix packet forwarding checks on postrouting selinux: Fix wrong checks for selinux_policycap_netpeer selinux: Fix check for xfrm selinux context algorithm ima: remove unnecessary call to ima_must_measure IMA: remove IMA imbalance checking ...
Diffstat (limited to 'security')
-rw-r--r--security/apparmor/Makefile38
-rw-r--r--security/apparmor/lsm.c2
-rw-r--r--security/capability.c15
-rw-r--r--security/integrity/ima/ima.h3
-rw-r--r--security/integrity/ima/ima_api.c13
-rw-r--r--security/integrity/ima/ima_iint.c5
-rw-r--r--security/integrity/ima/ima_main.c136
-rw-r--r--security/keys/compat.c50
-rw-r--r--security/keys/encrypted.c3
-rw-r--r--security/keys/internal.h8
-rw-r--r--security/keys/key.c27
-rw-r--r--security/keys/keyctl.c143
-rw-r--r--security/keys/keyring.c4
-rw-r--r--security/keys/request_key.c2
-rw-r--r--security/keys/trusted.c3
-rw-r--r--security/keys/user_defined.c3
-rw-r--r--security/security.c19
-rw-r--r--security/selinux/hooks.c350
-rw-r--r--security/selinux/include/classmap.h7
-rw-r--r--security/selinux/include/security.h8
-rw-r--r--security/selinux/ss/avtab.h23
-rw-r--r--security/selinux/ss/ebitmap.h1
-rw-r--r--security/selinux/ss/mls.c5
-rw-r--r--security/selinux/ss/mls.h3
-rw-r--r--security/selinux/ss/policydb.c130
-rw-r--r--security/selinux/ss/policydb.h14
-rw-r--r--security/selinux/ss/services.c73
-rw-r--r--security/selinux/xfrm.c2
-rw-r--r--security/smack/smack.h17
-rw-r--r--security/smack/smack_access.c52
-rw-r--r--security/smack/smack_lsm.c287
-rw-r--r--security/smack/smackfs.c370
-rw-r--r--security/tomoyo/file.c5
33 files changed, 1243 insertions, 578 deletions
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
index f204869399ea..2dafe50a2e25 100644
--- a/security/apparmor/Makefile
+++ b/security/apparmor/Makefile
@@ -6,19 +6,47 @@ apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
resource.o sid.o file.o
-clean-files: capability_names.h af_names.h
+clean-files := capability_names.h rlim_names.h
+
+# Build a lower case string table of capability names
+# Transforms lines from
+# #define CAP_DAC_OVERRIDE 1
+# to
+# [1] = "dac_override",
quiet_cmd_make-caps = GEN $@
-cmd_make-caps = echo "static const char *capability_names[] = {" > $@ ; sed -n -e "/CAP_FS_MASK/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z >> $@ ; echo "};" >> $@
+cmd_make-caps = echo "static const char *capability_names[] = {" > $@ ;\
+ sed $< >>$@ -r -n -e '/CAP_FS_MASK/d' \
+ -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\
+ echo "};" >> $@
+
+# Build a lower case string table of rlimit names.
+# Transforms lines from
+# #define RLIMIT_STACK 3 /* max stack size */
+# to
+# [RLIMIT_STACK] = "stack",
+#
+# and build a second integer table (with the second sed cmd), that maps
+# RLIMIT defines to the order defined in asm-generic/resource.h Thi is
+# required by policy load to map policy ordering of RLIMITs to internal
+# ordering for architectures that redefine an RLIMIT.
+# Transforms lines from
+# #define RLIMIT_STACK 3 /* max stack size */
+# to
+# RLIMIT_STACK,
quiet_cmd_make-rlim = GEN $@
-cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ; sed -n --e "/AF_MAX/d" -e "s/^\# \\?define[ \\t]\\+RLIMIT_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z >> $@ ; echo "};" >> $@ ; echo "static const int rlim_map[] = {" >> $@ ; sed -n -e "/AF_MAX/d" -e "s/^\# \\?define[ \\t]\\+\\(RLIMIT_[A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/\\1,/p" $< >> $@ ; echo "};" >> $@
+cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ;\
+ sed $< >> $@ -r -n \
+ -e 's/^\# ?define[ \t]+(RLIMIT_([A-Z0-9_]+)).*/[\1] = "\L\2",/p';\
+ echo "};" >> $@ ;\
+ echo "static const int rlim_map[] = {" >> $@ ;\
+ sed -r -n "s/^\# ?define[ \t]+(RLIMIT_[A-Z0-9_]+).*/\1,/p" $< >> $@ ;\
+ echo "};" >> $@
$(obj)/capability.o : $(obj)/capability_names.h
$(obj)/resource.o : $(obj)/rlim_names.h
$(obj)/capability_names.h : $(srctree)/include/linux/capability.h
$(call cmd,make-caps)
-$(obj)/af_names.h : $(srctree)/include/linux/socket.h
- $(call cmd,make-af)
$(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h
$(call cmd,make-rlim)
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index b7106f192b75..d21a427a35ae 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -693,11 +693,9 @@ static struct kernel_param_ops param_ops_aalockpolicy = {
static int param_set_audit(const char *val, struct kernel_param *kp);
static int param_get_audit(char *buffer, struct kernel_param *kp);
-#define param_check_audit(name, p) __param_check(name, p, int)
static int param_set_mode(const char *val, struct kernel_param *kp);
static int param_get_mode(char *buffer, struct kernel_param *kp);
-#define param_check_mode(name, p) __param_check(name, p, int)
/* Flag values, also controllable via /sys/module/apparmor/parameters
* We define special types as we want to do additional mediation.
diff --git a/security/capability.c b/security/capability.c
index 2a5df2b7da83..ab3d807accc3 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -12,11 +12,6 @@
#include <linux/security.h>
-static int cap_sysctl(ctl_table *table, int op)
-{
- return 0;
-}
-
static int cap_syslog(int type)
{
return 0;
@@ -59,6 +54,11 @@ static int cap_sb_copy_data(char *orig, char *copy)
return 0;
}
+static int cap_sb_remount(struct super_block *sb, void *data)
+{
+ return 0;
+}
+
static int cap_sb_kern_mount(struct super_block *sb, int flags, void *data)
{
return 0;
@@ -118,7 +118,8 @@ static void cap_inode_free_security(struct inode *inode)
}
static int cap_inode_init_security(struct inode *inode, struct inode *dir,
- char **name, void **value, size_t *len)
+ const struct qstr *qstr, char **name,
+ void **value, size_t *len)
{
return -EOPNOTSUPP;
}
@@ -880,7 +881,6 @@ void __init security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, capable);
set_to_cap_if_null(ops, quotactl);
set_to_cap_if_null(ops, quota_on);
- set_to_cap_if_null(ops, sysctl);
set_to_cap_if_null(ops, syslog);
set_to_cap_if_null(ops, settime);
set_to_cap_if_null(ops, vm_enough_memory);
@@ -892,6 +892,7 @@ void __init security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, sb_alloc_security);
set_to_cap_if_null(ops, sb_free_security);
set_to_cap_if_null(ops, sb_copy_data);
+ set_to_cap_if_null(ops, sb_remount);
set_to_cap_if_null(ops, sb_kern_mount);
set_to_cap_if_null(ops, sb_show_options);
set_to_cap_if_null(ops, sb_statfs);
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index ac79032bdf23..08408bd71462 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -110,8 +110,7 @@ struct ima_iint_cache {
};
/* LIM API function definitions */
-int ima_must_measure(struct ima_iint_cache *iint, struct inode *inode,
- int mask, int function);
+int ima_must_measure(struct inode *inode, int mask, int function);
int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file);
void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
const unsigned char *filename);
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index d3963de6003d..da36d2c085a4 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -105,20 +105,13 @@ err_out:
* mask: contains the permission mask
* fsmagic: hex value
*
- * Must be called with iint->mutex held.
- *
- * Return 0 to measure. Return 1 if already measured.
- * For matching a DONT_MEASURE policy, no policy, or other
- * error, return an error code.
+ * Return 0 to measure. For matching a DONT_MEASURE policy, no policy,
+ * or other error, return an error code.
*/
-int ima_must_measure(struct ima_iint_cache *iint, struct inode *inode,
- int mask, int function)
+int ima_must_measure(struct inode *inode, int mask, int function)
{
int must_measure;
- if (iint && iint->flags & IMA_MEASURED)
- return 1;
-
must_measure = ima_match_policy(inode, function, mask);
return must_measure ? 0 : -EACCES;
}
diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c
index c442e47b6785..4ae73040ab7b 100644
--- a/security/integrity/ima/ima_iint.c
+++ b/security/integrity/ima/ima_iint.c
@@ -137,11 +137,6 @@ void ima_inode_free(struct inode *inode)
{
struct ima_iint_cache *iint;
- if (inode->i_readcount)
- printk(KERN_INFO "%s: readcount: %u\n", __func__, inode->i_readcount);
-
- inode->i_readcount = 0;
-
if (!IS_IMA(inode))
return;
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 203de979d305..39d66dc2b8e9 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -36,67 +36,17 @@ static int __init hash_setup(char *str)
}
__setup("ima_hash=", hash_setup);
-struct ima_imbalance {
- struct hlist_node node;
- unsigned long fsmagic;
-};
-
-/*
- * ima_limit_imbalance - emit one imbalance message per filesystem type
- *
- * Maintain list of filesystem types that do not measure files properly.
- * Return false if unknown, true if known.
- */
-static bool ima_limit_imbalance(struct file *file)
-{
- static DEFINE_SPINLOCK(ima_imbalance_lock);
- static HLIST_HEAD(ima_imbalance_list);
-
- struct super_block *sb = file->f_dentry->d_sb;
- struct ima_imbalance *entry;
- struct hlist_node *node;
- bool found = false;
-
- rcu_read_lock();
- hlist_for_each_entry_rcu(entry, node, &ima_imbalance_list, node) {
- if (entry->fsmagic == sb->s_magic) {
- found = true;
- break;
- }
- }
- rcu_read_unlock();
- if (found)
- goto out;
-
- entry = kmalloc(sizeof(*entry), GFP_NOFS);
- if (!entry)
- goto out;
- entry->fsmagic = sb->s_magic;
- spin_lock(&ima_imbalance_lock);
- /*
- * we could have raced and something else might have added this fs
- * to the list, but we don't really care
- */
- hlist_add_head_rcu(&entry->node, &ima_imbalance_list);
- spin_unlock(&ima_imbalance_lock);
- printk(KERN_INFO "IMA: unmeasured files on fsmagic: %lX\n",
- entry->fsmagic);
-out:
- return found;
-}
-
/*
- * ima_counts_get - increment file counts
+ * ima_rdwr_violation_check
*
- * Maintain read/write counters for all files, but only
- * invalidate the PCR for measured files:
+ * Only invalidate the PCR for measured files:
* - Opening a file for write when already open for read,
* results in a time of measure, time of use (ToMToU) error.
* - Opening a file for read when already open for write,
* could result in a file measurement error.
*
*/
-void ima_counts_get(struct file *file)
+static void ima_rdwr_violation_check(struct file *file)
{
struct dentry *dentry = file->f_path.dentry;
struct inode *inode = dentry->d_inode;
@@ -104,32 +54,25 @@ void ima_counts_get(struct file *file)
int rc;
bool send_tomtou = false, send_writers = false;
- if (!S_ISREG(inode->i_mode))
+ if (!S_ISREG(inode->i_mode) || !ima_initialized)
return;
- spin_lock(&inode->i_lock);
-
- if (!ima_initialized)
- goto out;
+ mutex_lock(&inode->i_mutex); /* file metadata: permissions, xattr */
if (mode & FMODE_WRITE) {
- if (inode->i_readcount && IS_IMA(inode))
+ if (atomic_read(&inode->i_readcount) && IS_IMA(inode))
send_tomtou = true;
goto out;
}
- rc = ima_must_measure(NULL, inode, MAY_READ, FILE_CHECK);
+ rc = ima_must_measure(inode, MAY_READ, FILE_CHECK);
if (rc < 0)
goto out;
if (atomic_read(&inode->i_writecount) > 0)
send_writers = true;
out:
- /* remember the vfs deals with i_writecount */
- if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
- inode->i_readcount++;
-
- spin_unlock(&inode->i_lock);
+ mutex_unlock(&inode->i_mutex);
if (send_tomtou)
ima_add_violation(inode, dentry->d_name.name, "invalid_pcr",
@@ -139,71 +82,25 @@ out:
"open_writers");
}
-/*
- * Decrement ima counts
- */
-static void ima_dec_counts(struct inode *inode, struct file *file)
-{
- mode_t mode = file->f_mode;
-
- assert_spin_locked(&inode->i_lock);
-
- if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
- if (unlikely(inode->i_readcount == 0)) {
- if (!ima_limit_imbalance(file)) {
- printk(KERN_INFO "%s: open/free imbalance (r:%u)\n",
- __func__, inode->i_readcount);
- dump_stack();
- }
- return;
- }
- inode->i_readcount--;
- }
-}
-
static void ima_check_last_writer(struct ima_iint_cache *iint,
struct inode *inode,
struct file *file)
{
mode_t mode = file->f_mode;
- BUG_ON(!mutex_is_locked(&iint->mutex));
- assert_spin_locked(&inode->i_lock);
-
+ mutex_lock(&iint->mutex);
if (mode & FMODE_WRITE &&
atomic_read(&inode->i_writecount) == 1 &&
iint->version != inode->i_version)
iint->flags &= ~IMA_MEASURED;
-}
-
-static void ima_file_free_iint(struct ima_iint_cache *iint, struct inode *inode,
- struct file *file)
-{
- mutex_lock(&iint->mutex);
- spin_lock(&inode->i_lock);
-
- ima_dec_counts(inode, file);
- ima_check_last_writer(iint, inode, file);
-
- spin_unlock(&inode->i_lock);
mutex_unlock(&iint->mutex);
}
-static void ima_file_free_noiint(struct inode *inode, struct file *file)
-{
- spin_lock(&inode->i_lock);
-
- ima_dec_counts(inode, file);
-
- spin_unlock(&inode->i_lock);
-}
-
/**
* ima_file_free - called on __fput()
* @file: pointer to file structure being freed
*
- * Flag files that changed, based on i_version;
- * and decrement the i_readcount.
+ * Flag files that changed, based on i_version
*/
void ima_file_free(struct file *file)
{
@@ -214,12 +111,10 @@ void ima_file_free(struct file *file)
return;
iint = ima_iint_find(inode);
+ if (!iint)
+ return;
- if (iint)
- ima_file_free_iint(iint, inode, file);
- else
- ima_file_free_noiint(inode, file);
-
+ ima_check_last_writer(iint, inode, file);
}
static int process_measurement(struct file *file, const unsigned char *filename,
@@ -232,7 +127,7 @@ static int process_measurement(struct file *file, const unsigned char *filename,
if (!ima_initialized || !S_ISREG(inode->i_mode))
return 0;
- rc = ima_must_measure(NULL, inode, mask, function);
+ rc = ima_must_measure(inode, mask, function);
if (rc != 0)
return rc;
retry:
@@ -246,7 +141,7 @@ retry:
mutex_lock(&iint->mutex);
- rc = ima_must_measure(iint, inode, mask, function);
+ rc = iint->flags & IMA_MEASURED ? 1 : 0;
if (rc != 0)
goto out;
@@ -317,6 +212,7 @@ int ima_file_check(struct file *file, int mask)
{
int rc;
+ ima_rdwr_violation_check(file);
rc = process_measurement(file, file->f_dentry->d_name.name,
mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
FILE_CHECK);
diff --git a/security/keys/compat.c b/security/keys/compat.c
index 07a5f35e3970..338b510e9027 100644
--- a/security/keys/compat.c
+++ b/security/keys/compat.c
@@ -12,9 +12,52 @@
#include <linux/syscalls.h>
#include <linux/keyctl.h>
#include <linux/compat.h>
+#include <linux/slab.h>
#include "internal.h"
/*
+ * Instantiate a key with the specified compatibility multipart payload and
+ * link the key into the destination keyring if one is given.
+ *
+ * The caller must have the appropriate instantiation permit set for this to
+ * work (see keyctl_assume_authority). No other permissions are required.
+ *
+ * If successful, 0 will be returned.
+ */
+long compat_keyctl_instantiate_key_iov(
+ key_serial_t id,
+ const struct compat_iovec __user *_payload_iov,
+ unsigned ioc,
+ key_serial_t ringid)
+{
+ struct iovec iovstack[UIO_FASTIOV], *iov = iovstack;
+ long ret;
+
+ if (_payload_iov == 0 || ioc == 0)
+ goto no_payload;
+
+ ret = compat_rw_copy_check_uvector(WRITE, _payload_iov, ioc,
+ ARRAY_SIZE(iovstack),
+ iovstack, &iov);
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
+ goto no_payload_free;
+
+ ret = keyctl_instantiate_key_common(id, iov, ioc, ret, ringid);
+
+ if (iov != iovstack)
+ kfree(iov);
+ return ret;
+
+no_payload_free:
+ if (iov != iovstack)
+ kfree(iov);
+no_payload:
+ return keyctl_instantiate_key_common(id, NULL, 0, 0, ringid);
+}
+
+/*
* The key control system call, 32-bit compatibility version for 64-bit archs
*
* This should only be called if the 64-bit arch uses weird pointers in 32-bit
@@ -85,6 +128,13 @@ asmlinkage long compat_sys_keyctl(u32 option,
case KEYCTL_SESSION_TO_PARENT:
return keyctl_session_to_parent();
+ case KEYCTL_REJECT:
+ return keyctl_reject_key(arg2, arg3, arg4, arg5);
+
+ case KEYCTL_INSTANTIATE_IOV:
+ return compat_keyctl_instantiate_key_iov(
+ arg2, compat_ptr(arg3), arg4, arg5);
+
default:
return -EOPNOTSUPP;
}
diff --git a/security/keys/encrypted.c b/security/keys/encrypted.c
index 9e7e4ce3fae8..69907a58a683 100644
--- a/security/keys/encrypted.c
+++ b/security/keys/encrypted.c
@@ -765,8 +765,7 @@ static long encrypted_read(const struct key *key, char __user *buffer,
size_t asciiblob_len;
int ret;
- epayload = rcu_dereference_protected(key->payload.data,
- rwsem_is_locked(&((struct key *)key)->sem));
+ epayload = rcu_dereference_key(key);
/* returns the hex encoded iv, encrypted-data, and hmac as ascii */
asciiblob_len = epayload->datablob_len + ivsize + 1
diff --git a/security/keys/internal.h b/security/keys/internal.h
index a52aa7c88b41..07a025f81902 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -214,6 +214,14 @@ 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);
+extern long keyctl_reject_key(key_serial_t, unsigned, unsigned, key_serial_t);
+extern long keyctl_instantiate_key_iov(key_serial_t,
+ const struct iovec __user *,
+ unsigned, key_serial_t);
+
+extern long keyctl_instantiate_key_common(key_serial_t,
+ const struct iovec __user *,
+ unsigned, size_t, key_serial_t);
/*
* Debugging key validation
diff --git a/security/keys/key.c b/security/keys/key.c
index 1c2d43dc5107..f7f9d93f08d9 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -249,6 +249,14 @@ struct key *key_alloc(struct key_type *type, const char *desc,
if (!desc || !*desc)
goto error;
+ if (type->vet_description) {
+ ret = type->vet_description(desc);
+ if (ret < 0) {
+ key = ERR_PTR(ret);
+ goto error;
+ }
+ }
+
desclen = strlen(desc) + 1;
quotalen = desclen + type->def_datalen;
@@ -503,26 +511,29 @@ int key_instantiate_and_link(struct key *key,
EXPORT_SYMBOL(key_instantiate_and_link);
/**
- * key_negate_and_link - Negatively instantiate a key and link it into the keyring.
+ * key_reject_and_link - Negatively instantiate a key and link it into the keyring.
* @key: The key to instantiate.
* @timeout: The timeout on the negative key.
+ * @error: The error to return when the key is hit.
* @keyring: Keyring to create a link in on success (or NULL).
* @authkey: The authorisation token permitting instantiation.
*
* Negatively instantiate a key that's in the uninstantiated state and, if
- * successful, set its timeout and link it in to the destination keyring if one
- * is supplied. The key and any links to the key will be automatically garbage
- * collected after the timeout expires.
+ * successful, set its timeout and stored error and link it in to the
+ * destination keyring if one is supplied. The key and any links to the key
+ * will be automatically garbage collected after the timeout expires.
*
* Negative keys are used to rate limit repeated request_key() calls by causing
- * them to return -ENOKEY until the negative key expires.
+ * them to return the stored error code (typically ENOKEY) until the negative
+ * key expires.
*
* If successful, 0 is returned, the authorisation token is revoked and anyone
* waiting for the key is woken up. If the key was already instantiated,
* -EBUSY will be returned.
*/
-int key_negate_and_link(struct key *key,
+int key_reject_and_link(struct key *key,
unsigned timeout,
+ unsigned error,
struct key *keyring,
struct key *authkey)
{
@@ -548,6 +559,7 @@ int key_negate_and_link(struct key *key,
atomic_inc(&key->user->nikeys);
set_bit(KEY_FLAG_NEGATIVE, &key->flags);
set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
+ key->type_data.reject_error = -error;
now = current_kernel_time();
key->expiry = now.tv_sec + timeout;
key_schedule_gc(key->expiry + key_gc_delay);
@@ -577,8 +589,7 @@ int key_negate_and_link(struct key *key,
return ret == 0 ? link_ret : ret;
}
-
-EXPORT_SYMBOL(key_negate_and_link);
+EXPORT_SYMBOL(key_reject_and_link);
/*
* Garbage collect keys in process context so that we don't have to disable
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 31a0fd8189f1..427fddcaeb19 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -913,6 +913,21 @@ static int keyctl_change_reqkey_auth(struct key *key)
}
/*
+ * Copy the iovec data from userspace
+ */
+static long copy_from_user_iovec(void *buffer, const struct iovec *iov,
+ unsigned ioc)
+{
+ for (; ioc > 0; ioc--) {
+ if (copy_from_user(buffer, iov->iov_base, iov->iov_len) != 0)
+ return -EFAULT;
+ buffer += iov->iov_len;
+ iov++;
+ }
+ return 0;
+}
+
+/*
* Instantiate a key with the specified payload and link the key into the
* destination keyring if one is given.
*
@@ -921,10 +936,11 @@ static int keyctl_change_reqkey_auth(struct key *key)
*
* If successful, 0 will be returned.
*/
-long keyctl_instantiate_key(key_serial_t id,
- const void __user *_payload,
- size_t plen,
- key_serial_t ringid)
+long keyctl_instantiate_key_common(key_serial_t id,
+ const struct iovec *payload_iov,
+ unsigned ioc,
+ size_t plen,
+ key_serial_t ringid)
{
const struct cred *cred = current_cred();
struct request_key_auth *rka;
@@ -953,7 +969,7 @@ long keyctl_instantiate_key(key_serial_t id,
/* pull the payload in if one was supplied */
payload = NULL;
- if (_payload) {
+ if (payload_iov) {
ret = -ENOMEM;
payload = kmalloc(plen, GFP_KERNEL);
if (!payload) {
@@ -965,8 +981,8 @@ long keyctl_instantiate_key(key_serial_t id,
goto error;
}
- ret = -EFAULT;
- if (copy_from_user(payload, _payload, plen) != 0)
+ ret = copy_from_user_iovec(payload, payload_iov, ioc);
+ if (ret < 0)
goto error2;
}
@@ -997,6 +1013,72 @@ error:
}
/*
+ * Instantiate a key with the specified payload and link the key into the
+ * destination keyring if one is given.
+ *
+ * The caller must have the appropriate instantiation permit set for this to
+ * work (see keyctl_assume_authority). No other permissions are required.
+ *
+ * If successful, 0 will be returned.
+ */
+long keyctl_instantiate_key(key_serial_t id,
+ const void __user *_payload,
+ size_t plen,
+ key_serial_t ringid)
+{
+ if (_payload && plen) {
+ struct iovec iov[1] = {
+ [0].iov_base = (void __user *)_payload,
+ [0].iov_len = plen
+ };
+
+ return keyctl_instantiate_key_common(id, iov, 1, plen, ringid);
+ }
+
+ return keyctl_instantiate_key_common(id, NULL, 0, 0, ringid);
+}
+
+/*
+ * Instantiate a key with the specified multipart payload and link the key into
+ * the destination keyring if one is given.
+ *
+ * The caller must have the appropriate instantiation permit set for this to
+ * work (see keyctl_assume_authority). No other permissions are required.
+ *
+ * If successful, 0 will be returned.
+ */
+long keyctl_instantiate_key_iov(key_serial_t id,
+ const struct iovec __user *_payload_iov,
+ unsigned ioc,
+ key_serial_t ringid)
+{
+ struct iovec iovstack[UIO_FASTIOV], *iov = iovstack;
+ long ret;
+
+ if (_payload_iov == 0 || ioc == 0)
+ goto no_payload;
+
+ ret = rw_copy_check_uvector(WRITE, _payload_iov, ioc,
+ ARRAY_SIZE(iovstack), iovstack, &iov);
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
+ goto no_payload_free;
+
+ ret = keyctl_instantiate_key_common(id, iov, ioc, ret, ringid);
+
+ if (iov != iovstack)
+ kfree(iov);
+ return ret;
+
+no_payload_free:
+ if (iov != iovstack)
+ kfree(iov);
+no_payload:
+ return keyctl_instantiate_key_common(id, NULL, 0, 0, ringid);
+}
+
+/*
* Negatively instantiate the key with the given timeout (in seconds) and link
* the key into the destination keyring if one is given.
*
@@ -1013,12 +1095,42 @@ error:
*/
long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
{
+ return keyctl_reject_key(id, timeout, ENOKEY, ringid);
+}
+
+/*
+ * Negatively instantiate the key with the given timeout (in seconds) and error
+ * code and link the key into the destination keyring if one is given.
+ *
+ * The caller must have the appropriate instantiation permit set for this to
+ * work (see keyctl_assume_authority). No other permissions are required.
+ *
+ * The key and any links to the key will be automatically garbage collected
+ * after the timeout expires.
+ *
+ * Negative keys are used to rate limit repeated request_key() calls by causing
+ * them to return the specified error code until the negative key expires.
+ *
+ * If successful, 0 will be returned.
+ */
+long keyctl_reject_key(key_serial_t id, unsigned timeout, unsigned error,
+ key_serial_t ringid)
+{
const struct cred *cred = current_cred();
struct request_key_auth *rka;
struct key *instkey, *dest_keyring;
long ret;
- kenter("%d,%u,%d", id, timeout, ringid);
+ kenter("%d,%u,%u,%d", id, timeout, error, ringid);
+
+ /* must be a valid error code and mustn't be a kernel special */
+ if (error <= 0 ||
+ error >= MAX_ERRNO ||
+ error == ERESTARTSYS ||
+ error == ERESTARTNOINTR ||
+ error == ERESTARTNOHAND ||
+ error == ERESTART_RESTARTBLOCK)
+ return -EINVAL;
/* the appropriate instantiation authorisation key must have been
* assumed before calling this */
@@ -1038,7 +1150,7 @@ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
goto error;
/* instantiate the key and link it into a keyring */
- ret = key_negate_and_link(rka->target_key, timeout,
+ ret = key_reject_and_link(rka->target_key, timeout, error,
dest_keyring, instkey);
key_put(dest_keyring);
@@ -1492,6 +1604,19 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
case KEYCTL_SESSION_TO_PARENT:
return keyctl_session_to_parent();
+ case KEYCTL_REJECT:
+ return keyctl_reject_key((key_serial_