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