Coverage Report

Created: 2020-09-01 07:05

/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
107k
#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.50k
{
39
2.50k
        struct frame    *fp;
40
2.50k
        unsigned char    pkt[sizeof(*fp) + 1];
41
2.50k
        const size_t     len = d->tx_len + 1;
42
2.50k
        int              n;
43
2.50k
44
2.50k
        memset(&pkt, 0, sizeof(pkt));
45
2.50k
        fp = (struct frame *)(pkt + 1);
46
2.50k
        fp->cid = d->cid;
47
2.50k
        fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
48
2.50k
49
2.50k
        if (len > sizeof(pkt) || (n = d->io.write(d->io_handle, pkt,
50
2.50k
            len)) < 0 || (size_t)n != len)
51
17
                return (-1);
52
2.48k
53
2.48k
        return (0);
54
2.48k
}
55
56
static size_t
57
tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count)
58
47.7k
{
59
47.7k
        struct frame    *fp;
60
47.7k
        unsigned char    pkt[sizeof(*fp) + 1];
61
47.7k
        const size_t     len = d->tx_len + 1;
62
47.7k
        int              n;
63
47.7k
64
47.7k
        if (d->tx_len - CTAP_INIT_HEADER_LEN > sizeof(fp->body.init.data))
65
0
                return (0);
66
47.7k
67
47.7k
        memset(&pkt, 0, sizeof(pkt));
68
47.7k
        fp = (struct frame *)(pkt + 1);
69
47.7k
        fp->cid = d->cid;
70
47.7k
        fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
71
47.7k
        fp->body.init.bcnth = (count >> 8) & 0xff;
72
47.7k
        fp->body.init.bcntl = count & 0xff;
73
47.7k
        count = MIN(count, d->tx_len - CTAP_INIT_HEADER_LEN);
74
47.7k
        memcpy(&fp->body.init.data, buf, count);
75
47.7k
76
47.7k
        if (len > sizeof(pkt) || (n = d->io.write(d->io_handle, pkt,
77
47.7k
            len)) < 0 || (size_t)n != len)
78
130
                return (0);
79
47.6k
80
47.6k
        return (count);
81
47.6k
}
82
83
static size_t
84
tx_frame(fido_dev_t *d, uint8_t seq, const void *buf, size_t count)
85
60.2k
{
86
60.2k
        struct frame    *fp;
87
60.2k
        unsigned char    pkt[sizeof(*fp) + 1];
88
60.2k
        const size_t     len = d->tx_len + 1;
89
60.2k
        int              n;
90
60.2k
91
60.2k
        if (d->tx_len - CTAP_CONT_HEADER_LEN > sizeof(fp->body.cont.data))
92
0
                return (0);
93
60.2k
94
60.2k
        memset(&pkt, 0, sizeof(pkt));
95
60.2k
        fp = (struct frame *)(pkt + 1);
96
60.2k
        fp->cid = d->cid;
97
60.2k
        fp->body.cont.seq = seq;
98
60.2k
        count = MIN(count, d->tx_len - CTAP_CONT_HEADER_LEN);
99
60.2k
        memcpy(&fp->body.cont.data, buf, count);
100
60.2k
101
60.2k
        if (len > sizeof(pkt) || (n = d->io.write(d->io_handle, pkt,
102
60.2k
            len)) < 0 || (size_t)n != len)
103
113
                return (0);
104
60.1k
105
60.1k
        return (count);
106
60.1k
}
107
108
static int
109
tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count)
110
47.7k
{
111
47.7k
        size_t n, sent;
112
47.7k
113
47.7k
        if ((sent = tx_preamble(d, cmd, buf, count)) == 0) {
114
130
                fido_log_debug("%s: tx_preamble", __func__);
115
130
                return (-1);
116
130
        }
117
47.6k
118
107k
        for (uint8_t seq = 0; sent < count; sent += n) {
119
60.2k
                if (seq & 0x80) {
120
63
                        fido_log_debug("%s: seq & 0x80", __func__);
121
63
                        return (-1);
122
63
                }
123
60.2k
                if ((n = tx_frame(d, seq++, buf + sent, count - sent)) == 0) {
124
113
                        fido_log_debug("%s: tx_frame", __func__);
125
113
                        return (-1);
126
113
                }
127
60.2k
        }
128
47.6k
129
47.6k
        return (0);
130
47.6k
}
131
132
int
133
fido_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count)
134
50.2k
{
135
50.2k
        fido_log_debug("%s: d=%p, cmd=0x%02x, buf=%p, count=%zu", __func__,
136
50.2k
            (void *)d, cmd, (const void *)buf, count);
137
50.2k
        fido_log_xxd(buf, count);
138
50.2k
139
50.2k
        if (d->transport.tx != NULL)
140
50.2k
                return (d->transport.tx(d, cmd, buf, count));
141
50.2k
        if (d->io_handle == NULL || d->io.write == NULL || count > UINT16_MAX) {
142
22
                fido_log_debug("%s: invalid argument", __func__);
143
22
                return (-1);
144
22
        }
145
50.2k
146
50.2k
        return (count == 0 ? tx_empty(d, cmd) : tx(d, cmd, buf, count));
147
50.2k
}
148
149
static int
150
rx_frame(fido_dev_t *d, struct frame *fp, int ms)
151
97.1k
{
152
97.1k
        int n;
153
97.1k
154
97.1k
        memset(fp, 0, sizeof(*fp));
155
97.1k
156
97.1k
        if (d->rx_len > sizeof(*fp) || (n = d->io.read(d->io_handle,
157
97.1k
            (unsigned char *)fp, d->rx_len, ms)) < 0 || (size_t)n != d->rx_len)
158
19.3k
                return (-1);
159
77.7k
160
77.7k
        return (0);
161
77.7k
}
162
163
static int
164
rx_preamble(fido_dev_t *d, uint8_t cmd, struct frame *fp, int ms)
165
48.4k
{
166
50.4k
        do {
167
50.4k
                if (rx_frame(d, fp, ms) < 0)
168
18.9k
                        return (-1);
169
31.4k
#ifdef FIDO_FUZZ
170
31.4k
                fp->cid = d->cid;
171
31.4k
#endif
172
31.4k
        } while (fp->cid == d->cid &&
173
31.4k
            fp->body.init.cmd == (CTAP_FRAME_INIT | CTAP_KEEPALIVE));
174
48.4k
175
48.4k
        if (d->rx_len > sizeof(*fp))
176
0
                return (-1);
177
29.4k
178
29.4k
        fido_log_debug("%s: initiation frame at %p", __func__, (void *)fp);
179
29.4k
        fido_log_xxd(fp, d->rx_len);
180
29.4k
181
29.4k
#ifdef FIDO_FUZZ
182
29.4k
        fp->body.init.cmd = (CTAP_FRAME_INIT | cmd);
183
29.4k
#endif
184
29.4k
185
29.4k
        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
29.4k
191
29.4k
        return (0);
192
29.4k
}
193
194
static int
195
rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int ms)
196
48.4k
{
197
48.4k
        struct frame f;
198
48.4k
        size_t r, payload_len, init_data_len, cont_data_len;
199
48.4k
200
48.4k
        if (d->rx_len <= CTAP_INIT_HEADER_LEN ||
201
48.4k
            d->rx_len <= CTAP_CONT_HEADER_LEN)
202
48.4k
                return (-1);
203
48.4k
204
48.4k
        init_data_len = d->rx_len - CTAP_INIT_HEADER_LEN;
205
48.4k
        cont_data_len = d->rx_len - CTAP_CONT_HEADER_LEN;
206
48.4k
207
48.4k
        if (init_data_len > sizeof(f.body.init.data) ||
208
48.4k
            cont_data_len > sizeof(f.body.cont.data))
209
0
                return (-1);
210
48.4k
211
48.4k
        if (rx_preamble(d, cmd, &f, ms) < 0) {
212
18.9k
                fido_log_debug("%s: rx_preamble", __func__);
213
18.9k
                return (-1);
214
18.9k
        }
215
29.4k
216
29.4k
        payload_len = (size_t)((f.body.init.bcnth << 8) | f.body.init.bcntl);
217
29.4k
        fido_log_debug("%s: payload_len=%zu", __func__, payload_len);
218
29.4k
219
29.4k
        if (count < payload_len) {
220
1.69k
                fido_log_debug("%s: count < payload_len", __func__);
221
1.69k
                return (-1);
222
1.69k
        }
223
27.7k
224
27.7k
        if (payload_len < init_data_len) {
225
14.4k
                memcpy(buf, f.body.init.data, payload_len);
226
14.4k
                return ((int)payload_len);
227
14.4k
        }
228
13.3k
229
13.3k
        memcpy(buf, f.body.init.data, init_data_len);
230
13.3k
        r = init_data_len;
231
13.3k
232
59.6k
        for (int seq = 0; r < payload_len; seq++) {
233
46.7k
                if (rx_frame(d, &f, ms) < 0) {
234
393
                        fido_log_debug("%s: rx_frame", __func__);
235
393
                        return (-1);
236
393
                }
237
46.3k
238
46.3k
                fido_log_debug("%s: continuation frame at %p", __func__,
239
46.3k
                    (void *)&f);
240
46.3k
                fido_log_xxd(&f, d->rx_len);
241
46.3k
242
46.3k
#ifdef FIDO_FUZZ
243
46.3k
                f.cid = d->cid;
244
46.3k
                f.body.cont.seq = (uint8_t)seq;
245
46.3k
#endif
246
46.3k
247
46.3k
                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
46.3k
253
46.3k
                if (payload_len - r > cont_data_len) {
254
33.7k
                        memcpy(buf + r, f.body.cont.data, cont_data_len);
255
33.7k
                        r += cont_data_len;
256
33.7k
                } else {
257
12.5k
                        memcpy(buf + r, f.body.cont.data, payload_len - r);
258
12.5k
                        r += payload_len - r; /* break */
259
12.5k
                }
260
46.3k
        }
261
13.3k
262
13.3k
        return ((int)r);
263
13.3k
}
264
265
int
266
fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int ms)
267
48.4k
{
268
48.4k
        int n;
269
48.4k
270
48.4k
        fido_log_debug("%s: d=%p, cmd=0x%02x, buf=%p, count=%zu, ms=%d",
271
48.4k
            __func__, (void *)d, cmd, (const void *)buf, count, ms);
272
48.4k
273
48.4k
        if (d->transport.rx != NULL)
274
48.4k
                return (d->transport.rx(d, cmd, buf, count, ms));
275
48.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
48.4k
        if ((n = rx(d, cmd, buf, count, ms)) >= 0) {
280
27.3k
                fido_log_debug("%s: buf=%p, len=%d", __func__, (void *)buf, n);
281
27.3k
                fido_log_xxd(buf, (size_t)n);
282
27.3k
        }
283
48.4k
284
48.4k
        return (n);
285
48.4k
}
286
287
int
288
fido_rx_cbor_status(fido_dev_t *d, int ms)
289
414
{
290
414
        unsigned char   reply[FIDO_MAXMSG];
291
414
        int             reply_len;
292
414
293
414
        if ((reply_len = fido_rx(d, CTAP_CMD_CBOR, &reply, sizeof(reply),
294
414
            ms)) < 0 || (size_t)reply_len < 1) {
295
261
                fido_log_debug("%s: fido_rx", __func__);
296
261
                return (FIDO_ERR_RX);
297
261
        }
298
153
299
153
        return (reply[0]);
300
153
}