Skip to content

Commit

Permalink
Merge tag 'keys-misc-20190619' of git://git.kernel.org/pub/scm/linux/…
Browse files Browse the repository at this point in the history
…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()
  • Loading branch information
torvalds committed Jul 9, 2019
2 parents 7c0f896 + 45e0f30 commit d44a627
Show file tree
Hide file tree
Showing 12 changed files with 418 additions and 84 deletions.
21 changes: 21 additions & 0 deletions Documentation/security/keys/core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
13 changes: 9 additions & 4 deletions include/linux/key.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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 */
Expand All @@ -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 */
Expand Down
17 changes: 17 additions & 0 deletions include/uapi/linux/keyctl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 */
4 changes: 2 additions & 2 deletions kernel/cred.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions security/keys/compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
7 changes: 7 additions & 0 deletions security/keys/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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 *,
Expand Down Expand Up @@ -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
*/
Expand Down
27 changes: 21 additions & 6 deletions security/keys/key.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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;

Expand Down Expand Up @@ -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);
Expand All @@ -579,7 +583,12 @@ int key_reject_and_link(struct key *key,
if (keyring->restrict_link)
return -EPERM;

link_ret = __key_link_begin(keyring, &key->index_key, &edit);
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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
90 changes: 89 additions & 1 deletion security/keys/keyctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -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;
}
Expand Down
Loading

0 comments on commit d44a627

Please sign in to comment.