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 14static TLS bool disable_u2f_fallback; 15 16#ifdef FIDO_FUZZ 17static void 18set_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 27static void 28fido_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 38static void 39fido_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 62static void 63fido_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 83static void 84fido_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 91static int 92fido_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); 155fail: 156 dev->io.close(dev->io_handle); 157 dev->io_handle = NULL; 158 159 return (r); 160} 161 162static int 163fido_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; 216fail: 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 227static int 228fido_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 243static void 244run_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 261int 262fido_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 280int 281fido_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 291int 292fido_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 312int 313fido_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 329int 330fido_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 345int 346fido_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 362int 363fido_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 382int 383fido_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 396void * 397fido_dev_io_handle(const fido_dev_t *dev) 398{ 399 400 return (dev->io_handle); 401} 402 403void 404fido_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 412fido_dev_t * 413fido_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 432fido_dev_t * 433fido_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 464void 465fido_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 478uint8_t 479fido_dev_protocol(const fido_dev_t *dev) 480{ 481 return (dev->attr.protocol); 482} 483 484uint8_t 485fido_dev_major(const fido_dev_t *dev) 486{ 487 return (dev->attr.major); 488} 489 490uint8_t 491fido_dev_minor(const fido_dev_t *dev) 492{ 493 return (dev->attr.minor); 494} 495 496uint8_t 497fido_dev_build(const fido_dev_t *dev) 498{ 499 return (dev->attr.build); 500} 501 502uint8_t 503fido_dev_flags(const fido_dev_t *dev) 504{ 505 return (dev->attr.flags); 506} 507 508bool 509fido_dev_is_fido2(const fido_dev_t *dev) 510{ 511 return (dev->attr.flags & FIDO_CAP_CBOR); 512} 513 514bool 515fido_dev_is_winhello(const fido_dev_t *dev) 516{ 517 return (dev->flags & FIDO_DEV_WINHELLO); 518} 519 520bool 521fido_dev_supports_pin(const fido_dev_t *dev) 522{ 523 return (dev->flags & (FIDO_DEV_PIN_SET|FIDO_DEV_PIN_UNSET)); 524} 525 526bool 527fido_dev_has_pin(const fido_dev_t *dev) 528{ 529 return (dev->flags & FIDO_DEV_PIN_SET); 530} 531 532bool 533fido_dev_supports_cred_prot(const fido_dev_t *dev) 534{ 535 return (dev->flags & FIDO_DEV_CRED_PROT); 536} 537 538bool 539fido_dev_supports_credman(const fido_dev_t *dev) 540{ 541 return (dev->flags & FIDO_DEV_CREDMAN); 542} 543 544bool 545fido_dev_supports_uv(const fido_dev_t *dev) 546{ 547 return (dev->flags & (FIDO_DEV_UV_SET|FIDO_DEV_UV_UNSET)); 548} 549 550bool 551fido_dev_has_uv(const fido_dev_t *dev) 552{ 553 return (dev->flags & FIDO_DEV_UV_SET); 554} 555 556bool 557fido_dev_supports_permissions(const fido_dev_t *dev) 558{ 559 return (dev->flags & FIDO_DEV_TOKEN_PERMS); 560} 561 562void 563fido_dev_force_u2f(fido_dev_t *dev) 564{ 565 dev->attr.flags &= (uint8_t)~FIDO_CAP_CBOR; 566 dev->flags = 0; 567} 568 569void 570fido_dev_force_fido2(fido_dev_t *dev) 571{ 572 dev->attr.flags |= FIDO_CAP_CBOR; 573} 574 575uint8_t 576fido_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 586uint64_t 587fido_dev_maxmsgsize(const fido_dev_t *dev) 588{ 589 return (dev->maxmsgsize); 590} 591 592int 593fido_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