diff options
-rw-r--r-- | regress/lib/libssl/dtls/dtlstest.c | 304 |
1 files changed, 299 insertions, 5 deletions
diff --git a/regress/lib/libssl/dtls/dtlstest.c b/regress/lib/libssl/dtls/dtlstest.c index 166302db488..c25800be195 100644 --- a/regress/lib/libssl/dtls/dtlstest.c +++ b/regress/lib/libssl/dtls/dtlstest.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dtlstest.c,v 1.2 2020/10/15 17:51:58 jsing Exp $ */ +/* $OpenBSD: dtlstest.c,v 1.3 2020/10/15 18:05:06 jsing Exp $ */ /* * Copyright (c) 2020 Joel Sing <jsing@openbsd.org> * @@ -35,6 +35,177 @@ char dtls_cookie[32]; int debug = 0; +static void +hexdump(const unsigned char *buf, size_t len) +{ + size_t i; + + for (i = 1; i <= len; i++) + fprintf(stderr, " 0x%02hhx,%s", buf[i - 1], i % 8 ? "" : "\n"); + + if (len % 8) + fprintf(stderr, "\n"); +} + +#define BIO_C_DROP_PACKET 1000 +#define BIO_C_DROP_RANDOM 1001 + +struct bio_packet_monkey_ctx { + unsigned int drop_rand; + unsigned int drop_mask; +}; + +static int +bio_packet_monkey_new(BIO *bio) +{ + struct bio_packet_monkey_ctx *ctx; + + if ((ctx = calloc(1, sizeof(*ctx))) == NULL) + return 0; + + bio->flags = 0; + bio->init = 1; + bio->num = 0; + bio->ptr = ctx; + + return 1; +} + +static int +bio_packet_monkey_free(BIO *bio) +{ + struct bio_packet_monkey_ctx *ctx; + + if (bio == NULL) + return 1; + + ctx = bio->ptr; + free(ctx); + + return 1; +} + +static long +bio_packet_monkey_ctrl(BIO *bio, int cmd, long num, void *ptr) +{ + struct bio_packet_monkey_ctx *ctx; + + ctx = bio->ptr; + + switch (cmd) { + case BIO_C_DROP_PACKET: + if (num < 1 || num > 31) + return 0; + ctx->drop_mask |= 1 << ((unsigned int)num - 1); + return 1; + + case BIO_C_DROP_RANDOM: + if (num < 0 || num > UINT_MAX) + return 0; + ctx->drop_rand = (unsigned int)num; + return 1; + } + + if (bio->next_bio == NULL) + return 0; + + return BIO_ctrl(bio->next_bio, cmd, num, ptr); +} + +static int +bio_packet_monkey_read(BIO *bio, char *out, int out_len) +{ + struct bio_packet_monkey_ctx *ctx = bio->ptr; + int ret; + + if (ctx == NULL || bio->next_bio == NULL) + return 0; + + ret = BIO_read(bio->next_bio, out, out_len); + + BIO_clear_retry_flags(bio); + if (ret <= 0 && BIO_should_retry(bio->next_bio)) + BIO_set_retry_read(bio); + + return ret; +} + +static int +bio_packet_monkey_write(BIO *bio, const char *in, int in_len) +{ + struct bio_packet_monkey_ctx *ctx = bio->ptr; + int drop = 0; + int ret; + + if (ctx == NULL || bio->next_bio == NULL) + return 0; + + if (ctx->drop_rand > 0) { + drop = arc4random_uniform(ctx->drop_rand) == 0; + } else if (ctx->drop_mask > 0) { + drop = ctx->drop_mask & 1; + ctx->drop_mask >>= 1; + } + if (debug) { + fprintf(stderr, "DEBUG: %s packet...\n", + drop ? "dropping" : "writing"); + hexdump(in, in_len); + } + if (drop) + return in_len; + + ret = BIO_write(bio->next_bio, in, in_len); + + BIO_clear_retry_flags(bio); + if (ret <= 0 && BIO_should_retry(bio->next_bio)) + BIO_set_retry_write(bio); + + return ret; +} + +static int +bio_packet_monkey_puts(BIO *bio, const char *str) +{ + return bio_packet_monkey_write(bio, str, strlen(str)); +} + +static const BIO_METHOD bio_packet_monkey = { + .type = BIO_TYPE_BUFFER, + .name = "packet monkey", + .bread = bio_packet_monkey_read, + .bwrite = bio_packet_monkey_write, + .bputs = bio_packet_monkey_puts, + .ctrl = bio_packet_monkey_ctrl, + .create = bio_packet_monkey_new, + .destroy = bio_packet_monkey_free +}; + +static const BIO_METHOD * +BIO_f_packet_monkey(void) +{ + return &bio_packet_monkey; +} + +static BIO * +BIO_new_packet_monkey(void) +{ + return BIO_new(BIO_f_packet_monkey()); +} + +static int +BIO_packet_monkey_drop(BIO *bio, int num) +{ + return BIO_ctrl(bio, BIO_C_DROP_PACKET, num, NULL); +} + +#if 0 +static int +BIO_packet_monkey_drop_random(BIO *bio, int num) +{ + return BIO_ctrl(bio, BIO_C_DROP_RANDOM, num, NULL); +} +#endif + static int datagram_pair(int *client_sock, int *server_sock, struct sockaddr_in *server_sin) @@ -109,6 +280,17 @@ dtls_cookie_verify(SSL *ssl, const unsigned char *cookie, memcmp(cookie, dtls_cookie, sizeof(dtls_cookie)) == 0; } +static void +dtls_info_callback(const SSL *ssl, int type, int val) +{ + /* + * Squeal's ahead... remove the bbio from the info callback, so we can + * drop specific messages. Ideally this would be an option for the SSL. + */ + if (ssl->wbio == ssl->bbio) + ((SSL *)ssl)->wbio = BIO_pop(ssl->wbio); +} + static SSL * dtls_client(int sock, struct sockaddr_in *server_sin, long mtu) { @@ -305,13 +487,19 @@ do_client_server_loop(SSL *client, ssl_func client_func, SSL *server, return client_done && server_done; } +#define MAX_PACKET_DROPS 32 + struct dtls_test { const unsigned char *desc; - const long mtu; - const long ssl_options; + long mtu; + long ssl_options; + int client_bbio_off; + int server_bbio_off; + uint8_t client_drops[MAX_PACKET_DROPS]; + uint8_t server_drops[MAX_PACKET_DROPS]; }; -static struct dtls_test dtls_tests[] = { +static const struct dtls_test dtls_tests[] = { { .desc = "DTLS without cookies", .ssl_options = 0, @@ -323,18 +511,116 @@ static struct dtls_test dtls_tests[] = { { .desc = "DTLS with low MTU", .mtu = 256, + .ssl_options = 0, }, { .desc = "DTLS with low MTU and cookies", .mtu = 256, .ssl_options = SSL_OP_COOKIE_EXCHANGE, }, + { + .desc = "DTLS with dropped server response", + .ssl_options = 0, + .server_drops = { 1 }, + }, + { + .desc = "DTLS with two dropped server responses", + .ssl_options = 0, + .server_drops = { 1, 2 }, + }, + { + .desc = "DTLS with dropped ServerHello", + .ssl_options = 0, + .server_bbio_off = 1, + .server_drops = { 1 }, + }, + { + .desc = "DTLS with dropped server Certificate", + .ssl_options = 0, + .server_bbio_off = 1, + .server_drops = { 2 }, + }, + { + .desc = "DTLS with dropped ServerKeyExchange", + .ssl_options = 0, + .server_bbio_off = 1, + .server_drops = { 3 }, + }, +#if 0 + /* + * These three currently result in the server accept completing and the + * client looping on a timeout. Presumably the server should not + * complete until the client Finished is received... + */ + { + .desc = "DTLS with dropped ServerHelloDone", + .ssl_options = 0, + .server_bbio_off = 1, + .server_drops = { 4 }, + }, + { + .desc = "DTLS with dropped server CCS", + .ssl_options = 0, + .server_bbio_off = 1, + .server_drops = { 5 }, + }, + { + .desc = "DTLS with dropped server Finished", + .ssl_options = 0, + .server_bbio_off = 1, + .server_drops = { 6 }, + }, +#endif + { + .desc = "DTLS with dropped ClientKeyExchange", + .ssl_options = 0, + .client_bbio_off = 1, + .client_drops = { 2 }, + }, + { + .desc = "DTLS with dropped Client CCS", + .ssl_options = 0, + .client_bbio_off = 1, + .client_drops = { 3 }, + }, + { + .desc = "DTLS with dropped client Finished", + .ssl_options = 0, + .client_bbio_off = 1, + .client_drops = { 4 }, + }, }; #define N_DTLS_TESTS (sizeof(dtls_tests) / sizeof(*dtls_tests)) +static void +dtlstest_packet_monkey(SSL *ssl, const uint8_t drops[]) +{ + BIO *bio_monkey; + BIO *bio; + int i; + + if ((bio_monkey = BIO_new_packet_monkey()) == NULL) + errx(1, "packet monkey"); + + for (i = 0; i < MAX_PACKET_DROPS; i++) { + if (drops[i] == 0) + break; + if (!BIO_packet_monkey_drop(bio_monkey, drops[i])) + errx(1, "drop failure"); + } + + if ((bio = SSL_get_wbio(ssl)) == NULL) + errx(1, "SSL has NULL bio"); + + BIO_up_ref(bio); + bio = BIO_push(bio_monkey, bio); + + SSL_set_bio(ssl, bio, bio); +} + static int -dtlstest(struct dtls_test *dt) +dtlstest(const struct dtls_test *dt) { SSL *client = NULL, *server = NULL; struct sockaddr_in server_sin; @@ -353,6 +639,14 @@ dtlstest(struct dtls_test *dt) if ((server = dtls_server(server_sock, dt->ssl_options, dt->mtu)) == NULL) goto failure; + if (dt->client_bbio_off) + SSL_set_info_callback(client, dtls_info_callback); + if (dt->server_bbio_off) + SSL_set_info_callback(server, dtls_info_callback); + + dtlstest_packet_monkey(client, dt->client_drops); + dtlstest_packet_monkey(server, dt->server_drops); + pfd[0].fd = client_sock; pfd[0].events = POLLOUT; pfd[1].fd = server_sock; |