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