1f5b5a164SDavid Howells // SPDX-License-Identifier: GPL-2.0
2*8fe62e0cSGabriel Krisman Bertazi /* Use watch_queue API to watch for notifications.
3f5b5a164SDavid Howells *
4f5b5a164SDavid Howells * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
5f5b5a164SDavid Howells * Written by David Howells ([email protected])
6f5b5a164SDavid Howells */
7f5b5a164SDavid Howells
8f5b5a164SDavid Howells #define _GNU_SOURCE
9f5b5a164SDavid Howells #include <stdbool.h>
10f5b5a164SDavid Howells #include <stdarg.h>
11f5b5a164SDavid Howells #include <stdio.h>
12f5b5a164SDavid Howells #include <stdlib.h>
13f5b5a164SDavid Howells #include <string.h>
14f5b5a164SDavid Howells #include <signal.h>
15f5b5a164SDavid Howells #include <unistd.h>
16f5b5a164SDavid Howells #include <errno.h>
17f5b5a164SDavid Howells #include <sys/ioctl.h>
18f5b5a164SDavid Howells #include <limits.h>
19f5b5a164SDavid Howells #include <linux/watch_queue.h>
20f5b5a164SDavid Howells #include <linux/unistd.h>
21f5b5a164SDavid Howells #include <linux/keyctl.h>
22f5b5a164SDavid Howells
23f5b5a164SDavid Howells #ifndef KEYCTL_WATCH_KEY
24f5b5a164SDavid Howells #define KEYCTL_WATCH_KEY -1
25f5b5a164SDavid Howells #endif
26f5b5a164SDavid Howells #ifndef __NR_keyctl
27f5b5a164SDavid Howells #define __NR_keyctl -1
28f5b5a164SDavid Howells #endif
29f5b5a164SDavid Howells
30f5b5a164SDavid Howells #define BUF_SIZE 256
31f5b5a164SDavid Howells
keyctl_watch_key(int key,int watch_fd,int watch_id)32f5b5a164SDavid Howells static long keyctl_watch_key(int key, int watch_fd, int watch_id)
33f5b5a164SDavid Howells {
34f5b5a164SDavid Howells return syscall(__NR_keyctl, KEYCTL_WATCH_KEY, key, watch_fd, watch_id);
35f5b5a164SDavid Howells }
36f5b5a164SDavid Howells
37f5b5a164SDavid Howells static const char *key_subtypes[256] = {
38f5b5a164SDavid Howells [NOTIFY_KEY_INSTANTIATED] = "instantiated",
39f5b5a164SDavid Howells [NOTIFY_KEY_UPDATED] = "updated",
40f5b5a164SDavid Howells [NOTIFY_KEY_LINKED] = "linked",
41f5b5a164SDavid Howells [NOTIFY_KEY_UNLINKED] = "unlinked",
42f5b5a164SDavid Howells [NOTIFY_KEY_CLEARED] = "cleared",
43f5b5a164SDavid Howells [NOTIFY_KEY_REVOKED] = "revoked",
44f5b5a164SDavid Howells [NOTIFY_KEY_INVALIDATED] = "invalidated",
45f5b5a164SDavid Howells [NOTIFY_KEY_SETATTR] = "setattr",
46f5b5a164SDavid Howells };
47f5b5a164SDavid Howells
saw_key_change(struct watch_notification * n,size_t len)48f5b5a164SDavid Howells static void saw_key_change(struct watch_notification *n, size_t len)
49f5b5a164SDavid Howells {
50f5b5a164SDavid Howells struct key_notification *k = (struct key_notification *)n;
51f5b5a164SDavid Howells
52f5b5a164SDavid Howells if (len != sizeof(struct key_notification)) {
53f5b5a164SDavid Howells fprintf(stderr, "Incorrect key message length\n");
54f5b5a164SDavid Howells return;
55f5b5a164SDavid Howells }
56f5b5a164SDavid Howells
57f5b5a164SDavid Howells printf("KEY %08x change=%u[%s] aux=%u\n",
58f5b5a164SDavid Howells k->key_id, n->subtype, key_subtypes[n->subtype], k->aux);
59f5b5a164SDavid Howells }
60f5b5a164SDavid Howells
61f5b5a164SDavid Howells /*
62f5b5a164SDavid Howells * Consume and display events.
63f5b5a164SDavid Howells */
consumer(int fd)64f5b5a164SDavid Howells static void consumer(int fd)
65f5b5a164SDavid Howells {
668cfba763SDavid Howells unsigned char buffer[433], *p, *end;
67f5b5a164SDavid Howells union {
68f5b5a164SDavid Howells struct watch_notification n;
69f5b5a164SDavid Howells unsigned char buf1[128];
70f5b5a164SDavid Howells } n;
71f5b5a164SDavid Howells ssize_t buf_len;
72f5b5a164SDavid Howells
73f5b5a164SDavid Howells for (;;) {
74f5b5a164SDavid Howells buf_len = read(fd, buffer, sizeof(buffer));
75f5b5a164SDavid Howells if (buf_len == -1) {
76f5b5a164SDavid Howells perror("read");
77f5b5a164SDavid Howells exit(1);
78f5b5a164SDavid Howells }
79f5b5a164SDavid Howells
80f5b5a164SDavid Howells if (buf_len == 0) {
81f5b5a164SDavid Howells printf("-- END --\n");
82f5b5a164SDavid Howells return;
83f5b5a164SDavid Howells }
84f5b5a164SDavid Howells
85f5b5a164SDavid Howells if (buf_len > sizeof(buffer)) {
86f5b5a164SDavid Howells fprintf(stderr, "Read buffer overrun: %zd\n", buf_len);
87f5b5a164SDavid Howells return;
88f5b5a164SDavid Howells }
89f5b5a164SDavid Howells
90f5b5a164SDavid Howells printf("read() = %zd\n", buf_len);
91f5b5a164SDavid Howells
92f5b5a164SDavid Howells p = buffer;
93f5b5a164SDavid Howells end = buffer + buf_len;
94f5b5a164SDavid Howells while (p < end) {
95f5b5a164SDavid Howells size_t largest, len;
96f5b5a164SDavid Howells
97f5b5a164SDavid Howells largest = end - p;
98f5b5a164SDavid Howells if (largest > 128)
99f5b5a164SDavid Howells largest = 128;
100f5b5a164SDavid Howells if (largest < sizeof(struct watch_notification)) {
101f5b5a164SDavid Howells fprintf(stderr, "Short message header: %zu\n", largest);
102f5b5a164SDavid Howells return;
103f5b5a164SDavid Howells }
104f5b5a164SDavid Howells memcpy(&n, p, largest);
105f5b5a164SDavid Howells
106f5b5a164SDavid Howells printf("NOTIFY[%03zx]: ty=%06x sy=%02x i=%08x\n",
107f5b5a164SDavid Howells p - buffer, n.n.type, n.n.subtype, n.n.info);
108f5b5a164SDavid Howells
109f5b5a164SDavid Howells len = n.n.info & WATCH_INFO_LENGTH;
110f5b5a164SDavid Howells if (len < sizeof(n.n) || len > largest) {
111f5b5a164SDavid Howells fprintf(stderr, "Bad message length: %zu/%zu\n", len, largest);
112f5b5a164SDavid Howells exit(1);
113f5b5a164SDavid Howells }
114f5b5a164SDavid Howells
115f5b5a164SDavid Howells switch (n.n.type) {
116f5b5a164SDavid Howells case WATCH_TYPE_META:
117f5b5a164SDavid Howells switch (n.n.subtype) {
118f5b5a164SDavid Howells case WATCH_META_REMOVAL_NOTIFICATION:
119f5b5a164SDavid Howells printf("REMOVAL of watchpoint %08x\n",
120f5b5a164SDavid Howells (n.n.info & WATCH_INFO_ID) >>
121f5b5a164SDavid Howells WATCH_INFO_ID__SHIFT);
122f5b5a164SDavid Howells break;
123e7d553d6SDavid Howells case WATCH_META_LOSS_NOTIFICATION:
124e7d553d6SDavid Howells printf("-- LOSS --\n");
125e7d553d6SDavid Howells break;
126f5b5a164SDavid Howells default:
127f5b5a164SDavid Howells printf("other meta record\n");
128f5b5a164SDavid Howells break;
129f5b5a164SDavid Howells }
130f5b5a164SDavid Howells break;
131f5b5a164SDavid Howells case WATCH_TYPE_KEY_NOTIFY:
132f5b5a164SDavid Howells saw_key_change(&n.n, len);
133f5b5a164SDavid Howells break;
134f5b5a164SDavid Howells default:
135f5b5a164SDavid Howells printf("other type\n");
136f5b5a164SDavid Howells break;
137f5b5a164SDavid Howells }
138f5b5a164SDavid Howells
139f5b5a164SDavid Howells p += len;
140f5b5a164SDavid Howells }
141f5b5a164SDavid Howells }
142f5b5a164SDavid Howells }
143f5b5a164SDavid Howells
144f5b5a164SDavid Howells static struct watch_notification_filter filter = {
145f5b5a164SDavid Howells .nr_filters = 1,
146f5b5a164SDavid Howells .filters = {
147f5b5a164SDavid Howells [0] = {
148f5b5a164SDavid Howells .type = WATCH_TYPE_KEY_NOTIFY,
149f5b5a164SDavid Howells .subtype_filter[0] = UINT_MAX,
150f5b5a164SDavid Howells },
151f5b5a164SDavid Howells },
152f5b5a164SDavid Howells };
153f5b5a164SDavid Howells
main(int argc,char ** argv)154f5b5a164SDavid Howells int main(int argc, char **argv)
155f5b5a164SDavid Howells {
156f5b5a164SDavid Howells int pipefd[2], fd;
157f5b5a164SDavid Howells
158f5b5a164SDavid Howells if (pipe2(pipefd, O_NOTIFICATION_PIPE) == -1) {
159f5b5a164SDavid Howells perror("pipe2");
160f5b5a164SDavid Howells exit(1);
161f5b5a164SDavid Howells }
162f5b5a164SDavid Howells fd = pipefd[0];
163f5b5a164SDavid Howells
164f5b5a164SDavid Howells if (ioctl(fd, IOC_WATCH_QUEUE_SET_SIZE, BUF_SIZE) == -1) {
165f5b5a164SDavid Howells perror("watch_queue(size)");
166f5b5a164SDavid Howells exit(1);
167f5b5a164SDavid Howells }
168f5b5a164SDavid Howells
169f5b5a164SDavid Howells if (ioctl(fd, IOC_WATCH_QUEUE_SET_FILTER, &filter) == -1) {
170f5b5a164SDavid Howells perror("watch_queue(filter)");
171f5b5a164SDavid Howells exit(1);
172f5b5a164SDavid Howells }
173f5b5a164SDavid Howells
174f5b5a164SDavid Howells if (keyctl_watch_key(KEY_SPEC_SESSION_KEYRING, fd, 0x01) == -1) {
175f5b5a164SDavid Howells perror("keyctl");
176f5b5a164SDavid Howells exit(1);
177f5b5a164SDavid Howells }
178f5b5a164SDavid Howells
179f5b5a164SDavid Howells if (keyctl_watch_key(KEY_SPEC_USER_KEYRING, fd, 0x02) == -1) {
180f5b5a164SDavid Howells perror("keyctl");
181f5b5a164SDavid Howells exit(1);
182f5b5a164SDavid Howells }
183f5b5a164SDavid Howells
184f5b5a164SDavid Howells consumer(fd);
185f5b5a164SDavid Howells exit(0);
186f5b5a164SDavid Howells }
187