Coverage Report

Created: 2024-02-05 19:20

/libfido2/src/io.c
Line
Count
Source (jump to first uncovered line)
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
#include "packed.h"
10
11
PACKED_TYPE(frame_t,
12
struct frame {
13
        uint32_t cid; /* channel id */
14
        union {
15
                uint8_t type;
16
                struct {
17
                        uint8_t cmd;
18
                        uint8_t bcnth;
19
                        uint8_t bcntl;
20
                        uint8_t data[CTAP_MAX_REPORT_LEN - CTAP_INIT_HEADER_LEN];
21
                } init;
22
                struct {
23
                        uint8_t seq;
24
                        uint8_t data[CTAP_MAX_REPORT_LEN - CTAP_CONT_HEADER_LEN];
25
                } cont;
26
        } body;
27
})
28
29
#ifndef MIN
30
3.26M
#define MIN(x, y) ((x) > (y) ? (y) : (x))
31
#endif
32
33
static int
34
tx_pkt(fido_dev_t *d, const void *pkt, size_t len, int *ms)
35
3.28M
{
36
3.28M
        struct timespec ts;
37
3.28M
        int n;
38
39
3.28M
        if (fido_time_now(&ts) != 0)
40
7.94k
                return (-1);
41
42
3.28M
        n = d->io.write(d->io_handle, pkt, len);
43
44
3.28M
        if (fido_time_delta(&ts, ms) != 0)
45
14.7k
                return (-1);
46
47
3.26M
        return (n);
48
3.28M
}
49
50
static int
51
tx_empty(fido_dev_t *d, uint8_t cmd, int *ms)
52
20.0k
{
53
20.0k
        struct frame    *fp;
54
20.0k
        unsigned char    pkt[sizeof(*fp) + 1];
55
20.0k
        const size_t     len = d->tx_len + 1;
56
20.0k
        int              n;
57
58
20.0k
        memset(&pkt, 0, sizeof(pkt));
59
20.0k
        fp = (struct frame *)(pkt + 1);
60
20.0k
        fp->cid = d->cid;
61
20.0k
        fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
62
63
20.0k
        if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
64
20.0k
            (size_t)n != len)
65
225
                return (-1);
66
67
19.8k
        return (0);
68
20.0k
}
69
70
static size_t
71
tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
72
2.21M
{
73
2.21M
        struct frame    *fp;
74
2.21M
        unsigned char    pkt[sizeof(*fp) + 1];
75
2.21M
        const size_t     len = d->tx_len + 1;
76
2.21M
        int              n;
77
78
2.21M
        if (d->tx_len - CTAP_INIT_HEADER_LEN > sizeof(fp->body.init.data))
79
0
                return (0);
80
81
2.21M
        memset(&pkt, 0, sizeof(pkt));
82
2.21M
        fp = (struct frame *)(pkt + 1);
83
2.21M
        fp->cid = d->cid;
84
2.21M
        fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
85
2.21M
        fp->body.init.bcnth = (count >> 8) & 0xff;
86
2.21M
        fp->body.init.bcntl = count & 0xff;
87
2.21M
        count = MIN(count, d->tx_len - CTAP_INIT_HEADER_LEN);
88
2.21M
        memcpy(&fp->body.init.data, buf, count);
89
90
2.21M
        if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
91
2.21M
            (size_t)n != len)
92
19.5k
                return (0);
93
94
2.19M
        return (count);
95
2.21M
}
96
97
static size_t
98
tx_frame(fido_dev_t *d, uint8_t seq, const void *buf, size_t count, int *ms)
99
1.05M
{
100
1.05M
        struct frame    *fp;
101
1.05M
        unsigned char    pkt[sizeof(*fp) + 1];
102
1.05M
        const size_t     len = d->tx_len + 1;
103
1.05M
        int              n;
104
105
1.05M
        if (d->tx_len - CTAP_CONT_HEADER_LEN > sizeof(fp->body.cont.data))
106
0
                return (0);
107
108
1.05M
        memset(&pkt, 0, sizeof(pkt));
109
1.05M
        fp = (struct frame *)(pkt + 1);
110
1.05M
        fp->cid = d->cid;
111
1.05M
        fp->body.cont.seq = seq;
112
1.05M
        count = MIN(count, d->tx_len - CTAP_CONT_HEADER_LEN);
113
1.05M
        memcpy(&fp->body.cont.data, buf, count);
114
115
1.05M
        if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
116
1.05M
            (size_t)n != len)
117
9.38k
                return (0);
118
119
1.04M
        return (count);
