1 /* 2 * Copyright (c) 2019 Yubico AB. All rights reserved. 3 * Use of this source code is governed by a BSD-style 4 * license that can be found in the LICENSE file. 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8 /* 9 * cc -fPIC -D_GNU_SOURCE -shared -o preload-snoop.so preload-snoop.c 10 * LD_PRELOAD=$(realpath preload-snoop.so) 11 */ 12 13 #include <sys/types.h> 14 #include <sys/stat.h> 15 16 #include <dlfcn.h> 17 #include <err.h> 18 #include <errno.h> 19 #include <fcntl.h> 20 #include <limits.h> 21 #include <stdarg.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <unistd.h> 26 27 #define SNOOP_DEV_PREFIX "/dev/hidraw" 28 29 struct fd_tuple { 30 int snoop_in; 31 int snoop_out; 32 int real_dev; 33 }; 34 35 static struct fd_tuple *fd_tuple; 36 static int (*open_f)(const char *, int, mode_t); 37 static int (*close_f)(int); 38 static ssize_t (*read_f)(int, void *, size_t); 39 static ssize_t (*write_f)(int, const void *, size_t); 40 41 static int 42 get_fd(const char *hid_path, const char *suffix) 43 { 44 char *s = NULL; 45 char path[PATH_MAX]; 46 int fd; 47 int r; 48 49 if ((s = strdup(hid_path)) == NULL) { 50 warnx("%s: strdup", __func__); 51 return (-1); 52 } 53 54 for (size_t i = 0; i < strlen(s); i++) 55 if (s[i] == '/') 56 s[i] = '_'; 57 58 if ((r = snprintf(path, sizeof(path), "%s-%s", s, suffix)) < 0 || 59 (size_t)r >= sizeof(path)) { 60 warnx("%s: snprintf", __func__); 61 free(s); 62 return (-1); 63 } 64 65 free(s); 66 s = NULL; 67 68 if ((fd = open_f(path, O_CREAT | O_WRONLY, 0644)) < 0) { 69 warn("%s: open", __func__); 70 return (-1); 71 } 72 73 return (fd); 74 } 75 76 int 77 open(const char *path, int flags, ...) 78 { 79 va_list ap; 80 mode_t mode; 81 82 va_start(ap, flags); 83 mode = va_arg(ap, mode_t); 84 va_end(ap); 85 86 if (open_f == NULL) { 87 open_f = dlsym(RTLD_NEXT, "open"); 88 if (open_f == NULL) { 89 warnx("%s: dlsym", __func__); 90 errno = EACCES; 91 return (-1); 92 } 93 } 94 95 if (strncmp(path, SNOOP_DEV_PREFIX, strlen(SNOOP_DEV_PREFIX)) != 0) 96 return (open_f(path, flags, mode)); 97 98 if (fd_tuple != NULL) { 99 warnx("%s: fd_tuple != NULL", __func__); 100 errno = EACCES; 101 return (-1); 102 } 103 104 if ((fd_tuple = calloc(1, sizeof(*fd_tuple))) == NULL) { 105 warn("%s: calloc", __func__); 106 errno = ENOMEM; 107 return (-1); 108 } 109 110 fd_tuple->snoop_in = -1; 111 fd_tuple->snoop_out = -1; 112 fd_tuple->real_dev = -1; 113 114 if ((fd_tuple->snoop_in = get_fd(path, "in")) < 0 || 115 (fd_tuple->snoop_out = get_fd(path, "out")) < 0 || 116 (fd_tuple->real_dev = open_f(path, flags, mode)) < 0) { 117 warn("%s: get_fd/open", __func__); 118 goto fail; 119 } 120 121 return (fd_tuple->real_dev); 122 fail: 123 if (fd_tuple->snoop_in != -1) 124 close(fd_tuple->snoop_in); 125 if (fd_tuple->snoop_out != -1) 126 close(fd_tuple->snoop_out); 127 if (fd_tuple->real_dev != -1) 128 close(fd_tuple->real_dev); 129 130 free(fd_tuple); 131 fd_tuple = NULL; 132 133 errno = EACCES; 134 135 return (-1); 136 } 137 138 int 139 close(int fd) 140 { 141 if (close_f == NULL) { 142 close_f = dlsym(RTLD_NEXT, "close"); 143 if (close_f == NULL) { 144 warnx("%s: dlsym", __func__); 145 errno = EBADF; 146 return (-1); 147 } 148 } 149 150 if (fd_tuple == NULL || fd_tuple->real_dev != fd) 151 return (close_f(fd)); 152 153 close_f(fd_tuple->snoop_in); 154 close_f(fd_tuple->snoop_out); 155 close_f(fd_tuple->real_dev); 156 157 free(fd_tuple); 158 fd_tuple = NULL; 159 160 return (0); 161 } 162 163 ssize_t 164 read(int fd, void *buf, size_t nbytes) 165 { 166 ssize_t n; 167 168 if (read_f == NULL) { 169 read_f = dlsym(RTLD_NEXT, "read"); 170 if (read_f == NULL) { 171 warnx("%s: dlsym", __func__); 172 errno = EBADF; 173 return (-1); 174 } 175 } 176 177 if (write_f == NULL) { 178 write_f = dlsym(RTLD_NEXT, "write"); 179 if (write_f == NULL) { 180 warnx("%s: dlsym", __func__); 181 errno = EBADF; 182 return (-1); 183 } 184 } 185 186 if (fd_tuple == NULL || fd_tuple->real_dev != fd) 187 return (read_f(fd, buf, nbytes)); 188 189 if ((n = read_f(fd, buf, nbytes)) < 0 || 190 write_f(fd_tuple->snoop_in, buf, n) != n) 191 return (-1); 192 193 return (n); 194 } 195 196 ssize_t 197 write(int fd, const void *buf, size_t nbytes) 198 { 199 ssize_t n; 200 201 if (write_f == NULL) { 202 write_f = dlsym(RTLD_NEXT, "write"); 203 if (write_f == NULL) { 204 warnx("%s: dlsym", __func__); 205 errno = EBADF; 206 return (-1); 207 } 208 } 209 210 if (fd_tuple == NULL || fd_tuple->real_dev != fd) 211 return (write_f(fd, buf, nbytes)); 212 213 if ((n = write_f(fd, buf, nbytes)) < 0 || 214 write_f(fd_tuple->snoop_out, buf, n) != n) 215 return (-1); 216 217 return (n); 218 } 219