Coverage Report

Created: 2020-12-02 17:02

/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 <stdint.h>
8
#include <stdio.h>
9
#include <string.h>
10
11
#include "fido.h"
12
#include "packed.h"
13
14
PACKED_TYPE(frame_t,
15
struct frame {
16
        uint32_t cid; /* channel id */
17
        union {
18
                uint8_t type;
19
                struct {
20
                        uint8_t cmd;
21
                        uint8_t bcnth;
22
                        uint8_t bcntl;
23
                        uint8_t data[CTAP_MAX_REPORT_LEN - CTAP_INIT_HEADER_LEN];
24
                } init;
25
                struct {
26
                        uint8_t seq;
27
                        uint8_t data[CTAP_MAX_REPORT_LEN - CTAP_CONT_HEADER_LEN];
28
                } cont;
29
        } body;
30
})
31
32
#ifndef MIN
33
111k
#define MIN(x, y) ((x) > (y) ? (y) : (x))
34
#endif
35
36
static int
37
tx_empty(fido_dev_t *d, uint8_t cmd)
38
2.61k
{
39
2.61k
        struct frame    *fp;
40
2.61k
        unsigned char    pkt[sizeof(*fp) + 1];
41
2.61k
        const size_t     len = d->tx_len + 1;
42
2.61k
        int              n;
43
2.61k
44
2.61k
        memset(&pkt, 0, sizeof(pkt));
45
2.61k
        fp = (struct frame *)(pkt + 1);
46
2.61k
        fp->cid = d->cid;
47
2.61k
        fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
48
2.61k
49
2.61k
        if (len > sizeof(pkt) || (n = d->io.write(d->io_handle, pkt,
50
2.61k
            len)) < 0 || (size_t)n != len)
51
17
                return (-1);
52
2.59k
53
2.59k
        return (0);
54
2.59k
}
55
56
static size_t
57
tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count)
58
49.7k
{
59
49.7k
        struct frame    *fp;
60
49.7k
        unsigned char    pkt[sizeof(*fp) + 1];
61
49.7k
        const size_t     len = d->tx_len + 1;
62
49.7k
        int              n;
63
49.7k
64
49.7k
        if (d->tx_len - CTAP_INIT_HEADER_LEN > sizeof(fp->body.init.data))
65
0
                return (0);
66
49.7k
67
49.7k
        memset(&pkt, 0, sizeof(pkt));
68
49.7k
        fp = (struct frame *)(pkt + 1);
69
49.7k
        fp->cid = d->cid;
70
49.7k
        fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
71
49.7k
        fp->body.init.bcnth = (count >> 8) & 0xff;
72
49.7k
        fp->body.init.bcntl = count & 0xff;
73
49.7k
        count = MIN(count, d->tx_len - CTAP_INIT_HEADER_LEN);
74
49.7k
        memcpy(&fp->body.init.data, buf, count);
75
49.7k
76
49.7k
        if (len > sizeof(pkt) || (n = d->io.write(d->io_handle, pkt,
77
49.7k
            len)) < 0 || (size_t)n != len)
78
142
                return (0);
79
49.6k
80
49.6k
        return (count);
81
49.6k
}
82
83
static size_t
84
tx_frame(fido_dev_t *d, uint8_t seq, const void *buf, size_t count)
85
62.0k
{
86
62.0k
        struct frame    *fp;
87
62.0k
        unsigned char    pkt[sizeof(*fp) + 1];
88
62.0k
        const size_t     len = d->tx_len + 1;
89
62.0k
        int              n;
90
62.0k
91
62.0k
        if (d->tx_len - CTAP_CONT_HEADER_LEN > sizeof(fp->body.cont.data))
92
0
                return (0);
93
62.0k
94
62.0k
        memset(&pkt, 0, sizeof(pkt));
95
62.0k
        fp = (struct frame *)(pkt + 1);
96
62.0k
        fp->cid = d->cid;
97
62.0k
        fp->body.cont.seq = seq;
98
62.0k
        count = MIN(count, d->tx_len - CTAP_CONT_HEADER_LEN);
99
62.0k
        memcpy(&fp->body.cont.data, buf, count);
100
62.0k
101
62.0k
        if (len > sizeof(pkt) || (n = d->io.write(d->io_handle, pkt,
102
62.0k
            len)) < 0 || (size_t)n != len)
103
122
                return (0);
104
61.9k
105
61.9k
        return (count);
106
61.9k
}
107
108
static int
109
tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count)
110
49.7k
{
111
49.7k
        size_t n, sent;
112
49.7k
113
49.7k
        if ((sent = tx_preamble(d, cmd, buf, count)) == 0) {
114
142
                fido_log_debug("%s: tx_preamble", __func__);
115
142
                return (-1);
116
142
        }
117
49.6k
118
111k
        for (uint8_t seq = 0; sent < count; sent += n) {
119
62.1k
                if (seq & 0x80) {
120
61
                        fido_log_debug("%s: seq & 0x80", __func__);
121
61
                        return (-1);
122
61
                }
123
62.0k
                if ((n = tx_frame(d, seq++, buf + sent, count - sent)) == 0) {
124
122
                        fido_log_debug("%s: tx_frame", __func__);
125
122
                        return (-1);
126
122
                }
127
62.0k
        }
128
49.6k
129
49.6k
        return (0);
130
49.6k
}
131
132
int
133
fido_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count)
134
52.4k
{
135
52.4k
        fido_log_debug("%s: d=%p, cmd=0x%02x, buf=%p, count=%zu", __func__,
136
52.4k
            (void *)d, cmd, (const void *)buf, count);
137
52.4k
        fido_log_xxd(buf, count);
138
52.4k
139
52.4k
        if (d->transport.tx != NULL)
140
52.4k
                return (d->transport.tx(d, cmd, buf, count));
141
52.4k
        if (d->io_handle == NULL || d->io.write == NULL || count > UINT16_MAX) {
142
23
                fido_log_debug("%s: invalid argument", __func__);
143
23
                return (-1);
144
23
        }
145
52.4k
146
52.4k
        return (count == 0 ? tx_empty(d, cmd) : tx(d, cmd, buf, count));
147
52.4k
}
148
149
static int
150
rx_frame(fido_dev_t *d, struct frame *fp, int ms)
151
100k
{
152
100k
        int n;
153
100k
154
100k
        memset(fp, 0, sizeof(*fp));
155
100k
156
100k
        if (d->rx_len > sizeof(*fp) || (n = d->io.read(d->io_handle,
157
100k
            (unsigned char *)fp, d->rx_len, ms)) < 0 || (size_t)n != d->rx_len)
158
20.1k
                return (-1);
159
80.5k
160
80.5k
        return (0);
161
80.5k
}
162
163
static int
164
rx_preamble(fido_dev_t *d, uint8_t cmd, struct frame *fp, int ms)
165
50.4k
{
166
52.5k
        do {
167
52.5k
                if (rx_frame(d, fp, ms) < 0)
168
19.7k
                        return (-1);
169
32.7k
#ifdef FIDO_FUZZ
170
32.7k
                fp->cid = d->cid;
171
32.7k
#endif
172
32.7k
        } while (fp->cid == d->cid &&
173
32.7k
            fp->body.init.cmd == (CTAP_FRAME_INIT | CTAP_KEEPALIVE));
174
50.4k
175
50.4k
        if (d->rx_len > sizeof(*fp))
176
0
                return (-1);
177
30.6k
178
30.6k
        fido_log_debug("%s: initiation frame at %p", __func__, (void *)fp);
179
30.6k
        fido_log_xxd(fp, d->rx_len);
180
30.6k
181
30.6k
#ifdef FIDO_FUZZ
182
30.6k
        fp->body.init.cmd = (CTAP_FRAME_INIT | cmd);
183
30.6k
#endif
184
30.6k
185
30.6k
        if (fp->cid != d->cid || fp->body.init.cmd != (CTAP_FRAME_INIT | cmd)) {
186
0
                fido_log_debug("%s: cid (0x%x, 0x%x), cmd (0x%02x, 0x%02x)",
187
0
                    __func__, fp->cid, d->cid, fp->body.init.cmd, cmd);
188
0
                return (-1);
189
0
        }
190
30.6k
191
30.6k
        return (0);
192
30.6k
}
193
194
static int
195
rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int ms)
196
50.4k
{
197
50.4k
        struct frame f;
198
50.4k
        size_t r, payload_len, init_data_len, cont_data_len;
199
50.4k
200
50.4k
        if (d->rx_len <= CTAP_INIT_HEADER_LEN ||
201
50.4k
            d->rx_len <= CTAP_CONT_HEADER_LEN)
202
50.4k
                return (-1);
203
50.4k
204
50.4k
        init_data_len = d->rx_len - CTAP_INIT_HEADER_LEN;
205
50.4k
        cont_data_len = d->rx_len - CTAP_CONT_HEADER_LEN;
206
50.4k
207
50.4k
        if (init_data_len > sizeof(f.body.init.data) ||
208
50.4k
            cont_data_len > sizeof(f.body.cont.data))
209
0
                return (-1);
210
50.4k
211
50.4k
        if (rx_preamble(d, cmd, &f, ms) < 0) {
212
19.7k
                fido_log_debug("%s: rx_preamble", __func__);
213
19.7k
                return (-1);
214
19.7k
        }
215
30.6k
216
30.6k
        payload_len = (size_t)((f.body.init.bcnth << 8) | f.body.init.bcntl);
217
30.6k
        fido_log_debug("%s: payload_len=%zu", __func__, payload_len);
218
30.6k
219
30.6k
        if (count < payload_len) {
220
1.61k
                fido_log_debug("%s: count < payload_len", __func__);
221
1.61k
                return (-1);
222
1.61k
        }
223
29.0k
224
29.0k
        if (payload_len < init_data_len) {
225
14.9k
                memcpy(buf, f.body.init.data, payload_len);
226
14.9k
                return ((int)payload_len);
227
14.9k
        }
228
14.0k
229
14.0k
        memcpy(buf, f.body.init.data, init_data_len);
230
14.0k
        r = init_data_len;
231
14.0k
232
61.8k
        for (int seq = 0; r < payload_len; seq++) {
233
48.1k
                if (rx_frame(d, &f, ms) < 0) {
234
390
                        fido_log_debug("%s: rx_frame", __func__);
235
390
                        return (-1);
236
390
                }
237
47.7k
238
47.7k
                fido_log_debug("%s: continuation frame at %p", __func__,
239
47.7k
                    (void *)&f);
240
47.7k
                fido_log_xxd(&f, d->rx_len);
241
47.7k
242
47.7k
#ifdef FIDO_FUZZ
243
47.7k
                f.cid = d->cid;
244
47.7k
                f.body.cont.seq = (uint8_t)seq;
245
47.7k
#endif
246
47.7k
247
47.7k
                if (f.cid != d->cid || f.body.cont.seq != seq) {
248
7
                        fido_log_debug("%s: cid (0x%x, 0x%x), seq (%d, %d)",
249
7
                            __func__, f.cid, d->cid, f.body.cont.seq, seq);
250
7
                        return (-1);
251
7
                }
252
47.7k
253
47.7k
                if (payload_len - r > cont_data_len) {
254
34.5k
                        memcpy(buf + r, f.body.cont.data, cont_data_len);
255
34.5k
                        r += cont_data_len;
256
34.5k
                } else {
257
13.2k
                        memcpy(buf + r, f.body.cont.data, payload_len - r);
258
13.2k
                        r += payload_len - r; /* break */
259
13.2k
                }
260
47.7k
        }
261
14.0k
262
14.0k
        return ((int)r);
263
14.0k
}
264
265
int
266
fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int ms)
267
50.4k
{
268
50.4k
        int n;
269
50.4k
270
50.4k
        fido_log_debug("%s: d=%p, cmd=0x%02x, buf=%p, count=%zu, ms=%d",
271
50.4k
            __func__, (void *)d, cmd, (const void *)buf, count, ms);
272
50.4k
273
50.4k
        if (d->transport.rx != NULL)
274
50.4k
                return (d->transport.rx(d, cmd, buf, count, ms));
275
50.4k
        if (d->io_handle == NULL || d->io.read == NULL || count > UINT16_MAX) {
276
0
                fido_log_debug("%s: invalid argument", __func__);
277
0
                return (-1);
278
0
        }
279
50.4k
        if ((n = rx(d, cmd, buf, count, ms)) >= 0) {
280
28.6k
                fido_log_debug("%s: buf=%p, len=%d", __func__, (void *)buf, n);
281
28.6k
                fido_log_xxd(buf, (size_t)n);
282
28.6k
        }
283
50.4k
284
50.4k
        return (n);
285
50.4k
}
286
287
int
288
fido_rx_cbor_status(fido_dev_t *d, int ms)
289
473
{
290
473
        unsigned char   reply[FIDO_MAXMSG];
291
473
        int             reply_len;
292
473
293
473
        if ((reply_len = fido_rx(d, CTAP_CMD_CBOR, &reply, sizeof(reply),
294
473
            ms)) < 0 || (size_t)reply_len < 1) {
295
307
                fido_log_debug("%s: fido_rx", __func__);
296
307
                return (FIDO_ERR_RX);
297
307
        }
298
166
299
166
        return (reply[0]);
300
166
}