diff options
author | David Howells <dhowells@redhat.com> | 2019-05-20 22:51:50 +0200 |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2019-05-30 23:44:48 +0200 |
commit | ed0ac5c7ec3763e3261c48e3c5d4b7528b60fd85 (patch) | |
tree | 865eac3b9b967b974e23190e4e8bccab8f2b87bf /security/keys/keyctl.c | |
parent | keys: Hoist locking out of __key_link_begin() (diff) | |
download | linux-ed0ac5c7ec3763e3261c48e3c5d4b7528b60fd85.tar.xz linux-ed0ac5c7ec3763e3261c48e3c5d4b7528b60fd85.zip |
keys: Add a keyctl to move a key between keyrings
Add a keyctl to atomically move a link to a key from one keyring to
another. The key must exist in "from" keyring and a flag can be given to
cause the operation to fail if there's a matching key already in the "to"
keyring.
This can be done with:
keyctl(KEYCTL_MOVE,
key_serial_t key,
key_serial_t from_keyring,
key_serial_t to_keyring,
unsigned int flags);
The key being moved must grant Link permission and both keyrings must grant
Write permission.
flags should be 0 or KEYCTL_MOVE_EXCL, with the latter preventing
displacement of a matching key from the "to" keyring.
Signed-off-by: David Howells <dhowells@redhat.com>
Diffstat (limited to 'security/keys/keyctl.c')
-rw-r--r-- | security/keys/keyctl.c | 52 |
1 files changed, 52 insertions, 0 deletions
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 0f947bcbad46..bbfe7d92d41c 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -573,6 +573,52 @@ error: } /* + * 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. * * The key must grant the caller View permission for this to work. @@ -1772,6 +1818,12 @@ 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); + default: return -EOPNOTSUPP; } |