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