Coverage Report

Created: 2022-07-22 12:05

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