## Coverage Report

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

    /libfido2/src/pcsc.c

    Line
    Count
    Source (jump to first uncovered line)
    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
    3.03k
    #define SCARD_PROTOCOL_Tx SCARD_PROTOCOL_ANY
    29
    #endif
    30
    31
    9.41k
    #define BUFSIZE 1024        /* in bytes; passed to SCardListReaders() */
    32
    #define APDULEN 264     /* 261 rounded up to the nearest multiple of 8 */
    33
    3.73k
    #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
    3.95k
    {
    46
    3.95k
            LONG s;
    47
    3.95k
            DWORD len;
    48
    49
    3.95k
            len = BUFSIZE;
    50
    3.95k
            if ((*buf = calloc(1, len)) == NULL)
    51
    25
                    goto fail;
    52
    3.93k
            if ((s = SCardListReaders(ctx, NULL, *buf, &len)) != SCARD_S_SUCCESS) {
    53
    1.20k
                    fido_log_debug("%s: SCardListReaders 0x%lx", __func__, (long)s);
    54
    1.20k
                    goto fail;
    55
    1.20k
            }
    56
            /* sanity check "multi-string" */
    57
    2.72k
            if (len > BUFSIZE || len < 2) {
    58
    262
                    fido_log_debug("%s: bogus len=%u", __func__, (unsigned)len);
    59
    262
                    goto fail;
    60
    262
            }
    61
    2.46k
            if ((*buf)[len - 1] != 0 || (*buf)[len - 2] != '\0') {
    62
    66
                    fido_log_debug("%s: bogus buf", __func__);
    63
    66
                    goto fail;
    64
    66
            }
    65
    2.39k
            return (LONG)SCARD_S_SUCCESS;
    66
    1.56k
    fail:
    67
    1.56k
            free(*buf);
    68
    1.56k
            *buf = NULL;
    69
    70
    1.56k
            return (LONG)SCARD_E_NO_READERS_AVAILABLE;
    71
    2.46k
    }
    72
    73
    static char *
    74
    get_reader(SCARDCONTEXT ctx, const char *path)
    75
    5.11k
    {
    76
    5.11k
            char *reader = NULL, *buf = NULL;
    77
    5.11k
            const char prefix[] = FIDO_PCSC_PREFIX "//slot";
    78
    5.11k
            uint64_t n;
    79
    80
    5.11k
            if (path == NULL)
    81
    808
                    goto out;
    82
    4.31k
            if (strncmp(path, prefix, strlen(prefix)) != 0 ||
    83
    4.31k
                fido_to_uint64(path + strlen(prefix), 10, &n) < 0 ||
    84
    4.31k
                n > READERS - 1) {
    85
    2.32k
                    fido_log_debug("%s: invalid path %s", __func__, path);
    86
    2.32k
                    goto out;
    87
    2.32k
            }
    88
    1.99k
            if (list_readers(ctx, &buf) != SCARD_S_SUCCESS) {
    89
    103
                    fido_log_debug("%s: list_readers", __func__);
    90
    103
                    goto out;
    91
    103
            }
    92
    4.31k
            for (const char *name = buf; *name != 0; name += strlen(name) + 1) {
    93
    4.29k
                    if (n == 0) {
    94
    1.87k
                            reader = strdup(name);
    95
    1.87k
                            goto out;
    96
    1.87k
                    }
    97
    2.42k
                    n--;
    98
    2.42k
            }
    99
    14
            fido_log_debug("%s: failed to find reader %s", __func__, path);
    100
    5.11k
    out:
    101
    5.11k
            free(buf);
    102
    103
    5.11k
            return reader;
    104
    14
    }
    105
    106
    static int
    107
    prepare_io_request(DWORD prot, SCARD_IO_REQUEST *req)
    108
    3.00k
    {
    109
    3.00k
            switch (prot) {
    110
    1.47k
            case SCARD_PROTOCOL_T0:
    111
    1.47k
                    req->dwProtocol = SCARD_PCI_T0->dwProtocol;
    112
    1.47k
                    req->cbPciLength = SCARD_PCI_T0->cbPciLength;
    113
    1.47k
                    break;
    114
    1.49k
            case SCARD_PROTOCOL_T1:
    115
    1.49k
                    req->dwProtocol = SCARD_PCI_T1->dwProtocol;
    116
    1.49k
                    req->cbPciLength = SCARD_PCI_T1->cbPciLength;
    117
    1.49k
                    break;
    118
    33
            default:
    119
    33
                    fido_log_debug("%s: unknown protocol %lu", __func__,
    120
    33
                        (u_long)prot);
    121
    33
                    return -1;
    122
    3.00k
            }
    123
    124
    2.96k
            return 0;
    125
    3.00k
    }
    126
    127
    static int
    128
    copy_info(fido_dev_info_t *di, SCARDCONTEXT ctx, const char *reader, size_t idx)
    129
    1.17k
    {
    130
    1.17k
            SCARDHANDLE h = 0;
    131
    1.17k
            SCARD_IO_REQUEST req;
    132
    1.17k
            DWORD prot = 0;
    133
    1.17k
            LONG s;
    134
    1.17k
            int ok = -1;
    135
    136
    1.17k
            memset(di, 0, sizeof(*di));
    137
    1.17k
            memset(&req, 0, sizeof(req));
    138
    139
    1.17k
            if ((s = SCardConnect(ctx, reader, SCARD_SHARE_SHARED,
    140
    1.17k
                SCARD_PROTOCOL_Tx, &h, &prot)) != SCARD_S_SUCCESS) {
    141
    13
                    fido_log_debug("%s: SCardConnect 0x%lx", __func__, (long)s);
    142
    13
                    goto fail;
    143
    13
            }
    144
    1.15k
            if (prepare_io_request(prot, &req) < 0) {
    145
    13
                    fido_log_debug("%s: prepare_io_request", __func__);
    146
    13
                    goto fail;
    147
    13
            }
    148
    1.14k
            if (asprintf(&di->path, "%s//slot%zu", FIDO_PCSC_PREFIX, idx) == -1) {
    149
    11
                    di->path = NULL;
    150
    11
                    fido_log_debug("%s: asprintf", __func__);
    151
    11
                    goto fail;
    152
    11
            }
    153
    1.13k
            if (nfc_is_fido(di->path) == false) {
    154
    1.03k
                    fido_log_debug("%s: nfc_is_fido: %s", __func__, di->path);
    155
    1.03k
                    goto fail;
    156
    1.03k
            }
    157
    94
            if ((di->manufacturer = strdup("PC/SC")) == NULL ||
    158
    94
                (di->product = strdup(reader)) == NULL)
    159
    2
                    goto fail;
    160
    161
    92
            ok = 0;
    162
    1.17k
    fail:
    163
    1.17k
            if (h != 0)
    164
    1.15k
                    SCardDisconnect(h, SCARD_LEAVE_CARD);
    165
    1.17k
            if (ok < 0) {
    166
    1.07k
                    free(di->path);
    167
    1.07k
                    free(di->manufacturer);
    168
    1.07k
                    free(di->product);
    169
    1.07k
                    explicit_bzero(di, sizeof(*di));
    170
    1.07k
            }
    171
    172
    1.17k
            return ok;
    173
    92
    }
    174
    175
    int
    176
    fido_pcsc_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
    177
    3.66k
    {
    178
    3.66k
            SCARDCONTEXT ctx = 0;
    179
    3.66k
            char *buf = NULL;
    180
    3.66k
            LONG s;
    181
    3.66k
            size_t idx = 0;
    182
    3.66k
            int r = FIDO_ERR_INTERNAL;
    183
    184
    3.66k
            *olen = 0;
    185
    186
    3.66k
            if (ilen == 0)
    187
    843
                    return FIDO_OK;
    188
    2.82k
            if (devlist == NULL)
    189
    831
                    return FIDO_ERR_INVALID_ARGUMENT;
    190
    191
    1.99k
            if ((s = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL,
    192
    1.99k
                &ctx)) != SCARD_S_SUCCESS || ctx == 0) {
    193
    24
                    fido_log_debug("%s: SCardEstablishContext 0x%lx", __func__,
    194
    24
                        (long)s);
    195
    24
                    if (s == (LONG)SCARD_E_NO_SERVICE ||
    196
    24
                        s == (LONG)SCARD_E_NO_SMARTCARD)
    197
    13
                            r = FIDO_OK; /* suppress error */
    198
    24
                    goto out;
    199
    24
            }
    200
    1.96k
            if ((s = list_readers(ctx, &buf)) != SCARD_S_SUCCESS) {
    201
    1.45k
                    fido_log_debug("%s: list_readers 0x%lx", __func__, (long)s);
    202
    1.45k
                    if (s == (LONG)SCARD_E_NO_READERS_AVAILABLE)
    203
    1.45k
                            r = FIDO_OK; /* suppress error */
    204
    1.45k
                    goto out;
    205
    1.45k
            }
    206
    207
    1.67k
            for (const char *name = buf; *name != 0; name += strlen(name) + 1) {
    208
    1.17k
                    if (idx == READERS) {
    209
    7
                            fido_log_debug("%s: stopping at %zu readers", __func__,
    210
    7
                                idx);
    211
    7
                            r = FIDO_OK;
    212
    7
                            goto out;
    213
    7
                    }
    214
    1.17k
                    if (copy_info(&devlist[*olen], ctx, name, idx++) == 0) {
    215
    92
                            devlist[*olen].io = (fido_dev_io_t) {
    216
    92
                                    fido_pcsc_open,
    217
    92
                                    fido_pcsc_close,
    218
    92
                                    fido_pcsc_read,
    219
    92
                                    fido_pcsc_write,
    220
    92
                            };
    221
    92
                            devlist[*olen].transport = (fido_dev_transport_t) {
    222
    92
                                    fido_pcsc_rx,
    223
    92
                                    fido_pcsc_tx,
    224
    92
                            };
    225
    92
                            if (++(*olen) == ilen)
    226
    1
                                    break;
    227
    92
                    }
    228
    1.17k
            }
    229
    230
    503
            r = FIDO_OK;
    231
    1.99k
    out:
    232
    1.99k
            free(buf);
    233
    1.99k
            if (ctx != 0)
    234
    1.98k
                    SCardReleaseContext(ctx);
    235
    236
    1.99k
            return r;
    237
    503
    }
    238
    239
    void *
    240
    fido_pcsc_open(const char *path)
    241
    5.26k
    {
    242
    5.26k
            char *reader = NULL;
    243
    5.26k
            struct pcsc *dev = NULL;
    244
    5.26k
            SCARDCONTEXT ctx = 0;
    245
    5.26k
            SCARDHANDLE h = 0;
    246
    5.26k
            SCARD_IO_REQUEST req;
    247
    5.26k
            DWORD prot = 0;
    248
    5.26k
            LONG s;
    249
    250
    5.26k
            memset(&req, 0, sizeof(req));
    251
    252
    5.26k
            if ((s = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL,
    253
    5.26k
                &ctx)) != SCARD_S_SUCCESS || ctx == 0) {
    254
    143
                    fido_log_debug("%s: SCardEstablishContext 0x%lx", __func__,
    255
    143
                        (long)s);
    256
    143
                    goto fail;
    257
    258
    143
            }
    259
    5.11k
            if ((reader = get_reader(ctx, path)) == NULL) {
    260
    3.25k
                    fido_log_debug("%s: get_reader(%s)", __func__, path);
    261
    3.25k
                    goto fail;
    262
    3.25k
            }
    263
    1.86k
            if ((s = SCardConnect(ctx, reader, SCARD_SHARE_SHARED,
    264
    1.86k
                SCARD_PROTOCOL_Tx, &h, &prot)) != SCARD_S_SUCCESS) {
    265
    15
                    fido_log_debug("%s: SCardConnect 0x%lx", __func__, (long)s);
    266
    15
                    goto fail;
    267
    15
            }
    268
    1.84k
            if (prepare_io_request(prot, &req) < 0) {
    269
    20
                    fido_log_debug("%s: prepare_io_request", __func__);
    270
    20
                    goto fail;
    271
    20
            }
    272
    1.82k
            if ((dev = calloc(1, sizeof(*dev))) == NULL)
    273
    15
                    goto fail;
    274
    275
    1.81k
            dev->ctx = ctx;
    276
    1.81k
            dev->h = h;
    277
    1.81k
            dev->req = req;
    278
    1.81k
            ctx = 0;
    279
    1.81k
            h = 0;
    280
    5.26k
    fail:
    281
    5.26k
            if (h != 0)
    282
    35
                    SCardDisconnect(h, SCARD_LEAVE_CARD);
    283
    5.26k
            if (ctx != 0)
    284
    3.41k
                    SCardReleaseContext(ctx);
    285
    5.26k
            free(reader);
    286
    287
    5.26k
            return dev;
    288
    1.81k
    }
    289
    290
    void
    291
    fido_pcsc_close(void *handle)
    292
    1.81k
    {
    293
    1.81k
            struct pcsc *dev = handle;
    294
    295
    1.81k
            if (dev->h != 0)
    296
    1.81k
                    SCardDisconnect(dev->h, SCARD_LEAVE_CARD);
    297
    1.81k
            if (dev->ctx != 0)
    298
    1.81k
                    SCardReleaseContext(dev->ctx);
    299
    300
    1.81k
            explicit_bzero(dev->rx_buf, sizeof(dev->rx_buf));
    301
    1.81k
            free(dev);
    302
    1.81k
    }
    303
    304
    int
    305
    fido_pcsc_read(void *handle, unsigned char *buf, size_t len, int ms)
    306
    1.59k
    {
    307
    1.59k
            struct pcsc *dev = handle;
    308
    1.59k
            int r;
    309
    310
    1.59k
            (void)ms;
    311
    1.59k
            if (dev->rx_len == 0 || dev->rx_len > len ||
    312
    1.59k
                dev->rx_len > sizeof(dev->rx_buf)) {
    313
    864
                    fido_log_debug("%s: rx_len", __func__);
    314
    864
                    return -1;
    315
    864
            }
    316
    734
            fido_log_xxd(dev->rx_buf, dev->rx_len, "%s: reading", __func__);
    317
    734
            memcpy(buf, dev->rx_buf, dev->rx_len);
    318
    734
            explicit_bzero(dev->rx_buf, sizeof(dev->rx_buf));
    319
    734
            r = (int)dev->rx_len;
    320
    734
            dev->rx_len = 0;
    321
    322
    734
            return r;
    323
    1.59k
    }
    324
    325
    int
    326
    fido_pcsc_write(void *handle, const unsigned char *buf, size_t len)
    327
    2.48k
    {
    328
    2.48k
            struct pcsc *dev = handle;
    329
    2.48k
            DWORD n;
    330
    2.48k
            LONG s;
    331
    332
    2.48k
            if (len > INT_MAX) {
    333
    831
                    fido_log_debug("%s: len", __func__);
    334
    831
                    return -1;
    335
    831
            }
    336
    337
    1.65k
            explicit_bzero(dev->rx_buf, sizeof(dev->rx_buf));
    338
    1.65k
            dev->rx_len = 0;
    339
    1.65k
            n = (DWORD)sizeof(dev->rx_buf);
    340
    341
    1.65k
            fido_log_xxd(buf, len, "%s: writing", __func__);
    342
    343
    1.65k
            if ((s = SCardTransmit(dev->h, &dev->req, buf, (DWORD)len, NULL,
    344
    1.65k
                dev->rx_buf, &n)) != SCARD_S_SUCCESS) {
    345
    40
                    fido_log_debug("%s: SCardTransmit 0x%lx", __func__, (long)s);
    346
    40
                    explicit_bzero(dev->rx_buf, sizeof(dev->rx_buf));
    347
    40
                    return -1;
    348
    40
            }
    349
    1.61k
            dev->rx_len = (size_t)n;
    350
    351
    1.61k
            fido_log_xxd(dev->rx_buf, dev->rx_len, "%s: read", __func__);
    352
    353
    1.61k
            return (int)len;
    354
    1.65k
    }
    355
    356
    int
    357
    fido_pcsc_tx(fido_dev_t *d, uint8_t cmd, const u_char *buf, size_t count)
    358
    1.87k
    {
    359
    1.87k
            return fido_nfc_tx(d, cmd, buf, count);
    360
    1.87k
    }
    361
    362
    int
    363
    fido_pcsc_rx(fido_dev_t *d, uint8_t cmd, u_char *buf, size_t count, int ms)
    364
    1.80k
    {
    365
    1.80k
            return fido_nfc_rx(d, cmd, buf, count, ms);
    366
    1.80k
    }
    367
    368
    bool
    369
    fido_is_pcsc(const char *path)
    370
    619k
    {
    371
    619k
            return strncmp(path, FIDO_PCSC_PREFIX, strlen(FIDO_PCSC_PREFIX)) == 0;
    372
    619k
    }
    373
    374
    int
    375
    fido_dev_set_pcsc(fido_dev_t *d)
    376
    4.44k
    {
    377
    4.44k
            if (d->io_handle != NULL) {
    378
    0
                    fido_log_debug("%s: device open", __func__);
    379
    0
                    return -1;
    380
    0
            }
    381
    4.44k
            d->io_own = true;
    382
    4.44k
            d->io = (fido_dev_io_t) {
    383
    4.44k
                    fido_pcsc_open,
    384
    4.44k
                    fido_pcsc_close,
    385
    4.44k
                    fido_pcsc_read,
    386
    4.44k
                    fido_pcsc_write,
    387
    4.44k
            };
    388
    4.44k
            d->transport = (fido_dev_transport_t) {
    389
    4.44k
                    fido_pcsc_rx,
    390
    4.44k
                    fido_pcsc_tx,
    391
    4.44k
            };
    392
    393
    4.44k
            return 0;
    394
    4.44k
    }