Coverage Report

Created: 2025-03-01 02:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/libfido2/src/hid_linux.c
Line
Count
Source
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
31
get_report_descriptor(int fd, struct hidraw_report_descriptor *hrd)
32
2.48M
{
33
2.48M
        int s = -1;
34
35
2.48M
        if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESCSIZE), &s) == -1) {
36
6.13k
                fido_log_error(errno, "%s: ioctl HIDIOCGRDESCSIZE", __func__);
37
6.13k
                return (-1);
38
6.13k
        }
39
40
2.47M
        if (s < 0 || (unsigned)s > HID_MAX_DESCRIPTOR_SIZE) {
41
0
                fido_log_debug("%s: HIDIOCGRDESCSIZE %d", __func__, s);
42
0
                return (-1);
43
0
        }
44
45
2.47M
        hrd->size = (unsigned)s;
46
47
2.47M
        if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESC), hrd) == -1) {
48
5.94k
                fido_log_error(errno, "%s: ioctl HIDIOCGRDESC", __func__);
49
5.94k
                return (-1);
50
5.94k
        }
51
52
2.47M
        return (0);
53
2.47M
}
54
55
static bool
56
is_fido(const char *path)
57
2.49M
{
58
2.49M
        int                              fd = -1;
59
2.49M
        uint32_t                         usage_page = 0;
60
2.49M
        struct hidraw_report_descriptor *hrd = NULL;
61
62
2.49M
        if ((hrd = calloc(1, sizeof(*hrd))) == NULL ||
63
2.49M
            (fd = fido_hid_unix_open(path)) == -1)
64
6.15k
                goto out;
65
2.48M
        if (get_report_descriptor(fd, hrd) < 0 ||
66
2.48M
            fido_hid_get_usage(hrd->value, hrd->size, &usage_page) < 0)
67
1.27M
                usage_page = 0;
68
69
2.49M
out:
70
2.49M
        free(hrd);
71
72
2.49M
        if (fd != -1 && close(fd) == -1)
73
0
                fido_log_error(errno, "%s: close", __func__);
74
75
2.49M
        return (usage_page == 0xf1d0);
76
2.48M
}
77
78
static int
79
parse_uevent(const char *uevent, int *bus, int16_t *vendor_id,
80
    int16_t *product_id, char **hid_name)
81
357k
{
82
357k
        char                    *cp;
83
357k
        char                    *p;
84
357k
        char                    *s;
85
357k
        bool                     found_id = false;
86
357k
        bool                     found_name = false;
87
357k
        short unsigned int       x;
88
357k
        short unsigned int       y;
89
357k
        short unsigned int       z;
90
91
357k
        if ((s = cp = strdup(uevent)) == NULL)
92
1.10k
                return (-1);
93
94
720k
        while ((p = strsep(&cp, "\n")) != NULL && *p != '\0') {
95
364k
                if (!found_id && strncmp(p, "HID_ID=", 7) == 0) {
96
50.0k
                        if (sscanf(p + 7, "%hx:%hx:%hx", &x, &y, &z) == 3) {
97
36.6k
                                *bus = (int)x;
98
36.6k
                                *vendor_id = (int16_t)y;
99
36.6k
                                *product_id = (int16_t)z;
100
36.6k
                                found_id = true;
101
36.6k
                        }
102
313k
                } else if (!found_name && strncmp(p, "HID_NAME=", 9) == 0) {
103
78.1k
                        if ((*hid_name = strdup(p + 9)) != NULL)
104
77.8k
                                found_name = true;
105
78.1k
                }
106
364k
        }
107
108
356k
        free(s);
109
110
356k
        if (!found_name || !found_id)
111
327k
                return (-1);
112
113
28.7k
        return (0);
114
356k
}
115
116
static char *
117
get_parent_attr(struct udev_device *dev, const char *subsystem,
118
    const char *devtype, const char *attr)
119
377k
{
120
377k
        struct udev_device      *parent;
121
377k
        const char              *value;
122
123
377k
        if ((parent = udev_device_get_parent_with_subsystem_devtype(dev,
124
377k
            subsystem, devtype)) == NULL || (value =
125
376k
            udev_device_get_sysattr_value(parent, attr)) == NULL)
126
2.08k
                return (NULL);
127
128
375k
        return (strdup(value));
129
377k
}
130
131
static char *
132
get_usb_attr(struct udev_device *dev, const char *attr)
133
17.6k
{
134
17.6k
        return (get_parent_attr(dev, "usb", "usb_device", attr));
135
17.6k
}
136
137
static int
138
copy_info(fido_dev_info_t *di, struct udev *udev,
139
    struct udev_list_entry *udev_entry)
