xref: /netbsd-src/external/bsd/libfido2/dist/src/hid_linux.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
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 #include <sys/types.h>
8 
9 #include <sys/ioctl.h>
10 #include <linux/hidraw.h>
11 #include <linux/input.h>
12 
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <libudev.h>
16 #include <poll.h>
17 #include <string.h>
18 #include <unistd.h>
19 
20 #include "fido.h"
21 
22 struct hid_linux {
23 	int	fd;
24 	size_t	report_in_len;
25 	size_t	report_out_len;
26 };
27 
28 static int
29 get_key_len(uint8_t tag, uint8_t *key, size_t *key_len)
30 {
31 	*key = tag & 0xfc;
32 	if ((*key & 0xf0) == 0xf0) {
33 		fido_log_debug("%s: *key=0x%02x", __func__, *key);
34 		return (-1);
35 	}
36 
37 	*key_len = tag & 0x3;
38 	if (*key_len == 3) {
39 		*key_len = 4;
40 	}
41 
42 	return (0);
43 }
44 
45 static int
46 get_key_val(const void *body, size_t key_len, uint32_t *val)
47 {
48 	const uint8_t *ptr = body;
49 
50 	switch (key_len) {
51 	case 0:
52 		*val = 0;
53 		break;
54 	case 1:
55 		*val = ptr[0];
56 		break;
57 	case 2:
58 		*val = (uint32_t)((ptr[1] << 8) | ptr[0]);
59 		break;
60 	default:
61 		fido_log_debug("%s: key_len=%zu", __func__, key_len);
62 		return (-1);
63 	}
64 
65 	return (0);
66 }
67 
68 static int
69 get_usage_info(const struct hidraw_report_descriptor *hrd, uint32_t *usage_page,
70     uint32_t *usage)
71 {
72 	const uint8_t	*ptr = hrd->value;
73 	size_t		 len = hrd->size;
74 
75 	while (len > 0) {
76 		const uint8_t tag = ptr[0];
77 		ptr++;
78 		len--;
79 
80 		uint8_t  key;
81 		size_t   key_len;
82 		uint32_t key_val;
83 
84 		if (get_key_len(tag, &key, &key_len) < 0 || key_len > len ||
85 		    get_key_val(ptr, key_len, &key_val) < 0) {
86 			return (-1);
87 		}
88 
89 		if (key == 0x4) {
90 			*usage_page = key_val;
91 		} else if (key == 0x8) {
92 			*usage = key_val;
93 		}
94 
95 		ptr += key_len;
96 		len -= key_len;
97 	}
98 
99 	return (0);
100 }
101 
102 static int
103 get_report_sizes(const struct hidraw_report_descriptor *hrd,
104     size_t *report_in_len, size_t *report_out_len)
105 {
106 	const uint8_t	*ptr = hrd->value;
107 	size_t		 len = hrd->size;
108 	uint32_t	 report_size = 0;
109 
110 	while (len > 0) {
111 		const uint8_t tag = ptr[0];
112 		ptr++;
113 		len--;
114 
115 		uint8_t  key;
116 		size_t   key_len;
117 		uint32_t key_val;
118 
119 		if (get_key_len(tag, &key, &key_len) < 0 || key_len > len ||
120 		    get_key_val(ptr, key_len, &key_val) < 0) {
121 			return (-1);
122 		}
123 
124 		if (key == 0x94) {
125 			report_size = key_val;
126 		} else if (key == 0x80) {
127 			*report_in_len = (size_t)report_size;
128 		} else if (key == 0x90) {
129 			*report_out_len = (size_t)report_size;
130 		}
131 
132 		ptr += key_len;
133 		len -= key_len;
134 	}
135 
136 	return (0);
137 }
138 
139 static int
140 get_report_descriptor(int fd, struct hidraw_report_descriptor *hrd)
141 {
142 	int s = -1;
143 
144 	if (ioctl(fd, HIDIOCGRDESCSIZE, &s) < 0 || s < 0 ||
145 	    (unsigned)s > HID_MAX_DESCRIPTOR_SIZE) {
146 		fido_log_debug("%s: ioctl HIDIOCGRDESCSIZE", __func__);
147 		return (-1);
148 	}
149 
150 	hrd->size = (unsigned)s;
151 
152 	if (ioctl(fd, HIDIOCGRDESC, hrd) < 0) {
153 		fido_log_debug("%s: ioctl HIDIOCGRDESC", __func__);
154 		return (-1);
155 	}
156 
157 	return (0);
158 }
159 
160 static bool
161 is_fido(const char *path)
162 {
163 	int				fd;
164 	uint32_t			usage = 0;
165 	uint32_t			usage_page = 0;
166 	struct hidraw_report_descriptor	hrd;
167 
168 	memset(&hrd, 0, sizeof(hrd));
169 
170 	if ((fd = open(path, O_RDONLY)) == -1) {
171 		fido_log_debug("%s: open", __func__);
172 		return (false);
173 	}
174 
175 	if (get_report_descriptor(fd, &hrd) < 0 ||
176 	    get_usage_info(&hrd, &usage_page, &usage) < 0) {
177 		close(fd);
178 		return (false);
179 	}
180 
181 	close(fd);
182 
183 	return (usage_page == 0xf1d0);
184 }
185 
186 static int
187 parse_uevent(const char *uevent, int *bus, int16_t *vendor_id,
188     int16_t *product_id)
189 {
190 	char			*cp;
191 	char			*p;
192 	char			*s;
193 	int			 ok = -1;
194 	short unsigned int	 x;
195 	short unsigned int	 y;
196 	short unsigned int	 z;
197 
198 	if ((s = cp = strdup(uevent)) == NULL)
199 		return (-1);
200 
201 	while ((p = strsep(&cp, "\n")) != NULL && *p != '\0') {
202 		if (strncmp(p, "HID_ID=", 7) == 0) {
203 			if (sscanf(p + 7, "%hx:%hx:%hx", &x, &y, &z) == 3) {
204 				*bus = (int)x;
205 				*vendor_id = (int16_t)y;
206 				*product_id = (int16_t)z;
207 				ok = 0;
208 				break;
209 			}
210 		}
211 	}
212 
213 	free(s);
214 
215 	return (ok);
216 }
217 
218 static char *
219 get_parent_attr(struct udev_device *dev, const char *subsystem,
220     const char *devtype, const char *attr)
221 {
222 	struct udev_device	*parent;
223 	const char		*value;
224 
225 	if ((parent = udev_device_get_parent_with_subsystem_devtype(dev,
226 	    subsystem, devtype)) == NULL || (value =
227 	    udev_device_get_sysattr_value(parent, attr)) == NULL)
228 		return (NULL);
229 
230 	return (strdup(value));
231 }
232 
233 static char *
234 get_usb_attr(struct udev_device *dev, const char *attr)
235 {
236 	return (get_parent_attr(dev, "usb", "usb_device", attr));
237 }
238 
239 static int
240 copy_info(fido_dev_info_t *di, struct udev *udev,
241     struct udev_list_entry *udev_entry)
242 {
243 	const char		*name;
244 	const char		*path;
245 	char			*uevent = NULL;
246 	struct udev_device	*dev = NULL;
247 	int			 bus = 0;
248 	int			 ok = -1;
249 
250 	memset(di, 0, sizeof(*di));
251 
252 	if ((name = udev_list_entry_get_name(udev_entry)) == NULL ||
253 	    (dev = udev_device_new_from_syspath(udev, name)) == NULL ||
254 	    (path = udev_device_get_devnode(dev)) == NULL ||
255 	    is_fido(path) == 0)
256 		goto fail;
257 
258 	if ((uevent = get_parent_attr(dev, "hid", NULL, "uevent")) == NULL ||
259 	    parse_uevent(uevent, &bus, &di->vendor_id, &di->product_id) < 0) {
260 		fido_log_debug("%s: uevent", __func__);
261 		goto fail;
262 	}
263 
264 #ifndef FIDO_HID_ANY
265 	if (bus != BUS_USB) {
266 		fido_log_debug("%s: bus", __func__);
267 		goto fail;
268 	}
269 #endif
270 
271 	di->path = strdup(path);
272 	di->manufacturer = get_usb_attr(dev, "manufacturer");
273 	di->product = get_usb_attr(dev, "product");
274 
275 	if (di->path == NULL || di->manufacturer == NULL || di->product == NULL)
276 		goto fail;
277 
278 	ok = 0;
279 fail:
280 	if (dev != NULL)
281 		udev_device_unref(dev);
282 
283 	free(uevent);
284 
285 	if (ok < 0) {
286 		free(di->path);
287 		free(di->manufacturer);
288 		free(di->product);
289 		explicit_bzero(di, sizeof(*di));
290 	}
291 
292 	return (ok);
293 }
294 
295 int
296 fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
297 {
298 	struct udev		*udev = NULL;
299 	struct udev_enumerate	*udev_enum = NULL;
300 	struct udev_list_entry	*udev_list;
301 	struct udev_list_entry	*udev_entry;
302 	int			 r = FIDO_ERR_INTERNAL;
303 
304 	*olen = 0;
305 
306 	if (ilen == 0)
307 		return (FIDO_OK); /* nothing to do */
308 
309 	if (devlist == NULL)
310 		return (FIDO_ERR_INVALID_ARGUMENT);
311 
312 	if ((udev = udev_new()) == NULL ||
313 	    (udev_enum = udev_enumerate_new(udev)) == NULL)
314 		goto fail;
315 
316 	if (udev_enumerate_add_match_subsystem(udev_enum, "hidraw") < 0 ||
317 	    udev_enumerate_scan_devices(udev_enum) < 0)
318 		goto fail;
319 
320 	if ((udev_list = udev_enumerate_get_list_entry(udev_enum)) == NULL) {
321 		r = FIDO_OK; /* zero hidraw devices */
322 		goto fail;
323 	}
324 
325 	udev_list_entry_foreach(udev_entry, udev_list) {
326 		if (copy_info(&devlist[*olen], udev, udev_entry) == 0) {
327 			devlist[*olen].io = (fido_dev_io_t) {
328 				fido_hid_open,
329 				fido_hid_close,
330 				fido_hid_read,
331 				fido_hid_write,
332 			};
333 			if (++(*olen) == ilen)
334 				break;
335 		}
336 	}
337 
338 	r = FIDO_OK;
339 fail:
340 	if (udev_enum != NULL)
341 		udev_enumerate_unref(udev_enum);
342 	if (udev != NULL)
343 		udev_unref(udev);
344 
345 	return (r);
346 }
347 
348 void *
349 fido_hid_open(const char *path)
350 {
351 	struct hid_linux		*ctx;
352 	struct hidraw_report_descriptor	 hrd;
353 
354 	if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
355 		return (NULL);
356 
357 	if ((ctx->fd = open(path, O_RDWR)) < 0) {
358 		free(ctx);
359 		return (NULL);
360 	}
361 
362 	if (get_report_descriptor(ctx->fd, &hrd) < 0 || get_report_sizes(&hrd,
363 	    &ctx->report_in_len, &ctx->report_out_len) < 0 ||
364 	    ctx->report_in_len == 0 || ctx->report_out_len == 0) {
365 		fido_log_debug("%s: using default report sizes", __func__);
366 		ctx->report_in_len = CTAP_MAX_REPORT_LEN;
367 		ctx->report_out_len = CTAP_MAX_REPORT_LEN;
368 	}
369 
370 	return (ctx);
371 }
372 
373 void
374 fido_hid_close(void *handle)
375 {
376 	struct hid_linux *ctx = handle;
377 
378 	close(ctx->fd);
379 	free(ctx);
380 }
381 
382 static int
383 timespec_to_ms(const struct timespec *ts, int upper_bound)
384 {
385 	int64_t x;
386 	int64_t y;
387 
388 	if (ts->tv_sec < 0 || ts->tv_sec > INT64_MAX / 1000LL ||
389 	    ts->tv_nsec < 0 || ts->tv_nsec / 1000000LL > INT64_MAX)
390 		return (upper_bound);
391 
392 	x = ts->tv_sec * 1000LL;
393 	y = ts->tv_nsec / 1000000LL;
394 
395 	if (INT64_MAX - x < y || x + y > upper_bound)
396 		return (upper_bound);
397 
398 	return (int)(x + y);
399 }
400 
401 static int
402 waitfd(int fd, int ms)
403 {
404 	struct timespec	ts_start;
405 	struct timespec	ts_now;
406 	struct timespec	ts_delta;
407 	struct pollfd	pfd;
408 	int		ms_remain;
409 	int		r;
410 
411 	if (ms < 0)
412 		return (0);
413 
414 	memset(&pfd, 0, sizeof(pfd));
415 	pfd.events = POLLIN;
416 	pfd.fd = fd;
417 
418 	if (clock_gettime(CLOCK_MONOTONIC, &ts_start) != 0) {
419 		fido_log_debug("%s: clock_gettime: %s", __func__,
420 		    strerror(errno));
421 		return (-1);
422 	}
423 
424 	for (ms_remain = ms; ms_remain > 0;) {
425 		if ((r = poll(&pfd, 1, ms_remain)) > 0)
426 			return (0);
427 		else if (r == 0)
428 			break;
429 		else if (errno != EINTR) {
430 			fido_log_debug("%s: poll: %s", __func__,
431 			    strerror(errno));
432 			return (-1);
433 		}
434 		/* poll interrupted - subtract time already waited */
435 		if (clock_gettime(CLOCK_MONOTONIC, &ts_now) != 0) {
436 			fido_log_debug("%s: clock_gettime: %s", __func__,
437 			    strerror(errno));
438 			return (-1);
439 		}
440 		timespecsub(&ts_now, &ts_start, &ts_delta);
441 		ms_remain = ms - timespec_to_ms(&ts_delta, ms);
442 	}
443 
444 	return (-1);
445 }
446 
447 int
448 fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
449 {
450 	struct hid_linux	*ctx = handle;
451 	ssize_t			 r;
452 
453 	if (len != ctx->report_in_len) {
454 		fido_log_debug("%s: len %zu", __func__, len);
455 		return (-1);
456 	}
457 
458 	if (waitfd(ctx->fd, ms) < 0) {
459 		fido_log_debug("%s: fd not ready", __func__);
460 		return (-1);
461 	}
462 
463 	if ((r = read(ctx->fd, buf, len)) < 0 || (size_t)r != len) {
464 		fido_log_debug("%s: read", __func__);
465 		return (-1);
466 	}
467 
468 	return ((int)r);
469 }
470 
471 int
472 fido_hid_write(void *handle, const unsigned char *buf, size_t len)
473 {
474 	struct hid_linux	*ctx = handle;
475 	ssize_t			 r;
476 
477 	if (len != ctx->report_out_len + 1) {
478 		fido_log_debug("%s: len %zu", __func__, len);
479 		return (-1);
480 	}
481 
482 	if ((r = write(ctx->fd, buf, len)) < 0 || (size_t)r != len) {
483 		fido_log_debug("%s: write", __func__);
484 		return (-1);
485 	}
486 
487 	return ((int)r);
488 }
489 
490 size_t
491 fido_hid_report_in_len(void *handle)
492 {
493 	struct hid_linux *ctx = handle;
494 
495 	return (ctx->report_in_len);
496 }
497 
498 size_t
499 fido_hid_report_out_len(void *handle)
500 {
501 	struct hid_linux *ctx = handle;
502 
503 	return (ctx->report_out_len);
504 }
505