## Coverage Report

#### Created: 2023-09-23 17:42

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