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
13static TLS bool disable_u2f_fallback;
14
15#ifdef FIDO_FUZZ
16static void
17set_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
26static void
27fido_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
37static void
38fido_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
61static void
62fido_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
82static void
83fido_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
90static int
91fido_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);
154fail:
155	dev->io.close(dev->io_handle);
156	dev->io_handle = NULL;
157
158	return (r);
159}
160
161static int
162fido_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;
215fail:
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
226static int
227fido_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
242static void
243run_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
260int
261fido_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
279int
280fido_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
290int
291fido_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
311int
312fido_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
328int
329fido_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
344int
345fido_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
361int
362fido_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
381int
382fido_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
395void *
396fido_dev_io_handle(const fido_dev_t *dev)
397{
398
399	return (dev->io_handle);
400}
401
402void
403fido_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
411fido_dev_t *
412fido_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
431fido_dev_t *
432fido_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
463void
464fido_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
477uint8_t
478fido_dev_protocol(const fido_dev_t *dev)
479{
480	return (dev->attr.protocol);
481}
482
483uint8_t
484fido_dev_major(const fido_dev_t *dev)
485{
486	return (dev->attr.major);
487}
488
489uint8_t
490fido_dev_minor(const fido_dev_t *dev)
491{
492	return (dev->attr.minor);
493}
494
495uint8_t
496fido_dev_build(const fido_dev_t *dev)
497{
498	return (dev->attr.build);
499}
500
501uint8_t
502fido_dev_flags(const fido_dev_t *dev)
503{
504	return (dev->attr.flags);
505}
506
507bool
508fido_dev_is_fido2(const fido_dev_t *dev)
509{
510	return (dev->attr.flags & FIDO_CAP_CBOR);
511}
512
513bool
514fido_dev_is_winhello(const fido_dev_t *dev)
515{
516	return (dev->flags & FIDO_DEV_WINHELLO);
517}
518
519bool
520fido_dev_supports_pin(const fido_dev_t *dev)
521{
522	return (dev->flags & (FIDO_DEV_PIN_SET|FIDO_DEV_PIN_UNSET));
523}
524
525bool
526fido_dev_has_pin(const fido_dev_t *dev)
527{
528	return (dev->flags & FIDO_DEV_PIN_SET);
529}
530
531bool
532fido_dev_supports_cred_prot(const fido_dev_t *dev)
533{
534	return (dev->flags & FIDO_DEV_CRED_PROT);
535}
536
537bool
538fido_dev_supports_credman(const fido_dev_t *dev)
539{
540	return (dev->flags & FIDO_DEV_CREDMAN);
541}
542
543bool
544fido_dev_supports_uv(const fido_dev_t *dev)
545{
546	return (dev->flags & (FIDO_DEV_UV_SET|FIDO_DEV_UV_UNSET));
547}
548
549bool
550fido_dev_has_uv(const fido_dev_t *dev)
551{
552	return (dev->flags & FIDO_DEV_UV_SET);
553}
554
555bool
556fido_dev_supports_permissions(const fido_dev_t *dev)
557{
558	return (dev->flags & FIDO_DEV_TOKEN_PERMS);
559}
560
561void
562fido_dev_force_u2f(fido_dev_t *dev)
563{
564	dev->attr.flags &= (uint8_t)~FIDO_CAP_CBOR;
565	dev->flags = 0;
566}
567
568void
569fido_dev_force_fido2(fido_dev_t *dev)
570{
571	dev->attr.flags |= FIDO_CAP_CBOR;
572}
573
574uint8_t
575fido_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
585uint64_t
586fido_dev_maxmsgsize(const fido_dev_t *dev)
587{
588	return (dev->maxmsgsize);
589}
590
591int
592fido_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