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