140
2.51M
{
141
2.51M
        const char              *name;
142
2.51M
        const char              *path;
143
2.51M
        char                    *uevent = NULL;
144
2.51M
        struct udev_device      *dev = NULL;
145
2.51M
        int                      bus = 0;
146
2.51M
        char                    *hid_name = NULL;
147
2.51M
        int                      ok = -1;
148
149
2.51M
        memset(di, 0, sizeof(*di));
150
151
2.51M
        if ((name = udev_list_entry_get_name(udev_entry)) == NULL ||
152
2.51M
            (dev = udev_device_new_from_syspath(udev, name)) == NULL ||
153
2.51M
            (path = udev_device_get_devnode(dev)) == NULL ||
154
2.51M
            is_fido(path) == 0)
155
2.15M
                goto fail;
156
157
360k
        if ((uevent = get_parent_attr(dev, "hid", NULL, "uevent")) == NULL ||
158
360k
            parse_uevent(uevent, &bus, &di->vendor_id, &di->product_id,
159
357k
            &hid_name) < 0) {
160
331k
                fido_log_debug("%s: uevent", __func__);
161
331k
                goto fail;
162
331k
        }
163
164
28.7k
#ifndef FIDO_HID_ANY
165
28.7k
        if (bus != BUS_USB) {
166
19.8k
                fido_log_debug("%s: bus", __func__);
167
19.8k
                goto fail;
168
19.8k
        }
169
8.83k
#endif
170
171
8.83k
        di->path = strdup(path);
172
8.83k
        di->manufacturer = get_usb_attr(dev, "manufacturer");
173
8.83k
        di->product = get_usb_attr(dev, "product");
174
175
8.83k
        if (di->manufacturer == NULL && di->product == NULL) {
176
13
                di->product = hid_name;  /* fallback */
177
13
                hid_name = NULL;
178
13
        }
179
8.83k
        if (di->manufacturer == NULL)
180
242
                di->manufacturer = strdup("");
181
8.83k
        if (di->product == NULL)
182
116
                di->product = strdup("");
183
8.83k
        if (di->path == NULL || di->manufacturer == NULL || di->product == NULL)
184
186
                goto fail;
185
186
8.64k
        ok = 0;
187
2.51M
fail:
188
2.51M
        if (dev != NULL)
189
2.49M
                udev_device_unref(dev);
190
191
2.51M
        free(uevent);
192
2.51M
        free(hid_name);
193
194
2.51M
        if (ok < 0) {
195
2.50M
                free(di->path);
196
2.50M
                free(di->manufacturer);
197
2.50M
                free(di->product);
198
2.50M
                explicit_bzero(di, sizeof(*di));
199
2.50M
        }
200
201
2.51M
        return (ok);
202
8.64k
}
203
204
int
205
fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
206
5.96k
{
207
5.96k
        struct udev             *udev = NULL;
208
5.96k
        struct udev_enumerate   *udev_enum = NULL;
209
5.96k
        struct udev_list_entry  *udev_list;
210
5.96k
        struct udev_list_entry  *udev_entry;
211
5.96k
        int                      r = FIDO_ERR_INTERNAL;
212
213
5.96k
        *olen = 0;
214
215
5.96k
        if (ilen == 0)
216
0
                return (FIDO_OK); /* nothing to do */
217
218
5.96k
        if (devlist == NULL)
219
0
                return (FIDO_ERR_INVALID_ARGUMENT);
220
221
5.96k
        if ((udev = udev_new()) == NULL ||
222
5.96k
            (udev_enum = udev_enumerate_new(udev)) == NULL)
223
64
                goto fail;
224
225
5.90k
        if (udev_enumerate_add_match_subsystem(udev_enum, "hidraw") < 0 ||
226
5.90k
            udev_enumerate_scan_devices(udev_enum) < 0)
227
25
                goto fail;
228
229
5.87k
        if ((udev_list = udev_enumerate_get_list_entry(udev_enum)) == NULL) {
230
8
                r = FIDO_OK; /* zero hidraw devices */
231
8
                goto fail;
232
8
        }
233
234
2.51M
        udev_list_entry_foreach(udev_entry, udev_list) {
235
2.51M
                if (copy_info(&devlist[*olen], udev, udev_entry) == 0) {
236
8.64k
                        devlist[*olen].io = (fido_dev_io_t) {
237
8.64k
                                fido_hid_open,
238
8.64k
                                fido_hid_close,
239
8.64k
                                fido_hid_read,
240
8.64k
                                fido_hid_write,
241
8.64k
                        };
242
8.64k
                        if (++(*olen) == ilen)
243
160
                                break;
244
8.64k
                }
245
2.51M
        }
246
247
5.86k
        r = FIDO_OK;
248
5.96k
fail:
249
5.96k
        if (udev_enum != NULL)
250
5.90k
                udev_enumerate_unref(udev_enum);
251
5.96k
        if (udev != NULL)
252
5.94k
                udev_unref(udev);
253
254
5.96k
        return (r);
255
5.86k
}
256
257
void *
258
fido_hid_open(const char *path)
259
0
{
260
0
        struct hid_linux *ctx;
261
0
        struct hidraw_report_descriptor *hrd;
262
0
        struct timespec tv_pause;
263
0
        long interval_ms, retries = 0;
264
0
        bool looped;
265
266
0
retry:
267
0
        looped = false;
268
269
0
        if ((ctx = calloc(1, sizeof(*ctx))) == NULL ||
270
0
            (ctx->fd = fido_hid_unix_open(path)) == -1) {
271
0
                free(ctx);
272
0
                return (NULL);
273
0
        }
274
275
0
        while (flock(ctx->fd, LOCK_EX|LOCK_NB) == -1) {
276
0
                if (errno != EWOULDBLOCK) {
277
0
                        fido_log_error(errno, "%s: flock", __func__);
278
0
                        fido_hid_close(ctx);
279
0
                        return (NULL);
280
0
                }
281
0
                looped = true;
282
0
                if (retries++ >= 20) {
283
0
                        fido_log_debug("%s: flock timeout", __func__);
284
0
                        fido_hid_close(ctx);
285
0
                        return (NULL);
286
0
                }
287
0
                interval_ms = retries * 100000000L;
288
0
                tv_pause.tv_sec = interval_ms / 1000000000L;
289
0
                tv_pause.tv_nsec = interval_ms % 1000000000L;
290
0
                if (nanosleep(&tv_pause, NULL) == -1) {
291
0
                        fido_log_error(errno, "%s: nanosleep", __func__);
292
0
                        fido_hid_close(ctx);
293
0
                        return (NULL);
294
0
                }
295
0
        }
296
297
0
        if (looped) {
298
0
                fido_log_debug("%s: retrying", __func__);
299
0
                fido_hid_close(ctx);
300
0
                goto retry;
301
0
        }
302
303
0
        if ((hrd = calloc(1, sizeof(*hrd))) == NULL ||
304
0
            get_report_descriptor(ctx->fd, hrd) < 0 ||
305
0
            fido_hid_get_report_len(hrd->value, hrd->size, &ctx->report_in_len,
306
0
            &ctx->report_out_len) < 0 || ctx->report_in_len == 0 ||
307
0
            ctx->report_out_len == 0) {
308
0
                fido_log_debug("%s: using default report sizes", __func__);
309
0
                ctx->report_in_len = CTAP_MAX_REPORT_LEN;
310
0
                ctx->report_out_len = CTAP_MAX_REPORT_LEN;
311
0
        }
312
313
0
        free(hrd);
314
315
0
        return (ctx);
316
0
}
317
318
void
319
fido_hid_close(void *handle)
320
0
{
321
0
        struct hid_linux *ctx = handle;
322
323
0
        if (close(ctx->fd) == -1)
324
0
                fido_log_error(errno, "%s: close", __func__);
325
326
0
        free(ctx);
327
0
}
328
329
int
330
fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask)
331
0
{
332
0
        struct hid_linux *ctx = handle;
333
334
0
        ctx->sigmask = *sigmask;
335
0
        ctx->sigmaskp = &ctx->sigmask;
336
337
0
        return (FIDO_OK);
338
0
}
339
340
int
341
fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
342
0
{
343
0
        struct hid_linux        *ctx = handle;
344
0
        ssize_t                  r;
345
346
0
        if (len != ctx->report_in_len) {
347
0
                fido_log_debug("%s: len %zu", __func__, len);
348
0
                return (-1);
349
0
        }
350
351
0
        if (fido_hid_unix_wait(ctx->fd, ms, ctx->sigmaskp) < 0) {
352
0
                fido_log_debug("%s: fd not ready", __func__);
353
0
                return (-1);
354
0
        }
355
356
0
        if ((r = read(ctx->fd, buf, len)) == -1) {
357
0
                fido_log_error(errno, "%s: read", __func__);
358
0
                return (-1);
359
0
        }
360
361
0
        if (r < 0 || (size_t)r != len) {
362
0
                fido_log_debug("%s: %zd != %zu", __func__, r, len);
363
0
                return (-1);
364
0
        }
365
366
0
        return ((int)r);
367
0
}
368
369
int
370
fido_hid_write(void *handle, const unsigned char *buf, size_t len)
371
0
{
372
0
        struct hid_linux        *ctx = handle;
373
0
        ssize_t                  r;
374
375
0
        if (len != ctx->report_out_len + 1) {
376
0
                fido_log_debug("%s: len %zu", __func__, len);
377
0
                return (-1);
378
0
        }
379
380
0
        if ((r = write(ctx->fd, buf, len)) == -1) {
381
0
                fido_log_error(errno, "%s: write", __func__);
382
0
                return (-1);
383
0
        }
384
385
0
        if (r < 0 || (size_t)r != len) {
386
0
                fido_log_debug("%s: %zd != %zu", __func__, r, len);
387
0
                return (-1);
388
0
        }
389
390
0
        return ((int)r);
391
0
}
392
393
size_t
394
fido_hid_report_in_len(void *handle)
395
0
{
396
0
        struct hid_linux *ctx = handle;
397
398
0
        return (ctx->report_in_len);
399
0
}
400
401
size_t
402
fido_hid_report_out_len(void *handle)
403
0
{
404
0
        struct hid_linux *ctx = handle;
405
406
0
        return (ctx->report_out_len);
407
0
}