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