Coverage Report

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