1 /* Copyright 2014 The BoringSSL Authors
2 *
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14
15 #include "packeted_bio.h"
16
17 #include <assert.h>
18 #include <inttypes.h>
19 #include <limits.h>
20 #include <stdio.h>
21 #include <string.h>
22
23 #include <functional>
24 #include <utility>
25 #include <vector>
26
27 #include <openssl/mem.h>
28
29 #include "../../crypto/internal.h"
30
31
32 namespace {
33
34 extern const BIO_METHOD g_packeted_bio_method;
35
36 constexpr uint8_t kOpcodePacket = 'P';
37 constexpr uint8_t kOpcodeTimeout = 'T';
38 constexpr uint8_t kOpcodeTimeoutAck = 't';
39 constexpr uint8_t kOpcodeMTU = 'M';
40 constexpr uint8_t kOpcodeExpectNextTimeout = 'E';
41
42 struct PacketedBio {
PacketedBio__anon982380be0111::PacketedBio43 PacketedBio(timeval *clock_arg,
44 std::function<bool(timeval *)> get_timeout_arg,
45 std::function<bool(uint32_t)> set_mtu_arg)
46 : clock(clock_arg),
47 get_timeout(std::move(get_timeout_arg)),
48 set_mtu(std::move(set_mtu_arg)) {
49 OPENSSL_memset(&timeout, 0, sizeof(timeout));
50 }
51
HasTimeout__anon982380be0111::PacketedBio52 bool HasTimeout() const {
53 return timeout.tv_sec != 0 || timeout.tv_usec != 0;
54 }
55
56 timeval timeout;
57 timeval *clock;
58 std::function<bool(timeval *)> get_timeout;
59 std::function<bool(uint32_t)> set_mtu;
60 };
61
GetData(BIO * bio)62 PacketedBio *GetData(BIO *bio) {
63 if (bio->method != &g_packeted_bio_method) {
64 return NULL;
65 }
66 return (PacketedBio *)bio->ptr;
67 }
68
69 // ReadAll reads |len| bytes from |bio| into |out|. It returns 1 on success and
70 // 0 or -1 on error.
ReadAll(BIO * bio,uint8_t * out,size_t len)71 static int ReadAll(BIO *bio, uint8_t *out, size_t len) {
72 while (len > 0) {
73 int chunk_len = INT_MAX;
74 if (len <= INT_MAX) {
75 chunk_len = (int)len;
76 }
77 int ret = BIO_read(bio, out, chunk_len);
78 if (ret <= 0) {
79 return ret;
80 }
81 out += ret;
82 len -= ret;
83 }
84 return 1;
85 }
86
PacketedWrite(BIO * bio,const char * in,int inl)87 static int PacketedWrite(BIO *bio, const char *in, int inl) {
88 if (bio->next_bio == NULL) {
89 return 0;
90 }
91
92 BIO_clear_retry_flags(bio);
93
94 // Write the header.
95 uint8_t header[5];
96 header[0] = kOpcodePacket;
97 header[1] = (inl >> 24) & 0xff;
98 header[2] = (inl >> 16) & 0xff;
99 header[3] = (inl >> 8) & 0xff;
100 header[4] = inl & 0xff;
101 int ret = BIO_write(bio->next_bio, header, sizeof(header));
102 if (ret <= 0) {
103 BIO_copy_next_retry(bio);
104 return ret;
105 }
106
107 // Write the buffer.
108 ret = BIO_write(bio->next_bio, in, inl);
109 if (ret < 0 || (inl > 0 && ret == 0)) {
110 BIO_copy_next_retry(bio);
111 return ret;
112 }
113 assert(ret == inl);
114 return ret;
115 }
116
PacketedRead(BIO * bio,char * out,int outl)117 static int PacketedRead(BIO *bio, char *out, int outl) {
118 PacketedBio *data = GetData(bio);
119 if (bio->next_bio == NULL) {
120 return 0;
121 }
122
123 BIO_clear_retry_flags(bio);
124
125 for (;;) {
126 // Read the opcode.
127 uint8_t opcode;
128 int ret = ReadAll(bio->next_bio, &opcode, sizeof(opcode));
129 if (ret <= 0) {
130 BIO_copy_next_retry(bio);
131 return ret;
132 }
133
134 if (opcode == kOpcodeTimeout) {
135 // The caller is required to advance any pending timeouts before
136 // continuing.
137 if (data->HasTimeout()) {
138 fprintf(stderr, "Unprocessed timeout!\n");
139 return -1;
140 }
141
142 // Process the timeout.
143 uint8_t buf[8];
144 ret = ReadAll(bio->next_bio, buf, sizeof(buf));
145 if (ret <= 0) {
146 BIO_copy_next_retry(bio);
147 return ret;
148 }
149 uint64_t timeout = CRYPTO_load_u64_be(buf);
150 timeout /= 1000; // Convert nanoseconds to microseconds.
151
152 data->timeout.tv_usec = timeout % 1000000;
153 data->timeout.tv_sec = timeout / 1000000;
154
155 // Send an ACK to the peer.
156 ret = BIO_write(bio->next_bio, &kOpcodeTimeoutAck, 1);
157 if (ret <= 0) {
158 return ret;
159 }
160 assert(ret == 1);
161
162 // Signal to the caller to retry the read, after advancing the clock.
163 BIO_set_retry_read(bio);
164 return -1;
165 }
166
167 if (opcode == kOpcodeMTU) {
168 uint8_t buf[4];
169 ret = ReadAll(bio->next_bio, buf, sizeof(buf));
170 if (ret <= 0) {
171 BIO_copy_next_retry(bio);
172 return ret;
173 }
174 uint32_t mtu = CRYPTO_load_u32_be(buf);
175 if (!data->set_mtu(mtu)) {
176 fprintf(stderr, "Error setting MTU\n");
177 return -1;
178 }
179 // Continue reading.
180 continue;
181 }
182
183 if (opcode == kOpcodeExpectNextTimeout) {
184 uint8_t buf[8];
185 ret = ReadAll(bio->next_bio, buf, sizeof(buf));
186 if (ret <= 0) {
187 BIO_copy_next_retry(bio);
188 return ret;
189 }
190 uint64_t expected = CRYPTO_load_u64_be(buf);
191 timeval timeout;
192 bool has_timeout = data->get_timeout(&timeout);
193 if (expected == UINT64_MAX) {
194 if (has_timeout) {
195 fprintf(stderr,
196 "Expected no timeout, but got %" PRIu64 ".%06" PRIu64 "s.\n",
197 static_cast<uint64_t>(timeout.tv_sec),
198 static_cast<uint64_t>(timeout.tv_usec));
199 return -1;
200 }
201 } else {
202 expected /= 1000; // Convert nanoseconds to microseconds.
203 uint64_t expected_sec = expected / 1000000;
204 uint64_t expected_usec = expected % 1000000;
205 if (!has_timeout) {
206 fprintf(stderr,
207 "Expected timeout of %" PRIu64 ".%06" PRIu64
208 "s, but got none.\n",
209 expected_sec, expected_usec);
210 return -1;
211 }
212 if (static_cast<uint64_t>(timeout.tv_sec) != expected_sec ||
213 static_cast<uint64_t>(timeout.tv_usec) != expected_usec) {
214 fprintf(stderr,
215 "Expected timeout of %" PRIu64 ".%06" PRIu64
216 "s, but got %" PRIu64 ".%06" PRIu64 "s.\n",
217 expected_sec, expected_usec,
218 static_cast<uint64_t>(timeout.tv_sec),
219 static_cast<uint64_t>(timeout.tv_usec));
220 return -1;
221 }
222 }
223 // Continue reading.
224 continue;
225 }
226
227 if (opcode != kOpcodePacket) {
228 fprintf(stderr, "Unknown opcode, %u\n", opcode);
229 return -1;
230 }
231
232 // Read the length prefix.
233 uint8_t len_bytes[4];
234 ret = ReadAll(bio->next_bio, len_bytes, sizeof(len_bytes));
235 if (ret <= 0) {
236 BIO_copy_next_retry(bio);
237 return ret;
238 }
239
240 std::vector<uint8_t> buf(CRYPTO_load_u32_be(len_bytes), 0);
241 ret = ReadAll(bio->next_bio, buf.data(), buf.size());
242 if (ret <= 0) {
243 fprintf(stderr, "Packeted BIO was truncated\n");
244 return -1;
245 }
246
247 if (static_cast<size_t>(outl) > buf.size()) {
248 outl = static_cast<int>(buf.size());
249 }
250 OPENSSL_memcpy(out, buf.data(), outl);
251 return outl;
252 }
253 }
254
PacketedCtrl(BIO * bio,int cmd,long num,void * ptr)255 static long PacketedCtrl(BIO *bio, int cmd, long num, void *ptr) {
256 if (bio->next_bio == NULL) {
257 return 0;
258 }
259
260 BIO_clear_retry_flags(bio);
261 long ret = BIO_ctrl(bio->next_bio, cmd, num, ptr);
262 BIO_copy_next_retry(bio);
263 return ret;
264 }
265
PacketedNew(BIO * bio)266 static int PacketedNew(BIO *bio) {
267 bio->init = 1;
268 return 1;
269 }
270
PacketedFree(BIO * bio)271 static int PacketedFree(BIO *bio) {
272 if (bio == NULL) {
273 return 0;
274 }
275
276 delete GetData(bio);
277 bio->init = 0;
278 return 1;
279 }
280
PacketedCallbackCtrl(BIO * bio,int cmd,bio_info_cb fp)281 static long PacketedCallbackCtrl(BIO *bio, int cmd, bio_info_cb fp) {
282 if (bio->next_bio == NULL) {
283 return 0;
284 }
285 return BIO_callback_ctrl(bio->next_bio, cmd, fp);
286 }
287
288 const BIO_METHOD g_packeted_bio_method = {
289 BIO_TYPE_FILTER,
290 "packeted bio",
291 PacketedWrite,
292 PacketedRead,
293 NULL /* puts */,
294 NULL /* gets */,
295 PacketedCtrl,
296 PacketedNew,
297 PacketedFree,
298 PacketedCallbackCtrl,
299 };
300
301 } // namespace
302
PacketedBioCreate(timeval * clock,std::function<bool (timeval *)> get_timeout,std::function<bool (uint32_t)> set_mtu)303 bssl::UniquePtr<BIO> PacketedBioCreate(
304 timeval *clock, std::function<bool(timeval *)> get_timeout,
305 std::function<bool(uint32_t)> set_mtu) {
306 bssl::UniquePtr<BIO> bio(BIO_new(&g_packeted_bio_method));
307 if (!bio) {
308 return nullptr;
309 }
310 bio->ptr = new PacketedBio(clock, std::move(get_timeout), std::move(set_mtu));
311 return bio;
312 }
313
PacketedBioAdvanceClock(BIO * bio)314 bool PacketedBioAdvanceClock(BIO *bio) {
315 PacketedBio *data = GetData(bio);
316 if (data == nullptr) {
317 return false;
318 }
319
320 if (!data->HasTimeout()) {
321 return false;
322 }
323
324 data->clock->tv_usec += data->timeout.tv_usec;
325 data->clock->tv_sec += data->clock->tv_usec / 1000000;
326 data->clock->tv_usec %= 1000000;
327 data->clock->tv_sec += data->timeout.tv_sec;
328 OPENSSL_memset(&data->timeout, 0, sizeof(data->timeout));
329 return true;
330 }
331
PacketedBioGetClock(BIO * bio)332 timeval *PacketedBioGetClock(BIO *bio) {
333 PacketedBio *data = GetData(bio);
334 if (data == nullptr) {
335 return nullptr;
336 }
337 return data->clock;
338 }
339