Coverage Report

Created: 2022-01-17 10:46

/libfido2/src/io.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
#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
194k
#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
198k
{
35
198k
        struct timespec ts;
36
198k
        int n;
37
38
198k
        if (fido_time_now(&ts) != 0)
39
525
                return (-1);
40
41
198k
        n = d->io.write(d->io_handle, pkt, len);
42
43
198k
        if (fido_time_delta(&ts, ms) != 0)
44
1.06k
                return (-1);
45
46
197k
        return (n);
47
197k
}
48
49
static int
50
tx_empty(fido_dev_t *d, uint8_t cmd, int *ms)
51
3.90k
{
52
3.90k
        struct frame    *fp;
53
3.90k
        unsigned char    pkt[sizeof(*fp) + 1];
54
3.90k
        const size_t     len = d->tx_len + 1;
55
3.90k
        int              n;
56
57
3.90k
        memset(&pkt, 0, sizeof(pkt));
58
3.90k
        fp = (struct frame *)(pkt + 1);
59
3.90k
        fp->cid = d->cid;
60
3.90k
        fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
61
62
3.90k
        if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
63
3.90k
            (size_t)n != len)
64
67
                return (-1);
65
66
3.84k
        return (0);
67
3.84k
}
68
69
static size_t
70
tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
71
115k
{
72
115k
        struct frame    *fp;
73
115k
        unsigned char    pkt[sizeof(*fp) + 1];
74
115k
        const size_t     len = d->tx_len + 1;
75
115k
        int              n;
76
77
115k
        if (d->tx_len - CTAP_INIT_HEADER_LEN > sizeof(fp->body.init.data))
78
0
                return (0);
79
80
115k
        memset(&pkt, 0, sizeof(pkt));
81
115k
        fp = (struct frame *)(pkt + 1);
82
115k
        fp->cid = d->cid;
83
115k
        fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
84
115k
        fp->body.init.bcnth = (count >> 8) & 0xff;
85
115k
        fp->body.init.bcntl = count & 0xff;
86
115k
        count = MIN(count, d->tx_len - CTAP_INIT_HEADER_LEN);
87
115k
        memcpy(&fp->body.init.data, buf, count);
88
89
115k
        if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
90
115k
            (size_t)n != len)
91
1.47k
                return (0);
92
93
113k
        return (count);
94
113k
}
95
96
static size_t
97
tx_frame(fido_dev_t *d, uint8_t seq, const void *buf, size_t count, int *ms)
98
79.6k
{
99
79.6k
        struct frame    *fp;
100
79.6k
        unsigned char    pkt[sizeof(*fp) + 1];
101
79.6k
        const size_t     len = d->tx_len + 1;
102
79.6k
        int              n;
103
104
79.6k
        if (d->tx_len - CTAP_CONT_HEADER_LEN > sizeof(fp->body.cont.data))
105
0
                return (0);
106
107
79.6k
        memset(&pkt, 0, sizeof(pkt));
108
79.6k
        fp = (struct frame *)(pkt + 1);
109
79.6k
        fp->cid = d->cid;
110
79.6k
        fp->body.cont.seq = seq;
111
79.6k
        count = MIN(count, d->tx_len - CTAP_CONT_HEADER_LEN);
112
79.6k
        memcpy(&fp->body.cont.data, buf, count);
113
114
79.6k
        if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
115
79.6k
            (size_t)n != len)
116
463
                return (0);
117
118
79.1k
        return (count);
