watch_queue: Add a key/keyring notification facility

Add a key/keyring change notification facility whereby notifications about
changes in key and keyring content and attributes can be received.

Firstly, an event queue needs to be created:

	pipe2(fds, O_NOTIFICATION_PIPE);
	ioctl(fds[1], IOC_WATCH_QUEUE_SET_SIZE, 256);

then a notification can be set up to report notifications via that queue:

	struct watch_notification_filter filter = {
		.nr_filters = 1,
		.filters = {
			[0] = {
				.type = WATCH_TYPE_KEY_NOTIFY,
				.subtype_filter[0] = UINT_MAX,
			},
		},
	};
	ioctl(fds[1], IOC_WATCH_QUEUE_SET_FILTER, &filter);
	keyctl_watch_key(KEY_SPEC_SESSION_KEYRING, fds[1], 0x01);

After that, records will be placed into the queue when events occur in
which keys are changed in some way.  Records are of the following format:

	struct key_notification {
		struct watch_notification watch;
		__u32	key_id;
		__u32	aux;
	} *n;

Where:

	n->watch.type will be WATCH_TYPE_KEY_NOTIFY.

	n->watch.subtype will indicate the type of event, such as
	NOTIFY_KEY_REVOKED.

	n->watch.info & WATCH_INFO_LENGTH will indicate the length of the
	record.

	n->watch.info & WATCH_INFO_ID will be the second argument to
	keyctl_watch_key(), shifted.

	n->key will be the ID of the affected key.

	n->aux will hold subtype-dependent information, such as the key
	being linked into the keyring specified by n->key in the case of
	NOTIFY_KEY_LINKED.

Note that it is permissible for event records to be of variable length -
or, at least, the length may be dependent on the subtype.  Note also that
the queue can be shared between multiple notifications of various types.

Signed-off-by: David Howells <dhowells@redhat.com>
Reviewed-by: James Morris <jamorris@linux.microsoft.com>
This commit is contained in:
David Howells 2020-01-14 17:07:11 +00:00
parent 998f50407f
commit f7e47677e3
12 changed files with 269 additions and 27 deletions

View File

@ -1026,6 +1026,63 @@ The keyctl syscall functions are:
written into the output buffer. Verification returns 0 on success. written into the output buffer. Verification returns 0 on success.
* Watch a key or keyring for changes::
long keyctl(KEYCTL_WATCH_KEY, key_serial_t key, int queue_fd,
const struct watch_notification_filter *filter);
This will set or remove a watch for changes on the specified key or
keyring.
"key" is the ID of the key to be watched.
"queue_fd" is a file descriptor referring to an open "/dev/watch_queue"
which manages the buffer into which notifications will be delivered.
"filter" is either NULL to remove a watch or a filter specification to
indicate what events are required from the key.
See Documentation/watch_queue.rst for more information.
Note that only one watch may be emplaced for any particular { key,
queue_fd } combination.
Notification records look like::
struct key_notification {
struct watch_notification watch;
__u32 key_id;
__u32 aux;
};
In this, watch::type will be "WATCH_TYPE_KEY_NOTIFY" and subtype will be
one of::
NOTIFY_KEY_INSTANTIATED
NOTIFY_KEY_UPDATED
NOTIFY_KEY_LINKED
NOTIFY_KEY_UNLINKED
NOTIFY_KEY_CLEARED
NOTIFY_KEY_REVOKED
NOTIFY_KEY_INVALIDATED
NOTIFY_KEY_SETATTR
Where these indicate a key being instantiated/rejected, updated, a link
being made in a keyring, a link being removed from a keyring, a keyring
being cleared, a key being revoked, a key being invalidated or a key
having one of its attributes changed (user, group, perm, timeout,
restriction).
If a watched key is deleted, a basic watch_notification will be issued
with "type" set to WATCH_TYPE_META and "subtype" set to
watch_meta_removal_notification. The watchpoint ID will be set in the
"info" field.
This needs to be configured by enabling:
"Provide key/keyring change notifications" (KEY_NOTIFICATIONS)
Kernel Services Kernel Services
=============== ===============

