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