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