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