diff options
Diffstat (limited to '')
| -rw-r--r-- | src/regress/lib/libssl/dtls/Makefile | 18 | ||||
| -rw-r--r-- | src/regress/lib/libssl/dtls/dtlstest.c | 393 |
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 | |||
| 3 | PROG= dtlstest | ||
| 4 | LDADD= -lssl -lcrypto | ||
| 5 | DPADD= ${LIBSSL} ${LIBCRYPTO} | ||
| 6 | WARNINGS= Yes | ||
| 7 | CFLAGS+= -DLIBRESSL_INTERNAL -Werror | ||
| 8 | |||
| 9 | REGRESS_TARGETS= \ | ||
| 10 | regress-dtlstest | ||
| 11 | |||
| 12 | regress-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 | |||
| 30 | const char *server_ca_file; | ||
| 31 | const char *server_cert_file; | ||
| 32 | const char *server_key_file; | ||
| 33 | |||
| 34 | char dtls_cookie[32]; | ||
| 35 | |||
| 36 | static int | ||
| 37 | datagram_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 | |||
| 69 | static int | ||
| 70 | poll_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 | |||
| 87 | static int | ||
| 88 | dtls_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 | |||
| 98 | static int | ||
| 99 | dtls_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 | |||
| 106 | static SSL * | ||
| 107 | dtls_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 | |||
| 141 | static SSL * | ||
| 142 | dtls_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 | |||
| 190 | static int | ||
| 191 | ssl_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 | |||
| 215 | static int | ||
| 216 | do_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 | |||
| 229 | static int | ||
| 230 | do_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 | |||
| 243 | static int | ||
| 244 | do_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 | |||
| 257 | typedef int (*ssl_func)(SSL *ssl, const char *name, int *done, short *events); | ||
| 258 | |||
| 259 | static int | ||
| 260 | do_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 | |||
| 292 | struct dtls_test { | ||
| 293 | const unsigned char *desc; | ||
| 294 | const long mtu; | ||
| 295 | const long ssl_options; | ||
| 296 | }; | ||
| 297 | |||
| 298 | static 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 | |||
| 320 | static int | ||
| 321 | dtlstest(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 | |||
| 373 | int | ||
| 374 | main(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 | } | ||
