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