120
1.05M
}
121
122
static int
123
tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count, int *ms)
124
2.21M
{
125
2.21M
        size_t n, sent;
126
127
2.21M
        if ((sent = tx_preamble(d, cmd, buf, count, ms)) == 0) {
128
19.5k
                fido_log_debug("%s: tx_preamble", __func__);
129
19.5k
                return (-1);
130
19.5k
        }
131
132
3.24M
        for (uint8_t seq = 0; sent < count; sent += n) {
133
1.05M
                if (seq & 0x80) {
134
617
                        fido_log_debug("%s: seq & 0x80", __func__);
135
617
                        return (-1);
136
617
                }
137
1.05M
                if ((n = tx_frame(d, seq++, buf + sent, count - sent,
138
1.05M
                    ms)) == 0) {
139
9.38k
                        fido_log_debug("%s: tx_frame", __func__);
140
9.38k
                        return (-1);
141
9.38k
                }
142
1.05M
        }
143
144
2.18M
        return (0);
145
2.19M
}
146
147
static int
148
transport_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
149
116k
{
150
116k
        struct timespec ts;
151
116k
        int n;
152
153
116k
        if (fido_time_now(&ts) != 0)
154
652
                return (-1);
155
156
115k
        n = d->transport.tx(d, cmd, buf, count);
157
158
115k
        if (fido_time_delta(&ts, ms) != 0)
159
72
                return (-1);
160
161
115k
        return (n);
162
115k
}
163
164
int
165
fido_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
166
2.34M
{
167
2.34M
        fido_log_debug("%s: dev=%p, cmd=0x%02x", __func__, (void *)d, cmd);
168
2.34M
        fido_log_xxd(buf, count, "%s", __func__);
169
170
2.34M
        if (d->transport.tx != NULL)
171
116k
                return (transport_tx(d, cmd, buf, count, ms));
172
2.23M
        if (d->io_handle == NULL || d->io.write == NULL || count > UINT16_MAX) {
173
473
                fido_log_debug("%s: invalid argument", __func__);
174
473
                return (-1);
175
473
        }
176
177
2.23M
        return (count == 0 ? tx_empty(d, cmd, ms) : tx(d, cmd, buf, count, ms));
178
2.23M
}
179
180
static int
181
rx_frame(fido_dev_t *d, struct frame *fp, int *ms)
182
4.61M
{
183
4.61M
        struct timespec ts;
184
4.61M
        int n;
185
186
4.61M
        memset(fp, 0, sizeof(*fp));
187
188
4.61M
        if (fido_time_now(&ts) != 0)
189
5.59k
                return (-1);
190
191
4.61M
        if (d->rx_len > sizeof(*fp) || (n = d->io.read(d->io_handle,
192
4.61M
            (unsigned char *)fp, d->rx_len, *ms)) < 0 || (size_t)n != d->rx_len)
193
821k
                return (-1);
194
195
3.79M
        return (fido_time_delta(&ts, ms));
196
4.61M
}
197
198
static int
199
rx_preamble(fido_dev_t *d, uint8_t cmd, struct frame *fp, int *ms)
200
2.18M
{
201
2.21M
        do {
202
2.21M
                if (rx_frame(d, fp, ms) < 0)
203
794k
                        return (-1);
204
1.41M
#ifdef FIDO_FUZZ
205
1.41M
                fp->cid = d->cid;
206
1.41M
#endif
207
1.41M
        } while (fp->cid != d->cid || (fp->cid == d->cid &&
208
1.41M
            fp->body.init.cmd == (CTAP_FRAME_INIT | CTAP_KEEPALIVE)));
209
210
1.39M
        if (d->rx_len > sizeof(*fp))
211
0
                return (-1);
212
213
1.39M
        fido_log_xxd(fp, d->rx_len, "%s", __func__);
214
1.39M
#ifdef FIDO_FUZZ
215
1.39M
        fp->body.init.cmd = (CTAP_FRAME_INIT | cmd);
216
1.39M
#endif
217
218
1.39M
        if (fp->cid != d->cid || fp->body.init.cmd != (CTAP_FRAME_INIT | cmd)) {
219
0
                fido_log_debug("%s: cid (0x%x, 0x%x), cmd (0x%02x, 0x%02x)",
220
0
                    __func__, fp->cid, d->cid, fp->body.init.cmd, cmd);
221
0
                return (-1);
222
0
        }
223
224
1.39M
        return (0);
225
1.39M
}
226
227
static int
228
rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int *ms)
229
2.18M
{
230
2.18M
        struct frame f;
231
2.18M
        size_t r, payload_len, init_data_len, cont_data_len;
232
233
2.18M
        if (d->rx_len <= CTAP_INIT_HEADER_LEN ||
234
2.18M
            d->rx_len <= CTAP_CONT_HEADER_LEN)
235
0
                return (-1);
236
237
2.18M
        init_data_len = d->rx_len - CTAP_INIT_HEADER_LEN;
238
2.18M
        cont_data_len = d->rx_len - CTAP_CONT_HEADER_LEN;
239
240
2.18M
        if (init_data_len > sizeof(f.body.init.data) ||
241
2.18M
            cont_data_len > sizeof(f.body.cont.data))
242
0
                return (-1);
243
244
2.18M
        if (rx_preamble(d, cmd, &f, ms) < 0) {
245
794k
                fido_log_debug("%s: rx_preamble", __func__);
246
794k
                return (-1);
247
794k
        }
248
249
1.39M
        payload_len = (size_t)((f.body.init.bcnth << 8) | f.body.init.bcntl);
250
1.39M
        fido_log_debug("%s: payload_len=%zu", __func__, payload_len);
251
252
1.39M
        if (count < payload_len) {
253
195k
                fido_log_debug("%s: count < payload_len", __func__);
254
195k
                return (-1);
255
195k
        }
256
257
1.19M
        if (payload_len < init_data_len) {
258
693k
                memcpy(buf, f.body.init.data, payload_len);
259
693k
                return ((int)payload_len);
260
693k
        }
261
262
505k
        memcpy(buf, f.body.init.data, init_data_len);
263
505k
        r = init_data_len;
264
265
2.87M
        for (int seq = 0; r < payload_len; seq++) {
266
2.40M
                if (rx_frame(d, &f, ms) < 0) {
267
39.4k
                        fido_log_debug("%s: rx_frame", __func__);
268
39.4k
                        return (-1);
269
39.4k
                }
270
271
2.36M
                fido_log_xxd(&f, d->rx_len, "%s", __func__);
272
2.36M
#ifdef FIDO_FUZZ
273
2.36M
                f.cid = d->cid;
274
2.36M
                f.body.cont.seq = (uint8_t)seq;
275
2.36M
#endif
276
277
2.36M
                if (f.cid != d->cid || f.body.cont.seq != seq) {
278
336
                        fido_log_debug("%s: cid (0x%x, 0x%x), seq (%d, %d)",
279
336
                            __func__, f.cid, d->cid, f.body.cont.seq, seq);
280
336
                        return (-1);
281
336
                }
282
283
2.36M
                if (payload_len - r > cont_data_len) {
284
1.92M
                        memcpy(buf + r, f.body.cont.data, cont_data_len);
285
1.92M
                        r += cont_data_len;
286
1.92M
                } else {
287
446k
                        memcpy(buf + r, f.body.cont.data, payload_len - r);
288
446k
                        r += payload_len - r; /* break */
289
446k
                }
290
2.36M
        }
291
292
465k
        return ((int)r);
293
505k
}
294
295
static int
296
transport_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int *ms)
297
114k
{
298
114k
        struct timespec ts;
299
114k
        int n;
300
301
114k
        if (fido_time_now(&ts) != 0)
302
135
                return (-1);
303
304
114k
        n = d->transport.rx(d, cmd, buf, count, *ms);
305
306
114k
        if (fido_time_delta(&ts, ms) != 0)
307
74
                return (-1);
308
309
114k
        return (n);
310
114k
}
311
312
int
313
fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int *ms)
314
2.30M
{
315
2.30M
        int n;
316
317
2.30M
        fido_log_debug("%s: dev=%p, cmd=0x%02x, ms=%d", __func__, (void *)d,
318
2.30M
            cmd, *ms);
319
320
2.30M
        if (d->transport.rx != NULL)
321
114k
                return (transport_rx(d, cmd, buf, count, ms));
322
2.18M
        if (d->io_handle == NULL || d->io.read == NULL || count > UINT16_MAX) {
323
0
                fido_log_debug("%s: invalid argument", __func__);
324
0
                return (-1);
325
0
        }
326
2.18M
        if ((n = rx(d, cmd, buf, count, ms)) >= 0)
327
1.15M
                fido_log_xxd(buf, (size_t)n, "%s", __func__);
328
329
2.18M
        return (n);
330
2.18M
}
331
332
int
333
fido_rx_cbor_status(fido_dev_t *d, int *ms)
334
72.6k
{
335
72.6k
        unsigned char   *msg;
336
72.6k
        int              msglen;
337
72.6k
        int              r;
338
339
72.6k
        if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
340
466
                r = FIDO_ERR_INTERNAL;
341
466
                goto out;
342
466
        }
343
344
72.2k
        if ((msglen = fido_rx(d, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0 ||
345
72.2k
            (size_t)msglen < 1) {
346
44.9k
                fido_log_debug("%s: fido_rx", __func__);
347
44.9k
                r = FIDO_ERR_RX;
348
44.9k
                goto out;
349
44.9k
        }
350
351
27.2k
        r = msg[0];
352
72.6k
out:
353
72.6k
        freezero(msg, FIDO_MAXMSG);
354
355
72.6k
        return (r);
356
27.2k
}