## Coverage Report

#### Created: 2025-03-01 02:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /libfido2/src/pcsc.c

    Line
    Count
    Source
    1
    /*
    2
     * Copyright (c) 2022 Micro Focus or one of its affiliates.
    3
     * Copyright (c) 2022 Yubico AB. All rights reserved.
    4
     * Use of this source code is governed by a BSD-style
    5
     * license that can be found in the LICENSE file.
    6
     * SPDX-License-Identifier: BSD-2-Clause
    7
     */
    8
    9
    #if __APPLE__
    10
    #include <PCSC/wintypes.h>
    11
    #include <PCSC/winscard.h>
    12
    #else
    13
    #include <winscard.h>
    14
    #endif /* __APPLE__ */
    15
    16
    #include <errno.h>
    17
    18
    #include "fido.h"
    19
    #include "fido/param.h"
    20
    #include "iso7816.h"
    21
    22
    #if defined(_WIN32) && !defined(__MINGW32__)
    23
    #define SCardConnect SCardConnectA
    24
    #define SCardListReaders SCardListReadersA
    25
    #endif
    26
    27
    #ifndef SCARD_PROTOCOL_Tx
    28
    40.7k
    #define SCARD_PROTOCOL_Tx SCARD_PROTOCOL_ANY
    29
    #endif
    30
    31
    94.0k
    #define BUFSIZE 1024        /* in bytes; passed to SCardListReaders() */
    32
    #define APDULEN 264     /* 261 rounded up to the nearest multiple of 8 */
    33
    44.2k
    #define READERS 8        /* maximum number of readers */
    34
    35
    struct pcsc {
    36
            SCARDCONTEXT     ctx;
    37
            SCARDHANDLE      h;
    38
            SCARD_IO_REQUEST req;
    39
            uint8_t          rx_buf[APDULEN];
    40
            size_t           rx_len;
    41
    };
    42
    43
    static LONG
    44
    list_readers(SCARDCONTEXT ctx, char **buf)
    45
    35.5k
    {
    46
    35.5k
            LONG s;
    47
    35.5k
            DWORD len;
    48
    49
    35.5k
            len = BUFSIZE;
    50
    35.5k
            if ((*buf = calloc(1, len)) == NULL)
    51
    111
                    goto fail;
    52
    35.4k
            if ((s = SCardListReaders(ctx, NULL, *buf, &len)) != SCARD_S_SUCCESS) {
    53
    6.14k
                    fido_log_debug("%s: SCardListReaders 0x%lx", __func__, (long)s);
    54
    6.14k
                    goto fail;
    55
    6.14k
            }
    56
            /* sanity check "multi-string" */
    57
    29.2k
            if (len > BUFSIZE || len < 2) {
    58
    1.06k
                    fido_log_debug("%s: bogus len=%u", __func__, (unsigned)len);
    59
    1.06k
                    goto fail;
    60
    1.06k
            }
    61
    28.2k
            if ((*buf)[len - 1] != 0 || (*buf)[len - 2] != '\0') {
    62
    159
                    fido_log_debug("%s: bogus buf", __func__);
    63
    159
                    goto fail;
    64
    159
            }
    65
    28.0k
            return (LONG)SCARD_S_SUCCESS;
    66
    7.47k
    fail:
    67
    7.47k
            free(*buf);
    68
    7.47k
            *buf = NULL;
    69
    70
    7.47k
            return (LONG)SCARD_E_NO_READERS_AVAILABLE;
    71
    28.2k
    }
    72
    73
    static char *
    74
    get_reader(SCARDCONTEXT ctx, const char *path)
    75
    46.5k
    {
    76
    46.5k
            char *reader = NULL, *buf = NULL;
    77
    46.5k
            const char prefix[] = FIDO_PCSC_PREFIX "//slot";
    78
    46.5k
            uint64_t n;
    79
    80
    46.5k
            if (path == NULL)
    81
    5.80k
                    goto out;
    82
    40.6k
            if (strncmp(path, prefix, strlen(prefix)) != 0 ||
    83
    40.6k
                fido_to_uint64(path + strlen(prefix), 10, &n) < 0 ||
    84
    40.6k
                n > READERS - 1) {
    85
    16.7k
                    fido_log_debug("%s: invalid path %s", __func__, path);
    86
    16.7k
                    goto out;
    87
    16.7k
            }
    88
    23.9k
            if (list_readers(ctx, &buf) != SCARD_S_SUCCESS) {
    89
    592
                    fido_log_debug("%s: list_readers", __func__);
    90
    592
                    goto out;
    91
    592
            }
    92
    76.4k
            for (const char *name = buf; *name != 0; name += strlen(name) + 1) {
    93
    75.9k
                    if (n == 0) {
    94
    22.8k
                            reader = strdup(name);
    95
    22.8k
                            goto out;
    96
    22.8k
                    }
    97
    53.1k
                    n--;
    98
    53.1k
            }
    99
    525
            fido_log_debug("%s: failed to find reader %s", __func__, path);
    100
    46.5k
    out:
    101
    46.5k
            free(buf);
    102
    103
    46.5k
            return reader;
    104
    525
    }
    105
    106
    static int
    107
    prepare_io_request(DWORD prot, SCARD_IO_REQUEST *req)
    108
    40.5k
    {
    109
    40.5k
            switch (prot) {
    110
    22.5k
            case SCARD_PROTOCOL_T0:
    111
    22.5k
                    req->dwProtocol = SCARD_PCI_T0->dwProtocol;
    112
    22.5k
                    req->cbPciLength = SCARD_PCI_T0->cbPciLength;
    113
    22.5k
                    break;
    114
    17.6k
            case SCARD_PROTOCOL_T1:
    115
    17.6k
                    req->dwProtocol = SCARD_PCI_T1->dwProtocol;
    116
    17.6k
                    req->cbPciLength = SCARD_PCI_T1->cbPciLength;
    117
    17.6k
                    break;
    118
    424
            default:
    119
    424
                    fido_log_debug("%s: unknown protocol %lu", __func__,
    120
    424
                        (u_long)prot);
    121
    424
                    return -1;
    122
    40.5k
            }
    123
    124
    40.1k
            return 0;
    125
    40.5k
    }
    126
    127
    static int
    128
    copy_info(fido_dev_info_t *di, SCARDCONTEXT ctx, const char *reader, size_t idx)
    129
    18.0k
    {
    130
    18.0k
            SCARDHANDLE h = 0;
    131
    18.0k
            SCARD_IO_REQUEST req;
    132
    18.0k
            DWORD prot = 0;
    133
    18.0k
            LONG s;
    134
    18.0k
            int ok = -1;
    135
    136
    18.0k
            memset(di, 0, sizeof(*di));
    137
    18.0k
            memset(&req, 0, sizeof(req));
    138
    139
    18.0k
            if ((s = SCardConnect(ctx, reader, SCARD_SHARE_SHARED,
    140
    18.0k
                SCARD_PROTOCOL_Tx, &h, &prot)) != SCARD_S_SUCCESS) {
    141
    28
                    fido_log_debug("%s: SCardConnect 0x%lx", __func__, (long)s);
    142
    28
                    goto fail;
    143
    28
            }
    144
    17.9k
            if (prepare_io_request(prot, &req) < 0) {
    145
    25
                    fido_log_debug("%s: prepare_io_request", __func__);
    146
    25
                    goto fail;
    147
    25
            }
    148
    17.9k
            if (asprintf(&di->path, "%s//slot%zu", FIDO_PCSC_PREFIX, idx) == -1) {
    149
    45
                    di->path = NULL;
    150
    45
                    fido_log_debug("%s: asprintf", __func__);
    151
    45
                    goto fail;
    152
    45
            }
    153
    17.9k
            if (nfc_is_fido(di->path) == false) {
    154
    16.9k
                    fido_log_debug("%s: nfc_is_fido: %s", __func__, di->path);
    155
    16.9k
                    goto fail;
    156
    16.9k
            }
    157
    1.00k
            if ((di->manufacturer = strdup("PC/SC")) == NULL ||
    158
    1.00k
                (di->product = strdup(reader)) == NULL)
    159
    39
                    goto fail;
    160
    161
    961
            ok = 0;
    162
    18.0k
    fail:
    163
    18.0k
            if (h != 0)
    164
    17.9k
                    SCardDisconnect(h, SCARD_LEAVE_CARD);
    165
    18.0k
            if (ok < 0) {
    166
    17.0k
                    free(di->path);
    167
    17.0k
                    free(di->manufacturer);
    168
    17.0k
                    free(di->product);
    169
    17.0k
                    explicit_bzero(di, sizeof(*di));
    170
    17.0k
            }
    171
    172
    18.0k
            return ok;
    173
    961
    }
    174
    175
    int
    176
    fido_pcsc_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
    177
    23.5k
    {
    178
    23.5k
            SCARDCONTEXT ctx = 0;
    179
    23.5k
            char *buf = NULL;
    180
    23.5k
            LONG s;
    181
    23.5k
            size_t idx = 0;
    182
    23.5k
            int r = FIDO_ERR_INTERNAL;
    183
    184
    23.5k
            *olen = 0;
    185
    186
    23.5k
            if (ilen == 0)
    187
    5.95k
                    return FIDO_OK;
    188
    17.5k
            if (devlist == NULL)
    189
    5.91k
                    return FIDO_ERR_INVALID_ARGUMENT;
    190
    191
    11.6k
            if ((s = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL,
    192
    11.6k
                &ctx)) != SCARD_S_SUCCESS || ctx == 0) {
    193
    110
                    fido_log_debug("%s: SCardEstablishContext 0x%lx", __func__,
    194
    110
                        (long)s);
    195
    110
                    if (s == (LONG)SCARD_E_NO_SERVICE ||
    196
    110
                        s == (LONG)SCARD_E_NO_SMARTCARD)
    197
    71
                            r = FIDO_OK; /* suppress error */
    198
    110
                    goto out;
    199
    110
            }
    200
    11.5k
            if ((s = list_readers(ctx, &buf)) != SCARD_S_SUCCESS) {
    201
    6.88k
                    fido_log_debug("%s: list_readers 0x%lx", __func__, (long)s);
    202
    6.88k
                    if (s == (LONG)SCARD_E_NO_READERS_AVAILABLE)
    203
    6.88k
                            r = FIDO_OK; /* suppress error */
    204
    6.88k
                    goto out;
    205
    6.88k
            }
    206
    207
    22.6k
            for (const char *name = buf; *name != 0; name += strlen(name) + 1) {
    208
    18.6k
                    if (idx == READERS) {
    209
    612
                            fido_log_debug("%s: stopping at %zu readers", __func__,
    210
    612
                                idx);
    211
    612
                            r = FIDO_OK;
    212
    612
                            goto out;
    213
    612
                    }
    214
    18.0k
                    if (copy_info(&devlist[*olen], ctx, name, idx++) == 0) {
    215
    961
                            devlist[*olen].io = (fido_dev_io_t) {
    216
    961
                                    fido_pcsc_open,
    217
    961
                                    fido_pcsc_close,
    218
    961
                                    fido_pcsc_read,
    219
    961
                                    fido_pcsc_write,
    220
    961
                            };
    221
    961
                            devlist[*olen].transport = (fido_dev_transport_t) {
    222
    961
                                    fido_pcsc_rx,
    223
    961
                                    fido_pcsc_tx,
    224
    961
                            };
    225
    961
                            if (++(*olen) == ilen)
    226
    5
                                    break;
    227
    961
                    }
    228
    18.0k
            }
    229
    230
    4.05k
            r = FIDO_OK;
    231
    11.6k
    out:
    232
    11.6k
            free(buf);
    233
    11.6k
            if (ctx != 0)
    234
    11.6k
                    SCardReleaseContext(ctx);
    235
    236
    11.6k
            return r;
    237
    4.05k
    }
    238
    239
    void *
    240
    fido_pcsc_open(const char *path)
    241
    47.2k
    {
    242
    47.2k
            char *reader = NULL;
    243
    47.2k
            struct pcsc *dev = NULL;
    244
    47.2k
            SCARDCONTEXT ctx = 0;
    245
    47.2k
            SCARDHANDLE h = 0;
    246
    47.2k
            SCARD_IO_REQUEST req;
    247
    47.2k
            DWORD prot = 0;
    248
    47.2k
            LONG s;
    249
    250
    47.2k
            memset(&req, 0, sizeof(req));
    251
    252
    47.2k
            if ((s = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL,
    253
    47.2k
                &ctx)) != SCARD_S_SUCCESS || ctx == 0) {
    254
    751
                    fido_log_debug("%s: SCardEstablishContext 0x%lx", __func__,
    255
    751
                        (long)s);
    256
    751
                    goto fail;
    257
    258
    751
            }
    259
    46.5k
            if ((reader = get_reader(ctx, path)) == NULL) {
    260
    23.8k
                    fido_log_debug("%s: get_reader(%s)", __func__, path);
    261
    23.8k
                    goto fail;
    262
    23.8k
            }
    263
    22.7k
            if ((s = SCardConnect(ctx, reader, SCARD_SHARE_SHARED,
    264
    22.7k
                SCARD_PROTOCOL_Tx, &h, &prot)) != SCARD_S_SUCCESS) {
    265
    118
                    fido_log_debug("%s: SCardConnect 0x%lx", __func__, (long)s);
    266
    118
                    goto fail;
    267
    118
            }
    268
    22.5k
            if (prepare_io_request(prot, &req) < 0) {
    269
    399
                    fido_log_debug("%s: prepare_io_request", __func__);
    270
    399
                    goto fail;
    271
    399
            }
    272
    22.1k
            if ((dev = calloc(1, sizeof(*dev))) == NULL)
    273
    305
                    goto fail;
    274
    275
    21.8k
            dev->ctx = ctx;
    276
    21.8k
            dev->h = h;
    277
    21.8k
            dev->req = req;
    278
    21.8k
            ctx = 0;
    279
    21.8k
            h = 0;
    280
    47.2k
    fail:
    281
    47.2k
            if (h != 0)
    282
    704
                    SCardDisconnect(h, SCARD_LEAVE_CARD);
    283
    47.2k
            if (ctx != 0)
    284
    25.2k
                    SCardReleaseContext(ctx);
    285
    47.2k
            free(reader);
    286
    287
    47.2k
            return dev;
    288
    21.8k
    }
    289
    290
    void
    291
    fido_pcsc_close(void *handle)
    292
    21.8k
    {
    293
    21.8k
            struct pcsc *dev = handle;
    294
    295
    21.8k
            if (dev->h != 0)
    296
    21.8k
                    SCardDisconnect(dev->h, SCARD_LEAVE_CARD);
    297
    21.8k
            if (dev->ctx != 0)
    298
    21.8k
                    SCardReleaseContext(dev->ctx);
    299
    300
    21.8k
            explicit_bzero(dev->rx_buf, sizeof(dev->rx_buf));
    301
    21.8k
            free(dev);
    302
    21.8k
    }
    303
    304
    int
    305
    fido_pcsc_read(void *handle, unsigned char *buf, size_t len, int ms)
    306
    20.2k
    {
    307
    20.2k
            struct pcsc *dev = handle;
    308
    20.2k
            int r;
    309
    310
    20.2k
            (void)ms;
    311
    20.2k
            if (dev->rx_len == 0 || dev->rx_len > len ||
    312
    20.2k
                dev->rx_len > sizeof(dev->rx_buf)) {
    313
    13.4k
                    fido_log_debug("%s: rx_len", __func__);
    314
    13.4k
                    return -1;
    315
    13.4k
            }
    316
    6.75k
            fido_log_xxd(dev->rx_buf, dev->rx_len, "%s: reading", __func__);
    317
    6.75k
            memcpy(buf, dev->rx_buf, dev->rx_len);
    318
    6.75k
            explicit_bzero(dev->rx_buf, sizeof(dev->rx_buf));
    319
    6.75k
            r = (int)dev->rx_len;
    320
    6.75k
            dev->rx_len = 0;
    321
    322
    6.75k
            return r;
    323
    20.2k
    }
    324
    325
    int
    326
    fido_pcsc_write(void *handle, const unsigned char *buf, size_t len)
    327
    27.0k
    {
    328
    27.0k
            struct pcsc *dev = handle;
    329
    27.0k
            DWORD n;
    330
    27.0k
            LONG s;
    331
    332
    27.0k
            if (len > INT_MAX) {
    333
    5.91k
                    fido_log_debug("%s: len", __func__);
    334
    5.91k
                    return -1;
    335
    5.91k
            }
    336
    337
    21.1k
            explicit_bzero(dev->rx_buf, sizeof(dev->rx_buf));
    338
    21.1k
            dev->rx_len = 0;
    339
    21.1k
            n = (DWORD)sizeof(dev->rx_buf);
    340
    341
    21.1k
            fido_log_xxd(buf, len, "%s: writing", __func__);
    342
    343
    21.1k
            if ((s = SCardTransmit(dev->h, &dev->req, buf, (DWORD)len, NULL,
    344
    21.1k
                dev->rx_buf, &n)) != SCARD_S_SUCCESS) {
    345
    587
                    fido_log_debug("%s: SCardTransmit 0x%lx", __func__, (long)s);
    346
    587
                    explicit_bzero(dev->rx_buf, sizeof(dev->rx_buf));
    347
    587
                    return -1;
    348
    587
            }
    349
    20.5k
            dev->rx_len = (size_t)n;
    350
    351
    20.5k
            fido_log_xxd(dev->rx_buf, dev->rx_len, "%s: read", __func__);
    352
    353
    20.5k
            return (int)len;
    354
    21.1k
    }
    355
    356
    int
    357
    fido_pcsc_tx(fido_dev_t *d, uint8_t cmd, const u_char *buf, size_t count)
    358
    22.6k
    {
    359
    22.6k
            return fido_nfc_tx(d, cmd, buf, count);
    360
    22.6k
    }
    361
    362
    int
    363
    fido_pcsc_rx(fido_dev_t *d, uint8_t cmd, u_char *buf, size_t count, int ms)
    364
    21.3k
    {
    365
    21.3k
            return fido_nfc_rx(d, cmd, buf, count, ms);
    366
    21.3k
    }
    367
    368
    bool
    369
    fido_is_pcsc(const char *path)
    370
    3.34M
    {
    371
    3.34M
            return strncmp(path, FIDO_PCSC_PREFIX, strlen(FIDO_PCSC_PREFIX)) == 0;
    372
    3.34M
    }
    373
    374
    int
    375
    fido_dev_set_pcsc(fido_dev_t *d)
    376
    41.3k
    {
    377
    41.3k
            if (d->io_handle != NULL) {
    378
    0
                    fido_log_debug("%s: device open", __func__);
    379
    0
                    return -1;
    380
    0
            }
    381
    41.3k
            d->io_own = true;
    382
    41.3k
            d->io = (fido_dev_io_t) {
    383
    41.3k
                    fido_pcsc_open,
    384
    41.3k
                    fido_pcsc_close,
    385
    41.3k
                    fido_pcsc_read,
    386
    41.3k
                    fido_pcsc_write,
    387
    41.3k
            };
    388
    41.3k
            d->transport = (fido_dev_transport_t) {
    389
    41.3k
                    fido_pcsc_rx,
    390
    41.3k
                    fido_pcsc_tx,
    391
    41.3k
            };
    392
    393
    41.3k
            return 0;
    394
    41.3k
    }