Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/libfido2/src/hid_linux.c
39483 views
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
}
392
393