View File

@ -176,6 +176,9 @@ struct key {
struct list_head graveyard_link; struct list_head graveyard_link;
struct rb_node serial_node; struct rb_node serial_node;
}; };
#ifdef CONFIG_KEY_NOTIFICATIONS
struct watch_list *watchers; /* Entities watching this key for changes */
#endif
struct rw_semaphore sem; /* change vs change sem */ struct rw_semaphore sem; /* change vs change sem */
struct key_user *user; /* owner of this key */ struct key_user *user; /* owner of this key */
void *security; /* security data for this key */ void *security; /* security data for this key */

View File

@ -69,6 +69,7 @@
#define KEYCTL_RESTRICT_KEYRING 29 /* Restrict keys allowed to link to a keyring */ #define KEYCTL_RESTRICT_KEYRING 29 /* Restrict keys allowed to link to a keyring */
#define KEYCTL_MOVE 30 /* Move keys between keyrings */ #define KEYCTL_MOVE 30 /* Move keys between keyrings */
#define KEYCTL_CAPABILITIES 31 /* Find capabilities of keyrings subsystem */ #define KEYCTL_CAPABILITIES 31 /* Find capabilities of keyrings subsystem */
#define KEYCTL_WATCH_KEY 32 /* Watch a key or ring of keys for changes */
/* keyctl structures */ /* keyctl structures */
struct keyctl_dh_params { struct keyctl_dh_params {
@ -130,5 +131,6 @@ struct keyctl_pkey_params {
#define KEYCTL_CAPS0_MOVE 0x80 /* KEYCTL_MOVE supported */ #define KEYCTL_CAPS0_MOVE 0x80 /* KEYCTL_MOVE supported */
#define KEYCTL_CAPS1_NS_KEYRING_NAME 0x01 /* Keyring names are per-user_namespace */ #define KEYCTL_CAPS1_NS_KEYRING_NAME 0x01 /* Keyring names are per-user_namespace */
#define KEYCTL_CAPS1_NS_KEY_TAG 0x02 /* Key indexing can include a namespace tag */ #define KEYCTL_CAPS1_NS_KEY_TAG 0x02 /* Key indexing can include a namespace tag */
#define KEYCTL_CAPS1_NOTIFICATIONS 0x04 /* Keys generate watchable notifications */
#endif /* _LINUX_KEYCTL_H */ #endif /* _LINUX_KEYCTL_H */

View File

@ -13,7 +13,8 @@
enum watch_notification_type { enum watch_notification_type {
WATCH_TYPE_META = 0, /* Special record */ WATCH_TYPE_META = 0, /* Special record */
WATCH_TYPE__NR = 1 WATCH_TYPE_KEY_NOTIFY = 1, /* Key change event notification */
WATCH_TYPE__NR = 2
}; };
enum watch_meta_notification_subtype { enum watch_meta_notification_subtype {
@ -75,4 +76,29 @@ struct watch_notification_removal {
__u64 id; /* Type-dependent identifier */ __u64 id; /* Type-dependent identifier */
}; };
/*
* Type of key/keyring change notification.
*/
enum key_notification_subtype {
NOTIFY_KEY_INSTANTIATED = 0, /* Key was instantiated (aux is error code) */
NOTIFY_KEY_UPDATED = 1, /* Key was updated */
NOTIFY_KEY_LINKED = 2, /* Key (aux) was added to watched keyring */
NOTIFY_KEY_UNLINKED = 3, /* Key (aux) was removed from watched keyring */
NOTIFY_KEY_CLEARED = 4, /* Keyring was cleared */
NOTIFY_KEY_REVOKED = 5, /* Key was revoked */
NOTIFY_KEY_INVALIDATED = 6, /* Key was invalidated */
NOTIFY_KEY_SETATTR = 7, /* Key's attributes got changed */
};
/*
* Key/keyring notification record.
* - watch.type = WATCH_TYPE_KEY_NOTIFY
* - watch.subtype = enum key_notification_type
*/
struct key_notification {
struct watch_notification watch;
__u32 key_id; /* The key/keyring affected */
__u32 aux; /* Per-type auxiliary data */
};
#endif /* _UAPI_LINUX_WATCH_QUEUE_H */ #endif /* _UAPI_LINUX_WATCH_QUEUE_H */

View File

@ -116,3 +116,12 @@ config KEY_DH_OPERATIONS
in the kernel. in the kernel.
If you are unsure as to whether this is required, answer N. If you are unsure as to whether this is required, answer N.
config KEY_NOTIFICATIONS
bool "Provide key/keyring change notifications"
depends on KEYS && WATCH_QUEUE
help
This option provides support for getting change notifications on keys
and keyrings on which the caller has View permission. This makes use
of the /dev/watch_queue misc device to handle the notification
buffer and provides KEYCTL_WATCH_KEY to enable/disable watches.

View File

@ -156,6 +156,9 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option,
case KEYCTL_CAPABILITIES: case KEYCTL_CAPABILITIES:
return keyctl_capabilities(compat_ptr(arg2), arg3); return keyctl_capabilities(compat_ptr(arg2), arg3);
case KEYCTL_WATCH_KEY:
return keyctl_watch_key(arg2, arg3, arg4);
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }

View File

@ -131,6 +131,11 @@ static noinline void key_gc_unused_keys(struct list_head *keys)
kdebug("- %u", key->serial); kdebug("- %u", key->serial);
key_check(key); key_check(key);
#ifdef CONFIG_KEY_NOTIFICATIONS
remove_watch_list(key->watchers, key->serial);
key->watchers = NULL;
#endif
/* Throw away the key data if the key is instantiated */ /* Throw away the key data if the key is instantiated */
if (state == KEY_IS_POSITIVE && key->type->destroy) if (state == KEY_IS_POSITIVE && key->type->destroy)
key->type->destroy(key); key->type->destroy(key);

View File

@ -15,6 +15,7 @@
#include <linux/task_work.h> #include <linux/task_work.h>
#include <linux/keyctl.h> #include <linux/keyctl.h>
#include <linux/refcount.h> #include <linux/refcount.h>
#include <linux/watch_queue.h>
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
@ -99,7 +100,8 @@ extern int __key_link_begin(struct key *keyring,
const struct keyring_index_key *index_key, const struct keyring_index_key *index_key,
struct assoc_array_edit **_edit); struct assoc_array_edit **_edit);
extern int __key_link_check_live_key(struct key *keyring, struct key *key); extern int __key_link_check_live_key(struct key *keyring, struct key *key);
extern void __key_link(struct key *key, struct assoc_array_edit **_edit); extern void __key_link(struct key *keyring, struct key *key,
struct assoc_array_edit **_edit);
extern void __key_link_end(struct key *keyring, extern void __key_link_end(struct key *keyring,
const struct keyring_index_key *index_key, const struct keyring_index_key *index_key,
struct assoc_array_edit *edit); struct assoc_array_edit *edit);
@ -183,6 +185,23 @@ extern int key_task_permission(const key_ref_t key_ref,
const struct cred *cred, const struct cred *cred,
key_perm_t perm); key_perm_t perm);
static inline void notify_key(struct key *key,
enum key_notification_subtype subtype, u32 aux)
{
#ifdef CONFIG_KEY_NOTIFICATIONS
struct key_notification n = {
.watch.type = WATCH_TYPE_KEY_NOTIFY,
.watch.subtype = subtype,
.watch.info = watch_sizeof(n),
.key_id = key_serial(key),
.aux = aux,
};
post_watch_notification(key->watchers, &n.watch, current_cred(),
n.key_id);
#endif
}
/* /*
* Check to see whether permission is granted to use a key in the desired way. * Check to see whether permission is granted to use a key in the desired way.
*/ */
@ -333,6 +352,15 @@ static inline long keyctl_pkey_e_d_s(int op,
extern long keyctl_capabilities(unsigned char __user *_buffer, size_t buflen); extern long keyctl_capabilities(unsigned char __user *_buffer, size_t buflen);
#ifdef CONFIG_KEY_NOTIFICATIONS
extern long keyctl_watch_key(key_serial_t, int, int);
#else
static inline long keyctl_watch_key(key_serial_t key_id, int watch_fd, int watch_id)
{
return -EOPNOTSUPP;
}
#endif
/* /*
* Debugging key validation * Debugging key validation
*/ */

View File

@ -444,6 +444,7 @@ static int __key_instantiate_and_link(struct key *key,
/* mark the key as being instantiated */ /* mark the key as being instantiated */
atomic_inc(&key->user->nikeys); atomic_inc(&key->user->nikeys);
mark_key_instantiated(key, 0); mark_key_instantiated(key, 0);
notify_key(key, NOTIFY_KEY_INSTANTIATED, 0);
if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags)) if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags))
awaken = 1; awaken = 1;
@ -453,7 +454,7 @@ static int __key_instantiate_and_link(struct key *key,
if (test_bit(KEY_FLAG_KEEP, &keyring->flags)) if (test_bit(KEY_FLAG_KEEP, &keyring->flags))
set_bit(KEY_FLAG_KEEP, &key->flags); set_bit(KEY_FLAG_KEEP, &key->flags);
__key_link(key, _edit); __key_link(keyring, key, _edit);
} }
/* disable the authorisation key */ /* disable the authorisation key */
@ -601,6 +602,7 @@ int key_reject_and_link(struct key *key,
/* mark the key as being negatively instantiated */ /* mark the key as being negatively instantiated */
atomic_inc(&key->user->nikeys); atomic_inc(&key->user->nikeys);
mark_key_instantiated(key, -error); mark_key_instantiated(key, -error);
notify_key(key, NOTIFY_KEY_INSTANTIATED, -error);
key->expiry = ktime_get_real_seconds() + timeout; key->expiry = ktime_get_real_seconds() + timeout;
key_schedule_gc(key->expiry + key_gc_delay); key_schedule_gc(key->expiry + key_gc_delay);
@ -611,7 +613,7 @@ int key_reject_and_link(struct key *key,
/* and link it into the destination keyring */ /* and link it into the destination keyring */
if (keyring && link_ret == 0) if (keyring && link_ret == 0)
__key_link(key, &edit); __key_link(keyring, key, &edit);
/* disable the authorisation key */ /* disable the authorisation key */
if (authkey) if (authkey)
@ -764,9 +766,11 @@ static inline key_ref_t __key_update(key_ref_t key_ref,
down_write(&key->sem); down_write(&key->sem);
ret = key->type->update(key, prep); ret = key->type->update(key, prep);
if (ret == 0) if (ret == 0) {
/* Updating a negative key positively instantiates it */ /* Updating a negative key positively instantiates it */
mark_key_instantiated(key, 0); mark_key_instantiated(key, 0);
notify_key(key, NOTIFY_KEY_UPDATED, 0);
}
up_write(&key->sem); up_write(&key->sem);
@ -1023,9 +1027,11 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen)
down_write(&key->sem); down_write(&key->sem);
ret = key->type->update(key, &prep); ret = key->type->update(key, &prep);
if (ret == 0) if (ret == 0) {
/* Updating a negative key positively instantiates it */ /* Updating a negative key positively instantiates it */
mark_key_instantiated(key, 0); mark_key_instantiated(key, 0);
notify_key(key, NOTIFY_KEY_UPDATED, 0);
}
up_write(&key->sem); up_write(&key->sem);
@ -1057,15 +1063,17 @@ void key_revoke(struct key *key)
* instantiated * instantiated
*/ */
down_write_nested(&key->sem, 1); down_write_nested(&key->sem, 1);
if (!test_and_set_bit(KEY_FLAG_REVOKED, &key->flags) && if (!test_and_set_bit(KEY_FLAG_REVOKED, &key->flags)) {
key->type->revoke) notify_key(key, NOTIFY_KEY_REVOKED, 0);
key->type->revoke(key); if (key->type->revoke)
key->type->revoke(key);
/* set the death time to no more than the expiry time */ /* set the death time to no more than the expiry time */
time = ktime_get_real_seconds(); time = ktime_get_real_seconds();
if (key->revoked_at == 0 || key->revoked_at > time) { if (key->revoked_at == 0 || key->revoked_at > time) {
key->revoked_at = time; key->revoked_at = time;
key_schedule_gc(key->revoked_at + key_gc_delay); key_schedule_gc(key->revoked_at + key_gc_delay);
}
} }
up_write(&key->sem); up_write(&key->sem);
@ -1087,8 +1095,10 @@ void key_invalidate(struct key *key)
if (!test_bit(KEY_FLAG_INVALIDATED, &key->flags)) { if (!test_bit(KEY_FLAG_INVALIDATED, &key->flags)) {
down_write_nested(&key->sem, 1); down_write_nested(&key->sem, 1);
if (!test_and_set_bit(KEY_FLAG_INVALIDATED, &key->flags)) if (!test_and_set_bit(KEY_FLAG_INVALIDATED, &key->flags)) {
notify_key(key, NOTIFY_KEY_INVALIDATED, 0);
key_schedule_gc_links(); key_schedule_gc_links();
}
up_write(&key->sem); up_write(&key->sem);
} }
} }

