xref: /freebsd/contrib/libfido2/src/hid_linux.c (revision a3cefe7f2b4df0f70ff92d4570ce18e517af43ec)
1 /*
2  * Copyright (c) 2019-2024 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  * SPDX-License-Identifier: BSD-2-Clause
6  */
7 
8 #include <sys/types.h>
9 #include <sys/file.h>
10 #include <sys/ioctl.h>
11 
12 #include <linux/hidraw.h>
13 #include <linux/input.h>
14 
15 #include <errno.h>
16 #include <libudev.h>
17 #include <time.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 	sigset_t        sigmask;
27 	const sigset_t *sigmaskp;
28 };
29 
30 static int
31 get_report_descriptor(int fd, struct hidraw_report_descriptor *hrd)
32 {
33 	int s = -1;
34 
35 	if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESCSIZE), &s) == -1) {
36 		fido_log_error(errno, "%s: ioctl HIDIOCGRDESCSIZE", __func__);
37 		return (-1);
38 	}
39 
40 	if (s < 0 || (unsigned)s > HID_MAX_DESCRIPTOR_SIZE) {
41 		fido_log_debug("%s: HIDIOCGRDESCSIZE %d", __func__, s);
42 		return (-1);
43 	}
44 
45 	hrd->size = (unsigned)s;
46 
47 	if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESC), hrd) == -1) {
48 		fido_log_error(errno, "%s: ioctl HIDIOCGRDESC", __func__);
49 		return (-1);
50 	}
51 
52 	return (0);
53 }
54 
55 static bool
56 is_fido(const char *path)
57 {
58 	int				 fd = -1;
59 	uint32_t			 usage_page = 0;
60 	struct hidraw_report_descriptor	*hrd = NULL;
61 
62 	if ((hrd = calloc(1, sizeof(*hrd))) == NULL ||
63 	    (fd = fido_hid_unix_open(path)) == -1)
64 		goto out;
65 	if (get_report_descriptor(fd, hrd) < 0 ||
66 	    fido_hid_get_usage(hrd->value, hrd->size, &usage_page) < 0)
67 		usage_page = 0;
68 
69 out:
70 	free(hrd);
71 
72 	if (fd != -1 && close(fd) == -1)
73 		fido_log_error(errno, "%s: close", __func__);
74 
75 	return (usage_page == 0xf1d0);
76 }
77 
78 static int
79 parse_uevent(const char *uevent, int *bus, int16_t *vendor_id,
80     int16_t *product_id, char **hid_name)
81 {
82 	char			*cp;
83 	char			*p;
84 	char			*s;
85 	bool			 found_id = false;
86 	bool			 found_name = false;
87 	short unsigned int	 x;
88 	short unsigned int	 y;
89 	short unsigned int	 z;
90 
91 	if ((s = cp = strdup(uevent)) == NULL)
92 		return (-1);
93 
94 	while ((p = strsep(&cp, "\n")) != NULL && *p != '\0') {
95 		if (!found_id && strncmp(p, "HID_ID=", 7) == 0) {
96 			if (sscanf(p + 7, "%hx:%hx:%hx", &x, &y, &z) == 3) {
97 				*bus = (int)x;
98 				*vendor_id = (int16_t)y;
99 				*product_id = (int16_t)z;
100 				found_id = true;
101 			}
102 		} else if (!found_name && strncmp(p, "HID_NAME=", 9) == 0) {
103 			if ((*hid_name = strdup(p + 9)) != NULL)
104 				found_name = true;
105 		}
106 	}
107 
108 	free(s);
109 
110 	if (!found_name || !found_id)
111 		return (-1);
112 
113 	return (0);
114 }
115 
116 static char *
117 get_parent_attr(struct udev_device *dev, const char *subsystem,
118     const char *devtype, const char *attr)
119 {
120 	struct udev_device	*parent;
121 	const char		*value;
122 
123 	if ((parent = udev_device_get_parent_with_subsystem_devtype(dev,
124 	    subsystem, devtype)) == NULL || (value =
125 	    udev_device_get_sysattr_value(parent, attr)) == NULL)
126 		return (NULL);
127 
128 	return (strdup(value));
129 }
130 
131 static char *
132 get_usb_attr(struct udev_device *dev, const char *attr)
133 {
134 	return (get_parent_attr(dev, "usb", "usb_device", attr));
135 }
136 
137 static int
138 copy_info(fido_dev_info_t *di, struct udev *udev,
139     struct udev_list_entry *udev_entry)
140 {
141 	const char		*name;
142 	const char		*path;
143 	char			*uevent = NULL;
144 	struct udev_device	*dev = NULL;
145 	int			 bus = 0;
146 	char			*hid_name = NULL;
147 	int			 ok = -1;
148 
149 	memset(di, 0, sizeof(*di));
150 
151 	if ((name = udev_list_entry_get_name(udev_entry)) == NULL ||
152 	    (dev = udev_device_new_from_syspath(udev, name)) == NULL ||
153 	    (path = udev_device_get_devnode(dev)) == NULL ||
154 	    is_fido(path) == 0)
155 		goto fail;
156 
157 	if ((uevent = get_parent_attr(dev, "hid", NULL, "uevent")) == NULL ||
158 	    parse_uevent(uevent, &bus, &di->vendor_id, &di->product_id,
159 	    &hid_name) < 0) {
160 		fido_log_debug("%s: uevent", __func__);
161 		goto fail;
162 	}
163 
164 #ifndef FIDO_HID_ANY
165 	if (bus != BUS_USB) {
166 		fido_log_debug("%s: bus", __func__);
167 		goto fail;
168 	}
169 #endif
170 
171 	di->path = strdup(path);
172 	di->manufacturer = get_usb_attr(dev, "manufacturer");
173 	di->product = get_usb_attr(dev, "product");
174 
175 	if (di->manufacturer == NULL && di->product == NULL) {
176 		di->product = hid_name;  /* fallback */
177 		hid_name = NULL;
178 	}
179 	if (di->manufacturer == NULL)
180 		di->manufacturer = strdup("");
181 	if (di->product == NULL)
182 		di->product = strdup("");
183 	if (di->path == NULL || di->manufacturer == NULL || di->product == NULL)
184 		goto fail;
185 
186 	ok = 0;
187 fail:
188 	if (dev != NULL)
189 		udev_device_unref(dev);
190 
191 	free(uevent);
192 	free(hid_name);
193 
194 	if (ok < 0) {
195 		free(di->path);
196 		free(di->manufacturer);
197 		free(di->product);
198 		explicit_bzero(di, sizeof(*di));
199 	}
200 
201 	return (ok);
202 }
203 
204 int
205 fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
206 {
207 	struct udev		*udev = NULL;
208 	struct udev_enumerate	*udev_enum = NULL;
209 	struct udev_list_entry	*udev_list;
210 	struct udev_list_entry	*udev_entry;
211 	int			 r = FIDO_ERR_INTERNAL;
212 
213 	*olen = 0;
214 
215 	if (ilen == 0)
216 		return (FIDO_OK); /* nothing to do */
217 
218 	if (devlist == NULL)
219 		return (FIDO_ERR_INVALID_ARGUMENT);
220 
221 	if ((udev = udev_new()) == NULL ||
222 	    (udev_enum = udev_enumerate_new(udev)) == NULL)
223 		goto fail;
224 
225 	if (udev_enumerate_add_match_subsystem(udev_enum, "hidraw") < 0 ||
226 	    udev_enumerate_scan_devices(udev_enum) < 0)
227 		goto fail;
228 
229 	if ((udev_list = udev_enumerate_get_list_entry(udev_enum)) == NULL) {
230 		r = FIDO_OK; /* zero hidraw devices */
231 		goto fail;
232 	}
233 
234 	udev_list_entry_foreach(udev_entry, udev_list) {
235 		if (copy_info(&devlist[*olen], udev, udev_entry) == 0) {
236 			devlist[*olen].io = (fido_dev_io_t) {
237 				fido_hid_open,
238 				fido_hid_close,
239 				fido_hid_read,
240 				fido_hid_write,
241 			};
242 			if (++(*olen) == ilen)
243 				break;
244 		}
245 	}
246 
247 	r = FIDO_OK;
248 fail:
249 	if (udev_enum != NULL)
250 		udev_enumerate_unref(udev_enum);
251 	if (udev != NULL)
252 		udev_unref(udev);
253 
254 	return (r);
255 }
256 
257 void *
258 fido_hid_open(const char *path)
259 {
260 	struct hid_linux *ctx;
261 	struct hidraw_report_descriptor *hrd;
262 	struct timespec tv_pause;
263 	long interval_ms, retries = 0;
264 	bool looped;
265 
266 retry:
267 	looped = false;
268 
269 	if ((ctx = calloc(1, sizeof(*ctx))) == NULL ||
270 	    (ctx->fd = fido_hid_unix_open(path)) == -1) {
271 		free(ctx);
272 		return (NULL);
273 	}
274 
275 	while (flock(ctx->fd, LOCK_EX|LOCK_NB) == -1) {
276 		if (errno != EWOULDBLOCK) {
277 			fido_log_error(errno, "%s: flock", __func__);
278 			fido_hid_close(ctx);
279 			return (NULL);
280 		}
281 		looped = true;
282 		if (retries++ >= 20) {
283 			fido_log_debug("%s: flock timeout", __func__);
284 			fido_hid_close(ctx);
285 			return (NULL);
286 		}
287 		interval_ms = retries * 100000000L;
288 		tv_pause.tv_sec = interval_ms / 1000000000L;
289 		tv_pause.tv_nsec = interval_ms % 1000000000L;
290 		if (nanosleep(&tv_pause, NULL) == -1) {
291 			fido_log_error(errno, "%s: nanosleep", __func__);
292 			fido_hid_close(ctx);
293 			return (NULL);
294 		}
295 	}
296 
297 	if (looped) {
298 		fido_log_debug("%s: retrying", __func__);
299 		fido_hid_close(ctx);
300 		goto retry;
301 	}
302 
303 	if ((hrd = calloc(1, sizeof(*hrd))) == NULL ||
304 	    get_report_descriptor(ctx->fd, hrd) < 0 ||
305 	    fido_hid_get_report_len(hrd->value, hrd->size, &ctx->report_in_len,
306 	    &ctx->report_out_len) < 0 || ctx->report_in_len == 0 ||
307 	    ctx->report_out_len == 0) {
308 		fido_log_debug("%s: using default report sizes", __func__);
309 		ctx->report_in_len = CTAP_MAX_REPORT_LEN;
310 		ctx->report_out_len = CTAP_MAX_REPORT_LEN;
311 	}
312 
313 	free(hrd);
314 
315 	return (ctx);
316 }
317 
318 void
319 fido_hid_close(void *handle)
320 {
321 	struct hid_linux *ctx = handle;
322 
323 	if (close(ctx->fd) == -1)
324 		fido_log_error(errno, "%s: close", __func__);
325 
326 	free(ctx);
327 }
328 
329 int
330 fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask)
331 {
332 	struct hid_linux *ctx = handle;
333 
334 	ctx->sigmask = *sigmask;
335 	ctx->sigmaskp = &ctx->sigmask;
336 
337 	return (FIDO_OK);
338 }
339 
340 int
341 fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
342 {
343 	struct hid_linux	*ctx = handle;
344 	ssize_t			 r;
345 
346 	if (len != ctx->report_in_len) {
347 		fido_log_debug("%s: len %zu", __func__, len);
348 		return (-1);
349 	}
350 
351 	if (fido_hid_unix_wait(ctx->fd, ms, ctx->sigmaskp) < 0) {
352 		fido_log_debug("%s: fd not ready", __func__);
353 		return (-1);
354 	}
355 
356 	if ((r = read(ctx->fd, buf, len)) == -1) {
357 		fido_log_error(errno, "%s: read", __func__);
358 		return (-1);
359 	}
360 
361 	if (r < 0 || (size_t)r != len) {
362 		fido_log_debug("%s: %zd != %zu", __func__, r, len);
363 		return (-1);
364 	}
365 
366 	return ((int)r);
367 }
368 
369 int
370 fido_hid_write(void *handle, const unsigned char *buf, size_t len)
371 {
372 	struct hid_linux	*ctx = handle;
373 	ssize_t			 r;
374 
375 	if (len != ctx->report_out_len + 1) {
376 		fido_log_debug("%s: len %zu", __func__, len);
377 		return (-1);
378 	}
379 
380 	if ((r = write(ctx->fd, buf, len)) == -1) {
381 		fido_log_error(errno, "%s: write", __func__);
382 		return (-1);
383 	}
384 
385 	if (r < 0 || (size_t)r != len) {
386 		fido_log_debug("%s: %zd != %zu", __func__, r, len);
387 		return (-1);
388 	}
389 
390 	return ((int)r);
391 }
392 
393 size_t
394 fido_hid_report_in_len(void *handle)
395 {
396 	struct hid_linux *ctx = handle;
397 
398 	return (ctx->report_in_len);
399 }
400 
401 size_t
402 fido_hid_report_out_len(void *handle)
403 {
404 	struct hid_linux *ctx = handle;
405 
406 	return (ctx->report_out_len);
407 }
408