Commit d44a6274 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'keys-misc-20190619' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs

Pull misc keyring updates from David Howells:
 "These are some miscellaneous keyrings fixes and improvements:

   - Fix a bunch of warnings from sparse, including missing RCU bits and
     kdoc-function argument mismatches

   - Implement a keyctl to allow a key to be moved from one keyring to
     another, with the option of prohibiting key replacement in the
     destination keyring.

   - Grant Link permission to possessors of request_key_auth tokens so
     that upcall servicing daemons can more easily arrange things such
     that only the necessary auth key is passed to the actual service
     program, and not all the auth keys a daemon might possesss.

   - Improvement in lookup_user_key().

   - Implement a keyctl to allow keyrings subsystem capabilities to be
     queried.

  The keyutils next branch has commits to make available, document and
  test the move-key and capabilities code:

        https://git.kernel.org/pub/scm/linux/kernel/git/dhowells/keyutils.git/log

  They're currently on the 'next' branch"

* tag 'keys-misc-20190619' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs:
  keys: Add capability-checking keyctl function
  keys: Reuse keyring_index_key::desc_len in lookup_user_key()
  keys: Grant Link permission to possessers of request_key auth keys
  keys: Add a keyctl to move a key between keyrings
  keys: Hoist locking out of __key_link_begin()
  keys: Break bits out of key_unlink()
  keys: Change keyring_serialise_link_sem to a mutex
  keys: sparse: Fix kdoc mismatches
  keys: sparse: Fix incorrect RCU accesses
  keys: sparse: Fix key_fs[ug]id_changed()
