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 20 // Work around glibc header silliness 21 #undef AT_RENAME_NOREPLACE 22 #undef AT_RENAME_EXCHANGE 23 #undef AT_RENAME_WHITEOUT 24 25 #include <linux/watch_queue.h> 26 #include <linux/unistd.h> 27 #include <linux/keyctl.h> 28 29 #ifndef KEYCTL_WATCH_KEY 30 #define KEYCTL_WATCH_KEY -1 31 #endif 32 #ifndef __NR_keyctl 33 #define __NR_keyctl -1 34 #endif 35 36 #define BUF_SIZE 256 37 38 static long keyctl_watch_key(int key, int watch_fd, int watch_id) 39 { 40 return syscall(__NR_keyctl, KEYCTL_WATCH_KEY, key, watch_fd, watch_id); 41 } 42 43 static const char *key_subtypes[256] = { 44 [NOTIFY_KEY_INSTANTIATED] = "instantiated", 45 [NOTIFY_KEY_UPDATED] = "updated", 46 [NOTIFY_KEY_LINKED] = "linked", 47 [NOTIFY_KEY_UNLINKED] = "unlinked", 48 [NOTIFY_KEY_CLEARED] = "cleared", 49 [NOTIFY_KEY_REVOKED] = "revoked", 50 [NOTIFY_KEY_INVALIDATED] = "invalidated", 51 [NOTIFY_KEY_SETATTR] = "setattr", 52 }; 53 54 static void saw_key_change(struct watch_notification *n, size_t len) 55 { 56 struct key_notification *k = (struct key_notification *)n; 57 58 if (len != sizeof(struct key_notification)) { 59 fprintf(stderr, "Incorrect key message length\n"); 60 return; 61 } 62 63 printf("KEY %08x change=%u[%s] aux=%u\n", 64 k->key_id, n->subtype, key_subtypes[n->subtype], k->aux); 65 } 66 67 /* 68 * Consume and display events. 69 */ 70 static void consumer(int fd) 71 { 72 unsigned char buffer[433], *p, *end; 73 union { 74 struct watch_notification n; 75 unsigned char buf1[128]; 76 } n; 77 ssize_t buf_len; 78 79 for (;;) { 80 buf_len = read(fd, buffer, sizeof(buffer)); 81 if (buf_len == -1) { 82 perror("read"); 83 exit(1); 84 } 85 86 if (buf_len == 0) { 87 printf("-- END --\n"); 88 return; 89 } 90 91 if (buf_len > sizeof(buffer)) { 92 fprintf(stderr, "Read buffer overrun: %zd\n", buf_len); 93 return; 94 } 95 96 printf("read() = %zd\n", buf_len); 97 98 p = buffer; 99 end = buffer + buf_len; 100 while (p < end) { 101 size_t largest, len; 102 103 largest = end - p; 104 if (largest > 128) 105 largest = 128; 106 if (largest < sizeof(struct watch_notification)) { 107 fprintf(stderr, "Short message header: %zu\n", largest); 108 return; 109 } 110 memcpy(&n, p, largest); 111 112 printf("NOTIFY[%03zx]: ty=%06x sy=%02x i=%08x\n", 113 p - buffer, n.n.type, n.n.subtype, n.n.info); 114 115 len = n.n.info & WATCH_INFO_LENGTH; 116 if (len < sizeof(n.n) || len > largest) { 117 fprintf(stderr, "Bad message length: %zu/%zu\n", len, largest); 118 exit(1); 119 } 120 121 switch (n.n.type) { 122 case WATCH_TYPE_META: 123 switch (n.n.subtype) { 124 case WATCH_META_REMOVAL_NOTIFICATION: 125 printf("REMOVAL of watchpoint %08x\n", 126 (n.n.info & WATCH_INFO_ID) >> 127 WATCH_INFO_ID__SHIFT); 128 break; 129 case WATCH_META_LOSS_NOTIFICATION: 130 printf("-- LOSS --\n"); 131 break; 132 default: 133 printf("other meta record\n"); 134 break; 135 } 136 break; 137 case WATCH_TYPE_KEY_NOTIFY: 138 saw_key_change(&n.n, len); 139 break; 140 default: 141 printf("other type\n"); 142 break; 143 } 144 145 p += len; 146 } 147 } 148 } 149 150 static struct watch_notification_filter filter = { 151 .nr_filters = 1, 152 .filters = { 153 [0] = { 154 .type = WATCH_TYPE_KEY_NOTIFY, 155 .subtype_filter[0] = UINT_MAX, 156 }, 157 }, 158 }; 159 160 int main(int argc, char **argv) 161 { 162 int pipefd[2], fd; 163 164 if (pipe2(pipefd, O_NOTIFICATION_PIPE) == -1) { 165 perror("pipe2"); 166 exit(1); 167 } 168 fd = pipefd[0]; 169 170 if (ioctl(fd, IOC_WATCH_QUEUE_SET_SIZE, BUF_SIZE) == -1) { 171 perror("watch_queue(size)"); 172 exit(1); 173 } 174 175 if (ioctl(fd, IOC_WATCH_QUEUE_SET_FILTER, &filter) == -1) { 176 perror("watch_queue(filter)"); 177 exit(1); 178 } 179 180 if (keyctl_watch_key(KEY_SPEC_SESSION_KEYRING, fd, 0x01) == -1) { 181 perror("keyctl"); 182 exit(1); 183 } 184 185 if (keyctl_watch_key(KEY_SPEC_USER_KEYRING, fd, 0x02) == -1) { 186 perror("keyctl"); 187 exit(1); 188 } 189 190 consumer(fd); 191 exit(0); 192 } 193