diff options
Diffstat (limited to 'security/keys')
-rw-r--r-- | security/keys/compat.c | 50 | ||||
-rw-r--r-- | security/keys/encrypted.c | 3 | ||||
-rw-r--r-- | security/keys/internal.h | 8 | ||||
-rw-r--r-- | security/keys/key.c | 27 | ||||
-rw-r--r-- | security/keys/keyctl.c | 143 | ||||
-rw-r--r-- | security/keys/keyring.c | 4 | ||||
-rw-r--r-- | security/keys/request_key.c | 2 | ||||
-rw-r--r-- | security/keys/trusted.c | 3 | ||||
-rw-r--r-- | security/keys/user_defined.c | 3 |
9 files changed, 217 insertions, 26 deletions
diff --git a/security/keys/compat.c b/security/keys/compat.c index 07a5f35e397..338b510e902 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 9e7e4ce3fae..69907a58a68 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 a52aa7c88b4..07a025f8190 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 1c2d43dc510..f7f9d93f08d 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 31a0fd8189f..427fddcaeb1 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 5620f084ded..cdd2f3f88c8 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 a3dc0d460de..df3c0417ee4 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); } EXPORT_SYMBOL(wait_for_key_construction); diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 83fc92e297c..c99b9368368 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -1076,8 +1076,7 @@ static long trusted_read(const struct key *key, char __user *buffer, char *bufp; int i; - p = rcu_dereference_protected(key->payload.data, - rwsem_is_locked(&((struct key *)key)->sem)); + p = rcu_dereference_key(key); if (!p) return -EINVAL; if (!buffer || buflen <= 0) diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index 02807fb1634..c6ca8662a46 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -184,8 +184,7 @@ long user_read(const struct key *key, char __user *buffer, size_t buflen) struct user_key_payload *upayload; long ret; - upayload = rcu_dereference_protected( - key->payload.data, rwsem_is_locked(&((struct key *)key)->sem)); + upayload = rcu_dereference_key(key); ret = upayload->datalen; /* we can return the data as is */ |