1 /* 2 * Copyright (c) 2018-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 */ 6 7 #include "fido.h" 8 9 #ifndef TLS 10 #define TLS 11 #endif 12 13 static TLS bool disable_u2f_fallback; 14 15 #ifdef FIDO_FUZZ 16 static void 17 set_random_report_len(fido_dev_t *dev) 18 { 19 dev->rx_len = CTAP_MIN_REPORT_LEN + 20 uniform_random(CTAP_MAX_REPORT_LEN - CTAP_MIN_REPORT_LEN + 1); 21 dev->tx_len = CTAP_MIN_REPORT_LEN + 22 uniform_random(CTAP_MAX_REPORT_LEN - CTAP_MIN_REPORT_LEN + 1); 23 } 24 #endif 25 26 static void 27 fido_dev_set_extension_flags(fido_dev_t *dev, const fido_cbor_info_t *info) 28 { 29 char * const *ptr = fido_cbor_info_extensions_ptr(info); 30 size_t len = fido_cbor_info_extensions_len(info); 31 32 for (size_t i = 0; i < len; i++) 33 if (strcmp(ptr[i], "credProtect") == 0) 34 dev->flags |= FIDO_DEV_CRED_PROT; 35 } 36 37 static void 38 fido_dev_set_option_flags(fido_dev_t *dev, const fido_cbor_info_t *info) 39 { 40 char * const *ptr = fido_cbor_info_options_name_ptr(info); 41 const bool *val = fido_cbor_info_options_value_ptr(info); 42 size_t len = fido_cbor_info_options_len(info); 43 44 for (size_t i = 0; i < len; i++) 45 if (strcmp(ptr[i], "clientPin") == 0) { 46 dev->flags |= val[i] ? 47 FIDO_DEV_PIN_SET : FIDO_DEV_PIN_UNSET; 48 } else if (strcmp(ptr[i], "credMgmt") == 0 || 49 strcmp(ptr[i], "credentialMgmtPreview") == 0) { 50 if (val[i]) 51 dev->flags |= FIDO_DEV_CREDMAN; 52 } else if (strcmp(ptr[i], "uv") == 0) { 53 dev->flags |= val[i] ? 54 FIDO_DEV_UV_SET : FIDO_DEV_UV_UNSET; 55 } else if (strcmp(ptr[i], "pinUvAuthToken") == 0) { 56 if (val[i]) 57 dev->flags |= FIDO_DEV_TOKEN_PERMS; 58 } 59 } 60 61 static void 62 fido_dev_set_protocol_flags(fido_dev_t *dev, const fido_cbor_info_t *info) 63 { 64 const uint8_t *ptr = fido_cbor_info_protocols_ptr(info); 65 size_t len = fido_cbor_info_protocols_len(info); 66 67 for (size_t i = 0; i < len; i++) 68 switch (ptr[i]) { 69 case CTAP_PIN_PROTOCOL1: 70 dev->flags |= FIDO_DEV_PIN_PROTOCOL1; 71 break; 72 case CTAP_PIN_PROTOCOL2: 73 dev->flags |= FIDO_DEV_PIN_PROTOCOL2; 74 break; 75 default: 76 fido_log_debug("%s: unknown protocol %u", __func__, 77 ptr[i]); 78 break; 79 } 80 } 81 82 static void 83 fido_dev_set_flags(fido_dev_t *dev, const fido_cbor_info_t *info) 84 { 85 fido_dev_set_extension_flags(dev, info); 86 fido_dev_set_option_flags(dev, info); 87 fido_dev_set_protocol_flags(dev, info); 88 } 89 90 static int 91 fido_dev_open_tx(fido_dev_t *dev, const char *path, int *ms) 92 { 93 int r; 94 95 if (dev->io_handle != NULL) { 96 fido_log_debug("%s: handle=%p", __func__, dev->io_handle); 97 return (FIDO_ERR_INVALID_ARGUMENT); 98 } 99 100 if (dev->io.open == NULL || dev->io.close == NULL) { 101 fido_log_debug("%s: NULL open/close", __func__); 102 return (FIDO_ERR_INVALID_ARGUMENT); 103 } 104 105 if (dev->cid != CTAP_CID_BROADCAST) { 106 fido_log_debug("%s: cid=0x%x", __func__, dev->cid); 107 return (FIDO_ERR_INVALID_ARGUMENT); 108 } 109 110 if (fido_get_random(&dev->nonce, sizeof(dev->nonce)) < 0) { 111 fido_log_debug("%s: fido_get_random", __func__); 112 return (FIDO_ERR_INTERNAL); 113 } 114 115 if ((dev->io_handle = dev->io.open(path)) == NULL) { 116 fido_log_debug("%s: dev->io.open", __func__); 117 return (FIDO_ERR_INTERNAL); 118 } 119 120 if (dev->io_own) { 121 dev->rx_len = CTAP_MAX_REPORT_LEN; 122 dev->tx_len = CTAP_MAX_REPORT_LEN; 123 } else { 124 dev->rx_len = fido_hid_report_in_len(dev->io_handle); 125 dev->tx_len = fido_hid_report_out_len(dev->io_handle); 126 } 127 128 #ifdef FIDO_FUZZ 129 set_random_report_len(dev); 130 #endif 131 132 if (dev->rx_len < CTAP_MIN_REPORT_LEN || 133 dev->rx_len > CTAP_MAX_REPORT_LEN) { 134 fido_log_debug("%s: invalid rx_len %zu", __func__, dev->rx_len); 135 r = FIDO_ERR_RX; 136 goto fail; 137 } 138 139 if (dev->tx_len < CTAP_MIN_REPORT_LEN || 140 dev->tx_len > CTAP_MAX_REPORT_LEN) { 141 fido_log_debug("%s: invalid tx_len %zu", __func__, dev->tx_len); 142 r = FIDO_ERR_TX; 143 goto fail; 144 } 145 146 if (fido_tx(dev, CTAP_CMD_INIT, &dev->nonce, sizeof(dev->nonce), 147 ms) < 0) { 148 fido_log_debug("%s: fido_tx", __func__); 149 r = FIDO_ERR_TX; 150 goto fail; 151 } 152 153 return (FIDO_OK); 154 fail: 155 dev->io.close(dev->io_handle); 156 dev->io_handle = NULL; 157 158 return (r); 159 } 160 161 static int 162 fido_dev_open_rx(fido_dev_t *dev, int *ms) 163 { 164 fido_cbor_info_t *info = NULL; 165 int reply_len; 166 int r; 167 168 if ((reply_len = fido_rx(dev, CTAP_CMD_INIT, &dev->attr, 169 sizeof(dev->attr), ms)) < 0) { 170 fido_log_debug("%s: fido_rx", __func__); 171 r = FIDO_ERR_RX; 172 goto fail; 173 } 174 175 #ifdef FIDO_FUZZ 176 dev->attr.nonce = dev->nonce; 177 #endif 178 179 if ((size_t)reply_len != sizeof(dev->attr) || 180 dev->attr.nonce != dev->nonce) { 181 fido_log_debug("%s: invalid nonce", __func__); 182 r = FIDO_ERR_RX; 183 goto fail; 184 } 185 186 dev->flags = 0; 187 dev->cid = dev->attr.cid; 188 189 if (fido_dev_is_fido2(dev)) { 190 if ((info = fido_cbor_info_new()) == NULL) { 191 fido_log_debug("%s: fido_cbor_info_new", __func__); 192 r = FIDO_ERR_INTERNAL; 193 goto fail; 194 } 195 if ((r = fido_dev_get_cbor_info_wait(dev, info, 196 ms)) != FIDO_OK) { 197 fido_log_debug("%s: fido_dev_cbor_info_wait: %d", 198 __func__, r); 199 if (disable_u2f_fallback) 200 goto fail; 201 fido_log_debug("%s: falling back to u2f", __func__); 202 fido_dev_force_u2f(dev); 203 } else { 204 fido_dev_set_flags(dev, info); 205 } 206 } 207 208 if (fido_dev_is_fido2(dev) && info != NULL) { 209 dev->maxmsgsize = fido_cbor_info_maxmsgsiz(info); 210 fido_log_debug("%s: FIDO_MAXMSG=%d, maxmsgsiz=%lu", __func__, 211 FIDO_MAXMSG, (unsigned long)dev->maxmsgsize); 212 } 213 214 r = FIDO_OK; 215 fail: 216 fido_cbor_info_free(&info); 217 218 if (r != FIDO_OK) { 219 dev->io.close(dev->io_handle); 220 dev->io_handle = NULL; 221 } 222 223 return (r); 224 } 225 226 static int 227 fido_dev_open_wait(fido_dev_t *dev, const char *path, int *ms) 228 { 229 int r; 230 231 #ifdef USE_WINHELLO 232 if (strcmp(path, FIDO_WINHELLO_PATH) == 0) 233 return (fido_winhello_open(dev)); 234 #endif 235 if ((r = fido_dev_open_tx(dev, path, ms)) != FIDO_OK || 236 (r = fido_dev_open_rx(dev, ms)) != FIDO_OK) 237 return (r); 238 239 return (FIDO_OK); 240 } 241 242 static void 243 run_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen, 244 const char *type, int (*manifest)(fido_dev_info_t *, size_t, size_t *)) 245 { 246 size_t ndevs = 0; 247 int r; 248 249 if (*olen >= ilen) { 250 fido_log_debug("%s: skipping %s", __func__, type); 251 return; 252 } 253 if ((r = manifest(devlist + *olen, ilen - *olen, &ndevs)) != FIDO_OK) 254 fido_log_debug("%s: %s: 0x%x", __func__, type, r); 255 fido_log_debug("%s: found %zu %s device%s", __func__, ndevs, type, 256 ndevs == 1 ? "" : "s"); 257 *olen += ndevs; 258 } 259 260 int 261 fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) 262 { 263 *olen = 0; 264 265 run_manifest(devlist, ilen, olen, "hid", fido_hid_manifest); 266 #ifdef USE_NFC 267 run_manifest(devlist, ilen, olen, "nfc", fido_nfc_manifest); 268 #endif 269 #ifdef USE_PCSC 270 run_manifest(devlist, ilen, olen, "pcsc", fido_pcsc_manifest); 271 #endif 272 #ifdef USE_WINHELLO 273 run_manifest(devlist, ilen, olen, "winhello", fido_winhello_manifest); 274 #endif 275 276 return (FIDO_OK); 277 } 278 279 int 280 fido_dev_open_with_info(fido_dev_t *dev) 281 { 282 int ms = dev->timeout_ms; 283 284 if (dev->path == NULL) 285 return (FIDO_ERR_INVALID_ARGUMENT); 286 287 return (fido_dev_open_wait(dev, dev->path, &ms)); 288 } 289 290 int 291 fido_dev_open(fido_dev_t *dev, const char *path) 292 { 293 int ms = dev->timeout_ms; 294 295 #ifdef USE_NFC 296 if (fido_is_nfc(path) && fido_dev_set_nfc(dev) < 0) { 297 fido_log_debug("%s: fido_dev_set_nfc", __func__); 298 return FIDO_ERR_INTERNAL; 299 } 300 #endif 301 #ifdef USE_PCSC 302 if (fido_is_pcsc(path) && fido_dev_set_pcsc(dev) < 0) { 303 fido_log_debug("%s: fido_dev_set_pcsc", __func__); 304 return FIDO_ERR_INTERNAL; 305 } 306 #endif 307 308 return (fido_dev_open_wait(dev, path, &ms)); 309 } 310 311 int 312 fido_dev_close(fido_dev_t *dev) 313 { 314 #ifdef USE_WINHELLO 315 if (dev->flags & FIDO_DEV_WINHELLO) 316 return (fido_winhello_close(dev)); 317 #endif 318 if (dev->io_handle == NULL || dev->io.close == NULL) 319 return (FIDO_ERR_INVALID_ARGUMENT); 320 321 dev->io.close(dev->io_handle); 322 dev->io_handle = NULL; 323 dev->cid = CTAP_CID_BROADCAST; 324 325 return (FIDO_OK); 326 } 327 328 int 329 fido_dev_set_sigmask(fido_dev_t *dev, const fido_sigset_t *sigmask) 330 { 331 if (dev->io_handle == NULL || sigmask == NULL) 332 return (FIDO_ERR_INVALID_ARGUMENT); 333 334 #ifdef USE_NFC 335 if (dev->transport.rx == fido_nfc_rx && dev->io.read == fido_nfc_read) 336 return (fido_nfc_set_sigmask(dev->io_handle, sigmask)); 337 #endif 338 if (dev->transport.rx == NULL && dev->io.read == fido_hid_read) 339 return (fido_hid_set_sigmask(dev->io_handle, sigmask)); 340 341 return (FIDO_ERR_INVALID_ARGUMENT); 342 } 343 344 int 345 fido_dev_cancel(fido_dev_t *dev) 346 { 347 int ms = dev->timeout_ms; 348 349 #ifdef USE_WINHELLO 350 if (dev->flags & FIDO_DEV_WINHELLO) 351 return (fido_winhello_cancel(dev)); 352 #endif 353 if (fido_dev_is_fido2(dev) == false) 354 return (FIDO_ERR_INVALID_ARGUMENT); 355 if (fido_tx(dev, CTAP_CMD_CANCEL, NULL, 0, &ms) < 0) 356 return (FIDO_ERR_TX); 357 358 return (FIDO_OK); 359 } 360 361 int 362 fido_dev_set_io_functions(fido_dev_t *dev, const fido_dev_io_t *io) 363 { 364 if (dev->io_handle != NULL) { 365 fido_log_debug("%s: non-NULL handle", __func__); 366 return (FIDO_ERR_INVALID_ARGUMENT); 367 } 368 369 if (io == NULL || io->open == NULL || io->close == NULL || 370 io->read == NULL || io->write == NULL) { 371 fido_log_debug("%s: NULL function", __func__); 372 return (FIDO_ERR_INVALID_ARGUMENT); 373 } 374 375 dev->io = *io; 376 dev->io_own = true; 377 378 return (FIDO_OK); 379 } 380 381 int 382 fido_dev_set_transport_functions(fido_dev_t *dev, const fido_dev_transport_t *t) 383 { 384 if (dev->io_handle != NULL) { 385 fido_log_debug("%s: non-NULL handle", __func__); 386 return (FIDO_ERR_INVALID_ARGUMENT); 387 } 388 389 dev->transport = *t; 390 dev->io_own = true; 391 392 return (FIDO_OK); 393 } 394 395 void * 396 fido_dev_io_handle(const fido_dev_t *dev) 397 { 398 399 return (dev->io_handle); 400 } 401 402 void 403 fido_init(int flags) 404 { 405 if (flags & FIDO_DEBUG || getenv("FIDO_DEBUG") != NULL) 406 fido_log_init(); 407 408 disable_u2f_fallback = (flags & FIDO_DISABLE_U2F_FALLBACK); 409 } 410 411 fido_dev_t * 412 fido_dev_new(void) 413 { 414 fido_dev_t *dev; 415 416 if ((dev = calloc(1, sizeof(*dev))) == NULL) 417 return (NULL); 418 419 dev->cid = CTAP_CID_BROADCAST; 420 dev->timeout_ms = -1; 421 dev->io = (fido_dev_io_t) { 422 &fido_hid_open, 423 &fido_hid_close, 424 &fido_hid_read, 425 &fido_hid_write, 426 }; 427 428 return (dev); 429 } 430 431 fido_dev_t * 432 fido_dev_new_with_info(const fido_dev_info_t *di) 433 { 434 fido_dev_t *dev; 435 436 if ((dev = calloc(1, sizeof(*dev))) == NULL) 437 return (NULL); 438 439 #if 0 440 if (di->io.open == NULL || di->io.close == NULL || 441 di->io.read == NULL || di->io.write == NULL) { 442 fido_log_debug("%s: NULL function", __func__); 443 fido_dev_free(&dev); 444 return (NULL); 445 } 446 #endif 447 448 dev->io = di->io; 449 dev->io_own = di->transport.tx != NULL || di->transport.rx != NULL; 450 dev->transport = di->transport; 451 dev->cid = CTAP_CID_BROADCAST; 452 dev->timeout_ms = -1; 453 454 if ((dev->path = strdup(di->path)) == NULL) { 455 fido_log_debug("%s: strdup", __func__); 456 fido_dev_free(&dev); 457 return (NULL); 458 } 459 460 return (dev); 461 } 462 463 void 464 fido_dev_free(fido_dev_t **dev_p) 465 { 466 fido_dev_t *dev; 467 468 if (dev_p == NULL || (dev = *dev_p) == NULL) 469 return; 470 471 free(dev->path); 472 free(dev); 473 474 *dev_p = NULL; 475 } 476 477 uint8_t 478 fido_dev_protocol(const fido_dev_t *dev) 479 { 480 return (dev->attr.protocol); 481 } 482 483 uint8_t 484 fido_dev_major(const fido_dev_t *dev) 485 { 486 return (dev->attr.major); 487 } 488 489 uint8_t 490 fido_dev_minor(const fido_dev_t *dev) 491 { 492 return (dev->attr.minor); 493 } 494 495 uint8_t 496 fido_dev_build(const fido_dev_t *dev) 497 { 498 return (dev->attr.build); 499 } 500 501 uint8_t 502 fido_dev_flags(const fido_dev_t *dev) 503 { 504 return (dev->attr.flags); 505 } 506 507 bool 508 fido_dev_is_fido2(const fido_dev_t *dev) 509 { 510 return (dev->attr.flags & FIDO_CAP_CBOR); 511 } 512 513 bool 514 fido_dev_is_winhello(const fido_dev_t *dev) 515 { 516 return (dev->flags & FIDO_DEV_WINHELLO); 517 } 518 519 bool 520 fido_dev_supports_pin(const fido_dev_t *dev) 521 { 522 return (dev->flags & (FIDO_DEV_PIN_SET|FIDO_DEV_PIN_UNSET)); 523 } 524 525 bool 526 fido_dev_has_pin(const fido_dev_t *dev) 527 { 528 return (dev->flags & FIDO_DEV_PIN_SET); 529 } 530 531 bool 532 fido_dev_supports_cred_prot(const fido_dev_t *dev) 533 { 534 return (dev->flags & FIDO_DEV_CRED_PROT); 535 } 536 537 bool 538 fido_dev_supports_credman(const fido_dev_t *dev) 539 { 540 return (dev->flags & FIDO_DEV_CREDMAN); 541 } 542 543 bool 544 fido_dev_supports_uv(const fido_dev_t *dev) 545 { 546 return (dev->flags & (FIDO_DEV_UV_SET|FIDO_DEV_UV_UNSET)); 547 } 548 549 bool 550 fido_dev_has_uv(const fido_dev_t *dev) 551 { 552 return (dev->flags & FIDO_DEV_UV_SET); 553 } 554 555 bool 556 fido_dev_supports_permissions(const fido_dev_t *dev) 557 { 558 return (dev->flags & FIDO_DEV_TOKEN_PERMS); 559 } 560 561 void 562 fido_dev_force_u2f(fido_dev_t *dev) 563 { 564 dev->attr.flags &= (uint8_t)~FIDO_CAP_CBOR; 565 dev->flags = 0; 566 } 567 568 void 569 fido_dev_force_fido2(fido_dev_t *dev) 570 { 571 dev->attr.flags |= FIDO_CAP_CBOR; 572 } 573 574 uint8_t 575 fido_dev_get_pin_protocol(const fido_dev_t *dev) 576 { 577 if (dev->flags & FIDO_DEV_PIN_PROTOCOL2) 578 return (CTAP_PIN_PROTOCOL2); 579 else if (dev->flags & FIDO_DEV_PIN_PROTOCOL1) 580 return (CTAP_PIN_PROTOCOL1); 581 582 return (0); 583 } 584 585 uint64_t 586 fido_dev_maxmsgsize(const fido_dev_t *dev) 587 { 588 return (dev->maxmsgsize); 589 } 590 591 int 592 fido_dev_set_timeout(fido_dev_t *dev, int ms) 593 { 594 if (ms < -1) 595 return (FIDO_ERR_INVALID_ARGUMENT); 596 597 dev->timeout_ms = ms; 598 599 return (FIDO_OK); 600 } 601