119
79.1k
}
120
121
static int
122
tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count, int *ms)
123
115k
{
124
115k
        size_t n, sent;
125
126
115k
        if ((sent = tx_preamble(d, cmd, buf, count, ms)) == 0) {
127
1.47k
                fido_log_debug("%s: tx_preamble", __func__);
128
1.47k
                return (-1);
129
1.47k
        }
130
131
192k
        for (uint8_t seq = 0; sent < count; sent += n) {
132
79.6k
                if (seq & 0x80) {
133
36
                        fido_log_debug("%s: seq & 0x80", __func__);
134
36
                        return (-1);
135
36
                }
136
79.6k
                if ((n = tx_frame(d, seq++, buf + sent, count - sent,
137
79.6k
                    ms)) == 0) {
138
463
                        fido_log_debug("%s: tx_frame", __func__);
139
463
                        return (-1);
140
463
                }
141
79.6k
        }
142
143
113k
        return (0);
144
113k
}
145
146
static int
147
transport_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
148
1.60k
{
149
1.60k
        struct timespec ts;
150
1.60k
        int n;
151
152
1.60k
        if (fido_time_now(&ts) != 0)
153
13
                return (-1);
154
155
1.59k
        n = d->transport.tx(d, cmd, buf, count);
156
157
1.59k
        if (fido_time_delta(&ts, ms) != 0)
158
39
                return (-1);
159
160
1.55k
        return (n);
161
1.55k
}
162
163
int
164
fido_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
165
120k
{
166
120k
        fido_log_debug("%s: dev=%p, cmd=0x%02x", __func__, (void *)d, cmd);
167
120k
        fido_log_xxd(buf, count, "%s", __func__);
168
169
120k
        if (d->transport.tx != NULL)
170
120k
                return (transport_tx(d, cmd, buf, count, ms));
171
119k
        if (d->io_handle == NULL || d->io.write == NULL || count > UINT16_MAX) {
172
19
                fido_log_debug("%s: invalid argument", __func__);
173
19
                return (-1);
174
19
        }
175
176
119k
        return (count == 0 ? tx_empty(d, cmd, ms) : tx(d, cmd, buf, count, ms));
177
119k
}
178
179
static int
180
rx_frame(fido_dev_t *d, struct frame *fp, int *ms)
181
245k
{
182
245k
        struct timespec ts;
183
245k
        int n;
184
185
245k
        memset(fp, 0, sizeof(*fp));
186
187
245k
        if (fido_time_now(&ts) != 0)
188
411
                return (-1);
189
190
245k
        if (d->rx_len > sizeof(*fp) || (n = d->io.read(d->io_handle,
191
245k
            (unsigned char *)fp, d->rx_len, *ms)) < 0 || (size_t)n != d->rx_len)
192
47.8k
                return (-1);
193
194
197k
        return (fido_time_delta(&ts, ms));
195
197k
}
196
197
static int
198
rx_preamble(fido_dev_t *d, uint8_t cmd, struct frame *fp, int *ms)
199
114k
{
200
118k
        do {
201
118k
                if (rx_frame(d, fp, ms) < 0)
202
46.4k
                        return (-1);
203
71.8k
#ifdef FIDO_FUZZ
204
71.8k
                fp->cid = d->cid;
205
71.8k
#endif
206
71.8k
        } while (fp->cid != d->cid || (fp->cid == d->cid &&
207
71.8k
            fp->body.init.cmd == (CTAP_FRAME_INIT | CTAP_KEEPALIVE)));
208
209
114k
        if (d->rx_len > sizeof(*fp))
210
0
                return (-1);
211
212
68.3k
        fido_log_xxd(fp, d->rx_len, "%s", __func__);
213
68.3k
#ifdef FIDO_FUZZ
214
68.3k
        fp->body.init.cmd = (CTAP_FRAME_INIT | cmd);
215
68.3k
#endif
216
217
68.3k
        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
68.3k
        return (0);
224
68.3k
}
225
226
static int
227
rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int *ms)
228
114k
{
229
114k
        struct frame f;
230
114k
        size_t r, payload_len, init_data_len, cont_data_len;
231
232
114k
        if (d->rx_len <= CTAP_INIT_HEADER_LEN ||
233
114k
            d->rx_len <= CTAP_CONT_HEADER_LEN)
234
114k
                return (-1);
235
236
114k
        init_data_len = d->rx_len - CTAP_INIT_HEADER_LEN;
237
114k
        cont_data_len = d->rx_len - CTAP_CONT_HEADER_LEN;
238
239
114k
        if (init_data_len > sizeof(f.body.init.data) ||
240
114k
            cont_data_len > sizeof(f.body.cont.data))
241
0
                return (-1);
242
243
114k
        if (rx_preamble(d, cmd, &f, ms) < 0) {
244
46.4k
                fido_log_debug("%s: rx_preamble", __func__);
245
46.4k
                return (-1);
246
46.4k
        }
247
248
68.3k
        payload_len = (size_t)((f.body.init.bcnth << 8) | f.body.init.bcntl);
249
68.3k
        fido_log_debug("%s: payload_len=%zu", __func__, payload_len);
250
251
68.3k
        if (count < payload_len) {
252
5.46k
                fido_log_debug("%s: count < payload_len", __func__);
253
5.46k
                return (-1);
254
5.46k
        }
255
256
62.8k
        if (payload_len < init_data_len) {
257
33.6k
                memcpy(buf, f.body.init.data, payload_len);
258
33.6k
                return ((int)payload_len);
259
33.6k
        }
260
261
29.2k
        memcpy(buf, f.body.init.data, init_data_len);
262
29.2k
        r = init_data_len;
263
264
154k
        for (int seq = 0; r < payload_len; seq++) {
265
127k
                if (rx_frame(d, &f, ms) < 0) {
266
2.20k
                        fido_log_debug("%s: rx_frame", __func__);
267
2.20k
                        return (-1);
268
2.20k
                }
269
270
125k
                fido_log_xxd(&f, d->rx_len, "%s", __func__);
271
125k
#ifdef FIDO_FUZZ
272
125k
                f.cid = d->cid;
273
125k
                f.body.cont.seq = (uint8_t)seq;
274
125k
#endif
275
276
125k
                if (f.cid != d->cid || f.body.cont.seq != seq) {
277
22
                        fido_log_debug("%s: cid (0x%x, 0x%x), seq (%d, %d)",
278
22
                            __func__, f.cid, d->cid, f.body.cont.seq, seq);
279
22
                        return (-1);
280
22
                }
281
282
125k
                if (payload_len - r > cont_data_len) {
283
99.4k
                        memcpy(buf + r, f.body.cont.data, cont_data_len);
284
99.4k
                        r += cont_data_len;
285
99.4k
                } else {
286
25.7k
                        memcpy(buf + r, f.body.cont.data, payload_len - r);
287
25.7k
                        r += payload_len - r; /* break */
288
25.7k
                }
289
125k
        }
290
291
29.2k
        return ((int)r);
292
29.2k
}
293
294
static int
295
transport_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int *ms)
296
1.42k
{
297
1.42k
        struct timespec ts;
298
1.42k
        int n;
299
300
1.42k
        if (fido_time_now(&ts) != 0)
301
18
                return (-1);
302
303
1.40k
        n = d->transport.rx(d, cmd, buf, count, *ms);
304
305
1.40k
        if (fido_time_delta(&ts, ms) != 0)
306
34
                return (-1);
307
308
1.37k
        return (n);
309
1.37k
}
310
311
int
312
fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int *ms)
313
116k
{
314
116k
        int n;
315
316
116k
        fido_log_debug("%s: dev=%p, cmd=0x%02x, ms=%d", __func__, (void *)d,
317
116k
            cmd, *ms);
318
319
116k
        if (d->transport.rx != NULL)
320
116k
                return (transport_rx(d, cmd, buf, count, ms));
321
114k
        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
114k
        if ((n = rx(d, cmd, buf, count, ms)) >= 0)
326
60.6k
                fido_log_xxd(buf, (size_t)n, "%s", __func__);
327
328
114k
        return (n);
329
114k
}
330
331
int
332
fido_rx_cbor_status(fido_dev_t *d, int *ms)
333
2.33k
{
334
2.33k
        unsigned char   reply[FIDO_MAXMSG];
335
2.33k
        int             reply_len;
336
337
2.33k
        if ((reply_len = fido_rx(d, CTAP_CMD_CBOR, &reply, sizeof(reply),
338
2.33k
            ms)) < 0 || (size_t)reply_len < 1) {
339
1.76k
                fido_log_debug("%s: fido_rx", __func__);
340
1.76k
                return (FIDO_ERR_RX);
341
1.76k
        }
342
343
576
        return (reply[0]);
344
576
}