1219019Sgabor// SPDX-License-Identifier: GPL-2.0
2219019Sgabor/* Use watch_queue API to watch for notifications.
3219019Sgabor *
4219019Sgabor * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
5219019Sgabor * Written by David Howells (dhowells@redhat.com)
6219019Sgabor */
7219019Sgabor
8219019Sgabor#define _GNU_SOURCE
9219019Sgabor#include <stdbool.h>
10219019Sgabor#include <stdarg.h>
11219019Sgabor#include <stdio.h>
12219019Sgabor#include <stdlib.h>
13219019Sgabor#include <string.h>
14219019Sgabor#include <signal.h>
15219019Sgabor#include <unistd.h>
16219019Sgabor#include <errno.h>
17219019Sgabor#include <sys/ioctl.h>
18219019Sgabor#include <limits.h>
19219019Sgabor#include <linux/watch_queue.h>
20219019Sgabor#include <linux/unistd.h>
21219019Sgabor#include <linux/keyctl.h>
22219019Sgabor
23219019Sgabor#ifndef KEYCTL_WATCH_KEY
24219019Sgabor#define KEYCTL_WATCH_KEY -1
25219019Sgabor#endif
26219019Sgabor#ifndef __NR_keyctl
27219019Sgabor#define __NR_keyctl -1
28219019Sgabor#endif
29219019Sgabor
30219019Sgabor#define BUF_SIZE 256
31219019Sgabor
32219019Sgaborstatic long keyctl_watch_key(int key, int watch_fd, int watch_id)
33219019Sgabor{
34219019Sgabor	return syscall(__NR_keyctl, KEYCTL_WATCH_KEY, key, watch_fd, watch_id);
35219019Sgabor}
36219019Sgabor
37219019Sgaborstatic const char *key_subtypes[256] = {
38219019Sgabor	[NOTIFY_KEY_INSTANTIATED]	= "instantiated",
39219019Sgabor	[NOTIFY_KEY_UPDATED]		= "updated",
40219019Sgabor	[NOTIFY_KEY_LINKED]		= "linked",
41219019Sgabor	[NOTIFY_KEY_UNLINKED]		= "unlinked",
42219019Sgabor	[NOTIFY_KEY_CLEARED]		= "cleared",
43219019Sgabor	[NOTIFY_KEY_REVOKED]		= "revoked",
44219019Sgabor	[NOTIFY_KEY_INVALIDATED]	= "invalidated",
45219019Sgabor	[NOTIFY_KEY_SETATTR]		= "setattr",
46219019Sgabor};
47219019Sgabor
48219019Sgaborstatic void saw_key_change(struct watch_notification *n, size_t len)
49219019Sgabor{
50219019Sgabor	struct key_notification *k = (struct key_notification *)n;
51219019Sgabor
52219019Sgabor	if (len != sizeof(struct key_notification)) {
53219019Sgabor		fprintf(stderr, "Incorrect key message length\n");
54219019Sgabor		return;
55219019Sgabor	}
56219019Sgabor
57219019Sgabor	printf("KEY %08x change=%u[%s] aux=%u\n",
58219019Sgabor	       k->key_id, n->subtype, key_subtypes[n->subtype], k->aux);
59219019Sgabor}
60219019Sgabor
61219019Sgabor/*
62219019Sgabor * Consume and display events.
63219019Sgabor */
64219019Sgaborstatic void consumer(int fd)
65219019Sgabor{
66219019Sgabor	unsigned char buffer[433], *p, *end;
67219019Sgabor	union {
68219019Sgabor		struct watch_notification n;
69219019Sgabor		unsigned char buf1[128];
70219019Sgabor	} n;
71219019Sgabor	ssize_t buf_len;
72282275Stijl
73219019Sgabor	for (;;) {
74219019Sgabor		buf_len = read(fd, buffer, sizeof(buffer));
75219019Sgabor		if (buf_len == -1) {
76219019Sgabor			perror("read");
77219019Sgabor			exit(1);
78219019Sgabor		}
79219019Sgabor
80219019Sgabor		if (buf_len == 0) {
81219019Sgabor			printf("-- END --\n");
82219019Sgabor			return;
83219019Sgabor		}
84219019Sgabor
85219019Sgabor		if (buf_len > sizeof(buffer)) {
86219019Sgabor			fprintf(stderr, "Read buffer overrun: %zd\n", buf_len);
87219019Sgabor			return;
88219019Sgabor		}
89219019Sgabor
90219019Sgabor		printf("read() = %zd\n", buf_len);
91219019Sgabor
92219019Sgabor		p = buffer;
93219019Sgabor		end = buffer + buf_len;
94219019Sgabor		while (p < end) {
95219019Sgabor			size_t largest, len;
96219019Sgabor
97219019Sgabor			largest = end - p;
98219019Sgabor			if (largest > 128)
99219019Sgabor				largest = 128;
100219019Sgabor			if (largest < sizeof(struct watch_notification)) {
101219019Sgabor				fprintf(stderr, "Short message header: %zu\n", largest);
102219019Sgabor				return;
103219019Sgabor			}
104219019Sgabor			memcpy(&n, p, largest);
105219019Sgabor
106219019Sgabor			printf("NOTIFY[%03zx]: ty=%06x sy=%02x i=%08x\n",
107219019Sgabor			       p - buffer, n.n.type, n.n.subtype, n.n.info);
108219019Sgabor
109219019Sgabor			len = n.n.info & WATCH_INFO_LENGTH;
110219019Sgabor			if (len < sizeof(n.n) || len > largest) {
111219019Sgabor				fprintf(stderr, "Bad message length: %zu/%zu\n", len, largest);
112219019Sgabor				exit(1);
113219019Sgabor			}
114219019Sgabor
115219019Sgabor			switch (n.n.type) {
116219019Sgabor			case WATCH_TYPE_META:
117219019Sgabor				switch (n.n.subtype) {
118219019Sgabor				case WATCH_META_REMOVAL_NOTIFICATION:
119219019Sgabor					printf("REMOVAL of watchpoint %08x\n",
120219019Sgabor					       (n.n.info & WATCH_INFO_ID) >>
121219019Sgabor					       WATCH_INFO_ID__SHIFT);
122219019Sgabor					break;
123219019Sgabor				case WATCH_META_LOSS_NOTIFICATION:
124219019Sgabor					printf("-- LOSS --\n");
125					break;
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
144static 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
154int 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}
187