Skip to content

Commit f7e4767

Browse files
committed
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 <[email protected]> Reviewed-by: James Morris <[email protected]>
1 parent 998f504 commit f7e4767

File tree

12 files changed

+270
-28
lines changed

12 files changed

+270
-28
lines changed

Documentation/security/keys/core.rst

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,63 @@ The keyctl syscall functions are:
10261026
written into the output buffer. Verification returns 0 on success.
10271027

10281028

1029+
* Watch a key or keyring for changes::
1030+
1031+
long keyctl(KEYCTL_WATCH_KEY, key_serial_t key, int queue_fd,
1032+
const struct watch_notification_filter *filter);
1033+
1034+
This will set or remove a watch for changes on the specified key or
1035+
keyring.
1036+
1037+
"key" is the ID of the key to be watched.
1038+
1039+
"queue_fd" is a file descriptor referring to an open "/dev/watch_queue"
1040+
which manages the buffer into which notifications will be delivered.
1041+
1042+
"filter" is either NULL to remove a watch or a filter specification to
1043+
indicate what events are required from the key.
1044+
1045+
See Documentation/watch_queue.rst for more information.
1046+
1047+
Note that only one watch may be emplaced for any particular { key,
1048+
queue_fd } combination.
1049+
1050+
Notification records look like::
1051+
1052+
struct key_notification {
1053+
struct watch_notification watch;
1054+
__u32 key_id;
1055+
__u32 aux;
1056+
};
1057+
1058+
In this, watch::type will be "WATCH_TYPE_KEY_NOTIFY" and subtype will be
1059+
one of::
1060+
1061+
NOTIFY_KEY_INSTANTIATED
1062+
NOTIFY_KEY_UPDATED
1063+
NOTIFY_KEY_LINKED
1064+
NOTIFY_KEY_UNLINKED
1065+
NOTIFY_KEY_CLEARED
1066+
NOTIFY_KEY_REVOKED
1067+
NOTIFY_KEY_INVALIDATED
1068+
NOTIFY_KEY_SETATTR
1069+
1070+
Where these indicate a key being instantiated/rejected, updated, a link
1071+
being made in a keyring, a link being removed from a keyring, a keyring
1072+
being cleared, a key being revoked, a key being invalidated or a key
1073+
having one of its attributes changed (user, group, perm, timeout,
1074+
restriction).
1075+
1076+
If a watched key is deleted, a basic watch_notification will be issued
1077+
with "type" set to WATCH_TYPE_META and "subtype" set to
1078+
watch_meta_removal_notification. The watchpoint ID will be set in the
1079+
"info" field.
1080+
1081+
This needs to be configured by enabling:
1082+
1083+
"Provide key/keyring change notifications" (KEY_NOTIFICATIONS)
1084+
1085+
10291086
Kernel Services
10301087
===============
10311088

include/linux/key.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,9 @@ struct key {
176176
struct list_head graveyard_link;
177177
struct rb_node serial_node;
178178
};
179+
#ifdef CONFIG_KEY_NOTIFICATIONS
180+
struct watch_list *watchers; /* Entities watching this key for changes */
181+
#endif
179182
struct rw_semaphore sem; /* change vs change sem */
180183
struct key_user *user; /* owner of this key */
181184
void *security; /* security data for this key */

include/uapi/linux/keyctl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
#define KEYCTL_RESTRICT_KEYRING 29 /* Restrict keys allowed to link to a keyring */
7070
#define KEYCTL_MOVE 30 /* Move keys between keyrings */
7171
#define KEYCTL_CAPABILITIES 31 /* Find capabilities of keyrings subsystem */
72+
#define KEYCTL_WATCH_KEY 32 /* Watch a key or ring of keys for changes */
7273

