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
set_random_report_len(fido_dev_t * dev)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
fido_dev_set_extension_flags(fido_dev_t * dev,const fido_cbor_info_t * info)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
fido_dev_set_option_flags(fido_dev_t * dev,const fido_cbor_info_t * info)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
fido_dev_set_protocol_flags(fido_dev_t * dev,const fido_cbor_info_t * info)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
fido_dev_set_flags(fido_dev_t * dev,const fido_cbor_info_t * info)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
fido_dev_open_tx(fido_dev_t * dev,const char * path,int * ms)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
fido_dev_open_rx(fido_dev_t * dev,int * ms)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
fido_dev_open_wait(fido_dev_t * dev,const char * path,int * ms)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
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 *))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
fido_dev_info_manifest(fido_dev_info_t * devlist,size_t ilen,size_t * olen)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
fido_dev_open_with_info(fido_dev_t * dev)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
fido_dev_open(fido_dev_t * dev,const char * path)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
fido_dev_close(fido_dev_t * dev)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
fido_dev_set_sigmask(fido_dev_t * dev,const fido_sigset_t * sigmask)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
fido_dev_cancel(fido_dev_t * dev)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
fido_dev_set_io_functions(fido_dev_t * dev,const fido_dev_io_t * io)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
fido_dev_set_transport_functions(fido_dev_t * dev,const fido_dev_transport_t * t)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 *
fido_dev_io_handle(const fido_dev_t * dev)397 fido_dev_io_handle(const fido_dev_t *dev)
398 {
399
400 return (dev->io_handle);
401 }
402
403 void
fido_init(int flags)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 *
fido_dev_new(void)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 *
fido_dev_new_with_info(const fido_dev_info_t * di)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
fido_dev_free(fido_dev_t ** dev_p)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
fido_dev_protocol(const fido_dev_t * dev)479 fido_dev_protocol(const fido_dev_t *dev)
480 {
481 return (dev->attr.protocol);
482 }
483
484 uint8_t
fido_dev_major(const fido_dev_t * dev)485 fido_dev_major(const fido_dev_t *dev)
486 {
487 return (dev->attr.major);
488 }
489
490 uint8_t
fido_dev_minor(const fido_dev_t * dev)491 fido_dev_minor(const fido_dev_t *dev)
492 {
493 return (dev->attr.minor);
494 }
495
496 uint8_t
fido_dev_build(const fido_dev_t * dev)497 fido_dev_build(const fido_dev_t *dev)
498 {
499 return (dev->attr.build);
500 }
501
502 uint8_t
fido_dev_flags(const fido_dev_t * dev)503 fido_dev_flags(const fido_dev_t *dev)
504 {
505 return (dev->attr.flags);
506 }
507
508 bool
fido_dev_is_fido2(const fido_dev_t * dev)509 fido_dev_is_fido2(const fido_dev_t *dev)
510 {
511 return (dev->attr.flags & FIDO_CAP_CBOR);
512 }
513
514 bool
fido_dev_is_winhello(const fido_dev_t * dev)515 fido_dev_is_winhello(const fido_dev_t *dev)
516 {
517 return (dev->flags & FIDO_DEV_WINHELLO);
518 }
519
520 bool
fido_dev_supports_pin(const fido_dev_t * dev)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
fido_dev_has_pin(const fido_dev_t * dev)527 fido_dev_has_pin(const fido_dev_t *dev)
528 {
529 return (dev->flags & FIDO_DEV_PIN_SET);
530 }
531
532 bool
fido_dev_supports_cred_prot(const fido_dev_t * dev)533 fido_dev_supports_cred_prot(const fido_dev_t *dev)
534 {
535 return (dev->flags & FIDO_DEV_CRED_PROT);
536 }
537
538 bool
fido_dev_supports_credman(const fido_dev_t * dev)539 fido_dev_supports_credman(const fido_dev_t *dev)
540 {
541 return (dev->flags & FIDO_DEV_CREDMAN);
542 }
543
544 bool
fido_dev_supports_uv(const fido_dev_t * dev)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
fido_dev_has_uv(const fido_dev_t * dev)551 fido_dev_has_uv(const fido_dev_t *dev)
552 {
553 return (dev->flags & FIDO_DEV_UV_SET);
554 }
555
556 bool
fido_dev_supports_permissions(const fido_dev_t * dev)557 fido_dev_supports_permissions(const fido_dev_t *dev)
558 {
559 return (dev->flags & FIDO_DEV_TOKEN_PERMS);
560 }
561
562 void
fido_dev_force_u2f(fido_dev_t * dev)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
fido_dev_force_fido2(fido_dev_t * dev)570 fido_dev_force_fido2(fido_dev_t *dev)
571 {
572 dev->attr.flags |= FIDO_CAP_CBOR;
573 }
574
575 uint8_t
fido_dev_get_pin_protocol(const fido_dev_t * dev)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
fido_dev_maxmsgsize(const fido_dev_t * dev)587 fido_dev_maxmsgsize(const fido_dev_t *dev)
588 {
589 return (dev->maxmsgsize);
590 }
591
592 int
fido_dev_set_timeout(fido_dev_t * dev,int ms)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