Coverage Report

Created: 2021-03-26 11:35

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