7374
/* keyctl structures */
7475
struct keyctl_dh_params {
@@ -130,5 +131,6 @@ struct keyctl_pkey_params {
130131
#define KEYCTL_CAPS0_MOVE 0x80 /* KEYCTL_MOVE supported */
131132
#define KEYCTL_CAPS1_NS_KEYRING_NAME 0x01 /* Keyring names are per-user_namespace */
132133
#define KEYCTL_CAPS1_NS_KEY_TAG 0x02 /* Key indexing can include a namespace tag */
134+
#define KEYCTL_CAPS1_NOTIFICATIONS 0x04 /* Keys generate watchable notifications */
133135

134136
#endif /* _LINUX_KEYCTL_H */

include/uapi/linux/watch_queue.h

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313

1414
enum watch_notification_type {
1515
WATCH_TYPE_META = 0, /* Special record */
16-
WATCH_TYPE__NR = 1
16+
WATCH_TYPE_KEY_NOTIFY = 1, /* Key change event notification */
17+
WATCH_TYPE__NR = 2
1718
};
1819

1920
enum watch_meta_notification_subtype {
@@ -75,4 +76,29 @@ struct watch_notification_removal {
7576
__u64 id; /* Type-dependent identifier */
7677
};
7778

79+
/*
80+
* Type of key/keyring change notification.
81+
*/
82+
enum key_notification_subtype {
83+
NOTIFY_KEY_INSTANTIATED = 0, /* Key was instantiated (aux is error code) */
84+
NOTIFY_KEY_UPDATED = 1, /* Key was updated */
85+
NOTIFY_KEY_LINKED = 2, /* Key (aux) was added to watched keyring */
86+
NOTIFY_KEY_UNLINKED = 3, /* Key (aux) was removed from watched keyring */
87+
NOTIFY_KEY_CLEARED = 4, /* Keyring was cleared */
88+
NOTIFY_KEY_REVOKED = 5, /* Key was revoked */
89+
NOTIFY_KEY_INVALIDATED = 6, /* Key was invalidated */
90+
NOTIFY_KEY_SETATTR = 7, /* Key's attributes got changed */
91+
};
92+
93+
/*
94+
* Key/keyring notification record.
95+
* - watch.type = WATCH_TYPE_KEY_NOTIFY
96+
* - watch.subtype = enum key_notification_type
97+
*/
98+
struct key_notification {
99+
struct watch_notification watch;
100+
__u32 key_id; /* The key/keyring affected */
101+
__u32 aux; /* Per-type auxiliary data */
102+
};
103+
78104
#endif /* _UAPI_LINUX_WATCH_QUEUE_H */

security/keys/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,12 @@ config KEY_DH_OPERATIONS
116116
in the kernel.
117117

118118
If you are unsure as to whether this is required, answer N.
119+
120+
config KEY_NOTIFICATIONS
121+
bool "Provide key/keyring change notifications"
122+
depends on KEYS && WATCH_QUEUE
123+
help
124+
This option provides support for getting change notifications on keys
125+
and keyrings on which the caller has View permission. This makes use
126+
of the /dev/watch_queue misc device to handle the notification
127+
buffer and provides KEYCTL_WATCH_KEY to enable/disable watches.

security/keys/compat.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option,
156156
case KEYCTL_CAPABILITIES:
157157
return keyctl_capabilities(compat_ptr(arg2), arg3);
158158

159+
case KEYCTL_WATCH_KEY:
160+
return keyctl_watch_key(arg2, arg3, arg4);
161+
159162
default:
160163
return -EOPNOTSUPP;
161164
}

security/keys/gc.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,11 @@ static noinline void key_gc_unused_keys(struct list_head *keys)
131131
kdebug("- %u", key->serial);
132132
key_check(key);
133133

134+
#ifdef CONFIG_KEY_NOTIFICATIONS
135+
remove_watch_list(key->watchers, key->serial);
136+
key->watchers = NULL;
137+
#endif
138+
134139
/* Throw away the key data if the key is instantiated */
135140
if (state == KEY_IS_POSITIVE && key->type->destroy)
136141
key->type->destroy(key);

security/keys/internal.h

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <linux/task_work.h>
1616
#include <linux/keyctl.h>
1717
#include <linux/refcount.h>
18+
#include <linux/watch_queue.h>
1819
#include <linux/compat.h>
1920
#include <linux/mm.h>
2021
#include <linux/vmalloc.h>
@@ -99,7 +100,8 @@ extern int __key_link_begin(struct key *keyring,
99100
const struct keyring_index_key *index_key,
100101
struct assoc_array_edit **_edit);
101102
extern int __key_link_check_live_key(struct key *keyring, struct key *key);
102-
extern void __key_link(struct key *key, struct assoc_array_edit **_edit);
103+
extern void __key_link(struct key *keyring, struct key *key,
104+
struct assoc_array_edit **_edit);
103105
extern void __key_link_end(struct key *keyring,
104106
const struct keyring_index_key *index_key,
105107
struct assoc_array_edit *edit);
@@ -183,6 +185,23 @@ extern int key_task_permission(const key_ref_t key_ref,
183185
const struct cred *cred,
184186
key_perm_t perm);
185187

188+
static inline void notify_key(struct key *key,
189+
enum key_notification_subtype subtype, u32 aux)
190+
{
191+
#ifdef CONFIG_KEY_NOTIFICATIONS
192+
struct key_notification n = {
193+
.watch.type = WATCH_TYPE_KEY_NOTIFY,
194+
.watch.subtype = subtype,
195+
.watch.info = watch_sizeof(n),
196+
.key_id = key_serial(key),
197+
.aux = aux,
198+
};
199+
200+
post_watch_notification(key->watchers, &n.watch, current_cred(),
201+
n.key_id);
202+
#endif
203+
}
204+
186205
/*
187206
* Check to see whether permission is granted to use a key in the desired way.
188207
*/
@@ -333,6 +352,15 @@ static inline long keyctl_pkey_e_d_s(int op,
333352

334353
extern long keyctl_capabilities(unsigned char __user *_buffer, size_t buflen);
335354

355+
#ifdef CONFIG_KEY_NOTIFICATIONS
356+
extern long keyctl_watch_key(key_serial_t, int, int);
357+
#else
358+
static inline long keyctl_watch_key(key_serial_t key_id, int watch_fd, int watch_id)
359+
{
360+
return -EOPNOTSUPP;
361+
}
362+
#endif
363+
336364
/*
337365
* Debugging key validation
338366
*/

security/keys/key.c

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ static int __key_instantiate_and_link(struct key *key,
444444
/* mark the key as being instantiated */
445445
atomic_inc(&key->user->nikeys);
446446
mark_key_instantiated(key, 0);
447+
notify_key(key, NOTIFY_KEY_INSTANTIATED, 0);
447448

448449
if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags))
449450
awaken = 1;
@@ -453,7 +454,7 @@ static int __key_instantiate_and_link(struct key *key,
453454
if (test_bit(KEY_FLAG_KEEP, &keyring->flags))
454455
set_bit(KEY_FLAG_KEEP, &key->flags);
455456

456-
__key_link(key, _edit);
457+
__key_link(keyring, key, _edit);
457458
}
458459

459460
/* disable the authorisation key */
@@ -601,6 +602,7 @@ int key_reject_and_link(struct key *key,
601602
/* mark the key as being negatively instantiated */
602603
atomic_inc(&key->user->nikeys);
603604
mark_key_instantiated(key, -error);
605+
notify_key(key, NOTIFY_KEY_INSTANTIATED, -error);
604606
key->expiry = ktime_get_real_seconds() + timeout;
605607
key_schedule_gc(key->expiry + key_gc_delay);
606608

@@ -611,7 +613,7 @@ int key_reject_and_link(struct key *key,
611613

612614
/* and link it into the destination keyring */
613615
if (keyring && link_ret == 0)
614-
__key_link(key, &edit);
616+
__key_link(keyring, key, &edit);
615617

616618
/* disable the authorisation key */
617619
if (authkey)
@@ -764,9 +766,11 @@ static inline key_ref_t __key_update(key_ref_t key_ref,
764766
down_write(&key->sem);
765767

766768
ret = key->type->update(key, prep);
767-
if (ret == 0)
769+
if (ret == 0) {
768770
/* Updating a negative key positively instantiates it */
769771
mark_key_instantiated(key, 0);
772+
notify_key(key, NOTIFY_KEY_UPDATED, 0);
773+
}
770774

771775
up_write(&key->sem);
772776

@@ -1023,9 +1027,11 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen)
10231027
down_write(&key->sem);
10241028

10251029
ret = key->type->update(key, &prep);
1026-
if (ret == 0)
1030+
if (ret == 0) {
10271031
/* Updating a negative key positively instantiates it */
10281032
mark_key_instantiated(key, 0);
1033+
notify_key(key, NOTIFY_KEY_UPDATED, 0);
1034+
}
10291035

10301036
up_write(&key->sem);
10311037

@@ -1057,15 +1063,17 @@ void key_revoke(struct key *key)
10571063
* instantiated
10581064
*/
10591065
down_write_nested(&key->sem, 1);
1060-
if (!test_and_set_bit(KEY_FLAG_REVOKED, &key->flags) &&
1061-
key->type->revoke)
1062-
key->type->revoke(key);
1063-
1064-
/* set the death time to no more than the expiry time */
1065-
time = ktime_get_real_seconds();
1066-
if (key->revoked_at == 0 || key->revoked_at > time) {
1067-
key->revoked_at = time;
1068-
key_schedule_gc(key->revoked_at + key_gc_delay);
1066+
if (!test_and_set_bit(KEY_FLAG_REVOKED, &key->flags)) {
1067+
notify_key(key, NOTIFY_KEY_REVOKED, 0);
1068+
if (key->type->revoke)
1069+
key->type->revoke(key);
1070+
1071+
/* set the death time to no more than the expiry time */
1072+
time = ktime_get_real_seconds();
1073+
if (key->revoked_at == 0 || key->revoked_at > time) {
1074+
key->revoked_at = time;
1075+
key_schedule_gc(key->revoked_at + key_gc_delay);
1076+
}
10691077
}
10701078

10711079
up_write(&key->sem);
@@ -1087,8 +1095,10 @@ void key_invalidate(struct key *key)
10871095

10881096
if (!test_bit(KEY_FLAG_INVALIDATED, &key->flags)) {
10891097
down_write_nested(&key->sem, 1);
1090-
if (!test_and_set_bit(KEY_FLAG_INVALIDATED, &key->flags))
1098+
if (!test_and_set_bit(KEY_FLAG_INVALIDATED, &key->flags)) {
1099+
notify_key(key, NOTIFY_KEY_INVALIDATED, 0);
10911100
key_schedule_gc_links();
1101+
}
10921102
up_write(&key->sem);
10931103
}
10941104
}

0 commit comments

Comments
 (0)