diff options
Diffstat (limited to '')
-rw-r--r-- | tests/dtls-client-with-seccomp.c | 311 | ||||
-rw-r--r-- | tests/dtls-etm.c | 346 | ||||
-rw-r--r-- | tests/dtls-handshake-versions.c | 143 | ||||
-rw-r--r-- | tests/dtls-max-record.c | 153 | ||||
-rw-r--r-- | tests/dtls-pthread.c | 369 | ||||
-rw-r--r-- | tests/dtls-rehandshake-anon.c | 382 | ||||
-rw-r--r-- | tests/dtls-rehandshake-cert-2.c | 400 | ||||
-rw-r--r-- | tests/dtls-rehandshake-cert-3.c | 392 | ||||
-rw-r--r-- | tests/dtls-rehandshake-cert.c | 388 | ||||
-rw-r--r-- | tests/dtls-repro-20170915.c | 47 | ||||
-rw-r--r-- | tests/dtls-session-ticket-lost.c | 247 | ||||
-rw-r--r-- | tests/dtls-sliding-window.c | 508 | ||||
-rw-r--r-- | tests/dtls-with-seccomp.c | 307 | ||||
-rwxr-xr-x | tests/dtls/dtls-resume.sh | 45 | ||||
-rw-r--r-- | tests/dtls/dtls-stress.c | 1558 | ||||
-rwxr-xr-x | tests/dtls/dtls.sh | 44 | ||||
-rw-r--r-- | tests/dtls1-2-mtu-check.c | 242 | ||||
-rw-r--r-- | tests/dtls10-cert-key-exchange.c | 65 | ||||
-rw-r--r-- | tests/dtls12-cert-key-exchange.c | 76 |
19 files changed, 6023 insertions, 0 deletions
diff --git a/tests/dtls-client-with-seccomp.c b/tests/dtls-client-with-seccomp.c new file mode 100644 index 0000000..c39068c --- /dev/null +++ b/tests/dtls-client-with-seccomp.c @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2015 Nikos Mavrogiannopoulos + * + * Author: Nikos Mavrogiannopoulos + * + * 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> + +#if defined(_WIN32) || !defined(HAVE_LIBSECCOMP) + +int main() +{ + exit(77); +} + +#else + +#include <string.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <signal.h> +#include <gnutls/gnutls.h> +#include <gnutls/dtls.h> +#include <assert.h> + +#include "cert-common.h" +#include "utils.h" + +static void terminate(void); + +static void server_log_func(int level, const char *str) +{ + fprintf(stderr, "server|<%d>| %s", level, str); +} + +static void client_log_func(int level, const char *str) +{ + fprintf(stderr, "client|<%d>| %s", level, str); +} + +#define MAX_BUF 1024 + +static ssize_t +push(gnutls_transport_ptr_t tr, const void *data, size_t len) +{ + int fd = (long int) tr; + + return send(fd, data, len, 0); +} + +static void client(int fd, const char *prio) +{ + int ret; + char buffer[MAX_BUF + 1]; + gnutls_certificate_credentials_t xcred; + gnutls_session_t session; + + global_init(); + + if (debug) { + gnutls_global_set_log_function(client_log_func); + gnutls_global_set_log_level(4711); + } + + gnutls_certificate_allocate_credentials(&xcred); + + ret = disable_system_calls(); + if (ret < 0) { + fprintf(stderr, "could not enable seccomp\n"); + exit(2); + } + + /* Initialize TLS session + */ + gnutls_init(&session, GNUTLS_CLIENT | GNUTLS_DATAGRAM); + gnutls_dtls_set_mtu(session, 1500); + gnutls_handshake_set_timeout(session, get_timeout()); + + /* Use default priorities */ + assert(gnutls_priority_set_direct(session, + prio, + NULL) >= 0); + + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred); + + gnutls_transport_set_int(session, fd); + gnutls_transport_set_push_function(session, push); + + /* 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))); + + do { + ret = gnutls_record_recv(session, buffer, sizeof(buffer)-1); + } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + + if (ret == 0) { + if (debug) + success + ("client: Peer has closed the TLS connection\n"); + goto end; + } else if (ret < 0) { + fail("client: Error: %s\n", gnutls_strerror(ret)); + exit(1); + } + + ret = gnutls_bye(session, GNUTLS_SHUT_RDWR); + if (ret < 0) { + fail("server: error in closing session: %s\n", gnutls_strerror(ret)); + } + + end: + + close(fd); + + gnutls_deinit(session); + + gnutls_certificate_free_credentials(xcred); + + gnutls_global_deinit(); +} + + +/* These are global */ +pid_t child; + +static void terminate(void) +{ + int status; + assert(child); + kill(child, SIGTERM); + wait(&status); + exit(1); +} + +static void server(int fd, const char *prio) +{ + int ret; + gnutls_certificate_credentials_t xcred; + char buffer[MAX_BUF + 1]; + gnutls_session_t session; + + /* this must be called once in the program + */ + global_init(); + + if (debug) { + gnutls_global_set_log_function(server_log_func); + gnutls_global_set_log_level(4711); + } + + gnutls_certificate_allocate_credentials(&xcred); + + ret = gnutls_certificate_set_x509_key_mem(xcred, + &server_cert, &server_key, + GNUTLS_X509_FMT_PEM); + if (ret < 0) + exit(1); + + gnutls_init(&session, GNUTLS_SERVER | GNUTLS_DATAGRAM); + gnutls_handshake_set_timeout(session, get_timeout()); + gnutls_dtls_set_mtu(session, 1500); + + /* avoid calling all the priority functions, since the defaults + * are adequate. + */ + assert(gnutls_priority_set_direct(session, + prio, + NULL)>=0); + + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred); + + gnutls_transport_set_int(session, fd); + gnutls_transport_set_push_function(session, push); + + 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))); + + /* see the Getting peer's information example */ + /* print_info(session); */ + + memset(buffer, 1, sizeof(buffer)); + do { + ret = gnutls_record_send(session, buffer, sizeof(buffer)-1); + } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + + if (ret < 0) { + close(fd); + gnutls_deinit(session); + fail("server: data sending has failed (%s)\n\n", + gnutls_strerror(ret)); + terminate(); + } + + ret = gnutls_bye(session, GNUTLS_SHUT_RDWR); + if (ret < 0) { + fail("server: error in closing session: %s\n", gnutls_strerror(ret)); + } + + close(fd); + gnutls_deinit(session); + + gnutls_certificate_free_credentials(xcred); + + gnutls_global_deinit(); + + if (debug) + success("server: finished\n"); +} + +static +void run(const char *name, const char *prio) +{ + int fd[2]; + int ret; + + success("testing seccomp with %s\n", name); + signal(SIGPIPE, SIG_IGN); + + ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd); + if (ret < 0) { + perror("socketpair"); + exit(1); + } + + child = fork(); + if (child < 0) { + perror("fork"); + fail("fork"); + exit(1); + } + + if (child) { + int status; + /* parent */ + + close(fd[1]); + server(fd[0], prio); + + wait(&status); + check_wait_status(status); + } else { + close(fd[0]); + client(fd[1], prio); + exit(0); + } +} + +void doit(void) +{ + run("dtls1.2", "NORMAL:-KX-ALL:+ECDHE-RSA:-VERS-ALL:+VERS-DTLS1.2"); +} +#endif /* _WIN32 */ diff --git a/tests/dtls-etm.c b/tests/dtls-etm.c new file mode 100644 index 0000000..1a8c1b5 --- /dev/null +++ b/tests/dtls-etm.c @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * 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> + +#if defined(_WIN32) + +int main() +{ + exit(77); +} + +#else + +#include <string.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <gnutls/gnutls.h> +#include <gnutls/dtls.h> +#include <signal.h> +#include <assert.h> + +#include "utils.h" +#include "cert-common.h" + +static void terminate(void); + +/* This program tests whether EtM is negotiated as expected. + */ + +static void server_log_func(int level, const char *str) +{ + fprintf(stderr, "server|<%d>| %s", level, str); +} + +static void client_log_func(int level, const char *str) +{ + fprintf(stderr, "client|<%d>| %s", level, str); +} + +/* A very basic TLS client, with anonymous authentication. + */ + +#define MAX_BUF 1024 + +static void client(int fd, const char *prio, unsigned etm) +{ + int ret; + char buffer[MAX_BUF + 1]; + gnutls_anon_client_credentials_t anoncred; + gnutls_certificate_credentials_t x509_cred; + gnutls_session_t session; + /* Need to enable anonymous KX specifically. */ + + global_init(); + + if (debug) { + gnutls_global_set_log_function(client_log_func); + gnutls_global_set_log_level(7); + } + + gnutls_anon_allocate_client_credentials(&anoncred); + gnutls_certificate_allocate_credentials(&x509_cred); + + /* Initialize TLS session + */ + gnutls_init(&session, GNUTLS_CLIENT|GNUTLS_DATAGRAM); + + /* Use default priorities */ + assert(gnutls_priority_set_direct(session, prio, NULL)>=0); + + /* put the anonymous credentials to the current session + */ + gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred); + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred); + + gnutls_transport_set_int(session, fd); + + /* 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))); + + if (etm != 0 && gnutls_session_etm_status(session) == 0) { + fail("client: EtM was not negotiated with %s!\n", prio); + exit(1); + } else if (etm == 0 && gnutls_session_etm_status(session) != 0) { + fail("client: EtM was negotiated with %s!\n", prio); + exit(1); + } + + if (etm != 0 && ((gnutls_session_get_flags(session) & GNUTLS_SFLAGS_ETM) == 0)) { + fail("client: EtM was not negotiated with %s!\n", prio); + exit(1); + } else if (etm == 0 && ((gnutls_session_get_flags(session) & GNUTLS_SFLAGS_ETM) != 0)) { + fail("client: EtM was negotiated with %s!\n", prio); + exit(1); + } + + do { + do { + ret = gnutls_record_recv(session, buffer, MAX_BUF); + } while (ret == GNUTLS_E_AGAIN + || ret == GNUTLS_E_INTERRUPTED); + } while (ret > 0); + + if (ret == 0) { + if (debug) + success + ("client: Peer has closed the TLS connection\n"); + goto end; + } else if (ret < 0) { + if (ret != 0) { + fail("client: Error: %s\n", gnutls_strerror(ret)); + exit(1); + } + } + + gnutls_bye(session, GNUTLS_SHUT_WR); + + end: + + close(fd); + + gnutls_deinit(session); + + gnutls_anon_free_client_credentials(anoncred); + gnutls_certificate_free_credentials(x509_cred); + + gnutls_global_deinit(); +} + + +/* These are global */ +pid_t child; + +static void terminate(void) +{ + assert(child); + kill(child, SIGTERM); + exit(1); +} + +static void server(int fd, const char *prio, unsigned etm) +{ + int ret; + char buffer[MAX_BUF + 1]; + gnutls_session_t session; + gnutls_anon_server_credentials_t anoncred; + gnutls_certificate_credentials_t x509_cred; + unsigned to_send = sizeof(buffer)/4; + + /* this must be called once in the program + */ + global_init(); + memset(buffer, 0, sizeof(buffer)); + + if (debug) { + gnutls_global_set_log_function(server_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_anon_allocate_server_credentials(&anoncred); + + gnutls_init(&session, GNUTLS_SERVER|GNUTLS_DATAGRAM); + + /* avoid calling all the priority functions, since the defaults + * are adequate. + */ + assert(gnutls_priority_set_direct(session, prio, NULL) >= 0); + + gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred); + 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 (etm != 0 && gnutls_session_etm_status(session) == 0) { + fail("server: EtM was not negotiated with %s!\n", prio); + exit(1); + } else if (etm == 0 && gnutls_session_etm_status(session) != 0) { + fail("server: EtM was negotiated with %s!\n", prio); + exit(1); + } + + if (etm != 0 && ((gnutls_session_get_flags(session) & GNUTLS_SFLAGS_ETM) == 0)) { + fail("server: EtM was not negotiated with %s!\n", prio); + exit(1); + } else if (etm == 0 && ((gnutls_session_get_flags(session) & GNUTLS_SFLAGS_ETM) != 0)) { + fail("server: EtM was negotiated with %s!\n", prio); + exit(1); + } + + 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 { + do { + ret = + gnutls_record_send(session, buffer, + sizeof(buffer)); + } while (ret == GNUTLS_E_AGAIN + || ret == GNUTLS_E_INTERRUPTED); + + if (ret < 0) { + fail("Error sending %d byte packet: %s\n", to_send, + gnutls_strerror(ret)); + terminate(); + } + to_send++; + } + while (to_send < 64); + + to_send = -1; + /* do not wait for the peer to close the connection. + */ + gnutls_bye(session, GNUTLS_SHUT_WR); + + close(fd); + gnutls_deinit(session); + + gnutls_anon_free_server_credentials(anoncred); + gnutls_certificate_free_credentials(x509_cred); + + gnutls_global_deinit(); + + if (debug) + success("server: finished\n"); +} + +static void start(const char *prio, unsigned etm) +{ + int fd[2]; + int ret; + + ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd); + if (ret < 0) { + perror("socketpair"); + exit(1); + } + + child = fork(); + if (child < 0) { + perror("fork"); + fail("fork"); + exit(1); + } + + if (child) { + /* parent */ + close(fd[1]); + server(fd[0], prio, etm); + kill(child, SIGTERM); + } else { + close(fd[0]); + client(fd[1], prio, etm); + exit(0); + } +} + +#define AES_CBC "NONE:+VERS-DTLS1.0:-CIPHER-ALL:+AES-128-CBC:+SHA1:+SIGN-ALL:+COMP-ALL:+ANON-ECDH:+CURVE-ALL" +#define AES_CBC_SHA256 "NONE:+VERS-DTLS1.2:-CIPHER-ALL:+RSA:+AES-128-CBC:+AES-256-CBC:+SHA256:+SIGN-ALL:+COMP-ALL:+ANON-ECDH:+CURVE-ALL" +#define AES_GCM "NONE:+VERS-DTLS1.2:-CIPHER-ALL:+RSA:+AES-128-GCM:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ANON-ECDH:+CURVE-ALL" + +static void ch_handler(int sig) +{ + int status; + wait(&status); + check_wait_status(status); + return; +} + +void doit(void) +{ + signal(SIGCHLD, ch_handler); + + start(AES_CBC, 1); + start(AES_CBC_SHA256, 1); + start(AES_GCM, 0); +} + +#endif /* _WIN32 */ diff --git a/tests/dtls-handshake-versions.c b/tests/dtls-handshake-versions.c new file mode 100644 index 0000000..507aa06 --- /dev/null +++ b/tests/dtls-handshake-versions.c @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * 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 <string.h> +#include <errno.h> +#include <gnutls/gnutls.h> +#include <gnutls/dtls.h> +#include "utils.h" +#include "eagain-common.h" +#include "cert-common.h" + +/* This tests whether the server reacts as expected on various client + * hello TLS versions */ + +void _gnutls_hello_set_default_version(gnutls_session_t session, + unsigned char major, + unsigned char minor); + +const char *side; + +static void tls_log_func(int level, const char *str) +{ + fprintf(stderr, "%s|<%d>| %s", side, level, str); +} + +static void try(unsigned char major, unsigned char minor, int ret1, int ret2) +{ + int ret; + /* Server stuff. */ + gnutls_certificate_credentials_t serverx509cred; + gnutls_session_t server; + int sret = GNUTLS_E_AGAIN; + /* Client stuff. */ + gnutls_certificate_credentials_t clientx509cred; + gnutls_session_t client; + int cret = GNUTLS_E_AGAIN; + + /* General init. */ + gnutls_global_set_log_function(tls_log_func); + if (debug) + gnutls_global_set_log_level(6); + + /* Init server */ + gnutls_certificate_allocate_credentials(&serverx509cred); + gnutls_certificate_set_x509_key_mem(serverx509cred, + &server_cert, &server_key, + GNUTLS_X509_FMT_PEM); + + gnutls_init(&server, GNUTLS_SERVER|GNUTLS_DATAGRAM | GNUTLS_NONBLOCK); + gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE, + serverx509cred); + + gnutls_priority_set_direct(server, + "NORMAL", + NULL); + gnutls_transport_set_push_function(server, server_push); + gnutls_transport_set_pull_function(server, server_pull); + gnutls_transport_set_pull_timeout_function(server, + server_pull_timeout_func); + gnutls_transport_set_ptr(server, server); + + /* Init client */ + + ret = gnutls_certificate_allocate_credentials(&clientx509cred); + if (ret < 0) + exit(1); + + ret = gnutls_certificate_set_x509_trust_mem(clientx509cred, &ca_cert, GNUTLS_X509_FMT_PEM); + if (ret < 0) + exit(1); + + ret = gnutls_init(&client, GNUTLS_CLIENT|GNUTLS_DATAGRAM | GNUTLS_NONBLOCK); + if (ret < 0) + exit(1); + + ret = gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE, + clientx509cred); + if (ret < 0) + exit(1); + + ret = gnutls_priority_set_direct(client, "NORMAL", NULL); + if (ret < 0) + exit(1); + + gnutls_transport_set_push_function(client, client_push); + gnutls_transport_set_pull_function(client, client_pull); + gnutls_transport_set_pull_timeout_function(client, + client_pull_timeout_func); + gnutls_transport_set_ptr(client, client); + _gnutls_hello_set_default_version(client, major, minor); + + HANDSHAKE_DTLS_EXPECT(client, server, ret1, ret2); + + gnutls_bye(client, GNUTLS_SHUT_RDWR); + gnutls_bye(server, GNUTLS_SHUT_RDWR); + + gnutls_deinit(client); + gnutls_deinit(server); + + gnutls_certificate_free_credentials(serverx509cred); + gnutls_certificate_free_credentials(clientx509cred); +} + +void doit(void) +{ + global_init(); + + try(255, 255, GNUTLS_E_AGAIN, GNUTLS_E_UNSUPPORTED_VERSION_PACKET); + reset_buffers(); + try(255, 128, GNUTLS_E_AGAIN, GNUTLS_E_UNSUPPORTED_VERSION_PACKET); + reset_buffers(); + try(254, 128, 0, 0); + reset_buffers(); + try(252, 64, 0, 0); + reset_buffers(); + + gnutls_global_deinit(); +} diff --git a/tests/dtls-max-record.c b/tests/dtls-max-record.c new file mode 100644 index 0000000..7934668 --- /dev/null +++ b/tests/dtls-max-record.c @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * 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 <string.h> +#include <errno.h> +#include <gnutls/gnutls.h> +#include <gnutls/dtls.h> +#include <assert.h> +#include "utils.h" + +#define SERVER_PUSH_ADD if (len > 512 + 5+8+32) fail("max record set to 512, len: %d\n", (int)len); +#include "eagain-common.h" + +#include "cert-common.h" + +/* This tests whether the max-record extension is respected. + */ + +const char *side; + +static void tls_log_func(int level, const char *str) +{ + fprintf(stderr, "%s|<%d>| %s", side, level, str); +} + +static +void run(const char *prio) +{ + global_init(); + + int ret; + char buf[1024]; + /* Server stuff. */ + gnutls_certificate_credentials_t serverx509cred; + gnutls_session_t server; + int sret = GNUTLS_E_AGAIN; + /* Client stuff. */ + gnutls_certificate_credentials_t clientx509cred; + gnutls_session_t client; + int cret = GNUTLS_E_AGAIN; + + /* General init. */ + gnutls_global_set_log_function(tls_log_func); + if (debug) + gnutls_global_set_log_level(6); + + /* Init server */ + gnutls_certificate_allocate_credentials(&serverx509cred); + gnutls_certificate_set_x509_key_mem(serverx509cred, + &server2_cert, &server2_key, + GNUTLS_X509_FMT_PEM); + + gnutls_init(&server, GNUTLS_SERVER|GNUTLS_DATAGRAM | GNUTLS_NONBLOCK); + gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE, + serverx509cred); + + assert(gnutls_priority_set_direct(server, + prio, + NULL)>=0); + gnutls_transport_set_push_function(server, server_push); + gnutls_transport_set_pull_function(server, server_pull); + gnutls_transport_set_pull_timeout_function(server, + server_pull_timeout_func); + gnutls_transport_set_ptr(server, server); + + /* Init client */ + + ret = gnutls_certificate_allocate_credentials(&clientx509cred); + if (ret < 0) + exit(1); + + ret = gnutls_certificate_set_x509_trust_mem(clientx509cred, &ca2_cert, GNUTLS_X509_FMT_PEM); + if (ret < 0) + exit(1); + + ret = gnutls_init(&client, GNUTLS_CLIENT|GNUTLS_DATAGRAM | GNUTLS_NONBLOCK); + if (ret < 0) + exit(1); + + ret = gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE, + clientx509cred); + if (ret < 0) + exit(1); + + ret = gnutls_priority_set_direct(client, prio, NULL); + if (ret < 0) + exit(1); + + gnutls_record_set_max_size(client, 512); + gnutls_transport_set_push_function(client, client_push); + gnutls_transport_set_pull_function(client, client_pull); + gnutls_transport_set_pull_timeout_function(client, + client_pull_timeout_func); + gnutls_transport_set_ptr(client, client); + + HANDSHAKE_DTLS(client, server); + + memset(buf, 1, sizeof(buf)); + ret = gnutls_record_send(server, buf, 513); + if (ret != GNUTLS_E_LARGE_PACKET && ret < 0) { + gnutls_perror(ret); + exit(1); + } + success("did not send a 513-byte packet\n"); + + ret = gnutls_record_send(server, buf, 512); + if (ret < 0) { + gnutls_perror(ret); + exit(1); + } + success("did send a 512-byte packet\n"); + + gnutls_bye(client, GNUTLS_SHUT_RDWR); + gnutls_bye(server, GNUTLS_SHUT_RDWR); + + gnutls_deinit(client); + gnutls_deinit(server); + + gnutls_certificate_free_credentials(serverx509cred); + gnutls_certificate_free_credentials(clientx509cred); + + gnutls_global_deinit(); +} + +void doit(void) +{ + run("NORMAL:-VERS-ALL:+VERS-DTLS1.2"); +} diff --git a/tests/dtls-pthread.c b/tests/dtls-pthread.c new file mode 100644 index 0000000..aab1e73 --- /dev/null +++ b/tests/dtls-pthread.c @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * 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> +#ifndef _WIN32 +# include <netinet/in.h> +# include <sys/types.h> +# include <sys/socket.h> +# include <sys/wait.h> +# include <pthread.h> +#endif +#include <assert.h> +#include "utils.h" +#include "cert-common.h" + +#ifdef _WIN32 + +void doit(void) +{ + exit(77); +} + +#else + +/* These are global */ +pid_t child; + +/* Tests whether we can send and receive from different threads + * using DTLS, either as server or client. DTLS is a superset of + * TLS, so correct behavior under fork means TLS would operate too. + */ + +const char *side = ""; + +static void tls_log_func(int level, const char *str) +{ + fprintf(stderr, "%s|<%d>| %s", side, level, str); +} + +#define MSG "hello1111" +#define MSG2 "xxxxxxxxxxxx" + +#define NO_MSGS 128 + +static void *recv_thread(void *arg) +{ + gnutls_session_t session = arg; + int ret; + unsigned i; + char buf[64]; + + if (debug) + success("client: TLS version is: %s\n", + gnutls_protocol_get_name + (gnutls_protocol_get_version(session))); + + for (i=0;i<NO_MSGS;i++) { + /* the peer should reflect our messages */ + do { + ret = gnutls_record_recv(session, buf, sizeof(buf)); + } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + if (ret < 0) + fail("client: recv failed: %s\n", gnutls_strerror(ret)); + if (ret != sizeof(MSG)-1 || memcmp(buf, MSG, sizeof(MSG)-1) != 0) { + fail("client: recv failed; not the expected values (got: %d, exp: %d)\n", ret, (int)sizeof(MSG)-1); + } + + if (debug) + success("%d: client received: %.*s\n", i, ret, buf); + } + + /* final MSG is MSG2 */ + do { + ret = gnutls_record_recv(session, buf, sizeof(buf)); + } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + if (ret < 0) + fail("client: recv2 failed: %s\n", gnutls_strerror(ret)); + + if (ret != sizeof(MSG2)-1 || memcmp(buf, MSG2, sizeof(MSG2)-1) != 0) { + fail("client: recv2 failed; not the expected values\n"); + } + + if (debug) { + success("client received: %.*s\n", ret, buf); + success("closing recv thread\n"); + } + + pthread_exit(0); +} + +static +void do_thread_stuff(gnutls_session_t session) +{ + int ret; + unsigned i; + pthread_t id; + void *rval; + + sec_sleep(1); + /* separate sending from receiving */ + ret = pthread_create(&id, NULL, recv_thread, session); + if (ret != 0) { + fail("error in pthread_create\n"); + } + + for (i=0;i<NO_MSGS;i++) { + do { + ret = gnutls_record_send(session, MSG, sizeof(MSG)-1); + } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + if (ret != sizeof(MSG)-1) { + fail("client: send failed: %s\n", gnutls_strerror(ret)); + } + } + + do { + ret = gnutls_record_send(session, MSG2, sizeof(MSG2)-1); + } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + if (ret != sizeof(MSG2)-1) { + fail("client: send2 failed: %s\n", gnutls_strerror(ret)); + } + + if (debug) + success("closing sending thread\n"); + assert(pthread_join(id, &rval)==0); + assert(rval == 0); + + do { + ret = gnutls_bye(session, GNUTLS_SHUT_RDWR); + } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); +} + +static void do_reflect_stuff(gnutls_session_t session) +{ + char buf[64]; + unsigned buf_size; + int ret; + + do { + do { + ret = gnutls_record_recv(session, buf, sizeof(buf)); + } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + if (ret < 0) { + fail("server: recv failed: %s\n", gnutls_strerror(ret)); + } + + if (ret == 0) { + break; + } + + buf_size = ret; + if (debug) { + success("server received: %.*s\n", buf_size, buf); + } + + do { + ret = gnutls_record_send(session, buf, buf_size); + } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + if (ret < 0) { + fail("server: send failed: %s\n", gnutls_strerror(ret)); + } + if (debug) + success("reflected %d\n", ret); + } while(1); + + do { + gnutls_bye(session, GNUTLS_SHUT_WR); + } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); +} + +static void client(int fd, const char *prio, unsigned do_thread, unsigned false_start) +{ + int ret; + gnutls_certificate_credentials_t x509_cred; + gnutls_session_t session; + unsigned flags = GNUTLS_CLIENT; + + global_init(); + + if (debug) { + side = "client"; + gnutls_global_set_log_function(tls_log_func); + gnutls_global_set_log_level(4711); + } + + assert(gnutls_certificate_allocate_credentials(&x509_cred)>=0); + + if (false_start) + flags |= GNUTLS_ENABLE_FALSE_START; + + assert(gnutls_init(&session, flags|GNUTLS_DATAGRAM) >= 0); + gnutls_dtls_set_mtu(session, 1500); + gnutls_dtls_set_timeouts(session, get_dtls_retransmit_timeout(), get_timeout()); + + assert(gnutls_priority_set_direct(session, prio, NULL)>=0); + + assert(gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred)>=0); + + gnutls_transport_set_int(session, fd); + + do { + ret = gnutls_handshake(session); + } + while (ret < 0 && gnutls_error_is_fatal(ret) == 0); + + if (ret < 0) { + fail("client: Handshake failed: %s\n", gnutls_strerror(ret)); + } else { + if (debug) + success("client: Handshake was completed\n"); + } + + if (do_thread) + do_thread_stuff(session); + else + do_reflect_stuff(session); + + close(fd); + + gnutls_deinit(session); + + gnutls_certificate_free_credentials(x509_cred); + + gnutls_global_deinit(); +} + +static void server(int fd, const char *prio, unsigned do_thread) +{ + int ret; + gnutls_certificate_credentials_t x509_cred; + gnutls_session_t session; + + global_init(); + +#if 0 + if (debug) { + side = "server"; + gnutls_global_set_log_function(tls_log_func); + gnutls_global_set_log_level(4711); + } +#endif + + assert(gnutls_certificate_allocate_credentials(&x509_cred)>=0); + assert(gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert, + &server_key, + GNUTLS_X509_FMT_PEM)>=0); + + assert(gnutls_init(&session, GNUTLS_SERVER | GNUTLS_DATAGRAM)>=0); + gnutls_dtls_set_timeouts(session, get_dtls_retransmit_timeout(), get_timeout()); + gnutls_dtls_set_mtu(session, 400); + + assert(gnutls_priority_set_direct(session, prio, NULL)>=0); + + 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)); + } + 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))); + + if (do_thread) + do_thread_stuff(session); + else + do_reflect_stuff(session); + + + close(fd); + gnutls_deinit(session); + + gnutls_certificate_free_credentials(x509_cred); + + gnutls_global_deinit(); + + if (debug) + success("server: finished\n"); +} + +static +void run(const char *str, const char *prio, unsigned do_thread, unsigned false_start) +{ + int fd[2]; + int ret; + + if (str) + success("running %s\n", str); + + ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd); + if (ret < 0) { + perror("socketpair"); + exit(1); + } + + child = fork(); + if (child < 0) { + perror("fork"); + fail("fork"); + exit(1); + } + + if (child) { + int status; + /* parent */ + + close(fd[1]); + client(fd[0], prio, do_thread, false_start); + wait(&status); + check_wait_status(status); + } else { + close(fd[0]); + server(fd[1], prio, 1-do_thread); + exit(0); + } +} + +void doit(void) +{ + signal(SIGPIPE, SIG_IGN); + run("default, threaded client", "NORMAL", 0, 0); + run("default, threaded server", "NORMAL", 1, 0); + run("dtls1.2, threaded client", "NORMAL:-VERS-ALL:+VERS-DTLS1.2", 0, 0); + run("dtls1.2, threaded server", "NORMAL:-VERS-ALL:+VERS-DTLS1.2", 1, 0); + run("dtls1.2 false start, threaded client", "NORMAL:-VERS-ALL:+VERS-DTLS1.2", 0, 1); + run("dtls1.2 false start, threaded server", "NORMAL:-VERS-ALL:+VERS-DTLS1.2", 1, 1); +} +#endif /* _WIN32 */ diff --git a/tests/dtls-rehandshake-anon.c b/tests/dtls-rehandshake-anon.c new file mode 100644 index 0000000..f281f5d --- /dev/null +++ b/tests/dtls-rehandshake-anon.c @@ -0,0 +1,382 @@ +/* + * Copyright (C) 2012 Free Software Foundation, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * 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> + +#if defined(_WIN32) + +int main() +{ + exit(77); +} + +#else + +#include <string.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <gnutls/gnutls.h> +#include <gnutls/dtls.h> + +#include "utils.h" + +static void terminate(void); + +/* This program tests the rehandshake in DTLS + */ + +static void server_log_func(int level, const char *str) +{ + fprintf(stderr, "server|<%d>| %s", level, str); +} + +static void client_log_func(int level, const char *str) +{ + fprintf(stderr, "client|<%d>| %s", level, str); +} + +/* A very basic TLS client, with anonymous authentication. + */ + +#define MAX_BUF 1024 +#define MSG "Hello TLS" + +static ssize_t +push(gnutls_transport_ptr_t tr, const void *data, size_t len) +{ + int fd = (long int) tr; + + return send(fd, data, len, 0); +} + +static void client(int fd, int server_init) +{ + int ret; + char buffer[MAX_BUF + 1]; + gnutls_anon_client_credentials_t anoncred; + gnutls_session_t session; + + /* Need to enable anonymous KX specifically. */ + + global_init(); + + if (debug) { + gnutls_global_set_log_function(client_log_func); + gnutls_global_set_log_level(4711); + } + + gnutls_anon_allocate_client_credentials(&anoncred); + + /* Initialize TLS session + */ + gnutls_init(&session, GNUTLS_CLIENT | GNUTLS_DATAGRAM); + gnutls_dtls_set_mtu(session, 1500); + + /* Use default priorities */ + gnutls_priority_set_direct(session, + "NONE:+VERS-DTLS1.0:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ANON-ECDH:+CURVE-ALL", + NULL); + + /* put the anonymous credentials to the current session + */ + gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred); + + gnutls_transport_set_int(session, fd); + gnutls_transport_set_push_function(session, push); + + /* 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))); + + if (!server_init) { + sec_sleep(60); + if (debug) + success("Initiating client rehandshake\n"); + do { + ret = gnutls_handshake(session); + } + while (ret < 0 && gnutls_error_is_fatal(ret) == 0); + + if (ret < 0) { + fail("2nd client gnutls_handshake: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } else { + do { + ret = gnutls_record_recv(session, buffer, MAX_BUF); + } while (ret == GNUTLS_E_AGAIN + || ret == GNUTLS_E_INTERRUPTED); + } + + if (ret == 0) { + if (debug) + success + ("client: Peer has closed the TLS connection\n"); + goto end; + } else if (ret < 0) { + if (server_init && ret == GNUTLS_E_REHANDSHAKE) { + if (debug) + success + ("Initiating rehandshake due to server request\n"); + do { + ret = gnutls_handshake(session); + } + while (ret < 0 && gnutls_error_is_fatal(ret) == 0); + } + + if (ret != 0) { + fail("client: Error: %s\n", gnutls_strerror(ret)); + exit(1); + } + } + + do { + ret = gnutls_record_send(session, MSG, strlen(MSG)); + } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + gnutls_bye(session, GNUTLS_SHUT_WR); + + end: + + close(fd); + + gnutls_deinit(session); + + gnutls_anon_free_client_credentials(anoncred); + + gnutls_global_deinit(); +} + + +/* These are global */ +pid_t child; + + +static void terminate(void) +{ + int status; + assert(child); + kill(child, SIGTERM); + wait(&status); + exit(1); +} + +static void server(int fd, int server_init) +{ + int ret; + char buffer[MAX_BUF + 1]; + gnutls_anon_server_credentials_t anoncred; + gnutls_session_t session; + /* this must be called once in the program + */ + global_init(); + + if (debug) { + gnutls_global_set_log_function(server_log_func); + gnutls_global_set_log_level(4711); + } + + gnutls_anon_allocate_server_credentials(&anoncred); + + gnutls_init(&session, GNUTLS_SERVER | GNUTLS_DATAGRAM); + gnutls_dtls_set_mtu(session, 1500); + + /* avoid calling all the priority functions, since the defaults + * are adequate. + */ + gnutls_priority_set_direct(session, + "NONE:+VERS-DTLS1.0:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ANON-ECDH:+CURVE-ALL", + NULL); + + gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred); + + gnutls_transport_set_int(session, fd); + gnutls_transport_set_push_function(session, push); + + 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))); + + /* see the Getting peer's information example */ + /* print_info(session); */ + + if (server_init) { + if (debug) + success("server: Sending dummy packet\n"); + ret = gnutls_rehandshake(session); + if (ret < 0) { + fail("gnutls_rehandshake: %s\n", + gnutls_strerror(ret)); + terminate(); + } + + if (debug) + success("server: Initiating rehandshake\n"); + do { + ret = gnutls_handshake(session); + } + while (ret < 0 && gnutls_error_is_fatal(ret) == 0); + + if (ret < 0) { + fail("server: 2nd gnutls_handshake: %s\n", + gnutls_strerror(ret)); + terminate(); + } + } + + for (;;) { + memset(buffer, 0, MAX_BUF + 1); + + do { + ret = gnutls_record_recv(session, buffer, MAX_BUF); + } while (ret == GNUTLS_E_AGAIN + || ret == GNUTLS_E_INTERRUPTED); + + if (ret == 0) { + if (debug) + success + ("server: Peer has closed the GnuTLS connection\n"); + break; + } else if (ret < 0) { + if (!server_init && ret == GNUTLS_E_REHANDSHAKE) { + if (debug) + success + ("Initiating rehandshake due to client request\n"); + do { + ret = gnutls_handshake(session); + } + while (ret < 0 + && gnutls_error_is_fatal(ret) == 0); + if (ret == 0) + break; + } + + fail("server: Received corrupted data(%s). Closing...\n", gnutls_strerror(ret)); + terminate(); + } else if (ret > 0) { + /* echo data back to the client + */ + do { + ret = + gnutls_record_send(session, buffer, + strlen(buffer)); + } while (ret == GNUTLS_E_AGAIN + || ret == GNUTLS_E_INTERRUPTED); + } + } + + + /* do not wait for the peer to close the connection. + */ + gnutls_bye(session, GNUTLS_SHUT_WR); + + close(fd); + gnutls_deinit(session); + + gnutls_anon_free_server_credentials(anoncred); + + gnutls_global_deinit(); + + if (debug) + success("server: finished\n"); +} + +static void start(int server_initiated) +{ + int fd[2]; + int ret; + + ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd); + if (ret < 0) { + perror("socketpair"); + exit(1); + } + + child = fork(); + if (child < 0) { + perror("fork"); + fail("fork"); + exit(1); + } + + if (child) { + int status = 0; + /* parent */ + + server(fd[0], server_initiated); + wait(&status); + check_wait_status(status); + } else { + close(fd[0]); + client(fd[1], server_initiated); + exit(0); + } +} + +void doit(void) +{ + start(0); + start(1); +} + +#endif /* _WIN32 */ diff --git a/tests/dtls-rehandshake-cert-2.c b/tests/dtls-rehandshake-cert-2.c new file mode 100644 index 0000000..c9d5058 --- /dev/null +++ b/tests/dtls-rehandshake-cert-2.c @@ -0,0 +1,400 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * 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> + +#if defined(_WIN32) + +int main() +{ + exit(77); +} + +#else + +#include <string.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <gnutls/gnutls.h> +#include <gnutls/dtls.h> +#include <assert.h> + +#include "cert-common.h" +#include "utils.h" + +static void terminate(void); + +/* This program tests the rehandshake from anon + * to certification auth in DTLS. + */ + +static void server_log_func(int level, const char *str) +{ + fprintf(stderr, "server|<%d>| %s", level, str); +} + +static void client_log_func(int level, const char *str) +{ + fprintf(stderr, "client|<%d>| %s", level, str); +} + +/* Tests the operation of rehandshake under DTLS using + * certificates. + */ + +#define MAX_BUF 1024 +#define MSG "Hello TLS" + +static ssize_t push(gnutls_transport_ptr_t tr, const void *data, size_t len) +{ + int fd = (long int)tr; + + return send(fd, data, len, 0); +} + +static void client(int fd, int server_init, const char *prio) +{ + int ret; + char buffer[MAX_BUF + 1]; + gnutls_certificate_credentials_t clientx509cred; + gnutls_anon_client_credentials_t anoncred; + gnutls_session_t session; + + global_init(); + + if (debug) { + gnutls_global_set_log_function(client_log_func); + gnutls_global_set_log_level(4711); + } + + assert(gnutls_anon_allocate_client_credentials(&anoncred) >= 0); + assert(gnutls_certificate_allocate_credentials(&clientx509cred) >= 0); + + /* Initialize TLS session + */ + gnutls_init(&session, GNUTLS_CLIENT | GNUTLS_DATAGRAM); + gnutls_dtls_set_mtu(session, 1500); + + snprintf(buffer, sizeof(buffer), "%s:+ANON-ECDH", prio); + assert(gnutls_priority_set_direct(session, + buffer, + NULL) >= 0); + + gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred); + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, clientx509cred); + + gnutls_transport_set_int(session, fd); + gnutls_transport_set_push_function(session, push); + + /* 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))); + + /* update priorities to allow cert auth */ + snprintf(buffer, sizeof(buffer), "%s:+ECDHE-RSA", prio); + assert(gnutls_priority_set_direct(session, + buffer, + NULL) >= 0); + + if (!server_init) { + sec_sleep(60); + if (debug) + success("Initiating client rehandshake\n"); + do { + ret = gnutls_handshake(session); + } + while (ret < 0 && gnutls_error_is_fatal(ret) == 0); + + if (ret < 0) { + fail("2nd client gnutls_handshake: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } else { + do { + ret = gnutls_record_recv(session, buffer, MAX_BUF); + } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + } + + if (ret == 0) { + if (debug) + success("client: Peer has closed the TLS connection\n"); + goto end; + } else if (ret < 0) { + if (server_init && ret == GNUTLS_E_REHANDSHAKE) { + if (debug) + success + ("Initiating rehandshake due to server request\n"); + do { + ret = gnutls_handshake(session); + } + while (ret < 0 && gnutls_error_is_fatal(ret) == 0); + } + + if (ret != 0) { + fail("client: Error: %s\n", gnutls_strerror(ret)); + exit(1); + } + } + + do { + ret = gnutls_record_send(session, MSG, strlen(MSG)); + } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + gnutls_bye(session, GNUTLS_SHUT_WR); + + end: + + close(fd); + + gnutls_deinit(session); + + gnutls_certificate_free_credentials(clientx509cred); + gnutls_anon_free_client_credentials(anoncred); + + gnutls_global_deinit(); +} + +/* These are global */ +pid_t child; + +static void terminate(void) +{ + int status; + assert(child); + kill(child, SIGTERM); + wait(&status); + exit(1); +} + +static void server(int fd, int server_init, const char *prio) +{ + int ret; + char buffer[MAX_BUF + 1]; + gnutls_certificate_credentials_t serverx509cred; + gnutls_anon_server_credentials_t anoncred; + gnutls_session_t session; + /* this must be called once in the program + */ + global_init(); + + if (debug) { + gnutls_global_set_log_function(server_log_func); + gnutls_global_set_log_level(4711); + } + + assert(gnutls_anon_allocate_server_credentials(&anoncred) >= 0); + assert(gnutls_certificate_allocate_credentials(&serverx509cred) >= 0); + assert(gnutls_certificate_set_x509_key_mem(serverx509cred, + &server_cert, &server_key, + GNUTLS_X509_FMT_PEM) >= 0); + + gnutls_init(&session, GNUTLS_SERVER | GNUTLS_DATAGRAM); + gnutls_dtls_set_mtu(session, 1500); + + /* avoid calling all the priority functions, since the defaults + * are adequate. + */ + snprintf(buffer, sizeof(buffer), "%s:+ECDHE-RSA:+ANON-ECDH", prio); + assert(gnutls_priority_set_direct(session, + buffer, + NULL) >= 0); + + gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred); + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, serverx509cred); + + gnutls_transport_set_int(session, fd); + gnutls_transport_set_push_function(session, push); + + 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))); + + if (gnutls_kx_get(session) != GNUTLS_KX_ANON_ECDH) { + fail("did not negotiate an anonymous ciphersuite on initial auth\n"); + } + + /* see the Getting peer's information example */ + /* print_info(session); */ + + if (server_init) { + if (debug) + success("server: Sending dummy packet\n"); + ret = gnutls_rehandshake(session); + if (ret < 0) { + fail("gnutls_rehandshake: %s\n", gnutls_strerror(ret)); + terminate(); + } + + if (debug) + success("server: Initiating rehandshake\n"); + do { + ret = gnutls_handshake(session); + } + while (ret < 0 && gnutls_error_is_fatal(ret) == 0); + + if (ret < 0) { + fail("server: 2nd gnutls_handshake: %s\n", + gnutls_strerror(ret)); + terminate(); + } + } + + for (;;) { + memset(buffer, 0, MAX_BUF + 1); + + do { + ret = gnutls_record_recv(session, buffer, MAX_BUF); + } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + + if (ret == 0) { + if (debug) + success + ("server: Peer has closed the GnuTLS connection\n"); + break; + } else if (ret < 0) { + if (!server_init && ret == GNUTLS_E_REHANDSHAKE) { + if (debug) + success + ("Initiating rehandshake due to client request\n"); + do { + ret = gnutls_handshake(session); + } + while (ret < 0 + && gnutls_error_is_fatal(ret) == 0); + if (ret == 0) + break; + } + + fail("server: Received corrupted data(%s). Closing...\n", gnutls_strerror(ret)); + terminate(); + } else if (ret > 0) { + /* echo data back to the client + */ + do { + ret = + gnutls_record_send(session, buffer, + strlen(buffer)); + } while (ret == GNUTLS_E_AGAIN + || ret == GNUTLS_E_INTERRUPTED); + } + } + + if (gnutls_kx_get(session) != GNUTLS_KX_ECDHE_RSA) { + fail("did not negotiate a certificate ciphersuite on second auth\n"); + } + + /* 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(serverx509cred); + gnutls_anon_free_server_credentials(anoncred); + + gnutls_global_deinit(); + + if (debug) + success("server: finished\n"); +} + +static void start(int server_initiated, const char *prio) +{ + int fd[2]; + int ret; + + ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd); + if (ret < 0) { + perror("socketpair"); + exit(1); + } + + child = fork(); + if (child < 0) { + perror("fork"); + fail("fork"); + exit(1); + } + + if (child) { + int status = 0; + /* parent */ + + server(fd[0], server_initiated, prio); + wait(&status); + check_wait_status(status); + } else { + close(fd[0]); + client(fd[1], server_initiated, prio); + exit(0); + } +} + +void doit(void) +{ + start(0, "NONE:+VERS-DTLS1.2:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+CURVE-ALL"); + start(1, "NONE:+VERS-DTLS1.2:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+CURVE-ALL"); +} + +#endif /* _WIN32 */ diff --git a/tests/dtls-rehandshake-cert-3.c b/tests/dtls-rehandshake-cert-3.c new file mode 100644 index 0000000..855f63c --- /dev/null +++ b/tests/dtls-rehandshake-cert-3.c @@ -0,0 +1,392 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. + * Copyright (C) 2016 Guillaume Roguez + * + * Author: Nikos Mavrogiannopoulos + * + * 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> + +#if defined(_WIN32) + +int main() +{ + exit(77); +} + +#else + +#include <string.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <gnutls/gnutls.h> +#include <gnutls/dtls.h> +#include <assert.h> + +#include "cert-common.h" +#include "utils.h" + +#define MTU 1500 +#define MAX_BUF 4096 +#define MSG "Hello TLS" + +static int server_fd = -1; +static char pkt_buf[MAX_BUF]; +static int pkt_found = 0; +static int pkt_delivered = 0; + +static void terminate(void); + +/* This program tests the rehandshake from anon + * to certificate auth in DTLS, but will account for + * packet reconstruction (with loss/delay) for the certificate packet. + */ + +static void server_log_func(int level, const char *str) +{ + fprintf(stderr, "server|<%d>| %s", level, str); +} + +static void client_log_func(int level, const char *str) +{ + fprintf(stderr, "client|<%d>| %s", level, str); +} + +static ssize_t push(gnutls_transport_ptr_t tr, const void *data, size_t len) +{ + int fd = (long int)tr; + + if (fd == server_fd) { + if (!pkt_found && len > 1200) { + memcpy(pkt_buf, data, len); + pkt_found = 1; + if (debug) + success("*** packet delayed\n"); + return len; + } + if (pkt_found && !pkt_delivered) { + int res = send(fd, data, len, 0); + send(fd, pkt_buf, MTU, 0); + pkt_delivered = 1; + if (debug) + success("*** swap done\n"); + return res; + } + } + + return send(fd, data, len, 0); +} + +static void client(int fd, const char *prio) +{ + int ret; + char buffer[MAX_BUF + 1]; + gnutls_certificate_credentials_t clientx509cred; + gnutls_anon_client_credentials_t anoncred; + gnutls_session_t session; + + global_init(); + + if (debug) { + gnutls_global_set_log_function(client_log_func); + gnutls_global_set_log_level(4711); + } + + assert(gnutls_anon_allocate_client_credentials(&anoncred) >= 0); + assert(gnutls_certificate_allocate_credentials(&clientx509cred) >= 0); + + /* Initialize TLS session + */ + gnutls_init(&session, GNUTLS_CLIENT | GNUTLS_DATAGRAM); + gnutls_dtls_set_mtu(session, MTU); + + snprintf(buffer, sizeof(buffer), "%s:+ANON-ECDH", prio); + assert(gnutls_priority_set_direct(session, + buffer, + NULL) >= 0); + + gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred); + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, clientx509cred); + + gnutls_transport_set_int(session, fd); + gnutls_transport_set_push_function(session, push); + gnutls_dtls_set_timeouts(session, get_dtls_retransmit_timeout(), get_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))); + + /* update priorities to allow cert auth */ + snprintf(buffer, sizeof(buffer), "%s:+ECDHE-RSA", prio); + assert(gnutls_priority_set_direct(session, + buffer, + NULL) >= 0); + + do { + ret = gnutls_record_recv(session, buffer, MAX_BUF); + } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + + if (ret == 0) { + if (debug) + success("client: Peer has closed the TLS connection\n"); + goto end; + } else if (ret < 0) { + if (ret == GNUTLS_E_REHANDSHAKE) { + if (debug) + success + ("Initiating rehandshake due to server request\n"); + do { + ret = gnutls_handshake(session); + } + while (ret < 0 && gnutls_error_is_fatal(ret) == 0); + } + + if (ret != 0) { + fail("client: Error: %s\n", gnutls_strerror(ret)); + exit(1); + } + } + + do { + ret = gnutls_record_send(session, MSG, strlen(MSG)); + } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + gnutls_bye(session, GNUTLS_SHUT_WR); + + end: + + close(fd); + + gnutls_deinit(session); + + gnutls_certificate_free_credentials(clientx509cred); + gnutls_anon_free_client_credentials(anoncred); + + gnutls_global_deinit(); +} + +/* These are global */ +pid_t child; + +static void terminate(void) +{ + int status; + assert(child); + kill(child, SIGTERM); + wait(&status); + exit(1); +} + +static void server(int fd, const char *prio) +{ + int ret; + char buffer[MAX_BUF + 1]; + gnutls_certificate_credentials_t serverx509cred; + gnutls_anon_server_credentials_t anoncred; + gnutls_session_t session; + /* this must be called once in the program + */ + global_init(); + + if (debug) { + gnutls_global_set_log_function(server_log_func); + gnutls_global_set_log_level(4711); + } + + assert(gnutls_anon_allocate_server_credentials(&anoncred) >= 0); + assert(gnutls_certificate_allocate_credentials(&serverx509cred) >= 0); + assert(gnutls_certificate_set_x509_key_mem(serverx509cred, + &server_cert, &server_key, + GNUTLS_X509_FMT_PEM) >= 0); + + gnutls_init(&session, GNUTLS_SERVER | GNUTLS_DATAGRAM); + gnutls_dtls_set_mtu(session, MTU); + + /* avoid calling all the priority functions, since the defaults + * are adequate. + */ + snprintf(buffer, sizeof(buffer), "%s:+ECDHE-RSA:+ANON-ECDH", prio); + assert(gnutls_priority_set_direct(session, + buffer, + NULL) >= 0); + + gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred); + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, serverx509cred); + + gnutls_transport_set_int(session, fd); + gnutls_transport_set_push_function(session, push); + + 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))); + + if (gnutls_kx_get(session) != GNUTLS_KX_ANON_ECDH) { + fail("did not negotiate an anonymous ciphersuite on initial auth\n"); + } + + /* see the Getting peer's information example */ + /* print_info(session); */ + + if (debug) + success("server: Sending dummy packet\n"); + ret = gnutls_rehandshake(session); + if (ret < 0) { + fail("gnutls_rehandshake: %s\n", gnutls_strerror(ret)); + terminate(); + } + + if (debug) + success("server: Initiating rehandshake\n"); + do { + ret = gnutls_handshake(session); + } + while (ret < 0 && gnutls_error_is_fatal(ret) == 0); + + if (ret < 0) { + fail("server: 2nd gnutls_handshake: %s\n", + gnutls_strerror(ret)); + terminate(); + } + + for (;;) { + memset(buffer, 0, MAX_BUF + 1); + + do { + ret = gnutls_record_recv(session, buffer, MAX_BUF); + } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + + if (ret == 0) { + if (debug) + success + ("server: Peer has closed the GnuTLS connection\n"); + break; + } else if (ret < 0) { + fail("server: Received corrupted data(%s). Closing...\n", gnutls_strerror(ret)); + terminate(); + } else if (ret > 0) { + /* echo data back to the client + */ + do { + ret = + gnutls_record_send(session, buffer, + strlen(buffer)); + } while (ret == GNUTLS_E_AGAIN + || ret == GNUTLS_E_INTERRUPTED); + } + } + + if (gnutls_kx_get(session) != GNUTLS_KX_ECDHE_RSA) { + fail("did not negotiate a certificate ciphersuite on second auth\n"); + } + + /* 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(serverx509cred); + gnutls_anon_free_server_credentials(anoncred); + + gnutls_global_deinit(); + + if (debug) + success("server: finished\n"); +} + +static void start(const char *prio) +{ + int fd[2]; + int ret; + + ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd); + if (ret < 0) { + perror("socketpair"); + exit(1); + } + + child = fork(); + if (child < 0) { + perror("fork"); + fail("fork"); + exit(1); + } + + if (child) { + int status = 0; + /* parent */ + + server_fd = fd[0]; + server(fd[0], prio); + wait(&status); + check_wait_status(status); + } else { + close(fd[0]); + client(fd[1], prio); + exit(0); + } +} + +void doit(void) +{ + start("NONE:+VERS-DTLS1.2:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+CURVE-ALL"); +} + +#endif /* _WIN32 */ diff --git a/tests/dtls-rehandshake-cert.c b/tests/dtls-rehandshake-cert.c new file mode 100644 index 0000000..3439ee9 --- /dev/null +++ b/tests/dtls-rehandshake-cert.c @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * 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> + +#if defined(_WIN32) + +int main() +{ + exit(77); +} + +#else + +#include <string.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <gnutls/gnutls.h> +#include <gnutls/dtls.h> +#include <assert.h> + +#include "cert-common.h" +#include "utils.h" + +static void terminate(void); + +/* This program tests the rehandshake in DTLS + */ + +static void server_log_func(int level, const char *str) +{ + fprintf(stderr, "server|<%d>| %s", level, str); +} + +static void client_log_func(int level, const char *str) +{ + fprintf(stderr, "client|<%d>| %s", level, str); +} + +/* Tests the operation of rehandshake under DTLS using + * certificates. + */ + +#define MAX_BUF 1024 +#define MSG "Hello TLS" + +static ssize_t +push(gnutls_transport_ptr_t tr, const void *data, size_t len) +{ + int fd = (long int) tr; + + return send(fd, data, len, 0); +} + +static void client(int fd, int server_init, const char *prio) +{ + int ret; + char buffer[MAX_BUF + 1]; + gnutls_certificate_credentials_t clientx509cred; + gnutls_session_t session; + + global_init(); + + if (debug) { + gnutls_global_set_log_function(client_log_func); + gnutls_global_set_log_level(4711); + } + + assert(gnutls_certificate_allocate_credentials(&clientx509cred) >= 0); + + /* Initialize TLS session + */ + gnutls_init(&session, GNUTLS_CLIENT | GNUTLS_DATAGRAM); + gnutls_dtls_set_mtu(session, 1500); + + /* Use default priorities */ + snprintf(buffer, sizeof(buffer), "%s:+ECDHE-RSA", prio); + assert(gnutls_priority_set_direct(session, + buffer, + NULL) >= 0); + + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, + clientx509cred); + + gnutls_transport_set_int(session, fd); + gnutls_transport_set_push_function(session, push); + + /* 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))); + + if (!server_init) { + sec_sleep(60); + if (debug) + success("Initiating client rehandshake\n"); + do { + ret = gnutls_handshake(session); + } + while (ret < 0 && gnutls_error_is_fatal(ret) == 0); + + if (ret < 0) { + fail("2nd client gnutls_handshake: %s\n", + gnutls_strerror(ret)); + exit(1); + } + } else { + do { + ret = gnutls_record_recv(session, buffer, MAX_BUF); + } while (ret == GNUTLS_E_AGAIN + || ret == GNUTLS_E_INTERRUPTED); + } + + if (ret == 0) { + if (debug) + success + ("client: Peer has closed the TLS connection\n"); + goto end; + } else if (ret < 0) { + if (server_init && ret == GNUTLS_E_REHANDSHAKE) { + if (debug) + success + ("Initiating rehandshake due to server request\n"); + do { + ret = gnutls_handshake(session); + } + while (ret < 0 && gnutls_error_is_fatal(ret) == 0); + } + + if (ret != 0) { + fail("client: Error: %s\n", gnutls_strerror(ret)); + exit(1); + } + } + + do { + ret = gnutls_record_send(session, MSG, strlen(MSG)); + } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + gnutls_bye(session, GNUTLS_SHUT_WR); + + end: + + close(fd); + + gnutls_deinit(session); + + gnutls_certificate_free_credentials(clientx509cred); + + gnutls_global_deinit(); +} + + +/* These are global */ +pid_t child; + + +static void terminate(void) +{ + int status; + assert(child); + kill(child, SIGTERM); + wait(&status); + exit(1); +} + +static void server(int fd, int server_init, const char *prio) +{ + int ret; + char buffer[MAX_BUF + 1]; + gnutls_certificate_credentials_t serverx509cred; + gnutls_session_t session; + /* this must be called once in the program + */ + global_init(); + + if (debug) { + gnutls_global_set_log_function(server_log_func); + gnutls_global_set_log_level(4711); + } + + assert(gnutls_certificate_allocate_credentials(&serverx509cred) >= 0); + assert(gnutls_certificate_set_x509_key_mem(serverx509cred, + &server_cert, &server_key, + GNUTLS_X509_FMT_PEM) >= 0); + + gnutls_init(&session, GNUTLS_SERVER | GNUTLS_DATAGRAM); + gnutls_dtls_set_mtu(session, 1500); + + /* avoid calling all the priority functions, since the defaults + * are adequate. + */ + snprintf(buffer, sizeof(buffer), "%s:+ECDHE-RSA", prio); + assert(gnutls_priority_set_direct(session, + buffer, + NULL) >= 0); + + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, + serverx509cred); + + gnutls_transport_set_int(session, fd); + gnutls_transport_set_push_function(session, push); + + 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))); + + /* see the Getting peer's information example */ + /* print_info(session); */ + + if (server_init) { + if (debug) + success("server: Sending dummy packet\n"); + ret = gnutls_rehandshake(session); + if (ret < 0) { + fail("gnutls_rehandshake: %s\n", + gnutls_strerror(ret)); + terminate(); + } + + if (debug) + success("server: Initiating rehandshake\n"); + do { + ret = gnutls_handshake(session); + } + while (ret < 0 && gnutls_error_is_fatal(ret) == 0); + + if (ret < 0) { + fail("server: 2nd gnutls_handshake: %s\n", + gnutls_strerror(ret)); + terminate(); + } + } + + for (;;) { + memset(buffer, 0, MAX_BUF + 1); + + do { + ret = gnutls_record_recv(session, buffer, MAX_BUF); + } while (ret == GNUTLS_E_AGAIN + || ret == GNUTLS_E_INTERRUPTED); + + if (ret == 0) { + if (debug) + success + ("server: Peer has closed the GnuTLS connection\n"); + break; + } else if (ret < 0) { + if (!server_init && ret == GNUTLS_E_REHANDSHAKE) { + if (debug) + success + ("Initiating rehandshake due to client request\n"); + do { + ret = gnutls_handshake(session); + } + while (ret < 0 + && gnutls_error_is_fatal(ret) == 0); + if (ret == 0) + break; + } + + fail("server: Received corrupted data(%s). Closing...\n", gnutls_strerror(ret)); + terminate(); + } else if (ret > 0) { + /* echo data back to the client + */ + do { + ret = + gnutls_record_send(session, buffer, + strlen(buffer)); + } while (ret == GNUTLS_E_AGAIN + || ret == GNUTLS_E_INTERRUPTED); + } + } + + + /* 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(serverx509cred); + + gnutls_global_deinit(); + + if (debug) + success("server: finished\n"); +} + +static void start(int server_initiated, const char *prio) +{ + int fd[2]; + int ret; + + ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd); + if (ret < 0) { + perror("socketpair"); + exit(1); + } + + child = fork(); + if (child < 0) { + perror("fork"); + fail("fork"); + exit(1); + } + + if (child) { + int status = 0; + /* parent */ + + server(fd[0], server_initiated, prio); + wait(&status); + check_wait_status(status); + } else { + close(fd[0]); + client(fd[1], server_initiated, prio); + exit(0); + } +} + +void doit(void) +{ + start(0, "NONE:+VERS-DTLS1.2:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+CURVE-ALL"); + start(1, "NONE:+VERS-DTLS1.2:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+CURVE-ALL"); +} + +#endif /* _WIN32 */ diff --git a/tests/dtls-repro-20170915.c b/tests/dtls-repro-20170915.c new file mode 100644 index 0000000..78910a0 --- /dev/null +++ b/tests/dtls-repro-20170915.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015-2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * 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 + +/* This program tests the various certificate key exchange methods supported + * in gnutls */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <gnutls/gnutls.h> +#include "utils.h" +#include "common-cert-key-exchange.h" +#include "cert-repro-20170915.h" + +void doit(void) +{ + global_init(); + + dtls_try_with_key_mtu("DTLS 1.2 with cli-cert", "NONE:+VERS-DTLS1.0:+MAC-ALL:+KX-ALL:+CIPHER-ALL:+SIGN-ALL:+COMP-ALL:+CURVE-ALL", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_RSA_SHA256, + &server_repro_cert, &server_repro_key, &client_repro_cert, &client_repro_key, USE_CERT, 1452); + + gnutls_global_deinit(); +} diff --git a/tests/dtls-session-ticket-lost.c b/tests/dtls-session-ticket-lost.c new file mode 100644 index 0000000..df3a606 --- /dev/null +++ b/tests/dtls-session-ticket-lost.c @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2018 Red Hat, Inc + * + * Author: Nikos Mavrogiannopoulos + * + * 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 Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> + +#if defined(_WIN32) + +int main() +{ + exit(77); +} + +#else + +#include <string.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <gnutls/gnutls.h> +#include <gnutls/dtls.h> +#include <signal.h> +#include <assert.h> + +#include "cert-common.h" +#include "utils.h" + +/* This program is a reproducer for issue #543; the timeout + * of DTLS handshake when a NewSessionTicket is lost. + */ + +static void server_log_func(int level, const char *str) +{ + fprintf(stderr, "server|<%d>| %s", level, str); +} + +static void client_log_func(int level, const char *str) +{ + fprintf(stderr, "client|<%d>| %s", level, str); +} + +#define MAX_BUF 1024 + +static void client(int fd, const char *prio) +{ + int ret; + gnutls_certificate_credentials_t x509_cred; + gnutls_session_t session; + + if (debug) { + gnutls_global_set_log_function(client_log_func); + gnutls_global_set_log_level(6); + } + + gnutls_certificate_allocate_credentials(&x509_cred); + + assert(gnutls_init(&session, GNUTLS_CLIENT|GNUTLS_DATAGRAM)>=0); + + assert(gnutls_priority_set_direct(session, prio, NULL)>=0); + + 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) { + fail("client: Handshake failed: %s\n", gnutls_strerror(ret)); + } else { + if (debug) + success("client: Handshake was completed\n"); + } + + gnutls_record_set_timeout(session, 30*1000); + + do { + ret = gnutls_bye(session, GNUTLS_SHUT_WR); + } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + + close(fd); + + gnutls_deinit(session); + + gnutls_certificate_free_credentials(x509_cred); +} + + +static ssize_t +server_push(gnutls_transport_ptr_t tr, const void *data, size_t len) +{ + const uint8_t *d = data; + static int dropped = 0; + + if (d[13] == GNUTLS_HANDSHAKE_NEW_SESSION_TICKET) { + if (dropped == 0) { + success("dropping message: %s\n", gnutls_handshake_description_get_name(d[13])); + dropped = 1; + return len; + } + } + + return send((long)tr, data, len, 0); +} + +static void server(int fd, const char *prio) +{ + int ret; + char buffer[MAX_BUF + 1]; + gnutls_session_t session; + gnutls_certificate_credentials_t x509_cred; + gnutls_datum_t skey; + + memset(buffer, 0, sizeof(buffer)); + + if (debug) { + gnutls_global_set_log_function(server_log_func); + gnutls_global_set_log_level(6); + } + + assert(gnutls_certificate_allocate_credentials(&x509_cred)>=0); + assert(gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert, + &server_key, + GNUTLS_X509_FMT_PEM)>=0); + + assert(gnutls_init(&session, GNUTLS_SERVER|GNUTLS_DATAGRAM)>=0); + + assert(gnutls_session_ticket_key_generate(&skey)>=0); + assert(gnutls_session_ticket_enable_server(session, &skey) >= 0); + + gnutls_transport_set_push_function(session, server_push); + + assert(gnutls_priority_set_direct(session, prio, NULL)>=0); + + 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) { + /* failure is expected here */ + goto end; + } + + if (debug) { + success("server: Handshake was completed\n"); + } + + gnutls_record_set_timeout(session, 30*1000); + + success("waiting for EOF\n"); + do { + ret = gnutls_record_recv(session, buffer, sizeof(buffer)); + } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + if (ret != 0) + fail("error waiting for EOF: %s\n", gnutls_strerror(ret)); + + end: + close(fd); + gnutls_deinit(session); + gnutls_free(skey.data); + + gnutls_certificate_free_credentials(x509_cred); + + if (debug) + success("server: finished\n"); +} + +static void ch_handler(int sig) +{ + return; +} + +static +void start(const char *prio) +{ + int fd[2]; + int ret, status = 0; + pid_t child; + + success("trying %s\n", prio); + signal(SIGCHLD, ch_handler); + signal(SIGPIPE, SIG_IGN); + + ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd); + if (ret < 0) { + perror("socketpair"); + exit(1); + } + + child = fork(); + if (child < 0) { + perror("fork"); + fail("fork"); + exit(1); + } + + if (child) { + /* parent */ + close(fd[1]); + client(fd[0], prio); + waitpid(child, &status, 0); + check_wait_status(status); + } else { + close(fd[0]); + server(fd[1], prio); + exit(0); + } + + return; +} + +void doit(void) +{ + start("NORMAL:-VERS-ALL:+VERS-DTLS1.2"); +} + +#endif /* _WIN32 */ diff --git a/tests/dtls-sliding-window.c b/tests/dtls-sliding-window.c new file mode 100644 index 0000000..d8e3c78 --- /dev/null +++ b/tests/dtls-sliding-window.c @@ -0,0 +1,508 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * 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 + */ + +#include <config.h> +#include <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmocka.h> +#include <limits.h> +#include <stdint.h> +#include <string.h> + +/* Unit test for DTLS window handling */ + +#define LARGE_INT 4194304 +#define INT_OVER_32_BITS 281474976708836LL + +struct record_parameters_st { + uint64_t dtls_sw_bits; + uint64_t dtls_sw_next; + unsigned dtls_sw_have_recv; + unsigned epoch; +}; + +#define gnutls_assert_val(x) x + +void _dtls_reset_window(struct record_parameters_st *rp); +int _dtls_record_check(struct record_parameters_st *rp, uint64_t _seq); + +#define DTLS_SW_NO_INCLUDES +#include "../lib/dtls-sw.c" + +#define RESET_WINDOW \ + memset(&state, 0, sizeof(state)) + +#define SET_WINDOW_NEXT(x) \ + state.dtls_sw_next = (((x)&DTLS_SEQ_NUM_MASK)) + +#define SET_WINDOW_LAST_RECV(x) \ + t = x; \ + state.dtls_sw_have_recv = 1 + +static void check_dtls_window_uninit_0(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + + RESET_WINDOW; + SET_WINDOW_NEXT(0); + + t = 0; + + assert_int_equal(_dtls_record_check(&state, t), 0); +} + +static void check_dtls_window_uninit_large(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + + RESET_WINDOW; + + t = LARGE_INT+1+64; + + assert_int_equal(_dtls_record_check(&state, t), 0); +} + +static void check_dtls_window_uninit_very_large(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + + RESET_WINDOW; + + t = INT_OVER_32_BITS; + + assert_int_equal(_dtls_record_check(&state, t), 0); +} + +static void check_dtls_window_12(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + + RESET_WINDOW; + SET_WINDOW_NEXT(0); + SET_WINDOW_LAST_RECV(1); + + t = 2; + + assert_int_equal(_dtls_record_check(&state, t), 0); +} + +static void check_dtls_window_19(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + + RESET_WINDOW; + SET_WINDOW_NEXT(0); + SET_WINDOW_LAST_RECV(1); + + t = 9; + + assert_int_equal(_dtls_record_check(&state, t), 0); +} + +static void check_dtls_window_skip1(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + unsigned i; + + RESET_WINDOW; + SET_WINDOW_NEXT(0); + SET_WINDOW_LAST_RECV(1); + + for (i=2;i<256;i+=2) { + t = i; + assert_int_equal(_dtls_record_check(&state, t), 0); + } +} + +static void check_dtls_window_skip3(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + unsigned i; + + RESET_WINDOW; + SET_WINDOW_NEXT(0); + SET_WINDOW_LAST_RECV(1); + + for (i=5;i<256;i+=2) { + t = i; + assert_int_equal(_dtls_record_check(&state, t), 0); + } +} + +static void check_dtls_window_21(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + + RESET_WINDOW; + SET_WINDOW_NEXT(0); + SET_WINDOW_LAST_RECV(2); + + t = 1; + + assert_int_equal(_dtls_record_check(&state, t), 0); +} + +static void check_dtls_window_91(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + + RESET_WINDOW; + SET_WINDOW_NEXT(0); + SET_WINDOW_LAST_RECV(9); + + t = 1; + + assert_int_equal(_dtls_record_check(&state, t), 0); +} + +static void check_dtls_window_large_21(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + + RESET_WINDOW; + SET_WINDOW_NEXT(LARGE_INT); + SET_WINDOW_LAST_RECV(LARGE_INT+2); + + t = LARGE_INT+1; + + assert_int_equal(_dtls_record_check(&state, t), 0); +} + +static void check_dtls_window_large_12(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + + RESET_WINDOW; + SET_WINDOW_NEXT(LARGE_INT); + SET_WINDOW_LAST_RECV(LARGE_INT+1); + + t = LARGE_INT+2; + + assert_int_equal(_dtls_record_check(&state, t), 0); +} + +static void check_dtls_window_large_91(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + + RESET_WINDOW; + SET_WINDOW_NEXT(LARGE_INT); + SET_WINDOW_LAST_RECV(LARGE_INT+9); + + t = LARGE_INT+1; + + assert_int_equal(_dtls_record_check(&state, t), 0); +} + +static void check_dtls_window_large_19(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + + RESET_WINDOW; + SET_WINDOW_NEXT(LARGE_INT); + SET_WINDOW_LAST_RECV(LARGE_INT+1); + + t = LARGE_INT+9; + + assert_int_equal(_dtls_record_check(&state, t), 0); +} + +static void check_dtls_window_very_large_12(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + + RESET_WINDOW; + SET_WINDOW_NEXT(INT_OVER_32_BITS); + SET_WINDOW_LAST_RECV(INT_OVER_32_BITS+1); + + t = INT_OVER_32_BITS+2; + + assert_int_equal(_dtls_record_check(&state, t), 0); +} + +static void check_dtls_window_very_large_91(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + + RESET_WINDOW; + SET_WINDOW_NEXT(INT_OVER_32_BITS); + SET_WINDOW_LAST_RECV(INT_OVER_32_BITS+9); + + t = INT_OVER_32_BITS+1; + + assert_int_equal(_dtls_record_check(&state, t), 0); +} + +static void check_dtls_window_very_large_19(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + + RESET_WINDOW; + SET_WINDOW_NEXT(INT_OVER_32_BITS); + SET_WINDOW_LAST_RECV(INT_OVER_32_BITS+1); + + t = INT_OVER_32_BITS+9; + + assert_int_equal(_dtls_record_check(&state, t), 0); +} + +static void check_dtls_window_outside(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + + RESET_WINDOW; + SET_WINDOW_NEXT(0); + SET_WINDOW_LAST_RECV(1); + + t = 1+64; + + assert_int_equal(_dtls_record_check(&state, t), 0); +} + +static void check_dtls_window_large_outside(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + + RESET_WINDOW; + SET_WINDOW_NEXT(LARGE_INT); + SET_WINDOW_LAST_RECV(LARGE_INT+1); + + t = LARGE_INT+1+64; + + assert_int_equal(_dtls_record_check(&state, t), 0); +} + +static void check_dtls_window_very_large_outside(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + + RESET_WINDOW; + SET_WINDOW_NEXT(INT_OVER_32_BITS); + SET_WINDOW_LAST_RECV(INT_OVER_32_BITS+1); + + t = INT_OVER_32_BITS+1+64; + + assert_int_equal(_dtls_record_check(&state, t), 0); +} + +static void check_dtls_window_dup1(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + + RESET_WINDOW; + SET_WINDOW_NEXT(LARGE_INT-1); + SET_WINDOW_LAST_RECV(LARGE_INT); + + t = LARGE_INT; + assert_int_equal(_dtls_record_check(&state, t), 0); + + t = LARGE_INT+1; + assert_int_equal(_dtls_record_check(&state, t), 0); + + t = LARGE_INT+16; + assert_int_equal(_dtls_record_check(&state, t), 0); + + t = LARGE_INT+1; + assert_int_equal(_dtls_record_check(&state, t), -3); +} + +static void check_dtls_window_dup2(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + + RESET_WINDOW; + SET_WINDOW_NEXT(LARGE_INT-1); + SET_WINDOW_LAST_RECV(LARGE_INT); + + t = LARGE_INT; + assert_int_equal(_dtls_record_check(&state, t), 0); + + t = LARGE_INT+16; + assert_int_equal(_dtls_record_check(&state, t), 0); + + t = LARGE_INT+1; + assert_int_equal(_dtls_record_check(&state, t), 0); + + t = LARGE_INT+16; + assert_int_equal(_dtls_record_check(&state, t), -3); +} + +static void check_dtls_window_dup3(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + + RESET_WINDOW; + SET_WINDOW_NEXT(LARGE_INT-1); + SET_WINDOW_LAST_RECV(LARGE_INT); + + t = LARGE_INT; + assert_int_equal(_dtls_record_check(&state, t), 0); + + t = LARGE_INT+16; + assert_int_equal(_dtls_record_check(&state, t), 0); + + t = LARGE_INT+15; + assert_int_equal(_dtls_record_check(&state, t), 0); + + t = LARGE_INT+14; + assert_int_equal(_dtls_record_check(&state, t), 0); + + t = LARGE_INT+5; + assert_int_equal(_dtls_record_check(&state, t), 0); + + t = LARGE_INT+5; + assert_int_equal(_dtls_record_check(&state, t), -3); +} + +static void check_dtls_window_out_of_order(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + + RESET_WINDOW; + SET_WINDOW_NEXT(LARGE_INT-1); + SET_WINDOW_LAST_RECV(LARGE_INT); + + t = LARGE_INT; + assert_int_equal(_dtls_record_check(&state, t), 0); + + t = LARGE_INT+8; + assert_int_equal(_dtls_record_check(&state, t), 0); + + t = LARGE_INT+7; + assert_int_equal(_dtls_record_check(&state, t), 0); + + t = LARGE_INT+6; + assert_int_equal(_dtls_record_check(&state, t), 0); + + t = LARGE_INT+5; + assert_int_equal(_dtls_record_check(&state, t), 0); + + t = LARGE_INT+4; + assert_int_equal(_dtls_record_check(&state, t), 0); + + t = LARGE_INT+3; + assert_int_equal(_dtls_record_check(&state, t), 0); + + t = LARGE_INT+2; + assert_int_equal(_dtls_record_check(&state, t), 0); + + t = LARGE_INT+1; + assert_int_equal(_dtls_record_check(&state, t), 0); + + t = LARGE_INT+9; + assert_int_equal(_dtls_record_check(&state, t), 0); +} + +static void check_dtls_window_epoch_higher(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + + RESET_WINDOW; + SET_WINDOW_NEXT(LARGE_INT-1); + SET_WINDOW_LAST_RECV(LARGE_INT); + + t = LARGE_INT; + assert_int_equal(_dtls_record_check(&state, t), 0); + + t = (LARGE_INT+8)|0x1000000000000LL; + assert_int_equal(_dtls_record_check(&state, t), -1); +} + +static void check_dtls_window_epoch_lower(void **glob_state) +{ + struct record_parameters_st state; + uint64_t t; + + RESET_WINDOW; + t = 0x1000000000000LL; + + state.epoch = 1; + SET_WINDOW_NEXT(0x1000000000000LL); + SET_WINDOW_LAST_RECV((0x1000000000000LL) + 1); + + t = 2 | 0x1000000000000LL; + assert_int_equal(_dtls_record_check(&state, t), 0); + + t = 3 | 0x1000000000000LL; + assert_int_equal(_dtls_record_check(&state, t), 0); + + t = 5; + assert_int_equal(_dtls_record_check(&state, t), -1); +} + + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(check_dtls_window_uninit_0), + cmocka_unit_test(check_dtls_window_uninit_large), + cmocka_unit_test(check_dtls_window_uninit_very_large), + cmocka_unit_test(check_dtls_window_12), + cmocka_unit_test(check_dtls_window_21), + cmocka_unit_test(check_dtls_window_19), + cmocka_unit_test(check_dtls_window_91), + cmocka_unit_test(check_dtls_window_large_21), + cmocka_unit_test(check_dtls_window_large_12), + cmocka_unit_test(check_dtls_window_large_19), + cmocka_unit_test(check_dtls_window_large_91), + cmocka_unit_test(check_dtls_window_dup1), + cmocka_unit_test(check_dtls_window_dup2), + cmocka_unit_test(check_dtls_window_dup3), + cmocka_unit_test(check_dtls_window_outside), + cmocka_unit_test(check_dtls_window_large_outside), + cmocka_unit_test(check_dtls_window_out_of_order), + cmocka_unit_test(check_dtls_window_epoch_lower), + cmocka_unit_test(check_dtls_window_epoch_higher), + cmocka_unit_test(check_dtls_window_very_large_12), + cmocka_unit_test(check_dtls_window_very_large_19), + cmocka_unit_test(check_dtls_window_very_large_91), + cmocka_unit_test(check_dtls_window_very_large_outside), + cmocka_unit_test(check_dtls_window_skip3), + cmocka_unit_test(check_dtls_window_skip1) + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/dtls-with-seccomp.c b/tests/dtls-with-seccomp.c new file mode 100644 index 0000000..357c333 --- /dev/null +++ b/tests/dtls-with-seccomp.c @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2015 Nikos Mavrogiannopoulos + * + * Author: Nikos Mavrogiannopoulos + * + * 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> + +#if defined(_WIN32) || !defined(HAVE_LIBSECCOMP) + +int main() +{ + exit(77); +} + +#else + +#include <string.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <signal.h> +#include <assert.h> +#include <gnutls/gnutls.h> +#include <gnutls/dtls.h> + +#include "cert-common.h" +#include "utils.h" + +static void terminate(void); + +static void server_log_func(int level, const char *str) +{ + fprintf(stderr, "server|<%d>| %s", level, str); +} + +static void client_log_func(int level, const char *str) +{ + fprintf(stderr, "client|<%d>| %s", level, str); +} + +#define MAX_BUF 1024 + +static ssize_t +push(gnutls_transport_ptr_t tr, const void *data, size_t len) +{ + int fd = (long int) tr; + + return send(fd, data, len, 0); +} + +static void client(int fd, const char *prio) +{ + int ret; + char buffer[MAX_BUF + 1]; + gnutls_certificate_credentials_t xcred; + gnutls_session_t session; + + global_init(); + + if (debug) { + gnutls_global_set_log_function(client_log_func); + gnutls_global_set_log_level(4711); + } + + gnutls_certificate_allocate_credentials(&xcred); + + /* Initialize TLS session + */ + gnutls_init(&session, GNUTLS_CLIENT | GNUTLS_DATAGRAM); + gnutls_dtls_set_mtu(session, 1500); + gnutls_handshake_set_timeout(session, get_timeout()); + + assert(gnutls_priority_set_direct(session, prio, NULL) >= 0); + + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred); + + gnutls_transport_set_int(session, fd); + gnutls_transport_set_push_function(session, push); + + /* 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))); + + do { + ret = gnutls_record_recv(session, buffer, sizeof(buffer)-1); + } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + + if (ret == 0) { + if (debug) + success + ("client: Peer has closed the TLS connection\n"); + goto end; + } else if (ret < 0) { + fail("client: Error: %s\n", gnutls_strerror(ret)); + exit(1); + } + + ret = gnutls_bye(session, GNUTLS_SHUT_RDWR); + if (ret < 0) { + fail("server: error in closing session: %s\n", gnutls_strerror(ret)); + } + + end: + + close(fd); + + gnutls_deinit(session); + + gnutls_certificate_free_credentials(xcred); + + gnutls_global_deinit(); +} + + +/* These are global */ +pid_t child; + +static void terminate(void) +{ + int status; + assert(child); + kill(child, SIGTERM); + wait(&status); + exit(1); +} + +static void server(int fd, const char *prio) +{ + int ret; + gnutls_certificate_credentials_t xcred; + char buffer[MAX_BUF + 1]; + gnutls_session_t session; + + /* this must be called once in the program + */ + global_init(); + + if (debug) { + gnutls_global_set_log_function(server_log_func); + gnutls_global_set_log_level(4711); + } + + gnutls_certificate_allocate_credentials(&xcred); + + ret = gnutls_certificate_set_x509_key_mem(xcred, + &server_cert, &server_key, + GNUTLS_X509_FMT_PEM); + if (ret < 0) + exit(1); + + ret = disable_system_calls(); + if (ret < 0) { + fprintf(stderr, "could not enable seccomp\n"); + exit(2); + } + + gnutls_init(&session, GNUTLS_SERVER | GNUTLS_DATAGRAM); + gnutls_handshake_set_timeout(session, get_timeout()); + gnutls_dtls_set_mtu(session, 1500); + + assert(gnutls_priority_set_direct(session, prio, NULL) >= 0); + + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred); + + gnutls_transport_set_int(session, fd); + gnutls_transport_set_push_function(session, push); + + 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))); + + /* see the Getting peer's information example */ + /* print_info(session); */ + + memset(buffer, 1, sizeof(buffer)); + do { + ret = gnutls_record_send(session, buffer, sizeof(buffer)-1); + } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + + if (ret < 0) { + close(fd); + gnutls_deinit(session); + fail("server: data sending has failed (%s)\n\n", + gnutls_strerror(ret)); + terminate(); + } + + ret = gnutls_bye(session, GNUTLS_SHUT_RDWR); + if (ret < 0) { + fail("server: error in closing session: %s\n", gnutls_strerror(ret)); + } + + close(fd); + gnutls_deinit(session); + + gnutls_certificate_free_credentials(xcred); + + gnutls_global_deinit(); + + if (debug) + success("server: finished\n"); +} + +static +void run(const char *name, const char *prio) +{ + int fd[2]; + int ret; + + success("trying: %s\n", name); + signal(SIGPIPE, SIG_IGN); + + ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd); + if (ret < 0) { + perror("socketpair"); + exit(1); + } + + child = fork(); + if (child < 0) { + perror("fork"); + fail("fork"); + exit(1); + } + + if (child) { + int status; + /* parent */ + + close(fd[0]); + client(fd[1], prio); + + wait(&status); + check_wait_status(status); + } else { + close(fd[1]); + server(fd[0], prio); + exit(0); + } +} + +void doit(void) +{ + run("dtls1.0", "NORMAL:-KX-ALL:+ECDHE-RSA:-VERS-ALL:+VERS-DTLS1.0"); + run("dtls1.2", "NORMAL:-KX-ALL:+ECDHE-RSA:-VERS-ALL:+VERS-DTLS1.2"); + run("default", "NORMAL"); +} + + +#endif /* _WIN32 */ diff --git a/tests/dtls/dtls-resume.sh b/tests/dtls/dtls-resume.sh new file mode 100755 index 0000000..debd59e --- /dev/null +++ b/tests/dtls/dtls-resume.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +# Copyright (C) 2016 Red Hat, Inc. +# +# Author: Nikos Mavrogiannopoulos +# +# 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. + +set -e + +if test "${WINDIR}" != ""; then + exit 77 +fi + +./dtls-stress -resume -sfinished 012 -cfinished 01 -d +./dtls-stress -resume -sfinished 210 -cfinished 01 -d +./dtls-stress -resume -sfinished 120 -cfinished 01 -d +./dtls-stress -resume -sfinished 210 -cfinished 10 -d +./dtls-stress -resume -sfinished 120 -cfinished 10 -d + +./dtls-stress -resume -sfinished 012 -cfinished 01 -d SHello +./dtls-stress -resume -sfinished 012 -cfinished 01 -d CChangeCipherSpec +./dtls-stress -resume -sfinished 012 -cfinished 01 -d SChangeCipherSpec +./dtls-stress -resume -sfinished 012 -cfinished 01 -d SHello SChangeCipherSpec +./dtls-stress -resume -sfinished 012 -cfinished 01 -d CChangeCipherSpec SChangeCipherSpec CFinished +./dtls-stress -resume -sfinished 012 -cfinished 01 -d CChangeCipherSpec SChangeCipherSpec SFinished +./dtls-stress -resume -sfinished 012 -cfinished 01 -d CFinished SFinished +./dtls-stress -resume -sfinished 012 -cfinished 01 -d CFinished SFinished SChangeCipherSpec +./dtls-stress -resume -sfinished 012 -cfinished 01 -d CFinished SFinished CChangeCipherSpec + +exit 0 diff --git a/tests/dtls/dtls-stress.c b/tests/dtls/dtls-stress.c new file mode 100644 index 0000000..826bd29 --- /dev/null +++ b/tests/dtls/dtls-stress.c @@ -0,0 +1,1558 @@ +/* + * Copyright (C) 2012-2016 Sean Buckheister + * Copyright (C) 2016 Nikos Mavrogiannopoulos + * Copyright (C) 2016 Red Hat, Inc. + * + * 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 + */ + +/* + * DTLS stress test utility + * + * **** Available parameters **** + * + * -nb enable nonblocking operations on sessions + * -batch read test identifiers from stdin and run them + * -d increase debug level by one + * -r replay messages (very crude replay mechanism) + * -d <n> set debug level to <n> + * -die don't start new tests after the first detected failure + * -timeout <n> set handshake timeout to <n> seconds. Tests that don't make progress + * within twice this time will be forcibly killed. (default: 120) + * -retransmit <n> set retransmit timeout to <n> milliseconds (default: 100) + * -j <n> run up to <n> tests in parallel + * -full use full handshake with mutual certificate authentication + * -resume use resumed handshake + * -shello <perm> run only one test, with the server hello flight permuted as <perm> + * -sfinished <perm> run only one test, with the server finished flight permuted as <perm> + * -cfinished <perm> run only one test, with the client finished flight permuted as <perm> + * <packet name> run only one test, drop <packet name> three times + * valid values for <packet name> are: + * SHello, SCertificate, SKeyExchange, SCertificateRequest, SHelloDone, + * CCertificate, CKeyExchange, CCertificateVerify, CChangeCipherSpec, + * CFinished, SChangeCipherSpec, SFinished + * using *Certificate* without -full will yield unexpected results + * + * + * **** Permutation handling **** + * + * Flight length for -sfinished is 2, for -shello and -cfinished they are 5 with -full, 3 otherwise. + * Permutations are given with base 0 and specify the order in which reordered packets are transmitted. + * For example, -full -shello 42130 will transmit server hello flight packets in the order + * SHelloDone, SKeyExchange, SCertificate, SCertificateRequest, SHello + * + * When -resume is specified the -sfinished flight length is 3 (same as shello), cfinished is 2. + * The -resume option has to be combined with sfinished or cfinished. + * + * **** Output format **** + * + * Every line printed for any given test is prefixed by a unique id for that test. See run_test_by_id for + * exact composition. Errors encountered during execution are printed, with one status line after test + * completen. The format for this line is as follows: + * + * <id> <status> SHello(<shperm>), SFinished(<sfinperm>), CFinished(<cfinperm>) :- <drops> + * + * The format for error lines is <id> <role>| <text>, with <role> being the role of the child process + * that encountered the error, and <text> being obvious. + * + * <id> is the unique id for the test, it can be used as input to -batch. + * <status> can be ++ for a successful test, -- for a failure, TT for a deadlock timeout killed test, + * or !! for a test has died due to some unforeseen circumstances like syscall failures. + * <shperm>, <sfinperm>, <cfinperm> show the permutation for the respective flights used. + * They can be used as input to -shello, -sfinished, and -cfinished, respectively. + * <drops> is a comma separated list of <packet name>, one for every packet dropped thrice + * + * + * **** Exit status **** + * + * 0 all tests have passed + * 1 some tests have failed + * 4 the master processed has encountered unexpected errors + * 8 error parsing command line + */ + +#include <config.h> +#include <gnutls/gnutls.h> +#include <gnutls/openpgp.h> +#include <gnutls/dtls.h> +#include <unistd.h> +#include "../utils.h" +#include <sys/socket.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <poll.h> +#include <time.h> +#include <assert.h> +#include <sys/wait.h> + +#if _POSIX_TIMERS && (_POSIX_TIMERS - 200112L) >= 0 + +// {{{ types + +#define log(fmt, ...) \ + if (debug) fprintf(stdout, "%i %s| "fmt, run_id, role_name, ##__VA_ARGS__) + +typedef struct { + int count; +} filter_packet_state_t; + +typedef struct { + const char *name; + gnutls_datum_t packets[5]; + int *order; + int count; +} filter_permute_state_t; + +typedef void (*filter_fn) (gnutls_transport_ptr_t, const unsigned char *, + size_t); + +typedef int (*match_fn) (const unsigned char *, size_t); + +enum role { SERVER, CLIENT }; + +// }}} + +// {{{ static data + +static int permutations2[2][2] += { {0, 1}, {1, 0} }; + +static const char *permutation_names2[] += { "01", "10", 0 }; + +static int permutations3[6][3] += { {0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0} }; + +static const char *permutation_names3[] += { "012", "021", "102", "120", "201", "210", 0 }; + +static int permutations5[120][5] = { + {0, 1, 2, 3, 4}, {0, 2, 1, 3, 4}, {1, 0, 2, 3, 4}, {1, 2, 0, 3, 4}, + {2, 0, 1, 3, 4}, {2, 1, 0, 3, 4}, {0, 1, 3, 2, 4}, {0, 2, 3, 1, 4}, + {1, 0, 3, 2, 4}, {1, 2, 3, 0, 4}, {2, 0, 3, 1, 4}, {2, 1, 3, 0, 4}, + {0, 3, 1, 2, 4}, {0, 3, 2, 1, 4}, {1, 3, 0, 2, 4}, {1, 3, 2, 0, 4}, + {2, 3, 0, 1, 4}, {2, 3, 1, 0, 4}, {3, 0, 1, 2, 4}, {3, 0, 2, 1, 4}, + {3, 1, 0, 2, 4}, {3, 1, 2, 0, 4}, {3, 2, 0, 1, 4}, {3, 2, 1, 0, 4}, + {0, 1, 2, 4, 3}, {0, 2, 1, 4, 3}, {1, 0, 2, 4, 3}, {1, 2, 0, 4, 3}, + {2, 0, 1, 4, 3}, {2, 1, 0, 4, 3}, {0, 1, 3, 4, 2}, {0, 2, 3, 4, 1}, + {1, 0, 3, 4, 2}, {1, 2, 3, 4, 0}, {2, 0, 3, 4, 1}, {2, 1, 3, 4, 0}, + {0, 3, 1, 4, 2}, {0, 3, 2, 4, 1}, {1, 3, 0, 4, 2}, {1, 3, 2, 4, 0}, + {2, 3, 0, 4, 1}, {2, 3, 1, 4, 0}, {3, 0, 1, 4, 2}, {3, 0, 2, 4, 1}, + {3, 1, 0, 4, 2}, {3, 1, 2, 4, 0}, {3, 2, 0, 4, 1}, {3, 2, 1, 4, 0}, + {0, 1, 4, 2, 3}, {0, 2, 4, 1, 3}, {1, 0, 4, 2, 3}, {1, 2, 4, 0, 3}, + {2, 0, 4, 1, 3}, {2, 1, 4, 0, 3}, {0, 1, 4, 3, 2}, {0, 2, 4, 3, 1}, + {1, 0, 4, 3, 2}, {1, 2, 4, 3, 0}, {2, 0, 4, 3, 1}, {2, 1, 4, 3, 0}, + {0, 3, 4, 1, 2}, {0, 3, 4, 2, 1}, {1, 3, 4, 0, 2}, {1, 3, 4, 2, 0}, + {2, 3, 4, 0, 1}, {2, 3, 4, 1, 0}, {3, 0, 4, 1, 2}, {3, 0, 4, 2, 1}, + {3, 1, 4, 0, 2}, {3, 1, 4, 2, 0}, {3, 2, 4, 0, 1}, {3, 2, 4, 1, 0}, + {0, 4, 1, 2, 3}, {0, 4, 2, 1, 3}, {1, 4, 0, 2, 3}, {1, 4, 2, 0, 3}, + {2, 4, 0, 1, 3}, {2, 4, 1, 0, 3}, {0, 4, 1, 3, 2}, {0, 4, 2, 3, 1}, + {1, 4, 0, 3, 2}, {1, 4, 2, 3, 0}, {2, 4, 0, 3, 1}, {2, 4, 1, 3, 0}, + {0, 4, 3, 1, 2}, {0, 4, 3, 2, 1}, {1, 4, 3, 0, 2}, {1, 4, 3, 2, 0}, + {2, 4, 3, 0, 1}, {2, 4, 3, 1, 0}, {3, 4, 0, 1, 2}, {3, 4, 0, 2, 1}, + {3, 4, 1, 0, 2}, {3, 4, 1, 2, 0}, {3, 4, 2, 0, 1}, {3, 4, 2, 1, 0}, + {4, 0, 1, 2, 3}, {4, 0, 2, 1, 3}, {4, 1, 0, 2, 3}, {4, 1, 2, 0, 3}, + {4, 2, 0, 1, 3}, {4, 2, 1, 0, 3}, {4, 0, 1, 3, 2}, {4, 0, 2, 3, 1}, + {4, 1, 0, 3, 2}, {4, 1, 2, 3, 0}, {4, 2, 0, 3, 1}, {4, 2, 1, 3, 0}, + {4, 0, 3, 1, 2}, {4, 0, 3, 2, 1}, {4, 1, 3, 0, 2}, {4, 1, 3, 2, 0}, + {4, 2, 3, 0, 1}, {4, 2, 3, 1, 0}, {4, 3, 0, 1, 2}, {4, 3, 0, 2, 1}, + {4, 3, 1, 0, 2}, {4, 3, 1, 2, 0}, {4, 3, 2, 0, 1}, {4, 3, 2, 1, 0} +}; + +static const char *permutation_names5[] + = { "01234", "02134", "10234", "12034", "20134", "21034", "01324", + "02314", "10324", "12304", "20314", "21304", "03124", "03214", + "13024", "13204", "23014", "23104", "30124", "30214", "31024", + "31204", "32014", "32104", "01243", "02143", "10243", "12043", + "20143", "21043", "01342", "02341", "10342", "12340", "20341", + "21340", "03142", "03241", "13042", "13240", "23041", "23140", + "30142", "30241", "31042", "31240", "32041", "32140", "01423", + "02413", "10423", "12403", "20413", "21403", "01432", "02431", + "10432", "12430", "20431", "21430", "03412", "03421", "13402", + "13420", "23401", "23410", "30412", "30421", "31402", "31420", + "32401", "32410", "04123", "04213", "14023", "14203", "24013", + "24103", "04132", "04231", "14032", "14230", "24031", "24130", + "04312", "04321", "14302", "14320", "24301", "24310", "34012", + "34021", "34102", "34120", "34201", "34210", "40123", "40213", + "41023", "41203", "42013", "42103", "40132", "40231", "41032", + "41230", "42031", "42130", "40312", "40321", "41302", "41320", + "42301", "42310", "43012", "43021", "43102", "43120", "43201", + "43210", 0 +}; + +static const char *filter_names[8] + = { "SHello", + "SKeyExchange", + "SHelloDone", + "CKeyExchange", + "CChangeCipherSpec", + "CFinished", + "SChangeCipherSpec", + "SFinished" +}; + +static const char *filter_names_resume[] + = { "SHello", + "SChangeCipherSpec", + "SFinished", + "CChangeCipherSpec", + "CFinished" +}; + +static const char *filter_names_full[12] + = { "SHello", + "SCertificate", + "SKeyExchange", + "SCertificateRequest", + "SHelloDone", + "CCertificate", + "CKeyExchange", + "CCertificateVerify", + "CChangeCipherSpec", + "CFinished", + "SChangeCipherSpec", + "SFinished" +}; + +#include "cert-common.h" + +// }}} + +// {{{ other global state + +enum role role; + +#define role_name (role == SERVER ? "server" : "client") + +int debug; +int nonblock; +int replay; +int full; +int resume; +int timeout_seconds; +int retransmit_milliseconds; +int run_to_end; + +int run_id; + +// }}} + +// {{{ logging and error handling + +static void logfn(int level, const char *s) +{ + if (debug) { + fprintf(stdout, "%i %s|<%i> %s", run_id, role_name, level, s); + } +} + +static void auditfn(gnutls_session_t session, const char *s) +{ + if (debug) { + fprintf(stdout, "%i %s| %s", run_id, role_name, s); + } +} + +static void drop(const char *packet) +{ + if (debug) { + log("dropping %s\n", packet); + } +} + +static int _process_error(int loc, int code, int die) +{ + if (code < 0 && (die || code != GNUTLS_E_AGAIN)) { + fprintf(stdout, "%i <%s tls> line %i: %s", run_id, + role_name, loc, gnutls_strerror(code)); + if (gnutls_error_is_fatal(code) || die) { + fprintf(stdout, " (fatal)\n"); + exit(1); + } else { + fprintf(stdout, "\n"); + } + } + return code; +} + +#define die_on_error(code) _process_error(__LINE__, code, 1) +#define process_error(code) _process_error(__LINE__, code, 0) + +static void _process_error_or_timeout(int loc, int err, time_t tdiff) +{ + if (err < 0) { + if (err != GNUTLS_E_TIMEDOUT || tdiff >= 60) { + _process_error(loc, err, 0); + } else { + log("line %i: {spurious timeout} (fatal)", loc); + exit(1); + } + } +} + +#define process_error_or_timeout(code, tdiff) _process_error_or_timeout(__LINE__, code, tdiff) + +static void rperror(const char *name) +{ + fprintf(stdout, "%i %s| %s\n", run_id, role_name, name); +} + +// }}} + +// {{{ init, shared, and teardown code and data for packet stream filters + +filter_packet_state_t state_packet_ServerHello = { 0 }; +filter_packet_state_t state_packet_ServerCertificate = { 0 }; +filter_packet_state_t state_packet_ServerKeyExchange = { 0 }; +filter_packet_state_t state_packet_ServerCertificateRequest = { 0 }; +filter_packet_state_t state_packet_ServerHelloDone = { 0 }; +filter_packet_state_t state_packet_ClientCertificate = { 0 }; +filter_packet_state_t state_packet_ClientKeyExchange = { 0 }; +filter_packet_state_t state_packet_ClientCertificateVerify = { 0 }; +filter_packet_state_t state_packet_ClientChangeCipherSpec = { 0 }; +filter_packet_state_t state_packet_ClientFinished = { 0 }; +filter_packet_state_t state_packet_ClientFinishedResume = { 0 }; +filter_packet_state_t state_packet_ServerChangeCipherSpec = { 0 }; +filter_packet_state_t state_packet_ServerFinished = { 0 }; +filter_packet_state_t state_packet_ServerFinishedResume = { 0 }; + +static filter_permute_state_t state_permute_ServerHello = + { 0, {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 0, 0 }; +static filter_permute_state_t state_permute_ServerHelloFull = + { 0, {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 0, 0 }; +static filter_permute_state_t state_permute_ServerFinished = + { 0, {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 0, 0 }; +static filter_permute_state_t state_permute_ServerFinishedResume = + { 0, {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 0, 0 }; +static filter_permute_state_t state_permute_ClientFinished = + { 0, {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 0, 0 }; +static filter_permute_state_t state_permute_ClientFinishedResume = + { 0, {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 0, 0 }; +static filter_permute_state_t state_permute_ClientFinishedFull = + { 0, {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 0, 0 }; + +filter_fn filter_chain[32]; +int filter_current_idx; + +static void filter_permute_state_free_buffer(filter_permute_state_t * state) +{ + unsigned int i; + + for (i = 0; i < sizeof(state->packets) / sizeof(state->packets[0]); i++) { + free(state->packets[i].data); + state->packets[i].data = NULL; + } +} + +static void filter_clear_state(void) +{ + filter_current_idx = 0; + + filter_permute_state_free_buffer(&state_permute_ServerHello); + filter_permute_state_free_buffer(&state_permute_ServerHelloFull); + filter_permute_state_free_buffer(&state_permute_ServerFinished); + filter_permute_state_free_buffer(&state_permute_ServerFinishedResume); + filter_permute_state_free_buffer(&state_permute_ClientFinished); + filter_permute_state_free_buffer(&state_permute_ClientFinishedResume); + filter_permute_state_free_buffer(&state_permute_ClientFinishedFull); + + memset(&state_packet_ServerHello, 0, sizeof(state_packet_ServerHello)); + memset(&state_packet_ServerCertificate, 0, + sizeof(state_packet_ServerCertificate)); + memset(&state_packet_ServerKeyExchange, 0, + sizeof(state_packet_ServerKeyExchange)); + memset(&state_packet_ServerCertificateRequest, 0, + sizeof(state_packet_ServerCertificateRequest)); + memset(&state_packet_ServerHelloDone, 0, + sizeof(state_packet_ServerHelloDone)); + memset(&state_packet_ClientCertificate, 0, + sizeof(state_packet_ClientCertificate)); + memset(&state_packet_ClientKeyExchange, 0, + sizeof(state_packet_ClientKeyExchange)); + memset(&state_packet_ClientCertificateVerify, 0, + sizeof(state_packet_ClientCertificateVerify)); + memset(&state_packet_ClientChangeCipherSpec, 0, + sizeof(state_packet_ClientChangeCipherSpec)); + memset(&state_packet_ClientFinished, 0, + sizeof(state_packet_ClientFinished)); + memset(&state_packet_ClientFinishedResume, 0, + sizeof(state_packet_ClientFinishedResume)); + memset(&state_packet_ServerChangeCipherSpec, 0, + sizeof(state_packet_ServerChangeCipherSpec)); + memset(&state_packet_ServerFinished, 0, + sizeof(state_packet_ServerFinished)); + memset(&state_packet_ServerFinishedResume, 0, + sizeof(state_packet_ServerFinishedResume)); + memset(&state_permute_ServerHello, 0, + sizeof(state_permute_ServerHello)); + memset(&state_permute_ServerHelloFull, 0, + sizeof(state_permute_ServerHelloFull)); + memset(&state_permute_ServerFinished, 0, + sizeof(state_permute_ServerFinished)); + memset(&state_permute_ClientFinished, 0, + sizeof(state_permute_ClientFinished)); + memset(&state_permute_ClientFinishedResume, 0, + sizeof(state_permute_ClientFinishedResume)); + memset(&state_permute_ClientFinishedFull, 0, + sizeof(state_permute_ClientFinishedFull)); + + state_permute_ServerHello.name = "ServerHello"; + state_permute_ServerHelloFull.name = "ServerHelloFull"; + state_permute_ServerFinished.name = "ServerFinished"; + state_permute_ServerFinishedResume.name = "ServerFinishedResume"; + state_permute_ClientFinished.name = "ClientFinished"; + state_permute_ClientFinishedResume.name = "ClientFinishedResume"; + state_permute_ClientFinishedFull.name = "ClientFinishedFull"; +} + +/* replay buffer */ +static int rbuffer[5 * 1024]; +unsigned rbuffer_size = 0; + +static void filter_run_next(gnutls_transport_ptr_t fd, + const unsigned char *buffer, size_t len) +{ + int ret = 0; + filter_fn fn = filter_chain[filter_current_idx]; + filter_current_idx++; + if (fn) { + fn(fd, buffer, len); + } else { + ret = send((int)(intptr_t) fd, buffer, len, 0); + } + filter_current_idx--; + + if (ret > 0 && replay != 0) { + if (rbuffer_size == 0 && len < sizeof(rbuffer)) { + memcpy(rbuffer, buffer, len); + rbuffer_size = len; + } else if (rbuffer_size != 0) { + send((int)(intptr_t) fd, rbuffer, rbuffer_size, 0); + if (len < sizeof(rbuffer) && len > rbuffer_size) { + memcpy(rbuffer, buffer, len); + rbuffer_size = len; + } + } + } +} + +// }}} + +// {{{ packet match functions + +static int match_ServerHello(const unsigned char *buffer, size_t len) +{ + return role == SERVER && len >= 13 + 1 && buffer[0] == 22 + && buffer[13] == 2; +} + +static int match_ServerCertificate(const unsigned char *buffer, size_t len) +{ + return role == SERVER && len >= 13 + 1 && buffer[0] == 22 + && buffer[13] == 11; +} + +static int match_ServerKeyExchange(const unsigned char *buffer, size_t len) +{ + return role == SERVER && len >= 13 + 1 && buffer[0] == 22 + && buffer[13] == 12; +} + +static int match_ServerCertificateRequest(const unsigned char *buffer, + size_t len) +{ + return role == SERVER && len >= 13 + 1 && buffer[0] == 22 + && buffer[13] == 13; +} + +static int match_ServerHelloDone(const unsigned char *buffer, size_t len) +{ + return role == SERVER && len >= 13 + 1 && buffer[0] == 22 + && buffer[13] == 14; +} + +static int match_ClientCertificate(const unsigned char *buffer, size_t len) +{ + return role == CLIENT && len >= 13 + 1 && buffer[0] == 22 + && buffer[13] == 11; +} + +static int match_ClientKeyExchange(const unsigned char *buffer, size_t len) +{ + return role == CLIENT && len >= 13 + 1 && buffer[0] == 22 + && buffer[13] == 16; +} + +static int match_ClientCertificateVerify(const unsigned char *buffer, + size_t len) +{ + return role == CLIENT && len >= 13 + 1 && buffer[0] == 22 + && buffer[13] == 15; +} + +static int match_ClientChangeCipherSpec(const unsigned char *buffer, size_t len) +{ + return role == CLIENT && len >= 13 && buffer[0] == 20; +} + +static int match_ClientFinished(const unsigned char *buffer, size_t len) +{ + return role == CLIENT && len >= 13 && buffer[0] == 22 && buffer[4] == 1; +} + +static int match_ServerChangeCipherSpec(const unsigned char *buffer, size_t len) +{ + return role == SERVER && len >= 13 && buffer[0] == 20; +} + +static int match_ServerFinished(const unsigned char *buffer, size_t len) +{ + return role == SERVER && len >= 13 && buffer[0] == 22 && buffer[4] == 1; +} + +// }}} + +// {{{ packet drop filters + +#define FILTER_DROP_COUNT 3 +#define DECLARE_FILTER(packet) \ + static void filter_packet_##packet(gnutls_transport_ptr_t fd, \ + const unsigned char* buffer, size_t len) \ + { \ + if (match_##packet(buffer, len) && (state_packet_##packet).count++ < FILTER_DROP_COUNT) { \ + drop(#packet); \ + } else { \ + filter_run_next(fd, buffer, len); \ + } \ + } + +DECLARE_FILTER(ServerHello) + DECLARE_FILTER(ServerCertificate) + DECLARE_FILTER(ServerKeyExchange) + DECLARE_FILTER(ServerCertificateRequest) + DECLARE_FILTER(ServerHelloDone) + DECLARE_FILTER(ClientCertificate) + DECLARE_FILTER(ClientKeyExchange) + DECLARE_FILTER(ClientCertificateVerify) + DECLARE_FILTER(ClientChangeCipherSpec) + DECLARE_FILTER(ClientFinished) + DECLARE_FILTER(ServerChangeCipherSpec) + DECLARE_FILTER(ServerFinished) +// }}} +// {{{ flight permutation filters +static void filter_permute_state_run(filter_permute_state_t * state, + int packetCount, + gnutls_transport_ptr_t fd, + const unsigned char *buffer, size_t len) +{ + unsigned char *data; + int packet = state->order[state->count]; + + if (debug > 2) + log("running permutation for %s/%d/%d\n", state->name, packetCount, state->count); + + data = malloc(len); + assert(data); + memcpy(data, buffer, len); + state->packets[packet].data = data; + state->packets[packet].size = len; + state->count++; + + if (state->count == packetCount) { + for (packet = 0; packet < packetCount; packet++) { + filter_run_next(fd, state->packets[packet].data, + state->packets[packet].size); + } + filter_permute_state_free_buffer(state); + state->count = 0; + } +} + +#define DECLARE_PERMUTE(flight) \ + static void filter_permute_##flight(gnutls_transport_ptr_t fd, \ + const unsigned char* buffer, size_t len) \ + { \ + int count = sizeof(permute_match_##flight) / sizeof(permute_match_##flight[0]); \ + int i; \ + for (i = 0; i < count; i++) { \ + if (permute_match_##flight[i](buffer, len)) { \ + filter_permute_state_run(&state_permute_##flight, count, fd, buffer, len); \ + return; \ + } \ + } \ + filter_run_next(fd, buffer, len); \ + } + +static match_fn permute_match_ServerHello[] = + { match_ServerHello, match_ServerKeyExchange, match_ServerHelloDone }; + +static match_fn permute_match_ServerHelloFull[] = + { match_ServerHello, match_ServerCertificate, match_ServerKeyExchange, + match_ServerCertificateRequest, match_ServerHelloDone +}; + +static match_fn permute_match_ServerFinished[] = + { match_ServerChangeCipherSpec, match_ServerFinished }; + +static match_fn permute_match_ServerFinishedResume[] = + { match_ServerHello, match_ServerChangeCipherSpec, match_ServerFinished }; + +static match_fn permute_match_ClientFinished[] = + { match_ClientKeyExchange, match_ClientChangeCipherSpec, + match_ClientFinished +}; + +static match_fn permute_match_ClientFinishedResume[] = + { match_ClientChangeCipherSpec, match_ClientFinished +}; + +static match_fn permute_match_ClientFinishedFull[] = + { match_ClientCertificate, match_ClientKeyExchange, + match_ClientCertificateVerify, match_ClientChangeCipherSpec, + match_ClientFinished +}; + +DECLARE_PERMUTE(ServerHello) + DECLARE_PERMUTE(ServerHelloFull) + DECLARE_PERMUTE(ServerFinishedResume) + DECLARE_PERMUTE(ServerFinished) + DECLARE_PERMUTE(ClientFinished) + DECLARE_PERMUTE(ClientFinishedResume) + DECLARE_PERMUTE(ClientFinishedFull) +// }}} +// {{{ emergency deadlock resolution time bomb +timer_t killtimer_tid = 0; + +static void killtimer_set(void) +{ + struct sigevent sig; + struct itimerspec tout = { {0, 0}, {2 * timeout_seconds, 0} }; + + if (killtimer_tid != 0) { + timer_delete(killtimer_tid); + } + + memset(&sig, 0, sizeof(sig)); + sig.sigev_notify = SIGEV_SIGNAL; + sig.sigev_signo = 15; + if (timer_create(CLOCK_MONOTONIC, &sig, &killtimer_tid) < 0) { + rperror("timer_create"); + exit(3); + } + + timer_settime(killtimer_tid, 0, &tout, 0); +} + +// }}} + +// {{{ actual gnutls operations + +gnutls_certificate_credentials_t cred; +gnutls_session_t session; + +static ssize_t writefn(gnutls_transport_ptr_t fd, const void *buffer, + size_t len) +{ + filter_run_next(fd, (const unsigned char *)buffer, len); + return len; +} + +static void await(int fd, int timeout) +{ + if (nonblock) { + struct pollfd p = { fd, POLLIN, 0 }; + if (poll(&p, 1, timeout) < 0 && errno != EAGAIN + && errno != EINTR) { + rperror("poll"); + exit(3); + } + } +} + +static void cred_init(void) +{ + assert(gnutls_certificate_allocate_credentials(&cred)>=0); + + gnutls_certificate_set_x509_key_mem(cred, &cli_ca3_cert, &cli_ca3_key, + GNUTLS_X509_FMT_PEM); +} + +static void session_init(int sock, int server) +{ + gnutls_init(&session, + GNUTLS_DATAGRAM | (server ? GNUTLS_SERVER : GNUTLS_CLIENT) + | GNUTLS_NONBLOCK * nonblock); + gnutls_priority_set_direct(session, + "NORMAL:+ECDHE-RSA:+ANON-ECDH", + 0); + gnutls_transport_set_int(session, sock); + + if (full) { + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cred); + if (server) { + gnutls_certificate_server_set_request(session, + GNUTLS_CERT_REQUIRE); + } + } else if (server) { + gnutls_anon_server_credentials_t acred; + assert(gnutls_anon_allocate_server_credentials(&acred)>=0); + gnutls_credentials_set(session, GNUTLS_CRD_ANON, acred); + } else { + gnutls_anon_client_credentials_t acred; + assert(gnutls_anon_allocate_client_credentials(&acred)>=0); + gnutls_credentials_set(session, GNUTLS_CRD_ANON, acred); + } + + gnutls_dtls_set_mtu(session, 1400); + gnutls_dtls_set_timeouts(session, retransmit_milliseconds, + timeout_seconds * 1000); +} + +static void client(int sock) +{ + int err = 0; + time_t started = time(0); + const char *line = "foobar!"; + char buffer[8192]; + int len, ret; + gnutls_datum_t data = {NULL, 0}; + + session_init(sock, 0); + + killtimer_set(); + + if (resume) { + do { + err = process_error(gnutls_handshake(session)); + if (err != 0) { + int t = gnutls_dtls_get_timeout(session); + await(sock, t ? t : 100); + } + } while (err != 0); + process_error_or_timeout(err, time(0) - started); + + ret = gnutls_session_get_data2(session, &data); + if (ret < 0) { + exit(1); + } + gnutls_deinit(session); + + session_init(sock, 0); + gnutls_session_set_data(session, data.data, data.size); + gnutls_free(data.data); + data.data = NULL; + + if (debug) { + fprintf(stdout, "%i %s| initial handshake complete\n", run_id, role_name); + } + } + + gnutls_transport_set_push_function(session, writefn); + + killtimer_set(); + do { + err = process_error(gnutls_handshake(session)); + if (err != 0) { + int t = gnutls_dtls_get_timeout(session); + await(sock, t ? t : 100); + } + } while (err != 0); + process_error_or_timeout(err, time(0) - started); + + if (debug) { + fprintf(stdout, "%i %s| handshake complete\n", run_id, role_name); + } + + if (resume) { + killtimer_set(); + + do { + await(sock, -1); + len = + process_error(gnutls_record_recv + (session, buffer, sizeof(buffer))); + } while (len < 0); + + log("received data\n"); + + die_on_error(gnutls_record_send(session, buffer, len)); + + log("sent data\n"); + exit(0); + + } else { + killtimer_set(); + die_on_error(gnutls_record_send(session, line, strlen(line))); + + log("sent data\n"); + + do { + await(sock, -1); + len = + process_error(gnutls_record_recv + (session, buffer, sizeof(buffer))); + } while (len < 0); + + log("received data\n"); + + if (len > 0 && strncmp(line, buffer, len) == 0) { + exit(0); + } else { + exit(1); + } + } + +} + +static gnutls_datum_t saved_data = {NULL, 0}; + +static gnutls_datum_t db_fetch(void *dbf, gnutls_datum_t key) +{ + gnutls_datum_t t = {NULL, 0}; + t.data = malloc(saved_data.size); + if (t.data == NULL) + return t; + memcpy(t.data, saved_data.data, saved_data.size); + t.size = saved_data.size; + + return t; +} + +static int db_delete(void *dbf, gnutls_datum_t key) +{ + return 0; +} + +static int db_store(void *dbf, gnutls_datum_t key, gnutls_datum_t data) +{ + saved_data.data = malloc(data.size); + if (saved_data.data == NULL) + return -1; + memcpy(saved_data.data, data.data, data.size); + saved_data.size = data.size; + return 0; +} + +static void server(int sock) +{ + int err; + const char *line = "server foobar!"; + time_t started = time(0); + char buffer[8192]; + int len; + + session_init(sock, 1); + + await(sock, -1); + + killtimer_set(); + if (resume) { + gnutls_db_set_retrieve_function(session, db_fetch); + gnutls_db_set_store_function(session, db_store); + gnutls_db_set_remove_function(session, db_delete); + gnutls_db_set_ptr(session, NULL); + + do { + err = process_error(gnutls_handshake(session)); + if (err != 0) { + int t = gnutls_dtls_get_timeout(session); + await(sock, t ? t : 100); + } + } while (err != 0); + process_error_or_timeout(err, time(0) - started); + + gnutls_deinit(session); + + session_init(sock, 1); + gnutls_db_set_retrieve_function(session, db_fetch); + gnutls_db_set_store_function(session, db_store); + gnutls_db_set_remove_function(session, db_delete); + gnutls_db_set_ptr(session, NULL); + + if (debug) { + fprintf(stdout, "%i %s| initial handshake complete\n", run_id, role_name); + } + } + + gnutls_transport_set_push_function(session, writefn); + + await(sock, -1); + + killtimer_set(); + do { + err = process_error(gnutls_handshake(session)); + if (err != 0) { + int t = gnutls_dtls_get_timeout(session); + await(sock, t ? t : 100); + } + } while (err != 0); + process_error_or_timeout(err, time(0) - started); + + log("handshake complete\n"); + + if (resume) { + free(saved_data.data); + saved_data.data = NULL; + } + + if (resume) { + killtimer_set(); + die_on_error(gnutls_record_send(session, line, strlen(line))); + + log("sent data\n"); + + do { + await(sock, -1); + len = + process_error(gnutls_record_recv + (session, buffer, sizeof(buffer))); + } while (len < 0); + + log("received data\n"); + + if (len > 0 && strncmp(line, buffer, len) == 0) { + exit(0); + } else { + exit(1); + } + } else { + killtimer_set(); + + do { + await(sock, -1); + len = + process_error(gnutls_record_recv + (session, buffer, sizeof(buffer))); + } while (len < 0); + + log("received data\n"); + + die_on_error(gnutls_record_send(session, buffer, len)); + + log("sent data\n"); + } + + exit(0); +} + +// }}} + +// {{{ test running/handling itself + +#if 0 +static void udp_sockpair(int *socks) +{ + struct sockaddr_in6 sa = + { AF_INET6, htons(30000), 0, in6addr_loopback, 0 }; + struct sockaddr_in6 sb = + { AF_INET6, htons(20000), 0, in6addr_loopback, 0 }; + + socks[0] = socket(AF_INET6, SOCK_DGRAM, 0); + socks[1] = socket(AF_INET6, SOCK_DGRAM, 0); + + bind(socks[0], (struct sockaddr *)&sa, sizeof(sa)); + bind(socks[1], (struct sockaddr *)&sb, sizeof(sb)); + + connect(socks[1], (struct sockaddr *)&sa, sizeof(sa)); + connect(socks[0], (struct sockaddr *)&sb, sizeof(sb)); +} +#endif + +static int run_test(void) +{ + int fds[2]; + int pid1, pid2; + int status2; + + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) < 0) { + rperror("socketpair"); + exit(2); + } + + if (nonblock) { + fcntl(fds[0], F_SETFL, (long)O_NONBLOCK); + fcntl(fds[1], F_SETFL, (long)O_NONBLOCK); + } + + if (!(pid1 = fork())) { + role = SERVER; + server(fds[1]); // noreturn + } else if (pid1 < 0) { + rperror("fork server"); + exit(2); + } + if (!(pid2 = fork())) { + role = CLIENT; + client(fds[0]); // noreturn + } else if (pid2 < 0) { + rperror("fork client"); + exit(2); + } + while (waitpid(pid2, &status2, 0) < 0 && errno == EINTR) ; + kill(pid1, 15); + while (waitpid(pid1, 0, 0) < 0 && errno == EINTR) ; + + close(fds[0]); + close(fds[1]); + + if (!WIFSIGNALED(status2) && WEXITSTATUS(status2) != 3) { + return ! !WEXITSTATUS(status2); + } else { + return 3; + } +} + +static filter_fn filters[] + = { filter_packet_ServerHello, + filter_packet_ServerKeyExchange, + filter_packet_ServerHelloDone, + filter_packet_ClientKeyExchange, + filter_packet_ClientChangeCipherSpec, + filter_packet_ClientFinished, + filter_packet_ServerChangeCipherSpec, + filter_packet_ServerFinished +}; + +static filter_fn filters_resume[] + = { filter_packet_ServerHello, + filter_packet_ServerChangeCipherSpec, + filter_packet_ServerFinished, + filter_packet_ClientChangeCipherSpec, + filter_packet_ClientFinished +}; + +static filter_fn filters_full[] + = { filter_packet_ServerHello, + filter_packet_ServerCertificate, + filter_packet_ServerKeyExchange, + filter_packet_ServerCertificateRequest, + filter_packet_ServerHelloDone, + filter_packet_ClientCertificate, + filter_packet_ClientKeyExchange, + filter_packet_ClientCertificateVerify, + filter_packet_ClientChangeCipherSpec, + filter_packet_ClientFinished, + filter_packet_ServerChangeCipherSpec, + filter_packet_ServerFinished +}; + +static int run_one_test(int dropMode, int serverFinishedPermute, + int serverHelloPermute, int clientFinishedPermute) +{ + int fnIdx = 0; + int res, filterIdx; + filter_fn *local_filters; + const char **local_filter_names; + const char **client_finished_permutation_names; + const char **server_finished_permutation_names; + const char **server_hello_permutation_names; + int filter_count; + + if (full) { + local_filters = filters_full; + local_filter_names = filter_names_full; + filter_count = sizeof(filters_full)/sizeof(filters_full[0]); + client_finished_permutation_names = permutation_names5; + server_finished_permutation_names = permutation_names2; + server_hello_permutation_names = permutation_names5; + } else if (resume) { + local_filters = filters_resume; + local_filter_names = filter_names_resume; + filter_count = sizeof(filters_resume)/sizeof(filters_resume[0]); + client_finished_permutation_names = permutation_names2; + server_finished_permutation_names = permutation_names3; + server_hello_permutation_names = NULL; + } else { + local_filters = filters; + local_filter_names = filter_names; + filter_count = sizeof(filters)/sizeof(filters[0]); + client_finished_permutation_names = permutation_names3; + server_finished_permutation_names = permutation_names2; + server_hello_permutation_names = permutation_names3; + } + + run_id = + ((dropMode * 2 + serverFinishedPermute) * (full ? 120 : 6) + + serverHelloPermute) * (full ? 120 : 6) + clientFinishedPermute; + + filter_clear_state(); + + if (full) { + filter_chain[fnIdx++] = filter_permute_ServerHelloFull; + state_permute_ServerHelloFull.order = + permutations5[serverHelloPermute]; + + filter_chain[fnIdx++] = filter_permute_ClientFinishedFull; + state_permute_ClientFinishedFull.order = + permutations5[clientFinishedPermute]; + + filter_chain[fnIdx++] = filter_permute_ServerFinished; + state_permute_ServerFinished.order = + permutations2[serverFinishedPermute]; + } else if (resume) { + filter_chain[fnIdx++] = filter_permute_ServerFinishedResume; + state_permute_ServerFinishedResume.order = + permutations3[serverFinishedPermute]; + + filter_chain[fnIdx++] = filter_permute_ClientFinishedResume; + state_permute_ClientFinishedResume.order = + permutations2[clientFinishedPermute]; + } else { + filter_chain[fnIdx++] = filter_permute_ServerHello; + state_permute_ServerHello.order = + permutations3[serverHelloPermute]; + + filter_chain[fnIdx++] = filter_permute_ClientFinished; + state_permute_ClientFinished.order = + permutations3[clientFinishedPermute]; + + filter_chain[fnIdx++] = filter_permute_ServerFinished; + state_permute_ServerFinished.order = + permutations2[serverFinishedPermute]; + } + + if (dropMode) { + for (filterIdx = 0; filterIdx < filter_count; filterIdx++) { + if (dropMode & (1 << filterIdx)) { + filter_chain[fnIdx++] = + local_filters[filterIdx]; + } + } + } + filter_chain[fnIdx++] = NULL; + + res = run_test(); + + switch (res) { + case 0: + fprintf(stdout, "%i ++ ", run_id); + break; + case 1: + fprintf(stdout, "%i -- ", run_id); + break; + case 2: + fprintf(stdout, "%i !! ", run_id); + break; + case 3: + fprintf(stdout, "%i TT ", run_id); + break; + } + + if (!resume) + fprintf(stdout, "SHello(%s), ", server_hello_permutation_names[serverHelloPermute]); + fprintf(stdout, "SFinished(%s), ", + server_finished_permutation_names[serverFinishedPermute]); + fprintf(stdout, "CFinished(%s) :- ", + client_finished_permutation_names[clientFinishedPermute]); + if (dropMode) { + for (filterIdx = 0; filterIdx < filter_count; filterIdx++) { + if (dropMode & (1 << filterIdx)) { + if (dropMode & ((1 << filterIdx) - 1)) { + fprintf(stdout, ", "); + } + fprintf(stdout, "%s", + local_filter_names[filterIdx]); + } + } + } + fprintf(stdout, "\n"); + + return res; +} + +static int run_test_by_id(int id) +{ + int pscale = full ? 120 : 6; + int dropMode, serverFinishedPermute, serverHelloPermute, + clientFinishedPermute; + + clientFinishedPermute = id % pscale; + id /= pscale; + + serverHelloPermute = id % pscale; + id /= pscale; + + serverFinishedPermute = id % 2; + id /= 2; + + dropMode = id; + + return run_one_test(dropMode, serverFinishedPermute, + serverHelloPermute, clientFinishedPermute); +} + +int *job_pids; +int job_limit; +int children = 0; + +static void register_child(int pid) +{ + int idx; + + children++; + for (idx = 0; idx < job_limit; idx++) { + if (job_pids[idx] == 0) { + job_pids[idx] = pid; + return; + } + } +} + +static int wait_children(int child_limit) +{ + int fail = 0; + int result = 1; + + while (children > child_limit) { + int status; + int idx; + int pid = waitpid(0, &status, 0); + if (pid < 0 && errno == ECHILD) { + break; + } + for (idx = 0; idx < job_limit; idx++) { + if (job_pids[idx] == pid) { + children--; + if (WEXITSTATUS(status)) { + result = 1; + if (!run_to_end && !fail) { + fprintf(stderr, + "One test failed, waiting for remaining tests\n"); + fail = 1; + child_limit = 0; + } + } + job_pids[idx] = 0; + break; + } + } + } + + if (fail) { + exit(1); + } + + return result; +} + +static int run_tests_from_id_list(int childcount) +{ + int test_id; + int ret; + int result = 0; + + while ((ret = fscanf(stdin, "%i\n", &test_id)) > 0) { + int pid; + if (test_id < 0 + || test_id > + 2 * (full ? 120 * 120 * (1 << 12) : 6 * 6 * 256)) { + fprintf(stderr, "Invalid test id %i\n", test_id); + break; + } + if (!(pid = fork())) { + exit(run_test_by_id(test_id)); + } else if (pid < 0) { + rperror("fork"); + result = 4; + break; + } else { + register_child(pid); + result |= wait_children(childcount); + } + } + + if (ret < 0 && ret != EOF) { + fprintf(stderr, "Error reading test id list\n"); + } + + result |= wait_children(0); + + return result; +} + +static int run_all_tests(int childcount) +{ + int dropMode, serverFinishedPermute, serverHelloPermute, + clientFinishedPermute; + int result = 0; + + for (dropMode = 0; dropMode != 1 << (full ? 12 : 8); dropMode++) + for (serverFinishedPermute = 0; serverFinishedPermute < 2; + serverFinishedPermute++) + for (serverHelloPermute = 0; + serverHelloPermute < (full ? 120 : 6); + serverHelloPermute++) + for (clientFinishedPermute = 0; + clientFinishedPermute < + (full ? 120 : 6); + clientFinishedPermute++) { + int pid; + if (!(pid = fork())) { + exit(run_one_test + (dropMode, + serverFinishedPermute, + serverHelloPermute, + clientFinishedPermute)); + } else if (pid < 0) { + rperror("fork"); + result = 4; + break; + } else { + register_child(pid); + result |= + wait_children(childcount); + } + } + + result |= wait_children(0); + + return result; +} + +// }}} + +static int parse_permutation(const char *arg, const char *permutations[], + int *val) +{ + *val = 0; + while (permutations[*val]) { + if (strcmp(permutations[*val], arg) == 0) { + return 1; + } else { + *val += 1; + } + } + return 0; +} + +int main(int argc, const char *argv[]) +{ + int dropMode = 0; + int serverFinishedPermute = 0; + int serverHelloPermute = 0; + int clientFinishedPermute = 0; + int batch = 0; + unsigned single = 0; + int arg; + + nonblock = 0; + replay = 0; + debug = 0; + timeout_seconds = 120; + retransmit_milliseconds = 100; + full = 0; + run_to_end = 1; + job_limit = 1; + +#define NEXT_ARG(name) \ + do { \ + if (++arg >= argc) { \ + fprintf(stderr, "No argument for -" #name "\n"); \ + exit(8); \ + } \ + } while (0); +#define FAIL_ARG(name) \ + do { \ + fprintf(stderr, "Invalid argument for -" #name "\n"); \ + exit(8); \ + } while (0); + + for (arg = 1; arg < argc; arg++) { + if (strcmp("-die", argv[arg]) == 0) { + run_to_end = 0; + } else if (strcmp("-batch", argv[arg]) == 0) { + batch = 1; + } else if (strcmp("-d", argv[arg]) == 0) { + char *end; + int level; + + if (arg+1 < argc) { + level = strtol(argv[arg + 1], &end, 10); + if (*end == '\0') { + debug = level; + arg++; + } else + debug++; + } else { + debug++; + } + } else if (strcmp("-nb", argv[arg]) == 0) { + nonblock = 1; + } else if (strcmp("-r", argv[arg]) == 0) { + replay = 1; + } else if (strcmp("-timeout", argv[arg]) == 0) { + char *end; + int val; + + NEXT_ARG(timeout); + val = strtol(argv[arg], &end, 10); + if (*end == '\0') { + timeout_seconds = val; + } else { + FAIL_ARG(timeout); + } + } else if (strcmp("-retransmit", argv[arg]) == 0) { + char *end; + int val; + + NEXT_ARG(retransmit); + val = strtol(argv[arg], &end, 10); + if (*end == '\0') { + retransmit_milliseconds = val; + } else { + FAIL_ARG(retransmit); + } + } else if (strcmp("-j", argv[arg]) == 0) { + char *end; + int val; + + NEXT_ARG(timeout); + val = strtol(argv[arg], &end, 10); + if (*end == '\0') { + job_limit = val; + } else { + FAIL_ARG(j); + } + } else if (strcmp("-full", argv[arg]) == 0) { + if (resume) { + fprintf(stderr, "You cannot combine full with resume\n"); + exit(1); + } + + full = 1; + } else if (strcmp("-resume", argv[arg]) == 0) { + if (full) { + fprintf(stderr, "You cannot combine full with resume\n"); + exit(1); + } + + resume = 1; + } else if (strcmp("-shello", argv[arg]) == 0) { + if (resume) { + fprintf(stderr, "Please use -sfinished instead of -shello\n"); + exit(1); + } + + NEXT_ARG(shello); + if (!parse_permutation + (argv[arg], + full ? permutation_names5 : + permutation_names3, &serverHelloPermute)) { + FAIL_ARG(shell); + } + single++; + } else if (strcmp("-sfinished", argv[arg]) == 0) { + const char **pname; + NEXT_ARG(cfinished); + if (resume) pname = permutation_names3; + else pname = permutation_names2; + if (!parse_permutation + (argv[arg], pname, + &serverFinishedPermute)) { + FAIL_ARG(cfinished); + } + single++; + } else if (strcmp("-cfinished", argv[arg]) == 0) { + const char **pname; + NEXT_ARG(cfinished); + if (full) pname = permutation_names5; + else if (resume) pname = permutation_names2; + else pname = permutation_names3; + if (!parse_permutation + (argv[arg], pname, + &clientFinishedPermute)) { + FAIL_ARG(cfinished); + } + single++; + } else { + int drop; + int filter_count; + const char **local_filter_names; + + if (full) { + local_filter_names = filter_names_full; + filter_count = sizeof(filters_full)/sizeof(filters_full[0]); + } else if (resume) { + local_filter_names = filter_names_resume; + filter_count = sizeof(filters_resume)/sizeof(filters_resume[0]); + } else { + local_filter_names = filter_names; + filter_count = sizeof(filters)/sizeof(filters[0]); + } + + for (drop = 0; drop < filter_count; drop++) { + if (strcmp + (local_filter_names[drop], + argv[arg]) == 0) { + dropMode |= (1 << drop); + break; + } + } + if (drop == filter_count) { + fprintf(stderr, "Unknown packet %s\n", + argv[arg]); + exit(8); + } + single++; + } + } + + setlinebuf(stdout); + global_init(); + cred_init(); + gnutls_global_set_log_function(logfn); + gnutls_global_set_audit_log_function(auditfn); + gnutls_global_set_log_level(debug); + + if (single) { + if (debug) + fprintf(stderr, "single test mode\n"); + return run_one_test(dropMode, serverFinishedPermute, + serverHelloPermute, clientFinishedPermute); + } else { + if (debug) + fprintf(stderr, "multi test mode\n"); + + if (resume) { + fprintf(stderr, "full run not implemented yet for resumed runs\n"); + exit(5); + } + + job_pids = calloc(sizeof(int), job_limit); + if (batch) { + return run_tests_from_id_list(job_limit); + } else { + return run_all_tests(job_limit); + } + } +} + +// vim: foldmethod=marker + +#else /* NO POSIX TIMERS */ + +int main(int argc, const char *argv[]) +{ + exit(77); +} + +#endif diff --git a/tests/dtls/dtls.sh b/tests/dtls/dtls.sh new file mode 100755 index 0000000..0b79ae8 --- /dev/null +++ b/tests/dtls/dtls.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +# Copyright (C) 2012 Free Software Foundation, Inc. +# +# Author: Nikos Mavrogiannopoulos +# +# 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. + +set -e + +if test "${WINDIR}" != ""; then + exit 77 +fi + +./dtls-stress -full -shello 01234 -sfinished 01 -cfinished 01234 CCertificate CKeyExchange CCertificateVerify CChangeCipherSpec CFinished +./dtls-stress -full -r -shello 42130 -sfinished 10 -cfinished 43210 SHello SKeyExchange SHelloDone CKeyExchange CChangeCipherSpec CFinished SChangeCipherSpec SCertificate SFinished + +./dtls-stress -shello 021 -sfinished 01 -cfinished 012 SKeyExchange CKeyExchange CFinished +./dtls-stress -shello 012 -sfinished 10 -cfinished 210 SHello SKeyExchange SHelloDone +./dtls-stress -shello 012 -sfinished 01 -cfinished 021 SHello SKeyExchange SHelloDone +./dtls-stress -shello 021 -sfinished 01 -cfinished 201 SHello SHelloDone CChangeCipherSpec SChangeCipherSpec SFinished +./dtls-stress -shello 102 -sfinished 01 -cfinished 120 SHello SHelloDone CKeyExchange CFinished SChangeCipherSpec SFinished +./dtls-stress -shello 210 -sfinished 01 -cfinished 201 CChangeCipherSpec SChangeCipherSpec SFinished +./dtls-stress -shello 021 -sfinished 10 -cfinished 210 SHello SHelloDone SChangeCipherSpec CChangeCipherSpec CFinished +./dtls-stress -shello 210 -sfinished 10 -cfinished 210 SHello SKeyExchange SHelloDone CKeyExchange CChangeCipherSpec CFinished SChangeCipherSpec SFinished + +./dtls-stress -full -shello 42130 -sfinished 10 -cfinished 43210 SHello SKeyExchange SHelloDone CKeyExchange CChangeCipherSpec CFinished SChangeCipherSpec SCertificate SFinished +./dtls-stress -full -shello 12430 -sfinished 01 -cfinished 01324 SHello SKeyExchange SHelloDone CKeyExchange CChangeCipherSpec CFinished SCertificate SFinished + +exit 0 diff --git a/tests/dtls1-2-mtu-check.c b/tests/dtls1-2-mtu-check.c new file mode 100644 index 0000000..f27929b --- /dev/null +++ b/tests/dtls1-2-mtu-check.c @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * 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 + +/* This program tests the MTU calculation in various cipher/mac algorithm combinations + * in gnutls */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <gnutls/gnutls.h> +#include <gnutls/dtls.h> +#include "eagain-common.h" +#include "cert-common.h" +#include "utils.h" +#include <assert.h> + +#define myfail(fmt, ...) \ + fail("%s: "fmt, name, ##__VA_ARGS__) + +static void tls_log_func(int level, const char *str) +{ + fprintf(stderr, "|<%d>| %s", level, str); +} + +static void dtls_mtu_try(const char *name, const char *client_prio, + unsigned link_mtu, unsigned tunnel_mtu) +{ + int ret; + /* Server stuff. */ + gnutls_certificate_credentials_t serverx509cred; + gnutls_session_t server; + int sret = GNUTLS_E_AGAIN; + /* Client stuff. */ + gnutls_certificate_credentials_t clientx509cred; + gnutls_session_t client; + int cret = GNUTLS_E_AGAIN; + unsigned dmtu; + unsigned i; + + /* General init. */ + gnutls_global_set_log_function(tls_log_func); + if (debug) + gnutls_global_set_log_level(6); + + reset_buffers(); + /* Init server */ + assert(gnutls_certificate_allocate_credentials(&serverx509cred)>=0); + + assert(gnutls_certificate_set_x509_key_mem(serverx509cred, + &server_cert, &server_key, + GNUTLS_X509_FMT_PEM) >= 0); + + assert(gnutls_init(&server, GNUTLS_SERVER|GNUTLS_DATAGRAM|GNUTLS_NONBLOCK) >= 0); + gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE, + serverx509cred); + + assert(gnutls_priority_set_direct(server, + "NORMAL:+ANON-ECDH:+ANON-DH:+3DES-CBC:+ECDHE-RSA:+DHE-RSA:+RSA:+ECDHE-ECDSA:+SHA256:+CURVE-X25519", + NULL) >= 0); + gnutls_transport_set_push_function(server, server_push); + gnutls_transport_set_pull_function(server, server_pull); + gnutls_transport_set_pull_timeout_function(server, server_pull_timeout_func); + gnutls_transport_set_ptr(server, server); + + /* Init client */ + + ret = gnutls_init(&client, GNUTLS_CLIENT|GNUTLS_DATAGRAM|GNUTLS_NONBLOCK); + if (ret < 0) + exit(1); + + assert(gnutls_certificate_allocate_credentials(&clientx509cred) >= 0); + assert(gnutls_certificate_set_x509_trust_mem(clientx509cred, &ca_cert, GNUTLS_X509_FMT_PEM)>=0); + + assert(gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE, + clientx509cred) >= 0); + + gnutls_transport_set_push_function(client, client_push); + gnutls_transport_set_pull_function(client, client_pull); + gnutls_transport_set_pull_timeout_function(client, client_pull_timeout_func); + + gnutls_transport_set_ptr(client, client); + + ret = gnutls_priority_set_direct(client, client_prio, NULL); + if (ret < 0) { + fail("%s: error in priority setting\n", name); + exit(1); + } + success("negotiating %s\n", name); + HANDSHAKE_DTLS(client, server); + + gnutls_dtls_set_mtu(client, link_mtu); + dmtu = gnutls_dtls_get_data_mtu(client); + if (dmtu != tunnel_mtu) { + fail("%s: Calculated MTU (%d) does not match expected (%d)\n", name, dmtu, tunnel_mtu); + } + + { + char *msg = gnutls_malloc(dmtu+1); + assert(msg); + memset(msg, 1, dmtu+1); + ret = gnutls_record_send(client, msg, dmtu+1); + if (ret != (int)GNUTLS_E_LARGE_PACKET) { + myfail("could send larger packet than MTU (%d), ret: %d\n", dmtu, ret); + } + + ret = gnutls_record_send(client, msg, dmtu); + if (ret != (int)dmtu) { + myfail("could not send %d bytes (sent %d)\n", dmtu, ret); + } + + memset(msg, 2, dmtu); + ret = gnutls_record_recv(server, msg, dmtu); + if (ret != (int)dmtu) { + myfail("could not receive %d bytes (received %d)\n", dmtu, ret); + } + + for (i=0;i<dmtu;i++) + assert(msg[i]==1); + + gnutls_free(msg); + } + + gnutls_dtls_set_data_mtu(client, link_mtu); + dmtu = gnutls_dtls_get_data_mtu(client); + if (dmtu != link_mtu) { + if (gnutls_mac_get(client) == GNUTLS_MAC_AEAD) + fail("%s: got MTU (%d) which does not match expected (%d)\n", name, dmtu, link_mtu); + else if (dmtu < link_mtu) + fail("%s: got MTU (%d) smaller than expected (%d)\n", name, dmtu, link_mtu); + } + + gnutls_dtls_set_mtu(client, link_mtu); + dmtu = gnutls_dtls_get_mtu(client); + if (dmtu != link_mtu) { + fail("%s: got MTU (%d) which does not match expected (%d)\n", name, dmtu, link_mtu); + } + + gnutls_bye(client, GNUTLS_SHUT_RDWR); + gnutls_bye(server, GNUTLS_SHUT_RDWR); + + gnutls_deinit(client); + gnutls_deinit(server); + + gnutls_certificate_free_credentials(serverx509cred); + gnutls_certificate_free_credentials(clientx509cred); +} + + +void doit(void) +{ + global_init(); + + /* check padding in CBC */ + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1500, 1435); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1501", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1501, 1451); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1502", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1502, 1451); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1503", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1503, 1451); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1504", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1504, 1451); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1505", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1505, 1451); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1506", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1506, 1451); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1507", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1507, 1451); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1508", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1508, 1451); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1509", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1509, 1451); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1510", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1510, 1451); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1511", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1511, 1451); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1512", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1512, 1451); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1513", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1513, 1451); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1514", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1514, 1451); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1515", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1515, 1451); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1516", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1516, 1451); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1517", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1517, 1467); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1518", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1518, 1467); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1519", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1519, 1467); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1520", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1520, 1467); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1521", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1521, 1467); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1522", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1522, 1467); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1523", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1523, 1467); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1524", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1524, 1467); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1525", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1525, 1467); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1526", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1526, 1467); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1536", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1536, 1483); + + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA256", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA256", 1500, 1423); + if (!gnutls_fips140_mode_enabled()) + dtls_mtu_try("DTLS 1.2 with 3DES-CBC-HMAC-SHA1", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+3DES-CBC:-MAC-ALL:+SHA1", 1500, 1451); + + /* check non-CBC ciphers */ + dtls_mtu_try("DTLS 1.2 with AES-128-GCM", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-GCM", 1500, 1463); + if (!gnutls_fips140_mode_enabled()) + dtls_mtu_try("DTLS 1.2 with CHACHA20-POLY1305", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+CHACHA20-POLY1305", 1500, 1471); + + /* check EtM CBC */ + dtls_mtu_try("DTLS 1.2/EtM with AES-128-CBC-HMAC-SHA1", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1500, 1439); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1501", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1501, 1439); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1502", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1502, 1439); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1503", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1503, 1439); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1504", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1504, 1439); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1505", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1505, 1455); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1506", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1506, 1455); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1507", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1507, 1455); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1508", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1508, 1455); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1509", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1509, 1455); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1510", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1510, 1455); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1511", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1511, 1455); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1512", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1512, 1455); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1513", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1513, 1455); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1514", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1514, 1455); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1515", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1515, 1455); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1516", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1516, 1455); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1517", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1517, 1455); + dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1518", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1518, 1455); + + dtls_mtu_try("DTLS 1.2/EtM with AES-128-CBC-HMAC-SHA256", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA256", 1500, 1423); + if (!gnutls_fips140_mode_enabled()) + dtls_mtu_try("DTLS 1.2/EtM with 3DES-CBC-HMAC-SHA1", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+3DES-CBC:-MAC-ALL:+SHA1", 1500, 1455); + + gnutls_global_deinit(); +} diff --git a/tests/dtls10-cert-key-exchange.c b/tests/dtls10-cert-key-exchange.c new file mode 100644 index 0000000..90b19bd --- /dev/null +++ b/tests/dtls10-cert-key-exchange.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015-2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * 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 + +/* This program tests the various certificate key exchange methods supported + * in gnutls */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <gnutls/gnutls.h> +#include "utils.h" +#include "common-cert-key-exchange.h" +#include "cert-common.h" + +void doit(void) +{ + global_init(); + + dtls_try("DTLS 1.0 with anon-ecdh", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+ANON-ECDH", GNUTLS_KX_ANON_ECDH, GNUTLS_SIGN_UNKNOWN, GNUTLS_SIGN_UNKNOWN); + dtls_try("DTLS 1.0 with anon-dh", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+ANON-DH", GNUTLS_KX_ANON_DH, GNUTLS_SIGN_UNKNOWN, GNUTLS_SIGN_UNKNOWN); + dtls_try("DTLS 1.0 with dhe-rsa no cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+DHE-RSA", GNUTLS_KX_DHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_UNKNOWN); + dtls_try("DTLS 1.0 with ecdhe x25519 rsa no cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+ECDHE-RSA:-CURVE-ALL:+CURVE-X25519", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_UNKNOWN); + dtls_try("DTLS 1.0 with ecdhe rsa no cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+ECDHE-RSA", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_UNKNOWN); + dtls_try_with_key("DTLS 1.0 with ecdhe ecdsa no cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+ECDHE-ECDSA", GNUTLS_KX_ECDHE_ECDSA, GNUTLS_SIGN_ECDSA_SHA256, GNUTLS_SIGN_UNKNOWN, + &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, NULL, NULL, 0); + + dtls_try("DTLS 1.0 with rsa no cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+RSA", GNUTLS_KX_RSA, GNUTLS_SIGN_UNKNOWN, GNUTLS_SIGN_UNKNOWN); + dtls_try_cli("DTLS 1.0 with dhe-rsa cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+DHE-RSA", GNUTLS_KX_DHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_RSA_SHA256, USE_CERT); + dtls_try_cli("DTLS 1.0 with ecdhe-rsa cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+ECDHE-RSA", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_RSA_SHA256, USE_CERT); + dtls_try_cli("DTLS 1.0 with rsa cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+RSA", GNUTLS_KX_RSA, GNUTLS_SIGN_UNKNOWN, GNUTLS_SIGN_RSA_SHA256, USE_CERT); + dtls_try_with_key("DTLS 1.0 with ecdhe ecdsa cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+ECDHE-ECDSA", GNUTLS_KX_ECDHE_ECDSA, GNUTLS_SIGN_ECDSA_SHA256, GNUTLS_SIGN_RSA_SHA256, + &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, &cli_ca3_cert, &cli_ca3_key, USE_CERT); + + dtls_try_cli("DTLS 1.0 with dhe-rsa ask cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+DHE-RSA", GNUTLS_KX_DHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_UNKNOWN, ASK_CERT); + dtls_try_cli("DTLS 1.0 with ecdhe-rsa ask cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+ECDHE-RSA", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_UNKNOWN, ASK_CERT); + dtls_try_cli("DTLS 1.0 with rsa ask cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+RSA", GNUTLS_KX_RSA, GNUTLS_SIGN_UNKNOWN, GNUTLS_SIGN_UNKNOWN, ASK_CERT); + dtls_try_with_key("DTLS 1.0 with ecdhe ecdsa cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+ECDHE-ECDSA", GNUTLS_KX_ECDHE_ECDSA, GNUTLS_SIGN_ECDSA_SHA256, GNUTLS_SIGN_UNKNOWN, + &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, &cli_ca3_cert, &cli_ca3_key, ASK_CERT); + + gnutls_global_deinit(); +} diff --git a/tests/dtls12-cert-key-exchange.c b/tests/dtls12-cert-key-exchange.c new file mode 100644 index 0000000..8202804 --- /dev/null +++ b/tests/dtls12-cert-key-exchange.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2015-2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * 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 + +/* This program tests the various certificate key exchange methods supported + * in gnutls */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <gnutls/gnutls.h> +#include "utils.h" +#include "common-cert-key-exchange.h" +#include "cert-common.h" + +void doit(void) +{ + global_init(); + + dtls_try("DTLS 1.2 with anon-ecdh", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ANON-ECDH", GNUTLS_KX_ANON_ECDH, GNUTLS_SIGN_UNKNOWN, GNUTLS_SIGN_UNKNOWN); + dtls_try("DTLS 1.2 with anon-dh", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ANON-DH", GNUTLS_KX_ANON_DH, GNUTLS_SIGN_UNKNOWN, GNUTLS_SIGN_UNKNOWN); + dtls_try("DTLS 1.2 with dhe-rsa no-cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+DHE-RSA", GNUTLS_KX_DHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_UNKNOWN); + dtls_try("DTLS 1.2 with ecdhe x25519 rsa no-cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-RSA:-CURVE-ALL:+CURVE-X25519", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_UNKNOWN); + dtls_try("DTLS 1.2 with ecdhe rsa no-cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-RSA", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_UNKNOWN); + dtls_try_with_key("DTLS 1.2 with ecdhe ecdsa no-cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-ECDSA", GNUTLS_KX_ECDHE_ECDSA, GNUTLS_SIGN_ECDSA_SHA256, GNUTLS_SIGN_UNKNOWN, + &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, NULL, NULL, 0); + + dtls_try("DTLS 1.2 with ecdhe rsa-pss sig no-cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-RSA:-SIGN-ALL:+SIGN-RSA-PSS-RSAE-SHA256", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, GNUTLS_SIGN_UNKNOWN); + dtls_try("DTLS 1.2 with ecdhe rsa-pss no-cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-RSA:-SIGN-ALL:+SIGN-RSA-PSS-RSAE-SHA256", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, GNUTLS_SIGN_UNKNOWN); + dtls_try_with_key("TLS 1.2 with ecdhe rsa-pss/rsa-pss no-cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-RSA:-SIGN-ALL:+SIGN-RSA-PSS-SHA256", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_PSS_SHA256, GNUTLS_SIGN_UNKNOWN, + &server_ca3_rsa_pss_cert, &server_ca3_rsa_pss_key, NULL, NULL, 0); + dtls_try("DTLS 1.2 with rsa no-cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+RSA", GNUTLS_KX_RSA, GNUTLS_SIGN_UNKNOWN, GNUTLS_SIGN_UNKNOWN); + + dtls_try_cli("DTLS 1.2 with dhe-rsa cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+DHE-RSA", GNUTLS_KX_DHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_RSA_SHA256, USE_CERT); + dtls_try_cli("DTLS 1.2 with ecdhe-rsa cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-RSA", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_RSA_SHA256, USE_CERT); + dtls_try_cli("DTLS 1.2 with rsa cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+RSA", GNUTLS_KX_RSA, GNUTLS_SIGN_UNKNOWN, GNUTLS_SIGN_RSA_SHA256, USE_CERT); + dtls_try_with_key("DTLS 1.2 with ecdhe ecdsa cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-ECDSA", GNUTLS_KX_ECDHE_ECDSA, GNUTLS_SIGN_ECDSA_SHA256, GNUTLS_SIGN_RSA_SHA256, + &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, &cli_ca3_cert, &cli_ca3_key, USE_CERT); + dtls_try_with_key("DTLS 1.2 with ecdhe ecdsa/ecdsa cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-ECDSA", GNUTLS_KX_ECDHE_ECDSA, GNUTLS_SIGN_ECDSA_SHA256, GNUTLS_SIGN_ECDSA_SHA256, + &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, USE_CERT); + + + dtls_try_cli("DTLS 1.2 with ecdhe-rsa-pss cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-RSA:-SIGN-ALL:+SIGN-RSA-PSS-RSAE-SHA256", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, USE_CERT); + dtls_try_with_key("DTLS 1.2 with ecdhe-rsa-pss/rsa-pss cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-RSA:-SIGN-ALL:+SIGN-RSA-PSS-SHA256", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_PSS_SHA256, GNUTLS_SIGN_RSA_PSS_SHA256, + &server_ca3_rsa_pss_cert, &server_ca3_rsa_pss_key, &cli_ca3_rsa_pss_cert, &cli_ca3_rsa_pss_key, USE_CERT); + dtls_try_cli("DTLS 1.2 with dhe-rsa ask cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+DHE-RSA", GNUTLS_KX_DHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_UNKNOWN, ASK_CERT); + dtls_try_cli("DTLS 1.2 with ecdhe-rsa ask cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-RSA", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_UNKNOWN, ASK_CERT); + dtls_try_cli("DTLS 1.2 with rsa ask cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+RSA", GNUTLS_KX_RSA, GNUTLS_SIGN_UNKNOWN, GNUTLS_SIGN_UNKNOWN, ASK_CERT); + dtls_try_with_key("DTLS 1.2 with ecdhe ecdsa cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-ECDSA", GNUTLS_KX_ECDHE_ECDSA, GNUTLS_SIGN_ECDSA_SHA256, GNUTLS_SIGN_UNKNOWN, + &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, &cli_ca3_cert, &cli_ca3_key, ASK_CERT); + + gnutls_global_deinit(); +} |