Coverage Report

Created: 2021-07-20 18:14

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