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