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