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