forked from Minki/linux
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:
parent
998f50407f
commit
f7e47677e3
@ -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
|
||||||
===============
|
===============
|
||||||
|
|
||||||
|
@ -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 */
|
||||||
|
@ -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 */
|
||||||
|
@ -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 */
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user