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