lh | 9ed821d | 2023-04-07 01:36:19 -0700 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved. |
| 3 | * |
| 4 | * Licensed under the OpenSSL licenses, (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * https://www.openssl.org/source/license.html |
| 8 | * or in the file LICENSE in the source distribution. |
| 9 | */ |
| 10 | |
| 11 | #include <string.h> |
| 12 | #include <openssl/ssl.h> |
| 13 | #include <openssl/bio.h> |
| 14 | #include <openssl/err.h> |
| 15 | |
| 16 | #include "../ssl/packet_local.h" |
| 17 | |
| 18 | #include "ssltestlib.h" |
| 19 | #include "testutil.h" |
| 20 | |
| 21 | /* Should we fragment records or not? 0 = no, !0 = yes*/ |
| 22 | static int fragment = 0; |
| 23 | |
| 24 | static char *cert = NULL; |
| 25 | static char *privkey = NULL; |
| 26 | |
| 27 | static int async_new(BIO *bi); |
| 28 | static int async_free(BIO *a); |
| 29 | static int async_read(BIO *b, char *out, int outl); |
| 30 | static int async_write(BIO *b, const char *in, int inl); |
| 31 | static long async_ctrl(BIO *b, int cmd, long num, void *ptr); |
| 32 | static int async_gets(BIO *bp, char *buf, int size); |
| 33 | static int async_puts(BIO *bp, const char *str); |
| 34 | |
| 35 | /* Choose a sufficiently large type likely to be unused for this custom BIO */ |
| 36 | # define BIO_TYPE_ASYNC_FILTER (0x80 | BIO_TYPE_FILTER) |
| 37 | |
| 38 | static BIO_METHOD *methods_async = NULL; |
| 39 | |
| 40 | struct async_ctrs { |
| 41 | unsigned int rctr; |
| 42 | unsigned int wctr; |
| 43 | }; |
| 44 | |
| 45 | static const BIO_METHOD *bio_f_async_filter(void) |
| 46 | { |
| 47 | if (methods_async == NULL) { |
| 48 | methods_async = BIO_meth_new(BIO_TYPE_ASYNC_FILTER, "Async filter"); |
| 49 | if ( methods_async == NULL |
| 50 | || !BIO_meth_set_write(methods_async, async_write) |
| 51 | || !BIO_meth_set_read(methods_async, async_read) |
| 52 | || !BIO_meth_set_puts(methods_async, async_puts) |
| 53 | || !BIO_meth_set_gets(methods_async, async_gets) |
| 54 | || !BIO_meth_set_ctrl(methods_async, async_ctrl) |
| 55 | || !BIO_meth_set_create(methods_async, async_new) |
| 56 | || !BIO_meth_set_destroy(methods_async, async_free)) |
| 57 | return NULL; |
| 58 | } |
| 59 | return methods_async; |
| 60 | } |
| 61 | |
| 62 | static int async_new(BIO *bio) |
| 63 | { |
| 64 | struct async_ctrs *ctrs; |
| 65 | |
| 66 | ctrs = OPENSSL_zalloc(sizeof(struct async_ctrs)); |
| 67 | if (ctrs == NULL) |
| 68 | return 0; |
| 69 | |
| 70 | BIO_set_data(bio, ctrs); |
| 71 | BIO_set_init(bio, 1); |
| 72 | return 1; |
| 73 | } |
| 74 | |
| 75 | static int async_free(BIO *bio) |
| 76 | { |
| 77 | struct async_ctrs *ctrs; |
| 78 | |
| 79 | if (bio == NULL) |
| 80 | return 0; |
| 81 | ctrs = BIO_get_data(bio); |
| 82 | OPENSSL_free(ctrs); |
| 83 | BIO_set_data(bio, NULL); |
| 84 | BIO_set_init(bio, 0); |
| 85 | |
| 86 | return 1; |
| 87 | } |
| 88 | |
| 89 | static int async_read(BIO *bio, char *out, int outl) |
| 90 | { |
| 91 | struct async_ctrs *ctrs; |
| 92 | int ret = 0; |
| 93 | BIO *next = BIO_next(bio); |
| 94 | |
| 95 | if (outl <= 0) |
| 96 | return 0; |
| 97 | if (next == NULL) |
| 98 | return 0; |
| 99 | |
| 100 | ctrs = BIO_get_data(bio); |
| 101 | |
| 102 | BIO_clear_retry_flags(bio); |
| 103 | |
| 104 | if (ctrs->rctr > 0) { |
| 105 | ret = BIO_read(next, out, 1); |
| 106 | if (ret <= 0 && BIO_should_read(next)) |
| 107 | BIO_set_retry_read(bio); |
| 108 | ctrs->rctr = 0; |
| 109 | } else { |
| 110 | ctrs->rctr++; |
| 111 | BIO_set_retry_read(bio); |
| 112 | } |
| 113 | |
| 114 | return ret; |
| 115 | } |
| 116 | |
| 117 | #define MIN_RECORD_LEN 6 |
| 118 | |
| 119 | #define CONTENTTYPEPOS 0 |
| 120 | #define VERSIONHIPOS 1 |
| 121 | #define VERSIONLOPOS 2 |
| 122 | #define DATAPOS 5 |
| 123 | |
| 124 | static int async_write(BIO *bio, const char *in, int inl) |
| 125 | { |
| 126 | struct async_ctrs *ctrs; |
| 127 | int ret = 0; |
| 128 | size_t written = 0; |
| 129 | BIO *next = BIO_next(bio); |
| 130 | |
| 131 | if (inl <= 0) |
| 132 | return 0; |
| 133 | if (next == NULL) |
| 134 | return 0; |
| 135 | |
| 136 | ctrs = BIO_get_data(bio); |
| 137 | |
| 138 | BIO_clear_retry_flags(bio); |
| 139 | |
| 140 | if (ctrs->wctr > 0) { |
| 141 | ctrs->wctr = 0; |
| 142 | if (fragment) { |
| 143 | PACKET pkt; |
| 144 | |
| 145 | if (!PACKET_buf_init(&pkt, (const unsigned char *)in, inl)) |
| 146 | return -1; |
| 147 | |
| 148 | while (PACKET_remaining(&pkt) > 0) { |
| 149 | PACKET payload, wholebody, sessionid, extensions; |
| 150 | unsigned int contenttype, versionhi, versionlo, data; |
| 151 | unsigned int msgtype = 0, negversion = 0; |
| 152 | |
| 153 | if (!PACKET_get_1(&pkt, &contenttype) |
| 154 | || !PACKET_get_1(&pkt, &versionhi) |
| 155 | || !PACKET_get_1(&pkt, &versionlo) |
| 156 | || !PACKET_get_length_prefixed_2(&pkt, &payload)) |
| 157 | return -1; |
| 158 | |
| 159 | /* Pretend we wrote out the record header */ |
| 160 | written += SSL3_RT_HEADER_LENGTH; |
| 161 | |
| 162 | wholebody = payload; |
| 163 | if (contenttype == SSL3_RT_HANDSHAKE |
| 164 | && !PACKET_get_1(&wholebody, &msgtype)) |
| 165 | return -1; |
| 166 | |
| 167 | if (msgtype == SSL3_MT_SERVER_HELLO) { |
| 168 | if (!PACKET_forward(&wholebody, |
| 169 | SSL3_HM_HEADER_LENGTH - 1) |
| 170 | || !PACKET_get_net_2(&wholebody, &negversion) |
| 171 | /* Skip random (32 bytes) */ |
| 172 | || !PACKET_forward(&wholebody, 32) |
| 173 | /* Skip session id */ |
| 174 | || !PACKET_get_length_prefixed_1(&wholebody, |
| 175 | &sessionid) |
| 176 | /* |
| 177 | * Skip ciphersuite (2 bytes) and compression |
| 178 | * method (1 byte) |
| 179 | */ |
| 180 | || !PACKET_forward(&wholebody, 2 + 1) |
| 181 | || !PACKET_get_length_prefixed_2(&wholebody, |
| 182 | &extensions)) |
| 183 | return -1; |
| 184 | |
| 185 | /* |
| 186 | * Find the negotiated version in supported_versions |
| 187 | * extension, if present. |
| 188 | */ |
| 189 | while (PACKET_remaining(&extensions)) { |
| 190 | unsigned int type; |
| 191 | PACKET extbody; |
| 192 | |
| 193 | if (!PACKET_get_net_2(&extensions, &type) |
| 194 | || !PACKET_get_length_prefixed_2(&extensions, |
| 195 | &extbody)) |
| 196 | return -1; |
| 197 | |
| 198 | if (type == TLSEXT_TYPE_supported_versions |
| 199 | && (!PACKET_get_net_2(&extbody, &negversion) |
| 200 | || PACKET_remaining(&extbody) != 0)) |
| 201 | return -1; |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | while (PACKET_get_1(&payload, &data)) { |
| 206 | /* Create a new one byte long record for each byte in the |
| 207 | * record in the input buffer |
| 208 | */ |
| 209 | char smallrec[MIN_RECORD_LEN] = { |
| 210 | 0, /* Content type */ |
| 211 | 0, /* Version hi */ |
| 212 | 0, /* Version lo */ |
| 213 | 0, /* Length hi */ |
| 214 | 1, /* Length lo */ |
| 215 | 0 /* Data */ |
| 216 | }; |
| 217 | |
| 218 | smallrec[CONTENTTYPEPOS] = contenttype; |
| 219 | smallrec[VERSIONHIPOS] = versionhi; |
| 220 | smallrec[VERSIONLOPOS] = versionlo; |
| 221 | smallrec[DATAPOS] = data; |
| 222 | ret = BIO_write(next, smallrec, MIN_RECORD_LEN); |
| 223 | if (ret <= 0) |
| 224 | return -1; |
| 225 | written++; |
| 226 | } |
| 227 | /* |
| 228 | * We can't fragment anything after the ServerHello (or CCS <= |
| 229 | * TLS1.2), otherwise we get a bad record MAC |
| 230 | */ |
| 231 | if (contenttype == SSL3_RT_CHANGE_CIPHER_SPEC |
| 232 | || (negversion == TLS1_3_VERSION |
| 233 | && msgtype == SSL3_MT_SERVER_HELLO)) { |
| 234 | fragment = 0; |
| 235 | break; |
| 236 | } |
| 237 | } |
| 238 | } |
| 239 | /* Write any data we have left after fragmenting */ |
| 240 | ret = 0; |
| 241 | if ((int)written < inl) { |
| 242 | ret = BIO_write(next, in + written, inl - written); |
| 243 | } |
| 244 | |
| 245 | if (ret <= 0 && BIO_should_write(next)) |
| 246 | BIO_set_retry_write(bio); |
| 247 | else |
| 248 | ret += written; |
| 249 | } else { |
| 250 | ctrs->wctr++; |
| 251 | BIO_set_retry_write(bio); |
| 252 | } |
| 253 | |
| 254 | return ret; |
| 255 | } |
| 256 | |
| 257 | static long async_ctrl(BIO *bio, int cmd, long num, void *ptr) |
| 258 | { |
| 259 | long ret; |
| 260 | BIO *next = BIO_next(bio); |
| 261 | |
| 262 | if (next == NULL) |
| 263 | return 0; |
| 264 | |
| 265 | switch (cmd) { |
| 266 | case BIO_CTRL_DUP: |
| 267 | ret = 0L; |
| 268 | break; |
| 269 | default: |
| 270 | ret = BIO_ctrl(next, cmd, num, ptr); |
| 271 | break; |
| 272 | } |
| 273 | return ret; |
| 274 | } |
| 275 | |
| 276 | static int async_gets(BIO *bio, char *buf, int size) |
| 277 | { |
| 278 | /* We don't support this - not needed anyway */ |
| 279 | return -1; |
| 280 | } |
| 281 | |
| 282 | static int async_puts(BIO *bio, const char *str) |
| 283 | { |
| 284 | return async_write(bio, str, strlen(str)); |
| 285 | } |
| 286 | |
| 287 | #define MAX_ATTEMPTS 100 |
| 288 | |
| 289 | static int test_asyncio(int test) |
| 290 | { |
| 291 | SSL_CTX *serverctx = NULL, *clientctx = NULL; |
| 292 | SSL *serverssl = NULL, *clientssl = NULL; |
| 293 | BIO *s_to_c_fbio = NULL, *c_to_s_fbio = NULL; |
| 294 | int testresult = 0, ret; |
| 295 | size_t i, j; |
| 296 | const char testdata[] = "Test data"; |
| 297 | char buf[sizeof(testdata)]; |
| 298 | |
| 299 | if (!TEST_true(create_ssl_ctx_pair(TLS_server_method(), TLS_client_method(), |
| 300 | TLS1_VERSION, TLS_MAX_VERSION, |
| 301 | &serverctx, &clientctx, cert, privkey))) |
| 302 | goto end; |
| 303 | |
| 304 | /* |
| 305 | * We do 2 test runs. The first time around we just do a normal handshake |
| 306 | * with lots of async io going on. The second time around we also break up |
| 307 | * all records so that the content is only one byte length (up until the |
| 308 | * CCS) |
| 309 | */ |
| 310 | if (test == 1) |
| 311 | fragment = 1; |
| 312 | |
| 313 | |
| 314 | s_to_c_fbio = BIO_new(bio_f_async_filter()); |
| 315 | c_to_s_fbio = BIO_new(bio_f_async_filter()); |
| 316 | if (!TEST_ptr(s_to_c_fbio) |
| 317 | || !TEST_ptr(c_to_s_fbio)) { |
| 318 | BIO_free(s_to_c_fbio); |
| 319 | BIO_free(c_to_s_fbio); |
| 320 | goto end; |
| 321 | } |
| 322 | |
| 323 | /* BIOs get freed on error */ |
| 324 | if (!TEST_true(create_ssl_objects(serverctx, clientctx, &serverssl, |
| 325 | &clientssl, s_to_c_fbio, c_to_s_fbio)) |
| 326 | || !TEST_true(create_ssl_connection(serverssl, clientssl, |
| 327 | SSL_ERROR_NONE))) |
| 328 | goto end; |
| 329 | |
| 330 | /* |
| 331 | * Send and receive some test data. Do the whole thing twice to ensure |
| 332 | * we hit at least one async event in both reading and writing |
| 333 | */ |
| 334 | for (j = 0; j < 2; j++) { |
| 335 | int len; |
| 336 | |
| 337 | /* |
| 338 | * Write some test data. It should never take more than 2 attempts |
| 339 | * (the first one might be a retryable fail). |
| 340 | */ |
| 341 | for (ret = -1, i = 0, len = 0; len != sizeof(testdata) && i < 2; |
| 342 | i++) { |
| 343 | ret = SSL_write(clientssl, testdata + len, |
| 344 | sizeof(testdata) - len); |
| 345 | if (ret > 0) { |
| 346 | len += ret; |
| 347 | } else { |
| 348 | int ssl_error = SSL_get_error(clientssl, ret); |
| 349 | |
| 350 | if (!TEST_false(ssl_error == SSL_ERROR_SYSCALL || |
| 351 | ssl_error == SSL_ERROR_SSL)) |
| 352 | goto end; |
| 353 | } |
| 354 | } |
| 355 | if (!TEST_size_t_eq(len, sizeof(testdata))) |
| 356 | goto end; |
| 357 | |
| 358 | /* |
| 359 | * Now read the test data. It may take more attempts here because |
| 360 | * it could fail once for each byte read, including all overhead |
| 361 | * bytes from the record header/padding etc. |
| 362 | */ |
| 363 | for (ret = -1, i = 0, len = 0; len != sizeof(testdata) && |
| 364 | i < MAX_ATTEMPTS; i++) { |
| 365 | ret = SSL_read(serverssl, buf + len, sizeof(buf) - len); |
| 366 | if (ret > 0) { |
| 367 | len += ret; |
| 368 | } else { |
| 369 | int ssl_error = SSL_get_error(serverssl, ret); |
| 370 | |
| 371 | if (!TEST_false(ssl_error == SSL_ERROR_SYSCALL || |
| 372 | ssl_error == SSL_ERROR_SSL)) |
| 373 | goto end; |
| 374 | } |
| 375 | } |
| 376 | if (!TEST_mem_eq(testdata, sizeof(testdata), buf, len)) |
| 377 | goto end; |
| 378 | } |
| 379 | |
| 380 | /* Also frees the BIOs */ |
| 381 | SSL_free(clientssl); |
| 382 | SSL_free(serverssl); |
| 383 | clientssl = serverssl = NULL; |
| 384 | |
| 385 | testresult = 1; |
| 386 | |
| 387 | end: |
| 388 | SSL_free(clientssl); |
| 389 | SSL_free(serverssl); |
| 390 | SSL_CTX_free(clientctx); |
| 391 | SSL_CTX_free(serverctx); |
| 392 | |
| 393 | return testresult; |
| 394 | } |
| 395 | |
| 396 | int setup_tests(void) |
| 397 | { |
| 398 | if (!TEST_ptr(cert = test_get_argument(0)) |
| 399 | || !TEST_ptr(privkey = test_get_argument(1))) |
| 400 | return 0; |
| 401 | |
| 402 | ADD_ALL_TESTS(test_asyncio, 2); |
| 403 | return 1; |
| 404 | } |
| 405 | |
| 406 | void cleanup_tests(void) |
| 407 | { |
| 408 | BIO_meth_free(methods_async); |
| 409 | } |