KEYCTL_WATCH_KEY(3)

πŸ’– NAME πŸ’–

keyctl_watch_key β€” watch for changes to a key πŸ‘€βœ¨

okay so bestie, quick context before we dive in: this lil guy lives in the Linux Key Management Calls section of the manual (section 3, the library-function section, we love her). the header line up at the top of the page literally reads KEYCTL_GRANT_PERMISSION(3), which is a whole different function's name, and honestly? iconic messy behaviour, but we move 🫢


✨ SYNOPSIS ✨

#include <keyutils.h>

long keyctl_watch_key(key_serial_t key,
                      int watch_queue_fd
                      int watch_id);

(yes the comma situation in there is exactly how the original prints it, no I will not be fixing it, I am a messenger πŸ’…)


🌸 DESCRIPTION 🌸

keyctl_watch_key() sets or removes a watch on key. that's it, that's the girlboss move. πŸ‘‘

watch_id specifies the ID for a watch that will be included in notification messages. it can be between 0 and 255 to add a key; it should be -1 to remove a key. so like, adding = a cute lil number, removing = the dramatic -1, no in-between, periodt.

watch_queue_fd is a file descriptor attached to a watch_queue device instance. Multiple openings of a device provide separate instances. Each device instance can only have one watch on any particular key. so if you try to double-watch the same key from the same instance, that's a whole thing β€” see EBUSY below, bestie, it's giving possessive πŸ’€


πŸ“¬ Notification Record πŸ“¬

Key-specific notification messages that the kernel emits into the buffer have the following format:

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

The watch.type field will be set to WATCH_TYPE_KEY_NOTIFY and the watch.subtype field will contain one of the following constants, indicating the event that occurred β€” and the watch_id passed to keyctl_watch_key() will be placed in watch.info in the ID field. (so you always know which watch is gossiping to you, fr fr πŸ—£οΈ)

The following events are defined:

  • NOTIFY_KEY_INSTANTIATED β€” This indicates that a watched key got instantiated or negatively instantiated. key_id indicates the key that was instantiated and aux is unused. 🐣
  • NOTIFY_KEY_UPDATED β€” This indicates that a watched key got updated or instantiated by update. key_id indicates the key that was updated and aux is unused. πŸ”„
  • NOTIFY_KEY_LINKED β€” This indicates that a key got linked into a watched keyring. key_id indicates the keyring that was modified aux indicates the key that was added. πŸ”—
  • NOTIFY_KEY_UNLINKED β€” This indicates that a key got unlinked from a watched keyring. key_id indicates the keyring that was modified aux indicates the key that was removed. βœ‚οΈ
  • NOTIFY_KEY_CLEARED β€” This indicates that a watched keyring got cleared. key_id indicates the keyring that was cleared and aux is unused. 🧹
  • NOTIFY_KEY_REVOKED β€” This indicates that a watched key got revoked. key_id indicates the key that was revoked and aux is unused. 🚫
  • NOTIFY_KEY_INVALIDATED β€” This indicates that a watched key got invalidated. key_id indicates the key that was invalidated and aux is unused. πŸ₯€
  • NOTIFY_KEY_SETATTR β€” This indicates that a watched key had its attributes (owner, group, permissions, timeout) modified. key_id indicates the key that was modified and aux is unused. πŸ’…

πŸ—‘οΈ Removal Notification πŸ—‘οΈ

When a watched key is garbage collected, all of its watches are automatically destroyed and a notification is delivered to each watcher. (the key literally got collected, it's over, no cap 😭)

This will normally be an extended notification of the form:

struct watch_notification_removal {
     struct watch_notification watch;
     __u64     id;
};

The watch.type field will be set to WATCH_TYPE_META and the watch.subtype field will contain WATCH_META_REMOVAL_NOTIFICATION. If the extended notification is given, then the length will be 2 units, otherwise it will be 1 and only the header will be present.

The watch_id passed to keyctl_watch_key() will be placed in watch.info in the ID field.

If the extension is present, id will be set to the ID of the destroyed key.


🫢 RETURN VALUE 🫢

On success keyctl_watch_key() returns 0. On error, the value -1 will be returned and errno will have been set to an appropriate error. the duality of girl πŸŽ€


πŸ’€ ERRORS πŸ’€

  • ENOKEY β€” The specified key does not exist.
  • EKEYEXPIRED β€” The specified key has expired.
  • EKEYREVOKED β€” The specified key has been revoked.
  • EACCES β€” The named key exists, but does not grant view permission to the calling process. (it's there, you just can't look, so rude πŸ™ˆ)
  • EBUSY β€” The specified key already has a watch on it for that device instance (add only).
  • EBADSLT β€” The specified key doesn't have a watch on it (removal only).
notice how the last two are picky about which direction they apply to β€” adding and removing have different moods, and the kernel will absolutely let you know 🫠

πŸ”— LINKING πŸ”—

This is a library function that can be found in libkeyutils. When linking, -lkeyutils should be specified to the linker. don't skip this step bestie, the linker will simply not know her πŸ”₯


πŸ‘€ SEE ALSO πŸ‘€

keyctl(1), add_key(2), keyctl(2), request_key(2), keyctl(3), keyrings(7), keyutils(7)


and that's the whole page, you ate that πŸ’…βœ¨

this came to you from Linux, dated 28 Aug 2019, and the footer of the original signs off as KEYCTL_GRANT_PERMISSION(3) (yes, still that name, we've made peace with it 🌈). πŸ’–

πŸ“¦ where this page comes from