Coverage Report

Created: 2020-03-07 10:10

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