Coverage Report

Created: 2022-04-27 14:33

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