mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-01 10:45:49 +00:00
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>
This commit is contained in:
parent
df593ee23e
commit
ed0ac5c7ec
@ -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);
|
||||
|
@ -310,6 +310,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);
|
||||
|
||||
|
@ -67,6 +67,7 @@
|
||||
#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 */
|
||||
|
||||
/* keyctl structures */
|
||||
struct keyctl_dh_params {
|
||||
@ -112,4 +113,6 @@ struct keyctl_pkey_params {
|
||||
__u32 __spare[7];
|
||||
};
|
||||
|
||||
#define KEYCTL_MOVE_EXCL 0x00000001 /* Do not displace from the to-keyring */
|
||||
|
||||
#endif /* _LINUX_KEYCTL_H */
|
||||
|
@ -159,6 +159,9 @@ 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);
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
@ -95,6 +95,8 @@ 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);
|
||||
@ -217,6 +219,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 *,
|
||||
|
@ -572,6 +572,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.
|
||||
*
|
||||
@ -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;
|
||||
}
|
||||
|
@ -1221,6 +1221,40 @@ int __key_link_lock(struct key *keyring,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lock keyrings for move (link/unlink combination).
|
||||
*/
|
||||
int __key_move_lock(struct key *l_keyring, struct key *u_keyring,
|
||||
const struct keyring_index_key *index_key)
|
||||
__acquires(&l_keyring->sem)
|
||||
__acquires(&u_keyring->sem)
|
||||
__acquires(&keyring_serialise_link_lock)
|
||||
{
|
||||
if (l_keyring->type != &key_type_keyring ||
|
||||
u_keyring->type != &key_type_keyring)
|
||||
return -ENOTDIR;
|
||||
|
||||
/* We have to be very careful here to take the keyring locks in the
|
||||
* right order, lest we open ourselves to deadlocking against another
|
||||
* move operation.
|
||||
*/
|
||||
if (l_keyring < u_keyring) {
|
||||
down_write(&l_keyring->sem);
|
||||
down_write_nested(&u_keyring->sem, 1);
|
||||
} else {
|
||||
down_write(&u_keyring->sem);
|
||||
down_write_nested(&l_keyring->sem, 1);
|
||||
}
|
||||
|
||||
/* Serialise link/link calls to prevent parallel calls causing a cycle
|
||||
* when linking two keyring in opposite orders.
|
||||
*/
|
||||
if (index_key->type == &key_type_keyring)
|
||||
mutex_lock(&keyring_serialise_link_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Preallocate memory so that a key can be linked into to a keyring.
|
||||
*/
|
||||
@ -1494,6 +1528,80 @@ int key_unlink(struct key *keyring, struct key *key)
|
||||
}
|
||||
EXPORT_SYMBOL(key_unlink);
|
||||
|
||||
/**
|
||||
* key_move - Move a key from one keyring to another
|
||||
* @key: The key to move
|
||||
* @from_keyring: The keyring to remove the link from.
|
||||
* @to_keyring: The keyring to make the link in.
|
||||
* @flags: Qualifying flags, such as KEYCTL_MOVE_EXCL.
|
||||
*
|
||||
* Make a link in @to_keyring to a key, such that the keyring holds a reference
|
||||
* on that key and the key can potentially be found by searching that keyring
|
||||
* whilst simultaneously removing a link to the key from @from_keyring.
|
||||
*
|
||||
* This function will write-lock both keyring's semaphores and will consume
|
||||
* some of the user's key data quota to hold the link on @to_keyring.
|
||||
*
|
||||
* Returns 0 if successful, -ENOTDIR if either keyring isn't a keyring,
|
||||
* -EKEYREVOKED if either keyring has been revoked, -ENFILE if the second
|
||||
* keyring is full, -EDQUOT if there is insufficient key data quota remaining
|
||||
* to add another link or -ENOMEM if there's insufficient memory. If
|
||||
* KEYCTL_MOVE_EXCL is set, then -EEXIST will be returned if there's already a
|
||||
* matching key in @to_keyring.
|
||||
*
|
||||
* It is assumed that the caller has checked that it is permitted for a link to
|
||||
* be made (the keyring should have Write permission and the key Link
|
||||
* permission).
|
||||
*/
|
||||
int key_move(struct key *key,
|
||||
struct key *from_keyring,
|
||||
struct key *to_keyring,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct assoc_array_edit *from_edit = NULL, *to_edit = NULL;
|
||||
int ret;
|
||||
|
||||
kenter("%d,%d,%d", key->serial, from_keyring->serial, to_keyring->serial);
|
||||
|
||||
if (from_keyring == to_keyring)
|
||||
return 0;
|
||||
|
||||
key_check(key);
|
||||
key_check(from_keyring);
|
||||
key_check(to_keyring);
|
||||
|
||||
ret = __key_move_lock(from_keyring, to_keyring, &key->index_key);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
ret = __key_unlink_begin(from_keyring, key, &from_edit);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
ret = __key_link_begin(to_keyring, &key->index_key, &to_edit);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = -EEXIST;
|
||||
if (to_edit->dead_leaf && (flags & KEYCTL_MOVE_EXCL))
|
||||
goto error;
|
||||
|
||||
ret = __key_link_check_restriction(to_keyring, key);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
ret = __key_link_check_live_key(to_keyring, key);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
__key_unlink(from_keyring, key, &from_edit);
|
||||
__key_link(key, &to_edit);
|
||||
error:
|
||||
__key_link_end(to_keyring, &key->index_key, to_edit);
|
||||
__key_unlink_end(from_keyring, key, from_edit);
|
||||
out:
|
||||
kleave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(key_move);
|
||||
|
||||
/**
|
||||
* keyring_clear - Clear a keyring
|
||||
* @keyring: The keyring to clear.
|
||||
|
Loading…
Reference in New Issue
Block a user