summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorjsing <>2020-10-15 18:05:06 +0000
committerjsing <>2020-10-15 18:05:06 +0000
commit7c6ffc946324eead9f89ecc9ac680e2c861f8741 (patch)
treea53bbeff9461c2e47021a1ecc452d80f0821806f /src
parent698949bfcc12ecdf8b10bdffc544350feeeb348d (diff)
downloadopenbsd-7c6ffc946324eead9f89ecc9ac680e2c861f8741.tar.gz
openbsd-7c6ffc946324eead9f89ecc9ac680e2c861f8741.tar.bz2
openbsd-7c6ffc946324eead9f89ecc9ac680e2c861f8741.zip
Test DTLS timeouts and retransmissions by dropping specific messages.
Provide a BIO that can drop specific messages in order to trigger and test DTLS timeouts and retransmissions. Note that the SSL buffering BIO (bbio) has to be removed to ensure that handshake messages are sent individually. This would have detected the recent DTLS breakage with retransmissions for a flight that includes a CCS.
Diffstat (limited to 'src')
-rw-r--r--src/regress/lib/libssl/dtls/dtlstest.c304
1 files changed, 299 insertions, 5 deletions
diff --git a/src/regress/lib/libssl/dtls/dtlstest.c b/src/regress/lib/libssl/dtls/dtlstest.c
index 166302db48..c25800be19 100644
--- a/src/regress/lib/libssl/dtls/dtlstest.c
+++ b/src/regress/lib/libssl/dtls/dtlstest.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: dtlstest.c,v 1.2 2020/10/15 17:51:58 jsing Exp $ */ 1/* $OpenBSD: dtlstest.c,v 1.3 2020/10/15 18:05:06 jsing Exp $ */
2/* 2/*
3 * Copyright (c) 2020 Joel Sing <jsing@openbsd.org> 3 * Copyright (c) 2020 Joel Sing <jsing@openbsd.org>
4 * 4 *
@@ -35,6 +35,177 @@ char dtls_cookie[32];
35 35
36int debug = 0; 36int debug = 0;
37 37
38static void
39hexdump(const unsigned char *buf, size_t len)
40{
41 size_t i;
42
43 for (i = 1; i <= len; i++)
44 fprintf(stderr, " 0x%02hhx,%s", buf[i - 1], i % 8 ? "" : "\n");
45
46 if (len % 8)
47 fprintf(stderr, "\n");
48}
49
50#define BIO_C_DROP_PACKET 1000
51#define BIO_C_DROP_RANDOM 1001
52
53struct bio_packet_monkey_ctx {
54 unsigned int drop_rand;
55 unsigned int drop_mask;
56};
57
58static int
59bio_packet_monkey_new(BIO *bio)
60{
61 struct bio_packet_monkey_ctx *ctx;
62
63 if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
64 return 0;
65
66 bio->flags = 0;
67 bio->init = 1;
68 bio->num = 0;
69 bio->ptr = ctx;
70
71 return 1;
72}
73
74static int
75bio_packet_monkey_free(BIO *bio)
76{
77 struct bio_packet_monkey_ctx *ctx;
78
79 if (bio == NULL)
80 return 1;
81
82 ctx = bio->ptr;
83 free(ctx);
84
85 return 1;
86}
87
88static long
89bio_packet_monkey_ctrl(BIO *bio, int cmd, long num, void *ptr)
90{
91 struct bio_packet_monkey_ctx *ctx;
92
93 ctx = bio->ptr;
94
95 switch (cmd) {
96 case BIO_C_DROP_PACKET:
97 if (num < 1 || num > 31)
98 return 0;
99 ctx->drop_mask |= 1 << ((unsigned int)num - 1);
100 return 1;
101
102 case BIO_C_DROP_RANDOM:
103 if (num < 0 || num > UINT_MAX)
104 return 0;
105 ctx->drop_rand = (unsigned int)num;
106 return 1;
107 }
108
109 if (bio->next_bio == NULL)
110 return 0;
111
112 return BIO_ctrl(bio->next_bio, cmd, num, ptr);
113}
114
115static int
116bio_packet_monkey_read(BIO *bio, char *out, int out_len)
117{
118 struct bio_packet_monkey_ctx *ctx = bio->ptr;
119 int ret;
120
121 if (ctx == NULL || bio->next_bio == NULL)
122 return 0;
123
124 ret = BIO_read(bio->next_bio, out, out_len);
125
126 BIO_clear_retry_flags(bio);
127 if (ret <= 0 && BIO_should_retry(bio->next_bio))
128 BIO_set_retry_read(bio);
129
130 return ret;
131}
132
133static int
134bio_packet_monkey_write(BIO *bio, const char *in, int in_len)
135{
136 struct bio_packet_monkey_ctx *ctx = bio->ptr;
137 int drop = 0;
138 int ret;
139
140 if (ctx == NULL || bio->next_bio == NULL)
141 return 0;
142
143 if (ctx->drop_rand > 0) {
144 drop = arc4random_uniform(ctx->drop_rand) == 0;
145 } else if (ctx->drop_mask > 0) {
146 drop = ctx->drop_mask & 1;
147 ctx->drop_mask >>= 1;
148 }
149 if (debug) {
150 fprintf(stderr, "DEBUG: %s packet...\n",
151 drop ? "dropping" : "writing");
152 hexdump(in, in_len);
153 }
154 if (drop)
155 return in_len;
156
157 ret = BIO_write(bio->next_bio, in, in_len);
158
159 BIO_clear_retry_flags(bio);
160 if (ret <= 0 && BIO_should_retry(bio->next_bio))
161 BIO_set_retry_write(bio);
162
163 return ret;
164}
165
166static int
167bio_packet_monkey_puts(BIO *bio, const char *str)
168{
169 return bio_packet_monkey_write(bio, str, strlen(str));
170}
171
172static const BIO_METHOD bio_packet_monkey = {
173 .type = BIO_TYPE_BUFFER,
174 .name = "packet monkey",
175 .bread = bio_packet_monkey_read,
176 .bwrite = bio_packet_monkey_write,
177 .bputs = bio_packet_monkey_puts,
178 .ctrl = bio_packet_monkey_ctrl,
179 .create = bio_packet_monkey_new,
180 .destroy = bio_packet_monkey_free
181};
182
183static const BIO_METHOD *
184BIO_f_packet_monkey(void)
185{
186 return &bio_packet_monkey;
187}
188
189static BIO *
190BIO_new_packet_monkey(void)
191{
192 return BIO_new(BIO_f_packet_monkey());
193}
194
195static int
196BIO_packet_monkey_drop(BIO *bio, int num)
197{
198 return BIO_ctrl(bio, BIO_C_DROP_PACKET, num, NULL);
199}
200
201#if 0
202static int
203BIO_packet_monkey_drop_random(BIO *bio, int num)
204{
205 return BIO_ctrl(bio, BIO_C_DROP_RANDOM, num, NULL);
206}
207#endif
208
38static int 209static int
39datagram_pair(int *client_sock, int *server_sock, 210datagram_pair(int *client_sock, int *server_sock,
40 struct sockaddr_in *server_sin) 211 struct sockaddr_in *server_sin)
@@ -109,6 +280,17 @@ dtls_cookie_verify(SSL *ssl, const unsigned char *cookie,
109 memcmp(cookie, dtls_cookie, sizeof(dtls_cookie)) == 0; 280 memcmp(cookie, dtls_cookie, sizeof(dtls_cookie)) == 0;
110} 281}
111 282
283static void
284dtls_info_callback(const SSL *ssl, int type, int val)
285{
286 /*
287 * Squeal's ahead... remove the bbio from the info callback, so we can
288 * drop specific messages. Ideally this would be an option for the SSL.
289 */
290 if (ssl->wbio == ssl->bbio)
291 ((SSL *)ssl)->wbio = BIO_pop(ssl->wbio);
292}
293
112static SSL * 294static SSL *
113dtls_client(int sock, struct sockaddr_in *server_sin, long mtu) 295dtls_client(int sock, struct sockaddr_in *server_sin, long mtu)
114{ 296{
@@ -305,13 +487,19 @@ do_client_server_loop(SSL *client, ssl_func client_func, SSL *server,
305 return client_done && server_done; 487 return client_done && server_done;
306} 488}
307 489
490#define MAX_PACKET_DROPS 32
491
308struct dtls_test { 492struct dtls_test {
309 const unsigned char *desc; 493 const unsigned char *desc;
310 const long mtu; 494 long mtu;
311 const long ssl_options; 495 long ssl_options;
496 int client_bbio_off;
497 int server_bbio_off;
498 uint8_t client_drops[MAX_PACKET_DROPS];
499 uint8_t server_drops[MAX_PACKET_DROPS];
312}; 500};
313 501
314static struct dtls_test dtls_tests[] = { 502static const struct dtls_test dtls_tests[] = {
315 { 503 {
316 .desc = "DTLS without cookies", 504 .desc = "DTLS without cookies",
317 .ssl_options = 0, 505 .ssl_options = 0,
@@ -323,18 +511,116 @@ static struct dtls_test dtls_tests[] = {
323 { 511 {
324 .desc = "DTLS with low MTU", 512 .desc = "DTLS with low MTU",
325 .mtu = 256, 513 .mtu = 256,
514 .ssl_options = 0,
326 }, 515 },
327 { 516 {
328 .desc = "DTLS with low MTU and cookies", 517 .desc = "DTLS with low MTU and cookies",
329 .mtu = 256, 518 .mtu = 256,
330 .ssl_options = SSL_OP_COOKIE_EXCHANGE, 519 .ssl_options = SSL_OP_COOKIE_EXCHANGE,
331 }, 520 },
521 {
522 .desc = "DTLS with dropped server response",
523 .ssl_options = 0,
524 .server_drops = { 1 },
525 },
526 {
527 .desc = "DTLS with two dropped server responses",
528 .ssl_options = 0,
529 .server_drops = { 1, 2 },
530 },
531 {
532 .desc = "DTLS with dropped ServerHello",
533 .ssl_options = 0,
534 .server_bbio_off = 1,
535 .server_drops = { 1 },
536 },
537 {
538 .desc = "DTLS with dropped server Certificate",
539 .ssl_options = 0,
540 .server_bbio_off = 1,
541 .server_drops = { 2 },
542 },
543 {
544 .desc = "DTLS with dropped ServerKeyExchange",
545 .ssl_options = 0,
546 .server_bbio_off = 1,
547 .server_drops = { 3 },
548 },
549#if 0
550 /*
551 * These three currently result in the server accept completing and the
552 * client looping on a timeout. Presumably the server should not
553 * complete until the client Finished is received...
554 */
555 {
556 .desc = "DTLS with dropped ServerHelloDone",
557 .ssl_options = 0,
558 .server_bbio_off = 1,
559 .server_drops = { 4 },
560 },
561 {
562 .desc = "DTLS with dropped server CCS",
563 .ssl_options = 0,
564 .server_bbio_off = 1,
565 .server_drops = { 5 },
566 },
567 {
568 .desc = "DTLS with dropped server Finished",
569 .ssl_options = 0,
570 .server_bbio_off = 1,
571 .server_drops = { 6 },
572 },
573#endif
574 {
575 .desc = "DTLS with dropped ClientKeyExchange",
576 .ssl_options = 0,
577 .client_bbio_off = 1,
578 .client_drops = { 2 },
579 },
580 {
581 .desc = "DTLS with dropped Client CCS",
582 .ssl_options = 0,
583 .client_bbio_off = 1,
584 .client_drops = { 3 },
585 },
586 {
587 .desc = "DTLS with dropped client Finished",
588 .ssl_options = 0,
589 .client_bbio_off = 1,
590 .client_drops = { 4 },
591 },
332}; 592};
333 593
334#define N_DTLS_TESTS (sizeof(dtls_tests) / sizeof(*dtls_tests)) 594#define N_DTLS_TESTS (sizeof(dtls_tests) / sizeof(*dtls_tests))
335 595
596static void
597dtlstest_packet_monkey(SSL *ssl, const uint8_t drops[])
598{
599 BIO *bio_monkey;
600 BIO *bio;
601 int i;
602
603 if ((bio_monkey = BIO_new_packet_monkey()) == NULL)
604 errx(1, "packet monkey");
605
606 for (i = 0; i < MAX_PACKET_DROPS; i++) {
607 if (drops[i] == 0)
608 break;
609 if (!BIO_packet_monkey_drop(bio_monkey, drops[i]))
610 errx(1, "drop failure");
611 }
612
613 if ((bio = SSL_get_wbio(ssl)) == NULL)
614 errx(1, "SSL has NULL bio");
615
616 BIO_up_ref(bio);
617 bio = BIO_push(bio_monkey, bio);
618
619 SSL_set_bio(ssl, bio, bio);
620}
621
336static int 622static int
337dtlstest(struct dtls_test *dt) 623dtlstest(const struct dtls_test *dt)
338{ 624{
339 SSL *client = NULL, *server = NULL; 625 SSL *client = NULL, *server = NULL;
340 struct sockaddr_in server_sin; 626 struct sockaddr_in server_sin;
@@ -353,6 +639,14 @@ dtlstest(struct dtls_test *dt)
353 if ((server = dtls_server(server_sock, dt->ssl_options, dt->mtu)) == NULL) 639 if ((server = dtls_server(server_sock, dt->ssl_options, dt->mtu)) == NULL)
354 goto failure; 640 goto failure;
355 641
642 if (dt->client_bbio_off)
643 SSL_set_info_callback(client, dtls_info_callback);
644 if (dt->server_bbio_off)
645 SSL_set_info_callback(server, dtls_info_callback);
646
647 dtlstest_packet_monkey(client, dt->client_drops);
648 dtlstest_packet_monkey(server, dt->server_drops);
649
356 pfd[0].fd = client_sock; 650 pfd[0].fd = client_sock;
357 pfd[0].events = POLLOUT; 651 pfd[0].events = POLLOUT;
358 pfd[1].fd = server_sock; 652 pfd[1].fd = server_sock;