Coverage Report

Created: 2020-12-02 17:02

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