Commit f7e47677 authored by David Howells's avatar David Howells
Browse files

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: default avatarDavid Howells <dhowells@redhat.com>
Reviewed-by: default avatarJames Morris <jamorris@linux.microsoft.com>
parent 998f5040
Loading
Loading
Loading
Loading
+57 −0
Original line number Diff line number Diff line
@@ -1026,6 +1026,63 @@ The keyctl syscall functions are:
     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
===============

+3 −0
Original line number Diff line number Diff line
@@ -176,6 +176,9 @@ struct key {
		struct list_head graveyard_link;
		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 key_user		*user;		/* owner of this key */
	void			*security;	/* security data for this key */
+2 −0
Original line number Diff line number Diff line
@@ -69,6 +69,7 @@
#define KEYCTL_RESTRICT_KEYRING		29	/* Restrict keys allowed to link to a keyring */
#define KEYCTL_MOVE			30	/* Move keys between keyrings */
#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 */
struct keyctl_dh_params {
@@ -130,5 +131,6 @@ struct keyctl_pkey_params {
#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_KEY_TAG		0x02 /* Key indexing can include a namespace tag */
#define KEYCTL_CAPS1_NOTIFICATIONS	0x04 /* Keys generate watchable notifications */

#endif /*  _LINUX_KEYCTL_H */
+27 −1
Original line number Diff line number Diff line
@@ -13,7 +13,8 @@

enum watch_notification_type {
	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 {
@@ -75,4 +76,29 @@ struct watch_notification_removal {
	__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 */
+9 −0
Original line number Diff line number Diff line
@@ -116,3 +116,12 @@ config KEY_DH_OPERATIONS
	 in the kernel.

	 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.
Loading