xref: /freebsd/contrib/libfido2/src/dev.c (revision a3cefe7f2b4df0f70ff92d4570ce18e517af43ec)
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