Coverage Report

Created: 2021-07-20 18:14

/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
156k
#define MIN(x, y) ((x) > (y) ? (y) : (x))
30
#endif
31
32
static int
33
tx_empty(fido_dev_t *d, uint8_t cmd)
34
2.89k
{
35
2.89k
        struct frame    *fp;
36
2.89k
        unsigned char    pkt[sizeof(*fp) + 1];
37
2.89k
        const size_t     len = d->tx_len + 1;
38
2.89k
        int              n;
39
2.89k
40
2.89k
        memset(&pkt, 0, sizeof(pkt));
41
2.89k
        fp = (struct frame *)(pkt + 1);
42
2.89k
        fp->cid = d->cid;
43
2.89k
        fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
44
2.89k
45
2.89k
        if (len > sizeof(pkt) || (n = d->io.write(d->io_handle, pkt,
46
2.89k
            len)) < 0 || (size_t)n != len)
47
28
                return (-1);
48
2.86k
49
2.86k
        return (0);
50
2.86k
}
51
52
static size_t
53
tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count)
54
86.8k
{
55
86.8k
        struct frame    *fp;
56
86.8k
        unsigned char    pkt[sizeof(*fp) + 1];
57
86.8k
        const size_t     len = d->tx_len + 1;
58
86.8k
        int              n;
59
86.8k
60
86.8k
        if (d->tx_len - CTAP_INIT_HEADER_LEN > sizeof(fp->body.init.data))
61
0
                return (0);
62
86.8k
63
86.8k
        memset(&pkt, 0, sizeof(pkt));
64
86.8k
        fp = (struct frame *)(pkt + 1);
65
86.8k
        fp->cid = d->cid;
66
86.8k
        fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
67
86.8k
        fp->body.init.bcnth = (count >> 8) & 0xff;
68
86.8k
        fp->body.init.bcntl = count & 0xff;
69
86.8k
        count = MIN(count, d->tx_len - CTAP_INIT_HEADER_LEN);
70
86.8k
        memcpy(&fp->body.init.data, buf, count);
71
86.8k
72
86.8k
        if (len > sizeof(pkt) || (n = d->io.write(d->io_handle, pkt,
73
86.8k
            len)) < 0 || (size_t)n != len)
74
219
                return (0);
75
86.6k
76
86.6k
        return (count);
77
86.6k
}
78
79
static size_t
80
tx_frame(fido_dev_t *d, uint8_t seq, const void *buf, size_t count)
81
69.9k
{
82
69.9k
        struct frame    *fp;
83
69.9k
        unsigned char    pkt[sizeof(*fp) + 1];
84
69.9k
        const size_t     len = d->tx_len + 1;
85
69.9k
        int              n;
86
69.9k
87
69.9k
        if (d->tx_len - CTAP_CONT_HEADER_LEN > sizeof(fp->body.cont.data))
88
0
                return (0);
89
69.9k
90
69.9k
        memset(&pkt, 0, sizeof(pkt));
91
69.9k
        fp = (struct frame *)(pkt + 1);
92
69.9k
        fp->cid = d->cid;
93
69.9k
        fp->body.cont.seq = seq;
94
69.9k
        count = MIN(count, d->tx_len - CTAP_CONT_HEADER_LEN);
95
69.9k
        memcpy(&fp->body.cont.data, buf, count);
96
69.9k
97
69.9k
        if (len > sizeof(pkt) || (n = d->io.write(d->io_handle, pkt,
98
69.9k
            len)) < 0 || (size_t)n != len)
99
179
                return (0);
100
69.7k
101
69.7k
        return (count);
102
69.7k
}
103
104
static int
105
tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count)
106
86.8k
{
107
86.8k
        size_t n, sent;
108
86.8k
109
86.8k
        if ((sent = tx_preamble(d, cmd, buf, count)) == 0) {
110
219
                fido_log_debug("%s: tx_preamble", __func__);
111
219
                return (-1);
112
219
        }
113
86.6k
114
156k
        for (uint8_t seq = 0; sent < count; sent += n) {
115
69.9k
                if (seq & 0x80) {
116
70
                        fido_log_debug("%s: seq & 0x80", __func__);
117
70
                        return (-1);
118
70
                }
119
69.9k
                if ((n = tx_frame(d, seq++, buf + sent, count - sent)) == 0) {
120
179
                        fido_log_debug("%s: tx_frame", __func__);
121
179
                        return (-1);
122
179
                }
123
69.9k
        }
124
86.6k
125
86.6k
        return (0);
126
86.6k
}
127
128
int
129
fido_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count)
130
91.1k
{
131
91.1k
        fido_log_debug("%s: dev=%p, cmd=0x%02x", __func__, (void *)d, cmd);
132
91.1k
        fido_log_xxd(buf, count, "%s", __func__);
133
91.1k
134
91.1k
        if (d->transport.tx != NULL)
135
91.1k
                return (d->transport.tx(d, cmd, buf, count));
136
89.7k
        if (d->io_handle == NULL || d->io.write == NULL || count > UINT16_MAX) {
137
17
                fido_log_debug("%s: invalid argument", __func__);
138
17
                return (-1);
139
17
        }
140
89.7k
141
89.7k
        return (count == 0 ? tx_empty(d, cmd) : tx(d, cmd, buf, count));
142
89.7k
}
143
144
static int
145
rx_frame(fido_dev_t *d, struct frame *fp, int ms)
146
168k
{
147
168k
        int n;
148
168k
149
168k
        memset(fp, 0, sizeof(*fp));
150
168k
151
168k
        if (d->rx_len > sizeof(*fp) || (n = d->io.read(d->io_handle,
152
168k
            (unsigned char *)fp, d->rx_len, ms)) < 0 || (size_t)n != d->rx_len)
153
37.3k
                return (-1);
154
131k
155
131k
        return (0);
156
131k
}
157
158
static int
159
rx_preamble(fido_dev_t *d, uint8_t cmd, struct frame *fp, int ms)
160
87.4k
{
161
90.4k
        do {
162
90.4k
                if (rx_frame(d, fp, ms) < 0)
163
36.3k
                        return (-1);
164
54.0k
#ifdef FIDO_FUZZ
165
54.0k
                fp->cid = d->cid;
166
54.0k
#endif
167
54.0k
        } while (fp->cid != d->cid || (fp->cid == d->cid &&
168
54.0k
            fp->body.init.cmd == (CTAP_FRAME_INIT | CTAP_KEEPALIVE)));
169
87.4k
170
87.4k
        if (d->rx_len > sizeof(*fp))
171
0
                return (-1);
172
51.0k
173
51.0k
        fido_log_xxd(fp, d->rx_len, "%s", __func__);
174
51.0k
#ifdef FIDO_FUZZ
175
51.0k
        fp->body.init.cmd = (CTAP_FRAME_INIT | cmd);
176
51.0k
#endif
177
51.0k
178
51.0k
        if (fp->cid != d->cid || fp->body.init.cmd != (CTAP_FRAME_INIT | cmd)) {
179
0
                fido_log_debug("%s: cid (0x%x, 0x%x), cmd (0x%02x, 0x%02x)",
180
0
                    __func__, fp->cid, d->cid, fp->body.init.cmd, cmd);
181
0
                return (-1);
182
0
        }
183
51.0k
184
51.0k
        return (0);
185
51.0k
}
186
187
static int
188
rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int ms)
189
87.4k
{
190
87.4k
        struct frame f;
191
87.4k
        size_t r, payload_len, init_data_len, cont_data_len;
192
87.4k
193
87.4k
        if (d->rx_len <= CTAP_INIT_HEADER_LEN ||
194
87.4k
            d->rx_len <= CTAP_CONT_HEADER_LEN)
195
87.4k
                return (-1);
196
87.4k
197
87.4k
        init_data_len = d->rx_len - CTAP_INIT_HEADER_LEN;
198
87.4k
        cont_data_len = d->rx_len - CTAP_CONT_HEADER_LEN;
199
87.4k
200
87.4k
        if (init_data_len > sizeof(f.body.init.data) ||
201
87.4k
            cont_data_len > sizeof(f.body.cont.data))
202
0
                return (-1);
203
87.4k
204
87.4k
        if (rx_preamble(d, cmd, &f, ms) < 0) {
205
36.3k
                fido_log_debug("%s: rx_preamble", __func__);
206
36.3k
                return (-1);
207
36.3k
        }
208
51.0k
209
51.0k
        payload_len = (size_t)((f.body.init.bcnth << 8) | f.body.init.bcntl);
210
51.0k
        fido_log_debug("%s: payload_len=%zu", __func__, payload_len);
211
51.0k
212
51.0k
        if (count < payload_len) {
213
2.92k
                fido_log_debug("%s: count < payload_len", __func__);
214
2.92k
                return (-1);
215
2.92k
        }
216
48.1k
217
48.1k
        if (payload_len < init_data_len) {
218
25.3k
                memcpy(buf, f.body.init.data, payload_len);
219
25.3k
                return ((int)payload_len);
220
25.3k
        }
221
22.8k
222
22.8k
        memcpy(buf, f.body.init.data, init_data_len);
223
22.8k
        r = init_data_len;
224
22.8k
225
99.7k
        for (int seq = 0; r < payload_len; seq++) {
226
77.8k
                if (rx_frame(d, &f, ms) < 0) {
227
943
                        fido_log_debug("%s: rx_frame", __func__);
228
943
                        return (-1);
229
943
                }
230
76.9k
231
76.9k
                fido_log_xxd(&f, d->rx_len, "%s", __func__);
232
76.9k
#ifdef FIDO_FUZZ
233
76.9k
                f.cid = d->cid;
234
76.9k
                f.body.cont.seq = (uint8_t)seq;
235
76.9k
#endif
236
76.9k
237
76.9k
                if (f.cid != d->cid || f.body.cont.seq != seq) {
238
17
                        fido_log_debug("%s: cid (0x%x, 0x%x), seq (%d, %d)",
239
17
                            __func__, f.cid, d->cid, f.body.cont.seq, seq);
240
17
                        return (-1);
241
17
                }
242
76.9k
243
76.9k
                if (payload_len - r > cont_data_len) {
244
56.1k
                        memcpy(buf + r, f.body.cont.data, cont_data_len);
245
56.1k
                        r += cont_data_len;
246
56.1k
                } else {
247
20.7k
                        memcpy(buf + r, f.body.cont.data, payload_len - r);
248
20.7k
                        r += payload_len - r; /* break */
249
20.7k
                }
250
76.9k
        }
251
22.8k
252
22.8k
        return ((int)r);
253
22.8k
}
254
255
int
256
fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int ms)
257
88.6k
{
258
88.6k
        int n;
259
88.6k
260
88.6k
        fido_log_debug("%s: dev=%p, cmd=0x%02x, ms=%d", __func__, (void *)d,
261
88.6k
            cmd, ms);
262
88.6k
263
88.6k
        if (d->transport.rx != NULL)
264
88.6k
                return (d->transport.rx(d, cmd, buf, count, ms));
265
87.4k
        if (d->io_handle == NULL || d->io.read == NULL || count > UINT16_MAX) {
266
0
                fido_log_debug("%s: invalid argument", __func__);
267
0
                return (-1);
268
0
        }
269
87.4k
        if ((n = rx(d, cmd, buf, count, ms)) >= 0)
270
47.2k
                fido_log_xxd(buf, (size_t)n, "%s", __func__);
271
87.4k
272
87.4k
        return (n);
273
87.4k
}
274
275
int
276
fido_rx_cbor_status(fido_dev_t *d, int ms)
277
1.31k
{
278
1.31k
        unsigned char   reply[FIDO_MAXMSG];
279
1.31k
        int             reply_len;
280
1.31k
281
1.31k
        if ((reply_len = fido_rx(d, CTAP_CMD_CBOR, &reply, sizeof(reply),
282
1.31k
            ms)) < 0 || (size_t)reply_len < 1) {
283
875
                fido_log_debug("%s: fido_rx", __func__);
284
875
                return (FIDO_ERR_RX);
285
875
        }
286
437
287
437
        return (reply[0]);
288
437
}