View File

@ -37,7 +37,9 @@ static const unsigned char keyrings_capabilities[2] = {
KEYCTL_CAPS0_MOVE KEYCTL_CAPS0_MOVE
), ),
[1] = (KEYCTL_CAPS1_NS_KEYRING_NAME | [1] = (KEYCTL_CAPS1_NS_KEYRING_NAME |
KEYCTL_CAPS1_NS_KEY_TAG), KEYCTL_CAPS1_NS_KEY_TAG |
(IS_ENABLED(CONFIG_KEY_NOTIFICATIONS) ? KEYCTL_CAPS1_NOTIFICATIONS : 0)
),
}; };
static int key_get_type_from_user(char *type, static int key_get_type_from_user(char *type,
@ -1039,6 +1041,7 @@ long keyctl_chown_key(key_serial_t id, uid_t user, gid_t group)
if (group != (gid_t) -1) if (group != (gid_t) -1)
key->gid = gid; key->gid = gid;
notify_key(key, NOTIFY_KEY_SETATTR, 0);
ret = 0; ret = 0;
error_put: error_put:
@ -1089,6 +1092,7 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm)
/* if we're not the sysadmin, we can only change a key that we own */ /* if we're not the sysadmin, we can only change a key that we own */
if (capable(CAP_SYS_ADMIN) || uid_eq(key->uid, current_fsuid())) { if (capable(CAP_SYS_ADMIN) || uid_eq(key->uid, current_fsuid())) {
key->perm = perm; key->perm = perm;
notify_key(key, NOTIFY_KEY_SETATTR, 0);
ret = 0; ret = 0;
} }
@ -1480,10 +1484,12 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout)
okay: okay:
key = key_ref_to_ptr(key_ref); key = key_ref_to_ptr(key_ref);
ret = 0; ret = 0;
if (test_bit(KEY_FLAG_KEEP, &key->flags)) if (test_bit(KEY_FLAG_KEEP, &key->flags)) {
ret = -EPERM; ret = -EPERM;
else } else {
key_set_timeout(key, timeout); key_set_timeout(key, timeout);
notify_key(key, NOTIFY_KEY_SETATTR, 0);
}
key_put(key); key_put(key);
error: error:
@ -1757,6 +1763,90 @@ error:
return ret; return ret;
} }
#ifdef CONFIG_KEY_NOTIFICATIONS
/*
* Watch for changes to a key.
*
* The caller must have View permission to watch a key or keyring.
*/
long keyctl_watch_key(key_serial_t id, int watch_queue_fd, int watch_id)
{
struct watch_queue *wqueue;
struct watch_list *wlist = NULL;
struct watch *watch = NULL;
struct key *key;
key_ref_t key_ref;
long ret;
if (watch_id < -1 || watch_id > 0xff)
return -EINVAL;
key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE, KEY_NEED_VIEW);
if (IS_ERR(key_ref))
return PTR_ERR(key_ref);
key = key_ref_to_ptr(key_ref);
wqueue = get_watch_queue(watch_queue_fd);
if (IS_ERR(wqueue)) {
ret = PTR_ERR(wqueue);
goto err_key;
}
if (watch_id >= 0) {
ret = -ENOMEM;
if (!key->watchers) {
wlist = kzalloc(sizeof(*wlist), GFP_KERNEL);
if (!wlist)
goto err_wqueue;
init_watch_list(wlist, NULL);
}
watch = kzalloc(sizeof(*watch), GFP_KERNEL);
if (!watch)
goto err_wlist;
init_watch(watch, wqueue);
watch->id = key->serial;
watch->info_id = (u32)watch_id << WATCH_INFO_ID__SHIFT;
ret = security_watch_key(key);
if (ret < 0)
goto err_watch;
down_write(&key->sem);
if (!key->watchers) {
key->watchers = wlist;
wlist = NULL;
}
ret = add_watch_to_object(watch, key->watchers);
up_write(&key->sem);
if (ret == 0)
watch = NULL;
} else {
ret = -EBADSLT;
if (key->watchers) {
down_write(&key->sem);
ret = remove_watch_from_object(key->watchers,
wqueue, key_serial(key),
false);
up_write(&key->sem);
}
}
err_watch:
kfree(watch);
err_wlist:
kfree(wlist);
err_wqueue:
put_watch_queue(wqueue);
err_key:
key_put(key);
return ret;
}
#endif /* CONFIG_KEY_NOTIFICATIONS */
/* /*
* Get keyrings subsystem capabilities. * Get keyrings subsystem capabilities.
*/ */
@ -1926,6 +2016,9 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
case KEYCTL_CAPABILITIES: case KEYCTL_CAPABILITIES:
return keyctl_capabilities((unsigned char __user *)arg2, (size_t)arg3); return keyctl_capabilities((unsigned char __user *)arg2, (size_t)arg3);
case KEYCTL_WATCH_KEY:
return keyctl_watch_key((key_serial_t)arg2, (int)arg3, (int)arg4);
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }

