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