162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Use watch_queue API to watch for notifications.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define _GNU_SOURCE
962306a36Sopenharmony_ci#include <stdbool.h>
1062306a36Sopenharmony_ci#include <stdarg.h>
1162306a36Sopenharmony_ci#include <stdio.h>
1262306a36Sopenharmony_ci#include <stdlib.h>
1362306a36Sopenharmony_ci#include <string.h>
1462306a36Sopenharmony_ci#include <signal.h>
1562306a36Sopenharmony_ci#include <unistd.h>
1662306a36Sopenharmony_ci#include <errno.h>
1762306a36Sopenharmony_ci#include <sys/ioctl.h>
1862306a36Sopenharmony_ci#include <limits.h>
1962306a36Sopenharmony_ci#include <linux/watch_queue.h>
2062306a36Sopenharmony_ci#include <linux/unistd.h>
2162306a36Sopenharmony_ci#include <linux/keyctl.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#ifndef KEYCTL_WATCH_KEY
2462306a36Sopenharmony_ci#define KEYCTL_WATCH_KEY -1
2562306a36Sopenharmony_ci#endif
2662306a36Sopenharmony_ci#ifndef __NR_keyctl
2762306a36Sopenharmony_ci#define __NR_keyctl -1
2862306a36Sopenharmony_ci#endif
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define BUF_SIZE 256
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic long keyctl_watch_key(int key, int watch_fd, int watch_id)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	return syscall(__NR_keyctl, KEYCTL_WATCH_KEY, key, watch_fd, watch_id);
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic const char *key_subtypes[256] = {
3862306a36Sopenharmony_ci	[NOTIFY_KEY_INSTANTIATED]	= "instantiated",
3962306a36Sopenharmony_ci	[NOTIFY_KEY_UPDATED]		= "updated",
4062306a36Sopenharmony_ci	[NOTIFY_KEY_LINKED]		= "linked",
4162306a36Sopenharmony_ci	[NOTIFY_KEY_UNLINKED]		= "unlinked",
4262306a36Sopenharmony_ci	[NOTIFY_KEY_CLEARED]		= "cleared",
4362306a36Sopenharmony_ci	[NOTIFY_KEY_REVOKED]		= "revoked",
4462306a36Sopenharmony_ci	[NOTIFY_KEY_INVALIDATED]	= "invalidated",
4562306a36Sopenharmony_ci	[NOTIFY_KEY_SETATTR]		= "setattr",
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic void saw_key_change(struct watch_notification *n, size_t len)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct key_notification *k = (struct key_notification *)n;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (len != sizeof(struct key_notification)) {
5362306a36Sopenharmony_ci		fprintf(stderr, "Incorrect key message length\n");
5462306a36Sopenharmony_ci		return;
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	printf("KEY %08x change=%u[%s] aux=%u\n",
5862306a36Sopenharmony_ci	       k->key_id, n->subtype, key_subtypes[n->subtype], k->aux);
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/*
6262306a36Sopenharmony_ci * Consume and display events.
6362306a36Sopenharmony_ci */
6462306a36Sopenharmony_cistatic void consumer(int fd)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	unsigned char buffer[433], *p, *end;
6762306a36Sopenharmony_ci	union {
6862306a36Sopenharmony_ci		struct watch_notification n;
6962306a36Sopenharmony_ci		unsigned char buf1[128];
7062306a36Sopenharmony_ci	} n;
7162306a36Sopenharmony_ci	ssize_t buf_len;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	for (;;) {
7462306a36Sopenharmony_ci		buf_len = read(fd, buffer, sizeof(buffer));
7562306a36Sopenharmony_ci		if (buf_len == -1) {
7662306a36Sopenharmony_ci			perror("read");
7762306a36Sopenharmony_ci			exit(1);
7862306a36Sopenharmony_ci		}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci		if (buf_len == 0) {
8162306a36Sopenharmony_ci			printf("-- END --\n");
8262306a36Sopenharmony_ci			return;
8362306a36Sopenharmony_ci		}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci		if (buf_len > sizeof(buffer)) {
8662306a36Sopenharmony_ci			fprintf(stderr, "Read buffer overrun: %zd\n", buf_len);
8762306a36Sopenharmony_ci			return;
8862306a36Sopenharmony_ci		}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci		printf("read() = %zd\n", buf_len);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci		p = buffer;
9362306a36Sopenharmony_ci		end = buffer + buf_len;
9462306a36Sopenharmony_ci		while (p < end) {
9562306a36Sopenharmony_ci			size_t largest, len;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci			largest = end - p;
9862306a36Sopenharmony_ci			if (largest > 128)
9962306a36Sopenharmony_ci				largest = 128;
10062306a36Sopenharmony_ci			if (largest < sizeof(struct watch_notification)) {
10162306a36Sopenharmony_ci				fprintf(stderr, "Short message header: %zu\n", largest);
10262306a36Sopenharmony_ci				return;
10362306a36Sopenharmony_ci			}
10462306a36Sopenharmony_ci			memcpy(&n, p, largest);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci			printf("NOTIFY[%03zx]: ty=%06x sy=%02x i=%08x\n",
10762306a36Sopenharmony_ci			       p - buffer, n.n.type, n.n.subtype, n.n.info);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci			len = n.n.info & WATCH_INFO_LENGTH;
11062306a36Sopenharmony_ci			if (len < sizeof(n.n) || len > largest) {
11162306a36Sopenharmony_ci				fprintf(stderr, "Bad message length: %zu/%zu\n", len, largest);
11262306a36Sopenharmony_ci				exit(1);
11362306a36Sopenharmony_ci			}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci			switch (n.n.type) {
11662306a36Sopenharmony_ci			case WATCH_TYPE_META:
11762306a36Sopenharmony_ci				switch (n.n.subtype) {
11862306a36Sopenharmony_ci				case WATCH_META_REMOVAL_NOTIFICATION:
11962306a36Sopenharmony_ci					printf("REMOVAL of watchpoint %08x\n",
12062306a36Sopenharmony_ci					       (n.n.info & WATCH_INFO_ID) >>
12162306a36Sopenharmony_ci					       WATCH_INFO_ID__SHIFT);
12262306a36Sopenharmony_ci					break;
12362306a36Sopenharmony_ci				case WATCH_META_LOSS_NOTIFICATION:
12462306a36Sopenharmony_ci					printf("-- LOSS --\n");
12562306a36Sopenharmony_ci					break;
12662306a36Sopenharmony_ci				default:
12762306a36Sopenharmony_ci					printf("other meta record\n");
12862306a36Sopenharmony_ci					break;
12962306a36Sopenharmony_ci				}
13062306a36Sopenharmony_ci				break;
13162306a36Sopenharmony_ci			case WATCH_TYPE_KEY_NOTIFY:
13262306a36Sopenharmony_ci				saw_key_change(&n.n, len);
13362306a36Sopenharmony_ci				break;
13462306a36Sopenharmony_ci			default:
13562306a36Sopenharmony_ci				printf("other type\n");
13662306a36Sopenharmony_ci				break;
13762306a36Sopenharmony_ci			}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci			p += len;
14062306a36Sopenharmony_ci		}
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic struct watch_notification_filter filter = {
14562306a36Sopenharmony_ci	.nr_filters	= 1,
14662306a36Sopenharmony_ci	.filters = {
14762306a36Sopenharmony_ci		[0]	= {
14862306a36Sopenharmony_ci			.type			= WATCH_TYPE_KEY_NOTIFY,
14962306a36Sopenharmony_ci			.subtype_filter[0]	= UINT_MAX,
15062306a36Sopenharmony_ci		},
15162306a36Sopenharmony_ci	},
15262306a36Sopenharmony_ci};
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ciint main(int argc, char **argv)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	int pipefd[2], fd;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (pipe2(pipefd, O_NOTIFICATION_PIPE) == -1) {
15962306a36Sopenharmony_ci		perror("pipe2");
16062306a36Sopenharmony_ci		exit(1);
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci	fd = pipefd[0];
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (ioctl(fd, IOC_WATCH_QUEUE_SET_SIZE, BUF_SIZE) == -1) {
16562306a36Sopenharmony_ci		perror("watch_queue(size)");
16662306a36Sopenharmony_ci		exit(1);
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	if (ioctl(fd, IOC_WATCH_QUEUE_SET_FILTER, &filter) == -1) {
17062306a36Sopenharmony_ci		perror("watch_queue(filter)");
17162306a36Sopenharmony_ci		exit(1);
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	if (keyctl_watch_key(KEY_SPEC_SESSION_KEYRING, fd, 0x01) == -1) {
17562306a36Sopenharmony_ci		perror("keyctl");
17662306a36Sopenharmony_ci		exit(1);
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (keyctl_watch_key(KEY_SPEC_USER_KEYRING, fd, 0x02) == -1) {
18062306a36Sopenharmony_ci		perror("keyctl");
18162306a36Sopenharmony_ci		exit(1);
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	consumer(fd);
18562306a36Sopenharmony_ci	exit(0);
18662306a36Sopenharmony_ci}
187