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, "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 { 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, 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; 123 case WATCH_META_LOSS_NOTIFICATION: 124 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 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 } 187