diff options
author | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2015-01-05 16:28:58 +0100 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2015-01-05 16:28:58 +0100 |
commit | 7be51ddf6b861247e36f9a981739170a168fa712 (patch) | |
tree | dec2081eec1518c2f5be505a78f77fda948e1060 | |
parent | 0d0c612062da8c2c9744d6829857931c083ac91a (diff) | |
download | gnutls-7be51ddf6b861247e36f9a981739170a168fa712.tar.gz |
tests: added check to ensure that DTLS handshake packets will not exceed MTU
-rw-r--r-- | tests/Makefile.am | 2 | ||||
-rw-r--r-- | tests/mini-dtls-mtu.c | 363 |
2 files changed, 364 insertions, 1 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am index 0a70f2e621..565ed98447 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -86,7 +86,7 @@ ctests = mini-record-2 simple gc set_pkcs12_cred certder certuniqueid \ long-session-id mini-x509-callbacks-intr mini-dtls-lowmtu \ crlverify mini-dtls-discard init_fds mini-record-failure \ mini-rehandshake-2 custom-urls set_x509_key_mem set_x509_key_file \ - mini-chain-unsorted x509-verify-with-crl + mini-chain-unsorted x509-verify-with-crl mini-dtls-mtu if ENABLE_OCSP ctests += ocsp diff --git a/tests/mini-dtls-mtu.c b/tests/mini-dtls-mtu.c new file mode 100644 index 0000000000..f6b2586c80 --- /dev/null +++ b/tests/mini-dtls-mtu.c @@ -0,0 +1,363 @@ +/* + * Copyright (C) 2014 Nikos Mavrogiannopoulos, Andreas Schultz + * + * This file is part of GnuTLS. + * + * GnuTLS is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuTLS is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GnuTLS; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <gnutls/gnutls.h> +#include <gnutls/dtls.h> +#include <signal.h> +#include <unistd.h> +#include <netinet/in.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include "utils.h" + +#define SERVER_MTU 500 + +#ifdef _WIN32 + +void doit(void) +{ + exit(77); +} + +#else + +/* Tests whether packing multiple DTLS records in a single UDP packet + * will be handled correctly. + */ + +const char *side = ""; + +static void tls_log_func(int level, const char *str) +{ + fprintf(stderr, "%s|<%d>| %s", side, level, str); +} + +static unsigned char server_cert_pem[] = + "-----BEGIN CERTIFICATE-----\n" + "MIICHzCCAaWgAwIBAgIBCTAKBggqhkjOPQQDAjA+MQswCQYDVQQGEwJOTDERMA8G\n" + "A1UEChMIUG9sYXJTU0wxHDAaBgNVBAMTE1BvbGFyc3NsIFRlc3QgRUMgQ0EwHhcN\n" + "MTMwOTI0MTU1MjA0WhcNMjMwOTIyMTU1MjA0WjA0MQswCQYDVQQGEwJOTDERMA8G\n" + "A1UEChMIUG9sYXJTU0wxEjAQBgNVBAMTCWxvY2FsaG9zdDBZMBMGByqGSM49AgEG\n" + "CCqGSM49AwEHA0IABDfMVtl2CR5acj7HWS3/IG7ufPkGkXTQrRS192giWWKSTuUA\n" + "2CMR/+ov0jRdXRa9iojCa3cNVc2KKg76Aci07f+jgZ0wgZowCQYDVR0TBAIwADAd\n" + "BgNVHQ4EFgQUUGGlj9QH2deCAQzlZX+MY0anE74wbgYDVR0jBGcwZYAUnW0gJEkB\n" + "PyvLeLUZvH4kydv7NnyhQqRAMD4xCzAJBgNVBAYTAk5MMREwDwYDVQQKEwhQb2xh\n" + "clNTTDEcMBoGA1UEAxMTUG9sYXJzc2wgVGVzdCBFQyBDQYIJAMFD4n5iQ8zoMAoG\n" + "CCqGSM49BAMCA2gAMGUCMQCaLFzXptui5WQN8LlO3ddh1hMxx6tzgLvT03MTVK2S\n" + "C12r0Lz3ri/moSEpNZWqPjkCMCE2f53GXcYLqyfyJR078c/xNSUU5+Xxl7VZ414V\n" + "fGa5kHvHARBPc8YAIVIqDvHH1Q==\n" + "-----END CERTIFICATE-----\n"; + +const gnutls_datum_t server_cert = { server_cert_pem, + sizeof(server_cert_pem) +}; + +static unsigned char server_key_pem[] = + "-----BEGIN EC PRIVATE KEY-----\n" + "MHcCAQEEIPEqEyB2AnCoPL/9U/YDHvdqXYbIogTywwyp6/UfDw6noAoGCCqGSM49\n" + "AwEHoUQDQgAEN8xW2XYJHlpyPsdZLf8gbu58+QaRdNCtFLX3aCJZYpJO5QDYIxH/\n" + "6i/SNF1dFr2KiMJrdw1VzYoqDvoByLTt/w==\n" + "-----END EC PRIVATE KEY-----\n"; + +const gnutls_datum_t server_key = { server_key_pem, + sizeof(server_key_pem) +}; + +static int client_pull_timeout(gnutls_transport_ptr_t ptr, unsigned int ms) +{ + fd_set rfds; + struct timeval tv; + int ret; + int fd = (long int)ptr; + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + + tv.tv_sec = 0; + tv.tv_usec = ms * 1000; + + while (tv.tv_usec >= 1000000) { + tv.tv_usec -= 1000000; + tv.tv_sec++; + } + + ret = select(fd + 1, &rfds, NULL, NULL, &tv); + if (ret <= 0) + return ret; + + return ret; +} + +static ssize_t client_pull(gnutls_transport_ptr_t ptr, void *data, size_t len) +{ + int fd = (long int)ptr; + ssize_t ret; + + ret = recv(fd, data, len, 0); + if (ret > SERVER_MTU) { + fail("client: packet size beyond server MTU, got %zd bytes, expect max. %d bytes\n", ret, SERVER_MTU); + exit(1); + } + + return ret; +} + +static void client(int fd) +{ + int ret; + gnutls_certificate_credentials_t x509_cred; + gnutls_session_t session; + /* Need to enable anonymous KX specifically. */ + + global_init(); + + if (debug) { + side = "client"; + gnutls_global_set_log_function(tls_log_func); + gnutls_global_set_log_level(4711); + } + + gnutls_certificate_allocate_credentials(&x509_cred); + + /* Initialize TLS session + */ + gnutls_init(&session, GNUTLS_CLIENT | GNUTLS_DATAGRAM); + gnutls_dtls_set_mtu(session, 1500); + gnutls_handshake_set_timeout(session, 20 * 1000); + + /* Use default priorities */ + gnutls_priority_set_direct(session, + "NONE:+VERS-DTLS-ALL:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ECDHE-ECDSA:+CURVE-ALL", + NULL); + + /* put the anonymous credentials to the current session + */ + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred); + + gnutls_transport_set_int(session, fd); + gnutls_transport_set_pull_function(session, client_pull); + gnutls_transport_set_pull_timeout_function(session, client_pull_timeout); + + /* Perform the TLS handshake + */ + do { + ret = gnutls_handshake(session); + } + while (ret < 0 && gnutls_error_is_fatal(ret) == 0); + + if (ret < 0) { + fail("client: Handshake failed\n"); + gnutls_perror(ret); + exit(1); + } else { + if (debug) + success("client: Handshake was completed\n"); + } + + if (debug) + success("client: TLS version is: %s\n", + gnutls_protocol_get_name + (gnutls_protocol_get_version(session))); + + gnutls_bye(session, GNUTLS_SHUT_WR); + + close(fd); + + gnutls_deinit(session); + + gnutls_certificate_free_credentials(x509_cred); + + gnutls_global_deinit(); +} + +/* These are global */ +pid_t child; + +static void terminate(void) +{ + int status; + + kill(child, SIGTERM); + wait(&status); + exit(1); +} + +static void server(int fd) +{ + int ret; + gnutls_certificate_credentials_t x509_cred; + gnutls_session_t session; + + /* this must be called once in the program + */ + global_init(); + + if (debug) { + side = "server"; + gnutls_global_set_log_function(tls_log_func); + gnutls_global_set_log_level(4711); + } + + gnutls_certificate_allocate_credentials(&x509_cred); + gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert, + &server_key, + GNUTLS_X509_FMT_PEM); + + gnutls_init(&session, GNUTLS_SERVER | GNUTLS_DATAGRAM); + gnutls_handshake_set_timeout(session, 20 * 1000); + gnutls_dtls_set_mtu(session, SERVER_MTU); + + /* avoid calling all the priority functions, since the defaults + * are adequate. + */ + gnutls_priority_set_direct(session, + "NONE:+VERS-DTLS1.2:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ECDHE-ECDSA:+CURVE-ALL", + NULL); + + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred); + + gnutls_transport_set_int(session, fd); + + do { + ret = gnutls_handshake(session); + } + while (ret < 0 && gnutls_error_is_fatal(ret) == 0); + if (ret < 0) { + close(fd); + gnutls_deinit(session); + fail("server: Handshake has failed (%s)\n\n", + gnutls_strerror(ret)); + terminate(); + } + if (debug) + success("server: Handshake was completed\n"); + + if (debug) + success("server: TLS version is: %s\n", + gnutls_protocol_get_name + (gnutls_protocol_get_version(session))); + + /* do not wait for the peer to close the connection. + */ + gnutls_bye(session, GNUTLS_SHUT_WR); + + close(fd); + gnutls_deinit(session); + + gnutls_certificate_free_credentials(x509_cred); + + gnutls_global_deinit(); + + if (debug) + success("server: finished\n"); +} + +static int udp_socket(void) +{ + int on = 1; + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr = {htonl(INADDR_LOOPBACK)}, + .sin_port = 0 + }; + int fd; + + fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)); +#if defined(SO_REUSEPORT) + setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char*)&on, sizeof(on)); +#endif + + if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("bind"); + exit(EXIT_FAILURE); + } + + return fd; +} + +static void udp_connect(int fd1, int fd2) +{ + struct sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + + if (getsockname(fd1, &addr, &addrlen) < 0) { + perror("getsockname"); + exit(EXIT_FAILURE); + } + + if (connect(fd2, &addr, addrlen) < 0) { + perror("connect"); + exit(EXIT_FAILURE); + } +} + +static int udp_socketpair(int *fd) +{ + fd[0] = udp_socket(); + fd[1] = udp_socket(); + + udp_connect(fd[0], fd[1]); + udp_connect(fd[1], fd[0]); + + return 0; +} + +void doit(void) +{ + int fd[2]; + + udp_socketpair(fd); + + child = fork(); + if (child < 0) { + perror("fork"); + fail("fork"); + exit(1); + } + + if (child) { + int status; + /* parent */ + + close(fd[0]); + server(fd[1]); + wait(&status); + if (WEXITSTATUS(status) != 0) + fail("Child died with status %d\n", + WEXITSTATUS(status)); + } else { + close(fd[1]); + client(fd[0]); + exit(0); + } +} + +#endif /* _WIN32 */ |