summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjsing <>2020-10-14 15:49:14 +0000
committerjsing <>2020-10-14 15:49:14 +0000
commit2241235f2d550c606dd4e8ea6cf0f3bb8ee92356 (patch)
treeda9c5d7d607475d9f9ec58197ebbd66b2b4203dd
parentf7868ddf3ad814ba5bda3b57799654f9938281be (diff)
downloadopenbsd-2241235f2d550c606dd4e8ea6cf0f3bb8ee92356.tar.gz
openbsd-2241235f2d550c606dd4e8ea6cf0f3bb8ee92356.tar.bz2
openbsd-2241235f2d550c606dd4e8ea6cf0f3bb8ee92356.zip
Add regress tests for DTLS.
Test the operation of a DTLS client and server, with and without cookies, using the default MTU and a specifically lowered MTU. Further regress tests will be built on this to exercise other parts of the DTLS code base (such as retransmission, fragmentation and reassembly).
-rw-r--r--src/regress/lib/libssl/dtls/Makefile18
-rw-r--r--src/regress/lib/libssl/dtls/dtlstest.c393
2 files changed, 411 insertions, 0 deletions
diff --git a/src/regress/lib/libssl/dtls/Makefile b/src/regress/lib/libssl/dtls/Makefile
new file mode 100644
index 0000000000..5d25cde2ee
--- /dev/null
+++ b/src/regress/lib/libssl/dtls/Makefile
@@ -0,0 +1,18 @@
1# $OpenBSD: Makefile,v 1.1 2020/10/14 15:49:14 jsing Exp $
2
3PROG= dtlstest
4LDADD= -lssl -lcrypto
5DPADD= ${LIBSSL} ${LIBCRYPTO}
6WARNINGS= Yes
7CFLAGS+= -DLIBRESSL_INTERNAL -Werror
8
9REGRESS_TARGETS= \
10 regress-dtlstest
11
12regress-dtlstest: ${PROG}
13 ./dtlstest \
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/dtls/dtlstest.c b/src/regress/lib/libssl/dtls/dtlstest.c
new file mode 100644
index 0000000000..8cb4822b33
--- /dev/null
+++ b/src/regress/lib/libssl/dtls/dtlstest.c
@@ -0,0 +1,393 @@
1/* $OpenBSD: dtlstest.c,v 1.1 2020/10/14 15:49:14 jsing Exp $ */
2/*
3 * Copyright (c) 2020 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 <netinet/in.h>
19#include <sys/limits.h>
20#include <sys/socket.h>
21
22#include <err.h>
23#include <poll.h>
24#include <unistd.h>
25
26#include <openssl/bio.h>
27#include <openssl/err.h>
28#include <openssl/ssl.h>
29
30const char *server_ca_file;
31const char *server_cert_file;
32const char *server_key_file;
33
34char dtls_cookie[32];
35
36static int
37datagram_pair(int *client_sock, int *server_sock,
38 struct sockaddr_in *server_sin)
39{
40 struct sockaddr_in sin;
41 socklen_t sock_len;
42 int cs = -1, ss = -1;
43
44 memset(&sin, 0, sizeof(sin));
45 sin.sin_family = AF_INET;
46 sin.sin_port = 0;
47 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
48
49 if ((ss = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
50 err(1, "server socket");
51 if (bind(ss, (struct sockaddr *)&sin, sizeof(sin)) == -1)
52 err(1, "server bind");
53 sock_len = sizeof(sin);
54 if (getsockname(ss, (struct sockaddr *)&sin, &sock_len) == -1)
55 err(1, "server getsockname");
56
57 if ((cs = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
58 err(1, "client socket");
59 if (connect(cs, (struct sockaddr *)&sin, sizeof(sin)) == -1)
60 err(1, "client connect");
61
62 *client_sock = cs;
63 *server_sock = ss;
64 memcpy(server_sin, &sin, sizeof(sin));
65
66 return 1;
67}
68
69static int
70poll_timeout(SSL *client, SSL *server)
71{
72 int client_timeout = 0, server_timeout = 0;
73 struct timeval timeout;
74
75 if (DTLSv1_get_timeout(client, &timeout))
76 client_timeout = timeout.tv_sec * 1000 + timeout.tv_usec / 1000;
77
78 if (DTLSv1_get_timeout(server, &timeout))
79 server_timeout = timeout.tv_sec * 1000 + timeout.tv_usec / 1000;
80
81 if (client_timeout > 0 && client_timeout < server_timeout)
82 return client_timeout;
83
84 return server_timeout;
85}
86
87static int
88dtls_cookie_generate(SSL *ssl, unsigned char *cookie,
89 unsigned int *cookie_len)
90{
91 arc4random_buf(dtls_cookie, sizeof(dtls_cookie));
92 memcpy(cookie, dtls_cookie, sizeof(dtls_cookie));
93 *cookie_len = sizeof(dtls_cookie);
94
95 return 1;
96}
97
98static int
99dtls_cookie_verify(SSL *ssl, const unsigned char *cookie,
100 unsigned int cookie_len)
101{
102 return cookie_len == sizeof(dtls_cookie) &&
103 memcmp(cookie, dtls_cookie, sizeof(dtls_cookie)) == 0;
104}
105
106static SSL *
107dtls_client(int sock, struct sockaddr_in *server_sin, long mtu)
108{
109 SSL_CTX *ssl_ctx = NULL;
110 SSL *ssl = NULL;
111 BIO *bio = NULL;
112
113 if ((bio = BIO_new_dgram(sock, BIO_NOCLOSE)) == NULL)
114 errx(1, "client bio");
115 if (!BIO_socket_nbio(sock, 1))
116 errx(1, "client nbio");
117 if (!BIO_ctrl_set_connected(bio, 1, server_sin))
118 errx(1, "client set connected");
119
120 if ((ssl_ctx = SSL_CTX_new(DTLS_method())) == NULL)
121 errx(1, "client context");
122 SSL_CTX_set_read_ahead(ssl_ctx, 1);
123
124 if ((ssl = SSL_new(ssl_ctx)) == NULL)
125 errx(1, "client ssl");
126
127 SSL_set_bio(ssl, bio, bio);
128 bio = NULL;
129
130 if (mtu > 0) {
131 SSL_set_options(ssl, SSL_OP_NO_QUERY_MTU);
132 SSL_set_mtu(ssl, mtu);
133 }
134
135 SSL_CTX_free(ssl_ctx);
136 BIO_free(bio);
137
138 return ssl;
139}
140
141static SSL *
142dtls_server(int sock, long options, long mtu)
143{
144 SSL_CTX *ssl_ctx = NULL;
145 SSL *ssl = NULL;
146 BIO *bio = NULL;
147
148 if ((bio = BIO_new_dgram(sock, BIO_NOCLOSE)) == NULL)
149 errx(1, "server bio");
150 if (!BIO_socket_nbio(sock, 1))
151 errx(1, "server nbio");
152
153 if ((ssl_ctx = SSL_CTX_new(DTLS_method())) == NULL)
154 errx(1, "server context");
155
156 SSL_CTX_set_cookie_generate_cb(ssl_ctx, dtls_cookie_generate);
157 SSL_CTX_set_cookie_verify_cb(ssl_ctx, dtls_cookie_verify);
158 SSL_CTX_set_options(ssl_ctx, options);
159 SSL_CTX_set_read_ahead(ssl_ctx, 1);
160
161 if (SSL_CTX_use_certificate_file(ssl_ctx, server_cert_file,
162 SSL_FILETYPE_PEM) != 1) {
163 fprintf(stderr, "FAIL: Failed to load server certificate");
164 goto failure;
165 }
166 if (SSL_CTX_use_PrivateKey_file(ssl_ctx, server_key_file,
167 SSL_FILETYPE_PEM) != 1) {
168 fprintf(stderr, "FAIL: Failed to load server private key");
169 goto failure;
170 }
171
172 if ((ssl = SSL_new(ssl_ctx)) == NULL)
173 errx(1, "server ssl");
174
175 SSL_set_bio(ssl, bio, bio);
176 bio = NULL;
177
178 if (mtu > 0) {
179 SSL_set_options(ssl, SSL_OP_NO_QUERY_MTU);
180 SSL_set_mtu(ssl, mtu);
181 }
182
183 failure:
184 SSL_CTX_free(ssl_ctx);
185 BIO_free(bio);
186
187 return ssl;
188}
189
190static int
191ssl_error(SSL *ssl, const char *name, const char *desc, int ssl_ret,
192 short *events)
193{
194 int ssl_err;
195
196 ssl_err = SSL_get_error(ssl, ssl_ret);
197
198 if (ssl_err == SSL_ERROR_WANT_READ) {
199 *events = POLLIN;
200 } else if (ssl_err == SSL_ERROR_WANT_WRITE) {
201 *events = POLLOUT;
202 } else if (ssl_err == SSL_ERROR_SYSCALL && errno == 0) {
203 /* Yup, this is apparently a thing... */
204 *events = 0;
205 } else {
206 fprintf(stderr, "FAIL: %s %s failed - ssl err = %d, errno = %d\n",
207 name, desc, ssl_err, errno);
208 ERR_print_errors_fp(stderr);
209 return 0;
210 }
211
212 return 1;
213}
214
215static int
216do_connect(SSL *ssl, const char *name, int *done, short *events)
217{
218 int ssl_ret;
219
220 if ((ssl_ret = SSL_connect(ssl)) == 1) {
221 fprintf(stderr, "INFO: %s connect done\n", name);
222 *done = 1;
223 return 1;
224 }
225
226 return ssl_error(ssl, name, "connect", ssl_ret, events);
227}
228
229static int
230do_accept(SSL *ssl, const char *name, int *done, short *events)
231{
232 int ssl_ret;
233
234 if ((ssl_ret = SSL_accept(ssl)) == 1) {
235 fprintf(stderr, "INFO: %s accept done\n", name);
236 *done = 1;
237 return 1;
238 }
239
240 return ssl_error(ssl, name, "accept", ssl_ret, events);
241}
242
243static int
244do_shutdown(SSL *ssl, const char *name, int *done, short *events)
245{
246 int ssl_ret;
247
248 ssl_ret = SSL_shutdown(ssl);
249 if (ssl_ret == 1) {
250 fprintf(stderr, "INFO: %s shutdown done\n", name);
251 *done = 1;
252 return 1;
253 }
254 return ssl_error(ssl, name, "shutdown", ssl_ret, events);
255}
256
257typedef int (*ssl_func)(SSL *ssl, const char *name, int *done, short *events);
258
259static int
260do_client_server_loop(SSL *client, ssl_func client_func, SSL *server,
261 ssl_func server_func, struct pollfd pfd[2])
262{
263 int client_done = 0, server_done = 0;
264 int i = 0;
265
266 do {
267 if (poll(pfd, 2, poll_timeout(client, server)) == -1)
268 err(1, "poll");
269
270 if (!client_done) {
271 if (DTLSv1_handle_timeout(client) > 0)
272 fprintf(stderr, "INFO: client timeout\n");
273 if (!client_func(client, "client", &client_done,
274 &pfd[0].events))
275 return 0;
276 }
277 if (!server_done) {
278 if (DTLSv1_handle_timeout(server) > 0)
279 fprintf(stderr, "INFO: server timeout\n");
280 if (!server_func(server, "server", &server_done,
281 &pfd[1].events))
282 return 0;
283 }
284 } while (i++ < 1000 && (!client_done || !server_done));
285
286 if (!client_done || !server_done)
287 fprintf(stderr, "FAIL: gave up\n");
288
289 return client_done && server_done;
290}
291
292struct dtls_test {
293 const unsigned char *desc;
294 const long mtu;
295 const long ssl_options;
296};
297
298static struct dtls_test dtls_tests[] = {
299 {
300 .desc = "DTLS without cookies",
301 .ssl_options = 0,
302 },
303 {
304 .desc = "DTLS with cookies",
305 .ssl_options = SSL_OP_COOKIE_EXCHANGE,
306 },
307 {
308 .desc = "DTLS with low MTU",
309 .mtu = 256,
310 },
311 {
312 .desc = "DTLS with low MTU and cookies",
313 .mtu = 256,
314 .ssl_options = SSL_OP_COOKIE_EXCHANGE,
315 },
316};
317
318#define N_DTLS_TESTS (sizeof(dtls_tests) / sizeof(*dtls_tests))
319
320static int
321dtlstest(struct dtls_test *dt)
322{
323 SSL *client = NULL, *server = NULL;
324 struct sockaddr_in server_sin;
325 struct pollfd pfd[2];
326 int client_sock = -1;
327 int server_sock = -1;
328 int failed = 1;
329
330 fprintf(stderr, "\n== Testing %s... ==\n", dt->desc);
331
332 if (!datagram_pair(&client_sock, &server_sock, &server_sin))
333 goto failure;
334
335 if ((client = dtls_client(client_sock, &server_sin, dt->mtu)) == NULL)
336 goto failure;
337 if ((server = dtls_server(server_sock, dt->ssl_options, dt->mtu)) == NULL)
338 goto failure;
339
340 pfd[0].fd = client_sock;
341 pfd[0].events = POLLOUT;
342 pfd[1].fd = server_sock;
343 pfd[1].events = 0;
344
345 if (!do_client_server_loop(client, do_connect, server, do_accept, pfd)) {
346 fprintf(stderr, "FAIL: client and server handshake failed\n");
347 goto failure;
348 }
349
350 /* XXX - do reads and writes. */
351
352 if (!do_client_server_loop(client, do_shutdown, server, do_shutdown, pfd)) {
353 fprintf(stderr, "FAIL: client and server shutdown failed\n");
354 goto failure;
355 }
356
357 fprintf(stderr, "INFO: Done!\n");
358
359 failed = 0;
360
361 failure:
362 if (client_sock != -1)
363 close(client_sock);
364 if (server_sock != -1)
365 close(server_sock);
366
367 SSL_free(client);
368 SSL_free(server);
369
370 return failed;
371}
372
373int
374main(int argc, char **argv)
375{
376 int failed = 0;
377 size_t i;
378
379 if (argc != 4) {
380 fprintf(stderr, "usage: %s keyfile certfile cafile\n",
381 argv[0]);
382 exit(1);
383 }
384
385 server_key_file = argv[1];
386 server_cert_file = argv[2];
387 server_ca_file = argv[3];
388
389 for (i = 0; i < N_DTLS_TESTS; i++)
390 failed |= dtlstest(&dtls_tests[i]);
391
392 return failed;
393}