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