parents 7c0f8963 45e0f30c
......@@ -577,6 +577,27 @@ The keyctl syscall functions are:
added.
* Move a key from one keyring to another::
long keyctl(KEYCTL_MOVE,
key_serial_t id,
key_serial_t from_ring_id,
key_serial_t to_ring_id,
unsigned int flags);
Move the key specified by "id" from the keyring specified by
"from_ring_id" to the keyring specified by "to_ring_id". If the two
keyrings are the same, nothing is done.
"flags" can have KEYCTL_MOVE_EXCL set in it to cause the operation to fail
with EEXIST if a matching key exists in the destination keyring, otherwise
such a key will be replaced.
A process must have link permission on the key for this function to be
successful and write permission on both keyrings. Any errors that can
occur from KEYCTL_LINK also apply on the destination keyring here.
* Unlink a key or keyring from another keyring::
long keyctl(KEYCTL_UNLINK, key_serial_t keyring, key_serial_t key);
......
......@@ -305,6 +305,11 @@ extern int key_update(key_ref_t key,
extern int key_link(struct key *keyring,
struct key *key);
extern int key_move(struct key *key,
struct key *from_keyring,
struct key *to_keyring,
unsigned int flags);
extern int key_unlink(struct key *keyring,
struct key *key);
......@@ -397,8 +402,8 @@ extern struct ctl_table key_sysctls[];
* the userspace interface
*/
extern int install_thread_keyring_to_cred(struct cred *cred);
extern void key_fsuid_changed(struct task_struct *tsk);
extern void key_fsgid_changed(struct task_struct *tsk);
extern void key_fsuid_changed(struct cred *new_cred);
extern void key_fsgid_changed(struct cred *new_cred);
extern void key_init(void);
#else /* CONFIG_KEYS */
......@@ -413,8 +418,8 @@ extern void key_init(void);
#define make_key_ref(k, p) NULL
#define key_ref_to_ptr(k) NULL
#define is_key_possessed(k) 0
#define key_fsuid_changed(t) do { } while(0)
#define key_fsgid_changed(t) do { } while(0)
#define key_fsuid_changed(c) do { } while(0)
#define key_fsgid_changed(c) do { } while(0)
#define key_init() do { } while(0)
#endif /* CONFIG_KEYS */
......
......@@ -67,6 +67,8 @@
#define KEYCTL_PKEY_SIGN 27 /* Create a public key signature */
#define KEYCTL_PKEY_VERIFY 28 /* Verify a public key signature */
#define KEYCTL_RESTRICT_KEYRING 29 /* Restrict keys allowed to link to a keyring */
#define KEYCTL_MOVE 30 /* Move keys between keyrings */
#define KEYCTL_CAPABILITIES 31 /* Find capabilities of keyrings subsystem */
/* keyctl structures */
struct keyctl_dh_params {
......@@ -112,4 +114,19 @@ struct keyctl_pkey_params {
__u32 __spare[7];
};
#define KEYCTL_MOVE_EXCL 0x00000001 /* Do not displace from the to-keyring */
/*
* Capabilities flags. The capabilities list is an array of 8-bit integers;
* each integer can carry up to 8 flags.
*/
#define KEYCTL_CAPS0_CAPABILITIES 0x01 /* KEYCTL_CAPABILITIES supported */
#define KEYCTL_CAPS0_PERSISTENT_KEYRINGS 0x02 /* Persistent keyrings enabled */
#define KEYCTL_CAPS0_DIFFIE_HELLMAN 0x04 /* Diffie-Hellman computation enabled */
#define KEYCTL_CAPS0_PUBLIC_KEY 0x08 /* Public key ops enabled */
#define KEYCTL_CAPS0_BIG_KEY 0x10 /* big_key-type enabled */
#define KEYCTL_CAPS0_INVALIDATE 0x20 /* KEYCTL_INVALIDATE supported */
#define KEYCTL_CAPS0_RESTRICT_KEYRING 0x40 /* KEYCTL_RESTRICT_KEYRING supported */
#define KEYCTL_CAPS0_MOVE 0x80 /* KEYCTL_MOVE supported */
#endif /* _LINUX_KEYCTL_H */
......@@ -460,9 +460,9 @@ int commit_creds(struct cred *new)
/* alter the thread keyring */
if (!uid_eq(new->fsuid, old->fsuid))
key_fsuid_changed(task);
key_fsuid_changed(new);
if (!gid_eq(new->fsgid, old->fsgid))
key_fsgid_changed(task);
key_fsgid_changed(new);
/* do it
* RLIMIT_NPROC limits on user->processes have already been checked
......
......@@ -155,6 +155,12 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option,
return keyctl_pkey_verify(compat_ptr(arg2), compat_ptr(arg3),
compat_ptr(arg4), compat_ptr(arg5));
case KEYCTL_MOVE:
return keyctl_keyring_move(arg2, arg3, arg4, arg5);
case KEYCTL_CAPABILITIES:
return keyctl_capabilities(compat_ptr(arg2), arg3);
default:
return -EOPNOTSUPP;
}
......
......@@ -89,6 +89,10 @@ extern wait_queue_head_t request_key_conswq;
extern struct key_type *key_type_lookup(const char *type);
extern void key_type_put(struct key_type *ktype);
extern int __key_link_lock(struct key *keyring,
const struct keyring_index_key *index_key);
extern int __key_move_lock(struct key *l_keyring, struct key *u_keyring,
const struct keyring_index_key *index_key);
extern int __key_link_begin(struct key *keyring,
const struct keyring_index_key *index_key,
struct assoc_array_edit **_edit);
......@@ -211,6 +215,7 @@ extern long keyctl_update_key(key_serial_t, const void __user *, size_t);
extern long keyctl_revoke_key(key_serial_t);
extern long keyctl_keyring_clear(key_serial_t);
extern long keyctl_keyring_link(key_serial_t, key_serial_t);
extern long keyctl_keyring_move(key_serial_t, key_serial_t, key_serial_t, unsigned int);
extern long keyctl_keyring_unlink(key_serial_t, key_serial_t);
extern long keyctl_describe_key(key_serial_t, char __user *, size_t);
extern long keyctl_keyring_search(key_serial_t, const char __user *,
......@@ -320,6 +325,8 @@ static inline long keyctl_pkey_e_d_s(int op,
}
#endif
extern long keyctl_capabilities(unsigned char __user *_buffer, size_t buflen);
/*
* Debugging key validation
*/
......
......@@ -496,7 +496,7 @@ int key_instantiate_and_link(struct key *key,
struct key *authkey)
{
struct key_preparsed_payload prep;
struct assoc_array_edit *edit;
struct assoc_array_edit *edit = NULL;
int ret;
memset(&prep, 0, sizeof(prep));
......@@ -511,10 +511,14 @@ int key_instantiate_and_link(struct key *key,
}
if (keyring) {
ret = __key_link_begin(keyring, &key->index_key, &edit);
ret = __key_link_lock(keyring, &key->index_key);
if (ret < 0)
goto error;
ret = __key_link_begin(keyring, &key->index_key, &edit);
if (ret < 0)
goto error_link_end;
if (keyring->restrict_link && keyring->restrict_link->check) {
struct key_restriction *keyres = keyring->restrict_link;
......@@ -566,7 +570,7 @@ int key_reject_and_link(struct key *key,
struct key *keyring,
struct key *authkey)
{
struct assoc_array_edit *edit;
struct assoc_array_edit *edit = NULL;
int ret, awaken, link_ret = 0;
key_check(key);
......@@ -579,7 +583,12 @@ int key_reject_and_link(struct key *key,
if (keyring->restrict_link)
return -EPERM;
link_ret = __key_link_lock(keyring, &key->index_key);
if (link_ret == 0) {
link_ret = __key_link_begin(keyring, &key->index_key, &edit);
if (link_ret < 0)
__key_link_end(keyring, &key->index_key, edit);
}
}
mutex_lock(&key_construction_mutex);
......@@ -806,7 +815,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
.description = description,
};
struct key_preparsed_payload prep;
struct assoc_array_edit *edit;
struct assoc_array_edit *edit = NULL;
const struct cred *cred = current_cred();
struct key *keyring, *key = NULL;
key_ref_t key_ref;
......@@ -856,12 +865,18 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
}
index_key.desc_len = strlen(index_key.description);
ret = __key_link_begin(keyring, &index_key, &edit);
ret = __key_link_lock(keyring, &index_key);
if (ret < 0) {
key_ref = ERR_PTR(ret);
goto error_free_prep;
}
ret = __key_link_begin(keyring, &index_key, &edit);
if (ret < 0) {
key_ref = ERR_PTR(ret);
goto error_link_end;
}
if (restrict_link && restrict_link->check) {
ret = restrict_link->check(keyring, index_key.type,
&prep.payload, restrict_link->key);
......
......@@ -26,6 +26,18 @@
#define KEY_MAX_DESC_SIZE 4096
static const unsigned char keyrings_capabilities[1] = {
[0] = (KEYCTL_CAPS0_CAPABILITIES |
(IS_ENABLED(CONFIG_PERSISTENT_KEYRINGS) ? KEYCTL_CAPS0_PERSISTENT_KEYRINGS : 0) |
(IS_ENABLED(CONFIG_KEY_DH_OPERATIONS) ? KEYCTL_CAPS0_DIFFIE_HELLMAN : 0) |
(IS_ENABLED(CONFIG_ASYMMETRIC_KEY_TYPE) ? KEYCTL_CAPS0_PUBLIC_KEY : 0) |
(IS_ENABLED(CONFIG_BIG_KEYS) ? KEYCTL_CAPS0_BIG_KEY : 0) |
KEYCTL_CAPS0_INVALIDATE |
KEYCTL_CAPS0_RESTRICT_KEYRING |
KEYCTL_CAPS0_MOVE
),
};
static int key_get_type_from_user(char *type,
const char __user *_type,
unsigned len)
......@@ -568,6 +580,52 @@ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid)
return ret;
}
/*
* Move a link to a key from one keyring to another, displacing any matching
* key from the destination keyring.
*
* The key must grant the caller Link permission and both keyrings must grant
* the caller Write permission. There must also be a link in the from keyring
* to the key. If both keyrings are the same, nothing is done.
*
* If successful, 0 will be returned.
*/
long keyctl_keyring_move(key_serial_t id, key_serial_t from_ringid,
key_serial_t to_ringid, unsigned int flags)
{
key_ref_t key_ref, from_ref, to_ref;
long ret;
if (flags & ~KEYCTL_MOVE_EXCL)
return -EINVAL;
key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE, KEY_NEED_LINK);
if (IS_ERR(key_ref))
return PTR_ERR(key_ref);
from_ref = lookup_user_key(from_ringid, 0, KEY_NEED_WRITE);
if (IS_ERR(from_ref)) {
ret = PTR_ERR(from_ref);
goto error2;
}
to_ref = lookup_user_key(to_ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE);
if (IS_ERR(to_ref)) {
ret = PTR_ERR(to_ref);
goto error3;
}
ret = key_move(key_ref_to_ptr(key_ref), key_ref_to_ptr(from_ref),
key_ref_to_ptr(to_ref), flags);
key_ref_put(to_ref);
error3:
key_ref_put(from_ref);
error2:
key_ref_put(key_ref);
return ret;
}
/*
* Return a description of a key to userspace.
*
......@@ -1520,7 +1578,8 @@ long keyctl_session_to_parent(void)
ret = -EPERM;
oldwork = NULL;
parent = me->real_parent;
parent = rcu_dereference_protected(me->real_parent,
lockdep_is_held(&tasklist_lock));
/* the parent mustn't be init and mustn't be a kernel thread */
if (parent->pid <= 1 || !parent->mm)
......@@ -1627,6 +1686,26 @@ long keyctl_restrict_keyring(key_serial_t id, const char __user *_type,
return ret;
}
/*
* Get keyrings subsystem capabilities.
*/
long keyctl_capabilities(unsigned char __user *_buffer, size_t buflen)
{
size_t size = buflen;
if (size > 0) {
if (size > sizeof(keyrings_capabilities))
size = sizeof(keyrings_capabilities);
if (copy_to_user(_buffer, keyrings_capabilities, size) != 0)
return -EFAULT;
if (size < buflen &&
clear_user(_buffer + size, buflen - size) != 0)
return -EFAULT;
}
return sizeof(keyrings_capabilities);
}
/*
* The key control system call
*/
......@@ -1767,6 +1846,15 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
(const void __user *)arg4,
(const void __user *)arg5);
case KEYCTL_MOVE:
return keyctl_keyring_move((key_serial_t)arg2,
(key_serial_t)arg3,
(key_serial_t)arg4,
(unsigned int)arg5);
case KEYCTL_CAPABILITIES:
return keyctl_capabilities((unsigned char __user *)arg2, (size_t)arg3);
default:
return -EOPNOTSUPP;
}
......
This diff is collapsed.
......@@ -289,28 +289,26 @@ static int install_session_keyring(struct key *keyring)
/*
* Handle the fsuid changing.
*/
void key_fsuid_changed(struct task_struct *tsk)
void key_fsuid_changed(struct cred *new_cred)
{
/* update the ownership of the thread keyring */
BUG_ON(!tsk->cred);
if (tsk->cred->thread_keyring) {
down_write(&tsk->cred->thread_keyring->sem);
tsk->cred->thread_keyring->uid = tsk->cred->fsuid;
up_write(&tsk->cred->thread_keyring->sem);
if (new_cred->thread_keyring) {
down_write(&new_cred->thread_keyring->sem);
new_cred->thread_keyring->uid = new_cred->fsuid;
up_write(&new_cred->thread_keyring->sem);
}
}
/*
* Handle the fsgid changing.
*/
void key_fsgid_changed(struct task_struct *tsk)
void key_fsgid_changed(struct cred *new_cred)
{
/* update the ownership of the thread keyring */
BUG_ON(!tsk->cred);
if (tsk->cred->thread_keyring) {
down_write(&tsk->cred->thread_keyring->sem);
tsk->cred->thread_keyring->gid = tsk->cred->fsgid;
up_write(&tsk->cred->thread_keyring->sem);
if (new_cred->thread_keyring) {
down_write(&new_cred->thread_keyring->sem);
new_cred->thread_keyring->gid = new_cred->fsgid;
up_write(&new_cred->thread_keyring->sem);
}
}
......@@ -686,9 +684,7 @@ key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
key_ref = make_key_ref(key, 0);
/* check to see if we possess the key */
ctx.index_key.type = key->type;
ctx.index_key.description = key->description;
ctx.index_key.desc_len = strlen(key->description);
ctx.index_key = key->index_key;
ctx.match_data.raw_data = key;
kdebug("check possessed");
skey_ref = search_process_keyrings(&ctx);
......
......@@ -20,7 +20,7 @@
/**
* complete_request_key - Complete the construction of a key.
* @auth_key: The authorisation key.
* @authkey: The authorisation key.
* @error: The success or failute of the construction.
*
* Complete the attempt to construct a key. The key will be negated
......@@ -339,7 +339,7 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
struct key_user *user,
struct key **_key)
{
struct assoc_array_edit *edit;
struct assoc_array_edit *edit = NULL;
struct key *key;
key_perm_t perm;
key_ref_t key_ref;
......@@ -368,6 +368,9 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);
if (dest_keyring) {
ret = __key_link_lock(dest_keyring, &ctx->index_key);
if (ret < 0)
goto link_lock_failed;
ret = __key_link_begin(dest_keyring, &ctx->index_key, &edit);
if (ret < 0)
goto link_prealloc_failed;
......@@ -419,6 +422,8 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
return ret;
link_prealloc_failed:
__key_link_end(dest_keyring, &ctx->index_key, edit);
link_lock_failed:
mutex_unlock(&user->cons_lock);
key_put(key);
kleave(" = %d [prelink]", ret);
......
......@@ -148,7 +148,7 @@ struct key *request_key_auth_new(struct key *target, const char *op,
struct key *dest_keyring)
{
struct request_key_auth *rka, *irka;
const struct cred *cred = current->cred;
const struct cred *cred = current_cred();
struct key *authkey = NULL;
char desc[20];
int ret = -ENOMEM;
......@@ -200,7 +200,7 @@ struct key *request_key_auth_new(struct key *target, const char *op,
authkey = key_alloc(&key_type_request_key_auth, desc,
cred->fsuid, cred->fsgid, cred,
KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH |
KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH | KEY_POS_LINK |
KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA, NULL);
if (IS_ERR(authkey)) {
ret = PTR_ERR(authkey);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment