Coverage Report

Created: 2022-07-22 12:05

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