diff options
Diffstat (limited to 'security')
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_t) arg2, + (unsigned) arg3, + (unsigned) arg4, + (key_serial_t) arg5); + + case KEYCTL_INSTANTIATE_IOV: + return keyctl_instantiate_key_iov( + (key_serial_t) arg2, + (const struct iovec __user *) arg3, + (unsigned) arg4, + (key_serial_t) arg5); + default: return -EOPNOTSUPP; } diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 5620f084dede..cdd2f3f88c88 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -352,7 +352,7 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref, goto error_2; if (key->expiry && now.tv_sec >= key->expiry) goto error_2; - key_ref = ERR_PTR(-ENOKEY); + key_ref = ERR_PTR(key->type_data.reject_error); if (kflags & (1 << KEY_FLAG_NEGATIVE)) goto error_2; goto found; @@ -401,7 +401,7 @@ descend: /* we set a different error code if we pass a negative key */ if (kflags & (1 << KEY_FLAG_NEGATIVE)) { - err = -ENOKEY; + err = key->type_data.reject_error; continue; } diff --git a/security/keys/request_key.c b/security/keys/request_key.c index a3dc0d460def..df3c0417ee40 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -585,7 +585,7 @@ int wait_for_key_construction(struct key *key, bool intr) if (ret < 0) return ret; if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) - return -ENOKEY; + return key->type_data.reject_error; return key_validate(key); < |