Coverage Report

Created: 2021-10-21 13:35

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