View File

@ -1056,12 +1056,14 @@ int keyring_restrict(key_ref_t keyring_ref, const char *type,
down_write(&keyring->sem); down_write(&keyring->sem);
down_write(&keyring_serialise_restrict_sem); down_write(&keyring_serialise_restrict_sem);
if (keyring->restrict_link) if (keyring->restrict_link) {
ret = -EEXIST; ret = -EEXIST;
else if (keyring_detect_restriction_cycle(keyring, restrict_link)) } else if (keyring_detect_restriction_cycle(keyring, restrict_link)) {
ret = -EDEADLK; ret = -EDEADLK;
else } else {
keyring->restrict_link = restrict_link; keyring->restrict_link = restrict_link;
notify_key(keyring, NOTIFY_KEY_SETATTR, 0);
}
up_write(&keyring_serialise_restrict_sem); up_write(&keyring_serialise_restrict_sem);
up_write(&keyring->sem); up_write(&keyring->sem);
@ -1362,12 +1364,14 @@ int __key_link_check_live_key(struct key *keyring, struct key *key)
* holds at most one link to any given key of a particular type+description * holds at most one link to any given key of a particular type+description
* combination. * combination.
*/ */
void __key_link(struct key *key, struct assoc_array_edit **_edit) void __key_link(struct key *keyring, struct key *key,
struct assoc_array_edit **_edit)
{ {
__key_get(key); __key_get(key);
assoc_array_insert_set_object(*_edit, keyring_key_to_ptr(key)); assoc_array_insert_set_object(*_edit, keyring_key_to_ptr(key));
assoc_array_apply_edit(*_edit); assoc_array_apply_edit(*_edit);
*_edit = NULL; *_edit = NULL;
notify_key(keyring, NOTIFY_KEY_LINKED, key_serial(key));
} }
/* /*
@ -1451,7 +1455,7 @@ int key_link(struct key *keyring, struct key *key)
if (ret == 0) if (ret == 0)
ret = __key_link_check_live_key(keyring, key); ret = __key_link_check_live_key(keyring, key);
if (ret == 0) if (ret == 0)
__key_link(key, &edit); __key_link(keyring, key, &edit);
error_end: error_end:
__key_link_end(keyring, &key->index_key, edit); __key_link_end(keyring, &key->index_key, edit);
@ -1483,7 +1487,7 @@ static int __key_unlink_begin(struct key *keyring, struct key *key,
struct assoc_array_edit *edit; struct assoc_array_edit *edit;
BUG_ON(*_edit != NULL); BUG_ON(*_edit != NULL);
edit = assoc_array_delete(&keyring->keys, &keyring_assoc_array_ops, edit = assoc_array_delete(&keyring->keys, &keyring_assoc_array_ops,
&key->index_key); &key->index_key);
if (IS_ERR(edit)) if (IS_ERR(edit))
@ -1503,6 +1507,7 @@ static void __key_unlink(struct key *keyring, struct key *key,
struct assoc_array_edit **_edit) struct assoc_array_edit **_edit)
{ {
assoc_array_apply_edit(*_edit); assoc_array_apply_edit(*_edit);
notify_key(keyring, NOTIFY_KEY_UNLINKED, key_serial(key));
*_edit = NULL; *_edit = NULL;
key_payload_reserve(keyring, keyring->datalen - KEYQUOTA_LINK_BYTES); key_payload_reserve(keyring, keyring->datalen - KEYQUOTA_LINK_BYTES);
} }
@ -1621,7 +1626,7 @@ int key_move(struct key *key,
goto error; goto error;
__key_unlink(from_keyring, key, &from_edit); __key_unlink(from_keyring, key, &from_edit);
__key_link(key, &to_edit); __key_link(to_keyring, key, &to_edit);
error: error:
__key_link_end(to_keyring, &key->index_key, to_edit); __key_link_end(to_keyring, &key->index_key, to_edit);
__key_unlink_end(from_keyring, key, from_edit); __key_unlink_end(from_keyring, key, from_edit);
@ -1655,6 +1660,7 @@ int keyring_clear(struct key *keyring)
} else { } else {
if (edit) if (edit)
assoc_array_apply_edit(edit); assoc_array_apply_edit(edit);
notify_key(keyring, NOTIFY_KEY_CLEARED, 0);
key_payload_reserve(keyring, 0); key_payload_reserve(keyring, 0);
ret = 0; ret = 0;
} }

View File

@ -418,7 +418,7 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
goto key_already_present; goto key_already_present;
if (dest_keyring) if (dest_keyring)
__key_link(key, &edit); __key_link(dest_keyring, key, &edit);
mutex_unlock(&key_construction_mutex); mutex_unlock(&key_construction_mutex);
if (dest_keyring) if (dest_keyring)
@ -437,7 +437,7 @@ key_already_present:
if (dest_keyring) { if (dest_keyring) {
ret = __key_link_check_live_key(dest_keyring, key); ret = __key_link_check_live_key(dest_keyring, key);
if (ret == 0) if (ret == 0)
__key_link(key, &edit); __key_link(dest_keyring, key, &edit);
__key_link_end(dest_keyring, &ctx->index_key, edit); __key_link_end(dest_keyring, &ctx->index_key, edit);
if (ret < 0) if (ret < 0)
goto link_check_failed; goto link_check_failed;