]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/libfido2/src/hid_linux.c
MFV 66082b6c88b9: libbsdxml (expat) 2.4.9
[FreeBSD/FreeBSD.git] / contrib / libfido2 / src / hid_linux.c
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 }