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
get_report_descriptor(int fd,struct hidraw_report_descriptor * hrd)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
is_fido(const char * path)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
parse_uevent(const char * uevent,int * bus,int16_t * vendor_id,int16_t * product_id,char ** hid_name)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 *
get_parent_attr(struct udev_device * dev,const char * subsystem,const char * devtype,const char * attr)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 *
get_usb_attr(struct udev_device * dev,const char * attr)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
copy_info(fido_dev_info_t * di,struct udev * udev,struct udev_list_entry * udev_entry)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
fido_hid_manifest(fido_dev_info_t * devlist,size_t ilen,size_t * olen)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 *
fido_hid_open(const char * path)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
fido_hid_close(void * handle)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
fido_hid_set_sigmask(void * handle,const fido_sigset_t * sigmask)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
fido_hid_read(void * handle,unsigned char * buf,size_t len,int ms)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
fido_hid_write(void * handle,const unsigned char * buf,size_t len)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
fido_hid_report_in_len(void * handle)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
fido_hid_report_out_len(void * handle)402 fido_hid_report_out_len(void *handle)
403 {
404 struct hid_linux *ctx = handle;
405
406 return (ctx->report_out_len);
407 }
408