1 /*
2 * Copyright (c) 2019 Tobias Kortkamp <t@tobik.me>
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include "config.h"
18
19 #if HAVE_CAPSICUM
20 # include <sys/capsicum.h>
21 # include "capsicum_helpers.h"
22 #endif
23 #ifdef __FreeBSD__
24 # include <sys/sysctl.h>
25 #endif
26 #include <assert.h>
27 #if HAVE_ERR
28 # include <err.h>
29 #endif
30 #include <sys/param.h>
31 #include <assert.h>
32 #include <err.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <poll.h>
36 #include <string.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40
41 #if defined(__DragonFly__)
42 #include <dev/misc/evdev/input.h>
43 #elif defined(__FreeBSD__)
44 #include <dev/evdev/input.h>
45 #else
46 #include <linux/input.h>
47 #endif
48
49 #include "stroke.h"
50
51 #ifndef nitems
52 #define nitems(x) (sizeof((x)) / sizeof((x)[0]))
53 #endif
54
55 static struct pollfd fds[32];
56 static size_t nfds;
57 static int props_by_sysctl = 0;
58 #if HAVE_CAPSICUM
59 static int command_runner_fd[2] = { -1, -1 };
60 #endif
61
62 // From libudev-devd's udev-utils.c
63 #define LONG_BITS (sizeof(long) * 8)
64 #define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS)
65
66 static inline int
bit_is_set(const unsigned long * array,int bit)67 bit_is_set(const unsigned long *array, int bit)
68 {
69 return !!(array[bit / LONG_BITS] & (1LL << (bit % LONG_BITS)));
70 }
71
72 static int
open_device(size_t i)73 open_device(size_t i)
74 {
75 char buf[PATH_MAX];
76 unsigned long prp_bits[NLONGS(INPUT_PROP_CNT)];
77 size_t len = sizeof(prp_bits);
78
79 #ifdef __FreeBSD__
80 if (props_by_sysctl) {
81 snprintf(buf, sizeof(buf), "kern.evdev.input.%zu.props", i);
82 if (sysctlbyname(buf, prp_bits, &len, NULL, 0) < 0) {
83 return -1;
84 }
85 if (!bit_is_set(prp_bits, INPUT_PROP_POINTER)) {
86 return -1;
87 }
88 }
89 #endif
90
91 snprintf(buf, sizeof(buf), "/dev/input/event%zu", i);
92 int fd = open(buf, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
93 if (fd == -1) {
94 return -1;
95 }
96 if (!props_by_sysctl) {
97 if (ioctl(fd, EVIOCGPROP(len), prp_bits) < 0) {
98 close(fd);
99 return -1;
100 }
101 if (!bit_is_set(prp_bits, INPUT_PROP_POINTER)) {
102 close(fd);
103 return -1;
104 }
105 }
106
107 #if HAVE_CAPSICUM
108 cap_rights_t rights;
109 cap_rights_init(&rights, CAP_READ, CAP_EVENT);
110
111 if (cap_rights_limit(fd, &rights) < 0 && errno != ENOSYS) {
112 err(1, "cap_rights_limit");
113 }
114 #endif
115
116 return fd;
117 }
118
119 #if HAVE_CAPSICUM
120 static void
evdev_create_command_runner(void)121 evdev_create_command_runner(void)
122 {
123 int read_pipe[2];
124 int write_pipe[2];
125
126 if (pipe2(read_pipe, O_CLOEXEC) == -1) {
127 err(1, "pipe2");
128 }
129 if (pipe2(write_pipe, O_CLOEXEC) == -1) {
130 err(1, "pipe2");
131 }
132
133 cap_rights_t rights;
134 cap_rights_init(&rights, CAP_WRITE);
135 command_runner_fd[0] = read_pipe[1];
136 if (cap_rights_limit(command_runner_fd[0], &rights) < 0 && errno != ENOSYS) {
137 err(1, "cap_rights_limit");
138 }
139 if (cap_rights_limit(write_pipe[1], &rights) < 0 && errno != ENOSYS) {
140 err(1, "cap_rights_limit");
141 }
142
143 cap_rights_init(&rights, CAP_READ, CAP_EVENT);
144 command_runner_fd[1] = write_pipe[0];
145 if (cap_rights_limit(read_pipe[0], &rights) < 0 && errno != ENOSYS) {
146 err(1, "cap_rights_limit");
147 }
148 if (cap_rights_limit(write_pipe[0], &rights) < 0 && errno != ENOSYS) {
149 err(1, "cap_rights_limit");
150 }
151
152 pid_t child = fork();
153 if (child > 0) {
154 return;
155 } else if (child == -1) {
156 err(1, "fork");
157 }
158
159 // Drop suid privileges now
160 if (getuid() != geteuid() || getgid() != getegid()) {
161 if (setuid(getuid()) != 0 || setgid(getgid()) != 0) {
162 err(1, "setuid");
163 }
164 }
165 if (setuid(0) != -1) {
166 errx(1, "still root?");
167 }
168
169 setproctitle("command runner: %s", command);
170
171 close(read_pipe[1]);
172 close(write_pipe[0]);
173 closefrom(MAX(read_pipe[0], MAX(write_pipe[1], STDERR_FILENO)) + 1);
174
175 if (write(write_pipe[1], "done", 5) != 5) {
176 err(1, "write");
177 }
178 char buf[4];
179 ssize_t n;
180 while ((n = read(read_pipe[0], buf, 4)) > -1) {
181 if (n == 4 && strncmp(buf, "run", 3) == 0) {
182 tracker_run_command_internal();
183 if (write(write_pipe[1], "done", 5) != 5) {
184 err(1, "write");
185 }
186 }
187 }
188
189 err(1, "read");
190 }
191
192 static void
evdev_run_command(void)193 evdev_run_command(void)
194 {
195 assert(command != NULL);
196
197 char buf[5];
198 if (read(command_runner_fd[1], buf, 5) != 5) {
199 err(1, "read");
200 }
201 if (strncmp(buf, "done", 4) != 0) {
202 errx(1, "invalid response");
203 }
204
205 if (write(command_runner_fd[0], "run", 4) != 4) {
206 err(1, "write");
207 }
208 }
209 #endif
210
211
212 static int
evdev_init(void)213 evdev_init(void)
214 {
215 #if HAVE_CAPSICUM
216 close(STDIN_FILENO);
217 closefrom(STDERR_FILENO + 1);
218
219 if (command != NULL) {
220 evdev_create_command_runner();
221 }
222
223 if (caph_limit_stderr() < 0) {
224 err(1, "caph_limit_stderr");
225 }
226 if (caph_limit_stdout() < 0) {
227 err(1, "caph_limit_stdout");
228 }
229
230 #endif
231
232 #ifdef __FreeBSD__
233 // Getting device properties via sysctls is not supported on all
234 // FreeBSD versions. As a heuristic just check the 0th
235 // device to see if it supported. Fallback is graceful if
236 // this fails at the cost of a bunch of extra open(2) and
237 // ioctl(2).
238 if (sysctlbyname("kern.evdev.input.0.props", NULL, 0, NULL, 0) >= 0) {
239 props_by_sysctl = 1;
240 }
241 #endif
242
243 int maxfd = 0;
244 memset(fds, 0, sizeof(fds));
245 for (size_t i = 0; i < nitems(fds); i++) {
246 int fd = open_device(i);
247 if (fd < 0) {
248 continue;
249 }
250 maxfd = MAX(maxfd, fd);
251 fds[nfds].fd = fd;
252 fds[nfds].events = POLLIN;
253 fds[nfds].revents = 0;
254 nfds++;
255 }
256
257 // Drop suid privileges now
258 if (getuid() != geteuid() || getgid() != getegid()) {
259 if (setuid(getuid()) != 0 || setgid(getgid()) != 0) {
260 err(1, "setuid");
261 }
262 }
263 if (setuid(0) != -1) {
264 errx(1, "still root?");
265 }
266
267 if (nfds == 0) {
268 warnx("evdev: no mouse found");
269 return 0;
270 }
271
272 #if HAVE_CAPSICUM
273 closefrom(maxfd + 1);
274
275 if (caph_enter() < 0) {
276 err(1, "cap_enter");
277 }
278 #endif
279
280 return 1;
281 }
282
283 static int
evdev_record_stroke(struct stroke * stroke)284 evdev_record_stroke(/* out */ struct stroke *stroke)
285 {
286 double x = 0.0;
287 double y = 0.0;
288 while (poll(fds, nfds, -1) > -1) {
289 for (size_t i = 0; i < nfds; i++) {
290 struct input_event ev;
291 if ((fds[i].revents & POLLHUP) ||
292 (fds[i].revents & POLLIN) == 0) {
293 continue;
294 }
295 while (read(fds[i].fd, &ev, sizeof(struct input_event)) > 0) {
296 if (stroke != NULL && ev.type == EV_REL) {
297 switch (ev.code) {
298 case REL_X:
299 x += ev.value;
300 break;
301 case REL_Y:
302 y += ev.value;
303 break;
304 }
305 stroke_add_point(stroke, x, y);
306 } else if (ev.type == EV_KEY) {
307 goto end;
308 }
309 }
310 }
311 }
312
313 end:
314 if (stroke != NULL) {
315 stroke_finish(stroke);
316 }
317
318 return 1;
319 }
320