summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjsing <>2024-01-19 08:29:08 +0000
committerjsing <>2024-01-19 08:29:08 +0000
commitf70bb2029fa359f8b1d20005d789ee89c8c73875 (patch)
treef9518ca7596ab9ea04dccc15fa0c2075951bbfa2
parent4e651680c5324dce6a6a79a8acdd448a7e70a99f (diff)
downloadopenbsd-f70bb2029fa359f8b1d20005d789ee89c8c73875.tar.gz
openbsd-f70bb2029fa359f8b1d20005d789ee89c8c73875.tar.bz2
openbsd-f70bb2029fa359f8b1d20005d789ee89c8c73875.zip
Add regress test coverage for SSL_shutdown().
This tests and codifies the behaviour of SSL_shutdown() with respect to SSL_quiet_shutdown() and SSL_set_shutdown(). For now, only the legacy stack (TLSv1.2) is tested, as there are currently some subtle differences with the TLSv1.3 stack.
-rw-r--r--src/regress/lib/libssl/shutdown/Makefile18
-rw-r--r--src/regress/lib/libssl/shutdown/shutdowntest.c528
2 files changed, 546 insertions, 0 deletions
diff --git a/src/regress/lib/libssl/shutdown/Makefile b/src/regress/lib/libssl/shutdown/Makefile
new file mode 100644
index 0000000000..51305012d6
--- /dev/null
+++ b/src/regress/lib/libssl/shutdown/Makefile
@@ -0,0 +1,18 @@
1# $OpenBSD: Makefile,v 1.1 2024/01/19 08:29:08 jsing Exp $
2
3PROG= shutdowntest
4LDADD= -lssl -lcrypto
5DPADD= ${LIBSSL} ${LIBCRYPTO}
6WARNINGS= Yes
7CFLAGS+= -DLIBRESSL_INTERNAL -Werror
8
9REGRESS_TARGETS= \
10 regress-shutdowntest
11
12regress-shutdowntest: ${PROG}
13 ./shutdowntest \
14 ${.CURDIR}/../../libssl/certs/server.pem \
15 ${.CURDIR}/../../libssl/certs/server.pem \
16 ${.CURDIR}/../../libssl/certs/ca.pem
17
18.include <bsd.regress.mk>
diff --git a/src/regress/lib/libssl/shutdown/shutdowntest.c b/src/regress/lib/libssl/shutdown/shutdowntest.c
new file mode 100644
index 0000000000..7dea3a8501
--- /dev/null
+++ b/src/regress/lib/libssl/shutdown/shutdowntest.c
@@ -0,0 +1,528 @@
1/* $OpenBSD: shutdowntest.c,v 1.1 2024/01/19 08:29:08 jsing Exp $ */
2/*
3 * Copyright (c) 2020, 2021, 2024 Joel Sing <jsing@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <err.h>
19
20#include <openssl/bio.h>
21#include <openssl/err.h>
22#include <openssl/ssl.h>
23
24const char *server_ca_file;
25const char *server_cert_file;
26const char *server_key_file;
27
28int debug = 0;
29
30static void
31hexdump(const unsigned char *buf, size_t len)
32{
33 size_t i;
34
35 for (i = 1; i <= len; i++)
36 fprintf(stderr, " 0x%02hhx,%s", buf[i - 1], i % 8 ? "" : "\n");
37
38 if (len % 8)
39 fprintf(stderr, "\n");
40}
41
42static SSL *
43tls_client(BIO *rbio, BIO *wbio)
44{
45 SSL_CTX *ssl_ctx = NULL;
46 SSL *ssl = NULL;
47
48 if ((ssl_ctx = SSL_CTX_new(TLS_method())) == NULL)
49 errx(1, "client context");
50
51 if ((ssl = SSL_new(ssl_ctx)) == NULL)
52 errx(1, "client ssl");
53
54 BIO_up_ref(rbio);
55 BIO_up_ref(wbio);
56
57 SSL_set_bio(ssl, rbio, wbio);
58
59 SSL_CTX_free(ssl_ctx);
60
61 return ssl;
62}
63
64static SSL *
65tls_server(BIO *rbio, BIO *wbio)
66{
67 SSL_CTX *ssl_ctx = NULL;
68 SSL *ssl = NULL;
69
70 if ((ssl_ctx = SSL_CTX_new(TLS_method())) == NULL)
71 errx(1, "server context");
72
73 SSL_CTX_set_dh_auto(ssl_ctx, 2);
74
75 if (SSL_CTX_use_certificate_file(ssl_ctx, server_cert_file,
76 SSL_FILETYPE_PEM) != 1) {
77 fprintf(stderr, "FAIL: Failed to load server certificate");
78 goto failure;
79 }
80 if (SSL_CTX_use_PrivateKey_file(ssl_ctx, server_key_file,
81 SSL_FILETYPE_PEM) != 1) {
82 fprintf(stderr, "FAIL: Failed to load server private key");
83 goto failure;
84 }
85
86 if ((ssl = SSL_new(ssl_ctx)) == NULL)
87 errx(1, "server ssl");
88
89 BIO_up_ref(rbio);
90 BIO_up_ref(wbio);
91
92 SSL_set_bio(ssl, rbio, wbio);
93
94 failure:
95 SSL_CTX_free(ssl_ctx);
96
97 return ssl;
98}
99
100static int
101ssl_error(SSL *ssl, const char *name, const char *desc, int ssl_ret)
102{
103 int ssl_err;
104
105 ssl_err = SSL_get_error(ssl, ssl_ret);
106
107 if (ssl_err == SSL_ERROR_WANT_READ) {
108 return 1;
109 } else if (ssl_err == SSL_ERROR_WANT_WRITE) {
110 return 1;
111 } else if (ssl_err == SSL_ERROR_SYSCALL && errno == 0) {
112 /* Yup, this is apparently a thing... */
113 return 1;
114 } else {
115 fprintf(stderr, "FAIL: %s %s failed - ssl err = %d, errno = %d\n",
116 name, desc, ssl_err, errno);
117 ERR_print_errors_fp(stderr);
118 return 0;
119 }
120}
121
122static int
123do_connect(SSL *ssl, const char *name, int *done)
124{
125 int ssl_ret;
126
127 if ((ssl_ret = SSL_connect(ssl)) == 1) {
128 fprintf(stderr, "INFO: %s connect done\n", name);
129 *done = 1;
130 return 1;
131 }
132
133 return ssl_error(ssl, name, "connect", ssl_ret);
134}
135
136static int
137do_accept(SSL *ssl, const char *name, int *done)
138{
139 int ssl_ret;
140
141 if ((ssl_ret = SSL_accept(ssl)) == 1) {
142 fprintf(stderr, "INFO: %s accept done\n", name);
143 *done = 1;
144 return 1;
145 }
146
147 return ssl_error(ssl, name, "accept", ssl_ret);
148}
149
150static int
151do_read(SSL *ssl, const char *name, int *done)
152{
153 uint8_t buf[512];
154 int ssl_ret;
155
156 if ((ssl_ret = SSL_read(ssl, buf, sizeof(buf))) > 0) {
157 fprintf(stderr, "INFO: %s read done\n", name);
158 if (debug > 1)
159 hexdump(buf, ssl_ret);
160 *done = 1;
161 return 1;
162 }
163
164 return ssl_error(ssl, name, "read", ssl_ret);
165}
166
167static int
168do_write(SSL *ssl, const char *name, int *done)
169{
170 const uint8_t buf[] = "Hello, World!\n";
171 int ssl_ret;
172
173 if ((ssl_ret = SSL_write(ssl, buf, sizeof(buf))) > 0) {
174 fprintf(stderr, "INFO: %s write done\n", name);
175 *done = 1;
176 return 1;
177 }
178
179 return ssl_error(ssl, name, "write", ssl_ret);
180}
181
182static int
183do_shutdown(SSL *ssl, const char *name, int *done)
184{
185 int ssl_ret;
186
187 ssl_ret = SSL_shutdown(ssl);
188 if (ssl_ret == 1) {
189 fprintf(stderr, "INFO: %s shutdown done\n", name);
190 *done = 1;
191 return 1;
192 }
193
194 /* The astounding EOF condition. */
195 if (ssl_ret == -1 &&
196 SSL_get_error(ssl, ssl_ret) == SSL_ERROR_SYSCALL && errno == 0) {
197 fprintf(stderr, "INFO: %s shutdown encountered EOF\n", name);
198 *done = 1;
199 return 1;
200 }
201
202 return ssl_error(ssl, name, "shutdown", ssl_ret);
203}
204
205typedef int (*ssl_func)(SSL *ssl, const char *name, int *done);
206
207static int
208do_client_server_loop(SSL *client, ssl_func client_func, SSL *server,
209 ssl_func server_func)
210{
211 int client_done = 0, server_done = 0;
212 int i = 0;
213
214 do {
215 if (!client_done) {
216 if (debug)
217 fprintf(stderr, "DEBUG: client loop\n");
218 if (!client_func(client, "client", &client_done))
219 return 0;
220 }
221 if (!server_done) {
222 if (debug)
223 fprintf(stderr, "DEBUG: server loop\n");
224 if (!server_func(server, "server", &server_done))
225 return 0;
226 }
227 } while (i++ < 100 && (!client_done || !server_done));
228
229 if (!client_done || !server_done)
230 fprintf(stderr, "FAIL: gave up\n");
231
232 return client_done && server_done;
233}
234
235static int
236do_shutdown_loop(SSL *client, SSL *server)
237{
238 int client_done = 0, server_done = 0;
239 int i = 0;
240
241 do {
242 if (!client_done) {
243 if (debug)
244 fprintf(stderr, "DEBUG: client loop\n");
245 if (!do_shutdown(client, "client", &client_done))
246 return 0;
247 if (client_done)
248 BIO_set_mem_eof_return(SSL_get_wbio(client), 0);
249 }
250 if (!server_done) {
251 if (debug)
252 fprintf(stderr, "DEBUG: server loop\n");
253 if (!do_shutdown(server, "server", &server_done))
254 return 0;
255 if (server_done)
256 BIO_set_mem_eof_return(SSL_get_wbio(server), 0);
257 }
258 } while (i++ < 100 && (!client_done || !server_done));
259
260 if (!client_done || !server_done)
261 fprintf(stderr, "FAIL: gave up\n");
262
263 return client_done && server_done;
264}
265
266static void
267ssl_msg_callback(int is_write, int version, int content_type, const void *buf,
268 size_t len, SSL *ssl, void *arg)
269{
270 const uint8_t *msg = buf;
271 int *close_notify = arg;
272
273 if (is_write || content_type != SSL3_RT_ALERT)
274 return;
275 if (len == 2 && msg[0] == SSL3_AL_WARNING && msg[1] == SSL_AD_CLOSE_NOTIFY)
276 *close_notify = 1;
277}
278
279struct shutdown_test {
280 const unsigned char *desc;
281 int client_quiet_shutdown;
282 int client_set_shutdown;
283 int want_client_shutdown;
284 int want_client_close_notify;
285 int server_quiet_shutdown;
286 int server_set_shutdown;
287 int want_server_shutdown;
288 int want_server_close_notify;
289};
290
291static const struct shutdown_test shutdown_tests[] = {
292 {
293 .desc = "bidirectional shutdown",
294 .want_client_close_notify = 1,
295 .want_client_shutdown = SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN,
296 .want_server_close_notify = 1,
297 .want_server_shutdown = SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN,
298 },
299 {
300 .desc = "client quiet shutdown",
301 .client_quiet_shutdown = 1,
302 .want_client_shutdown = SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN,
303 .want_server_shutdown = SSL_SENT_SHUTDOWN,
304 },
305 {
306 .desc = "server quiet shutdown",
307 .server_quiet_shutdown = 1,
308 .want_client_shutdown = SSL_SENT_SHUTDOWN,
309 .want_server_shutdown = SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN,
310 },
311 {
312 .desc = "both quiet shutdown",
313 .client_quiet_shutdown = 1,
314 .server_quiet_shutdown = 1,
315 .want_client_shutdown = SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN,
316 .want_server_shutdown = SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN,
317 },
318 {
319 .desc = "client set sent shutdown",
320 .client_set_shutdown = SSL_SENT_SHUTDOWN,
321 .want_client_close_notify = 1,
322 .want_client_shutdown = SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN,
323 .want_server_shutdown = SSL_SENT_SHUTDOWN,
324 },
325 {
326 .desc = "client set received shutdown",
327 .client_set_shutdown = SSL_RECEIVED_SHUTDOWN,
328 .want_client_shutdown = SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN,
329 .want_server_close_notify = 1,
330 .want_server_shutdown = SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN,
331 },
332 {
333 .desc = "client set sent/received shutdown",
334 .client_set_shutdown = SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN,
335 .want_client_shutdown = SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN,
336 .want_server_shutdown = SSL_SENT_SHUTDOWN,
337 },
338 {
339 .desc = "server set sent shutdown",
340 .server_set_shutdown = SSL_SENT_SHUTDOWN,
341 .want_client_shutdown = SSL_SENT_SHUTDOWN,
342 .want_server_close_notify = 1,
343 .want_server_shutdown = SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN,
344 },
345 {
346 .desc = "server set received shutdown",
347 .server_set_shutdown = SSL_RECEIVED_SHUTDOWN,
348 .want_client_close_notify = 1,
349 .want_client_shutdown = SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN,
350 .want_server_shutdown = SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN,
351 },
352 {
353 .desc = "server set sent/received shutdown",
354 .server_set_shutdown = SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN,
355 .want_client_shutdown = SSL_SENT_SHUTDOWN,
356 .want_server_shutdown = SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN,
357 },
358};
359
360#define N_TLS_TESTS (sizeof(shutdown_tests) / sizeof(*shutdown_tests))
361
362static int
363shutdowntest(uint16_t ssl_version, const char *ssl_version_name,
364 const struct shutdown_test *st)
365{
366 BIO *client_wbio = NULL, *server_wbio = NULL;
367 SSL *client = NULL, *server = NULL;
368 int client_close_notify = 0, server_close_notify = 0;
369 int shutdown, ssl_err;
370 int failed = 1;
371
372 fprintf(stderr, "\n== Testing %s, %s... ==\n", ssl_version_name,
373 st->desc);
374
375 if ((client_wbio = BIO_new(BIO_s_mem())) == NULL)
376 goto failure;
377 if (BIO_set_mem_eof_return(client_wbio, -1) <= 0)
378 goto failure;
379
380 if ((server_wbio = BIO_new(BIO_s_mem())) == NULL)
381 goto failure;
382 if (BIO_set_mem_eof_return(server_wbio, -1) <= 0)
383 goto failure;
384
385 if ((client = tls_client(server_wbio, client_wbio)) == NULL)
386 goto failure;
387 if (!SSL_set_min_proto_version(client, ssl_version))
388 goto failure;
389 if (!SSL_set_max_proto_version(client, ssl_version))
390 goto failure;
391
392 if ((server = tls_server(client_wbio, server_wbio)) == NULL)
393 goto failure;
394 if (!SSL_set_min_proto_version(server, ssl_version))
395 goto failure;
396 if (!SSL_set_max_proto_version(server, ssl_version))
397 goto failure;
398
399 if (!do_client_server_loop(client, do_connect, server, do_accept)) {
400 fprintf(stderr, "FAIL: client and server handshake failed\n");
401 goto failure;
402 }
403
404 if (!do_client_server_loop(client, do_write, server, do_read)) {
405 fprintf(stderr, "FAIL: client write and server read I/O failed\n");
406 goto failure;
407 }
408
409 if (!do_client_server_loop(client, do_read, server, do_write)) {
410 fprintf(stderr, "FAIL: client read and server write I/O failed\n");
411 goto failure;
412 }
413
414 /* Seemingly this is the only way to find out about alerts... */
415 SSL_set_msg_callback(client, ssl_msg_callback);
416 SSL_set_msg_callback_arg(client, &client_close_notify);
417 SSL_set_msg_callback(server, ssl_msg_callback);
418 SSL_set_msg_callback_arg(server, &server_close_notify);
419
420 SSL_set_shutdown(client, st->client_set_shutdown);
421 SSL_set_shutdown(server, st->server_set_shutdown);
422
423 SSL_set_quiet_shutdown(client, st->client_quiet_shutdown);
424 SSL_set_quiet_shutdown(server, st->server_quiet_shutdown);
425
426 if (!do_shutdown_loop(client, server)) {
427 fprintf(stderr, "FAIL: client and server shutdown failed\n");
428 goto failure;
429 }
430
431 if ((shutdown = SSL_get_shutdown(client)) != st->want_client_shutdown) {
432 fprintf(stderr, "FAIL: client shutdown flags = %x, want %x\n",
433 shutdown, st->want_client_shutdown);
434 goto failure;
435 }
436 if ((shutdown = SSL_get_shutdown(server)) != st->want_server_shutdown) {
437 fprintf(stderr, "FAIL: server shutdown flags = %x, want %x\n",
438 shutdown, st->want_server_shutdown);
439 goto failure;
440 }
441
442 if (client_close_notify != st->want_client_close_notify) {
443 fprintf(stderr, "FAIL: client close notify = %d, want %d\n",
444 client_close_notify, st->want_client_close_notify);
445 goto failure;
446 }
447 if (server_close_notify != st->want_server_close_notify) {
448 fprintf(stderr, "FAIL: server close notify = %d, want %d\n",
449 server_close_notify, st->want_server_close_notify);
450 goto failure;
451 }
452
453 if (st->want_client_close_notify) {
454 if ((ssl_err = SSL_get_error(client, 0)) != SSL_ERROR_ZERO_RETURN) {
455 fprintf(stderr, "FAIL: client ssl error = %d, want %d\n",
456 ssl_err, SSL_ERROR_ZERO_RETURN);
457 goto failure;
458 }
459 }
460 if (st->want_server_close_notify) {
461 if ((ssl_err = SSL_get_error(server, 0)) != SSL_ERROR_ZERO_RETURN) {
462 fprintf(stderr, "FAIL: server ssl error = %d, want %d\n",
463 ssl_err, SSL_ERROR_ZERO_RETURN);
464 goto failure;
465 }
466 }
467
468 fprintf(stderr, "INFO: Done!\n");
469
470 failed = 0;
471
472 failure:
473 BIO_free(client_wbio);
474 BIO_free(server_wbio);
475
476 SSL_free(client);
477 SSL_free(server);
478
479 return failed;
480}
481
482struct ssl_version {
483 uint16_t version;
484 const char *name;
485};
486
487struct ssl_version ssl_versions[] = {
488 {
489 .version = TLS1_2_VERSION,
490 .name = SSL_TXT_TLSV1_2,
491 },
492#if 0
493 {
494 .version = TLS1_3_VERSION,
495 .name = SSL_TXT_TLSV1_3,
496 },
497#endif
498};
499
500#define N_SSL_VERSIONS (sizeof(ssl_versions) / sizeof(*ssl_versions))
501
502int
503main(int argc, char **argv)
504{
505 const struct ssl_version *sv;
506 int failed = 0;
507 size_t i, j;
508
509 if (argc != 4) {
510 fprintf(stderr, "usage: %s keyfile certfile cafile\n",
511 argv[0]);
512 exit(1);
513 }
514
515 server_key_file = argv[1];
516 server_cert_file = argv[2];
517 server_ca_file = argv[3];
518
519 for (i = 0; i < N_SSL_VERSIONS; i++) {
520 sv = &ssl_versions[i];
521 for (j = 0; j < N_TLS_TESTS; j++) {
522 failed |= shutdowntest(sv->version, sv->name,
523 &shutdown_tests[j]);
524 }
525 }
526
527 return failed;
528}