Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/libfido2/src/dev.c
39483 views
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
603