1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Use watch_queue API 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, format: "Incorrect key message length\n" ); |
54 | return; |
55 | } |
56 | |
57 | printf(format: "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 | { |
66 | unsigned char buffer[433], *p, *end; |
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: fd, buf: buffer, nbytes: sizeof(buffer)); |
75 | if (buf_len == -1) { |
76 | perror(s: "read" ); |
77 | exit(status: 1); |
78 | } |
79 | |
80 | if (buf_len == 0) { |
81 | printf(format: "-- END --\n" ); |
82 | return; |
83 | } |
84 | |
85 | if (buf_len > sizeof(buffer)) { |
86 | fprintf(stderr, format: "Read buffer overrun: %zd\n" , buf_len); |
87 | return; |
88 | } |
89 | |
90 | printf(format: "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, format: "Short message header: %zu\n" , largest); |
102 | return; |
103 | } |
104 | memcpy(dest: &n, src: p, n: largest); |
105 | |
106 | printf(format: "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, format: "Bad message length: %zu/%zu\n" , len, largest); |
112 | exit(status: 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(format: "REMOVAL of watchpoint %08x\n" , |
120 | (n.n.info & WATCH_INFO_ID) >> |
121 | WATCH_INFO_ID__SHIFT); |
122 | break; |
123 | case WATCH_META_LOSS_NOTIFICATION: |
124 | printf(format: "-- LOSS --\n" ); |
125 | break; |
126 | default: |
127 | printf(format: "other meta record\n" ); |
128 | break; |
129 | } |
130 | break; |
131 | case WATCH_TYPE_KEY_NOTIFY: |
132 | saw_key_change(n: &n.n, len); |
133 | break; |
134 | default: |
135 | printf(format: "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(pipedes: pipefd, O_NOTIFICATION_PIPE) == -1) { |
159 | perror(s: "pipe2" ); |
160 | exit(status: 1); |
161 | } |
162 | fd = pipefd[0]; |
163 | |
164 | if (ioctl(fd: fd, IOC_WATCH_QUEUE_SET_SIZE, BUF_SIZE) == -1) { |
165 | perror(s: "watch_queue(size)" ); |
166 | exit(status: 1); |
167 | } |
168 | |
169 | if (ioctl(fd: fd, IOC_WATCH_QUEUE_SET_FILTER, &filter) == -1) { |
170 | perror(s: "watch_queue(filter)" ); |
171 | exit(status: 1); |
172 | } |
173 | |
174 | if (keyctl_watch_key(KEY_SPEC_SESSION_KEYRING, watch_fd: fd, watch_id: 0x01) == -1) { |
175 | perror(s: "keyctl" ); |
176 | exit(status: 1); |
177 | } |
178 | |
179 | if (keyctl_watch_key(KEY_SPEC_USER_KEYRING, watch_fd: fd, watch_id: 0x02) == -1) { |
180 | perror(s: "keyctl" ); |
181 | exit(status: 1); |
182 | } |
183 | |
184 | consumer(fd); |
185 | exit(status: 0); |
186 | } |
187 | |