From c4343157f93bfeb4e6de858fdd61b8fb4eddafc2 Mon Sep 17 00:00:00 2001 From: Simon Ruderich Date: Sat, 12 Mar 2011 00:12:29 +0100 Subject: [PATCH] tests: Add. Test basic behavior. --- Makefile.am | 2 +- configure.ac | 2 +- tests/Makefile.am | 6 ++ tests/client.c | 251 +++++++++++++++++++++++++++++++++++++++++++ tests/server-bad.pem | 19 ++++ tests/server-key.pem | 27 +++++ tests/server.pem | 19 ++++ tests/tests.sh | 124 +++++++++++++++++++++ 8 files changed, 448 insertions(+), 2 deletions(-) create mode 100644 tests/Makefile.am create mode 100644 tests/client.c create mode 100644 tests/server-bad.pem create mode 100644 tests/server-key.pem create mode 100644 tests/server.pem create mode 100755 tests/tests.sh diff --git a/Makefile.am b/Makefile.am index af437a6..a459377 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1 +1 @@ -SUBDIRS = src +SUBDIRS = src tests diff --git a/configure.ac b/configure.ac index 9ca7e39..4a3786a 100644 --- a/configure.ac +++ b/configure.ac @@ -25,5 +25,5 @@ AC_ARG_ENABLE([debug], fi]) AC_CONFIG_HEADERS([config.h]) -AC_CONFIG_FILES([Makefile src/Makefile]) +AC_CONFIG_FILES([Makefile src/Makefile tests/Makefile]) AC_OUTPUT diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..6ec752e --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,6 @@ +TESTS = tests.sh +dist_check_SCRIPTS = tests.sh +check_PROGRAMS = client +client_SOURCES = client.c + +dist_check_DATA = server-bad.pem server-key.pem server.pem diff --git a/tests/client.c b/tests/client.c new file mode 100644 index 0000000..3f2dc4b --- /dev/null +++ b/tests/client.c @@ -0,0 +1,251 @@ +/* + * Simple GnuTLS client used for testing. + * + * Copyright (C) 2011 Simon Ruderich + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +#include + +#include +#include +#include +/* socket(), connect() */ +#include +#include +/* close() */ +#include +/* getaddrinfo() */ +#include +/* htons() */ +#include +/* errno */ +#include + +/* GnuTLS */ +#include +#include + + +#define MAX_REQUEST_LINE 4096 + +static int connect_to_host(const char *hostname, const char *port); +static int read_http_request(FILE *client_fd, char *request, size_t length); + + +int main (int argc, char *argv[]) { + int result, response; + unsigned int status; + char buffer[MAX_REQUEST_LINE]; + int server; + FILE *fd; + + gnutls_session_t session; + gnutls_certificate_credentials_t xcred; + + gnutls_x509_crt_t cert; + const gnutls_datum_t *cert_list; + unsigned int cert_list_size; + + if (5 != argc) { + fprintf(stderr, + "Usage: %s \n", + argv[0]); + return EXIT_FAILURE; + } + + gnutls_global_init(); + gnutls_certificate_allocate_credentials(&xcred); + + gnutls_certificate_set_x509_trust_file(xcred, + argv[1], GNUTLS_X509_FMT_PEM); + + gnutls_init(&session, GNUTLS_CLIENT); + gnutls_priority_set_direct(session, "NORMAL", NULL); + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred); + + server = connect_to_host("localhost", "4711"); + if (-1 == server) { + return EXIT_FAILURE; + } + fd = fdopen(server, "a+"); + if (NULL == fd) { + perror("fdopen()"); + return EXIT_FAILURE; + } + + /* Talk to tlsproxy. */ + fprintf(fd, "CONNECT %s:%s HTTP/1.0\r\n", argv[2], argv[3]); + fprintf(fd, "\r\n"); + fflush(fd); + if (-1 == read_http_request(fd, buffer, sizeof(buffer))) { + fprintf(stderr, "invalid proxy response\n"); + return EXIT_FAILURE; + } + + printf("response: %s\n", buffer); + + if (1 != sscanf(buffer, "HTTP/1.0 %d", &response)) { + fprintf(stderr, "invalid proxy response: %s\n", buffer); + return EXIT_FAILURE; + } + + if (200 != response) { + fprintf(stderr, "proxy failure\n"); + return EXIT_FAILURE; + } + + gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t)server); + + result = gnutls_handshake(session); + if (GNUTLS_E_SUCCESS != result) { + fprintf(stderr, "gnutls_handshake() failed\n"); + gnutls_perror(result); + return EXIT_FAILURE; + } + + /* Verify the proxy certificate. */ + result = gnutls_certificate_verify_peers2(session, &status); + if (0 > result) { + fprintf(stderr, "gnutls_certificate_verify_peers2() failed\n"); + gnutls_perror(result); + return EXIT_FAILURE; + } + + if (status & GNUTLS_CERT_INVALID) { + fprintf(stderr, "certificate invalid\n"); + } + + /* Get proxy certificate. */ + if (0 > (result = gnutls_x509_crt_init(&cert))) { + fprintf(stderr, "gnutls_x509_crt_init() failed"); + gnutls_perror(result); + return EXIT_FAILURE; + } + + cert_list = gnutls_certificate_get_peers(session, &cert_list_size); + if (NULL == cert_list) { + fprintf(stderr, "gnutls_certificate_get_peers() failed"); + return EXIT_FAILURE; + } + + if (0 > (result = gnutls_x509_crt_import(cert, &cert_list[0], + GNUTLS_X509_FMT_DER))) { + fprintf(stderr, "gnutls_x509_crt_import() failed"); + gnutls_perror(result); + return EXIT_FAILURE; + } + + /* Check hostname. */ + if (!gnutls_x509_crt_check_hostname(cert, argv[4])) { + fprintf(stderr, "hostname didn't match '%s'\n", argv[4]); + } + + gnutls_x509_crt_deinit(cert); + + gnutls_bye(session, GNUTLS_SHUT_RDWR); + fclose(fd); + + gnutls_deinit(session); + gnutls_certificate_free_credentials(xcred); + gnutls_global_deinit(); + + return EXIT_SUCCESS; +} + + +/* Copied from src/connection.c (and removed LOG_* stuff)! Don't modify. */ + +static int connect_to_host(const char *hostname, const char *port) { + struct addrinfo gai_hints; + struct addrinfo *gai_result; + int gai_return; + + int server_socket; + struct addrinfo *server; + + if (NULL == hostname || NULL == port) { + return -1; + } + + /* Get IP of hostname server. */ + memset(&gai_hints, 0, sizeof(gai_hints)); + gai_hints.ai_family = AF_UNSPEC; + gai_hints.ai_socktype = SOCK_STREAM; + gai_hints.ai_protocol = 0; + gai_hints.ai_flags = AI_NUMERICSERV /* given port is numeric */ + | AI_ADDRCONFIG /* supported by this computer */ + | AI_V4MAPPED; /* support IPv4 through IPv6 */ + gai_return = getaddrinfo(hostname, port, &gai_hints, &gai_result); + if (0 != gai_return) { + perror("connect_to_host(): getaddrinfo()"); + return -1; + } + + /* Now try to connect to each server returned by getaddrinfo(), use the + * first successful connect. */ + for (server = gai_result; NULL != server; server = server->ai_next) { + server_socket = socket(server->ai_family, + server->ai_socktype, + server->ai_protocol); + if (-1 == server_socket) { + perror("connect_to_host(): socket(), trying next"); + continue; + } + + if (-1 != connect(server_socket, server->ai_addr, + server->ai_addrlen)) { + break; + } + perror("connect_to_host(): connect(), trying next"); + + close(server_socket); + } + /* Make sure we free the result from getaddrinfo(). */ + freeaddrinfo(gai_result); + + if (NULL == server) { + perror("connect_to_host(): no server found, abort"); + return -1; + } + + return server_socket; +} + +static int read_http_request(FILE *client_fd, char *request, size_t length) { + char buffer[MAX_REQUEST_LINE]; + + if (NULL == fgets(request, (int)length, client_fd)) { + if (ferror(client_fd)) { + perror("read_http_request(): fgets()"); + return -1; + } + + return -2; + } + + while (NULL != fgets(buffer, MAX_REQUEST_LINE, client_fd)) { + /* End of header. */ + if (0 == strcmp(buffer, "\n") || 0 == strcmp(buffer, "\r\n")) { + break; + } + } + if (ferror(client_fd)) { + perror("read_http_request(): fgets()"); + return -1; + } + + return 0; +} diff --git a/tests/server-bad.pem b/tests/server-bad.pem new file mode 100644 index 0000000..ad9b553 --- /dev/null +++ b/tests/server-bad.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDJzCCAhGgAwIBAgIETXqdITALBgkqhkiG9w0BAQUwLTERMA8GA1UEChMIdGxz +cHJveHkxGDAWBgNVBAMTD3Rlc3Qgc2VydmVyIGJhZDAeFw0xMTAzMTEyMjA3Mjla +Fw0xMjAzMTAyMjA3MjlaMC0xETAPBgNVBAoTCHRsc3Byb3h5MRgwFgYDVQQDEw90 +ZXN0IHNlcnZlciBiYWQwggEgMAsGCSqGSIb3DQEBAQOCAQ8AMIIBCgKCAQEArDmV +jIF+IyXYe6KcDb4cY/lTFrxcfY0arhY9vEcLBVSDTfX6mZghonDcJDnlhwqjm+fU +TwOq8wOmBZI8hny00cCRmBf8RwP4gGFBRFWP9hNk3j8FH6xEz8jbX3mjJn+hAGaf +Kx2jbH3YqgMCNBwyHqLxlmkKOmmeb4pXiiO10mSILUWk3Ew/BET9+9hPvfBzLrbF +KwrOZ0DZw5U+vaybuCb4Nu/WQml9HW6ksI2R5MOEL9MlLMFgC3fsl5NBVUQvW8ON +fs795YJ7mSOVjPyTtaMy1lvBGsHZMQKyy6dih4NONwdJum9vznm2AOhfcwogHdD4 +N0ZLPx/jxxy+RIbQBQIDAQABo1UwUzAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoG +CCsGAQUFBwMBMA8GA1UdDwEB/wQFAwMHoAAwHQYDVR0OBBYEFLKzVL/pjHK1CpIQ +ghWY2RWks68EMAsGCSqGSIb3DQEBBQOCAQEABEf4u6zS6wmR/Grtf2XzyNgRml2Y +GjR5sKrOA58x/+ItgZltg8QqunmqzfuU3fNGBq/NaqaxVzsRbgqtE6AEF88JYgW/ +sV8qb1MRPCi7l2O2ueMIa9J3Bn8Cp1cwm4WzrKlUyunYLQ5YY7ahN9e6u9XpFb+P +vrIcApCpwg8N8TJo23ShdPmhZ+ONG+LBdzKxP3zg26BJR4YDKEs3lON3vHIZSoUh +jIBJGzBYwIq88T+wFQ8Lvj2L4UGsz14/u7VjEwfUoEwHQzZzweeMGYcjoEnFjsie +5LwQ8U1DOXsSl97EdioHvV9c+jyUoVZKbsAR70c/ftOSo4BZRLbvJj89SA== +-----END CERTIFICATE----- diff --git a/tests/server-key.pem b/tests/server-key.pem new file mode 100644 index 0000000..4f3c791 --- /dev/null +++ b/tests/server-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEArDmVjIF+IyXYe6KcDb4cY/lTFrxcfY0arhY9vEcLBVSDTfX6 +mZghonDcJDnlhwqjm+fUTwOq8wOmBZI8hny00cCRmBf8RwP4gGFBRFWP9hNk3j8F +H6xEz8jbX3mjJn+hAGafKx2jbH3YqgMCNBwyHqLxlmkKOmmeb4pXiiO10mSILUWk +3Ew/BET9+9hPvfBzLrbFKwrOZ0DZw5U+vaybuCb4Nu/WQml9HW6ksI2R5MOEL9Ml +LMFgC3fsl5NBVUQvW8ONfs795YJ7mSOVjPyTtaMy1lvBGsHZMQKyy6dih4NONwdJ +um9vznm2AOhfcwogHdD4N0ZLPx/jxxy+RIbQBQIDAQABAoIBAFNk1stgcKmoS7ki +NhpZcfne4qLhto984exmDKK8a9O65pG7iAsTpUW5DH/mIQRQ3IRDr4RLLF3b6GpO +M0yT2iKu8lEJXDku7QeNEEN8gucO4QrUzTBXBELw6XjDt4KOznEZP/1qrpV8IovA +RTLibEp1cDH6zt8WAYfVHypevE4RYCf999X++ZE6QVQMRLl5fP9FgXyFly9sxKUs +D6etp66Uq2i/R15sxiIcOAkoWVuHid4C9eON834ZigQcWz8C/KMcWtTe3bl0ErAL +eoVYIO72AGnTZ1wbom84DEh8yBFe/QSkXbQt994dZxVAIbVO7sRFoguyISXz6Enq +kBJy8YkCgYEAxFEURZVAHbiRvSGmVz51ht0X+fmfVJ57eNr+uHpfn5YkNOm74eOu +oNta0ypglRc9aXVa9WG5EPJAOgcn52oHhvWa41s8yA6yEJNCz5GboQffRCxhc5H0 +xLRKB55p9b2IJ7Zbx4NETDcAZjs6SE16XVGguq+MHRrRWmEQjTz/vL8CgYEA4JV9 +/pYIkWTWreqfe5sZnU3O/xBidYhlcc0roRiVSXpXnL7pH6TlYqoPFwjwi4KS2vAP +cd/Qgj5E8VQjqAudwXh+zhuvGgmW/uACiZn4yKxB4VatxOoldVVZqKvuvAGaW9Qv +YtisdB3L2BWEmhRfoduwqlPzlB/5bWQnLxntsDsCgYB15CrEToieUIRUi6yUn9F8 +F7GaUCk6a6HCReDJb9OUxlkf3W0SjeQEfTXhsYUmgzpB5lOlH/Y/Evs23a087xNu +HHfTKKsriIeuJIDunYZUAaI2EmDDQlc2+P2r7Zm8FQF6t6wN9+o/ypfWzKKHYN+m +wqo+8cS2hbBLEhcaehMpiQKBgQCEVJGg6RazVQ+J+q1uT72QCsDlWeoqBux/Wb1T +dwDAAu6M1ZDXrHnc4QdzMzlJeu69QqZXGuHpXHeTG1jHM9EIps9Q8s8CMJRgKe5U +1fzZgWZwOHq7hJv9vwTNdkF9VhR191jUjYdQBB6QfF1dzJNs893s/vqPVSfLZTAR +eN2s8QKBgQChJR4nxan4KN4MEGKF6cdYqWeCQSoJltQzD6FYEAT8XMlhpAb1ATSg +oi14oVCGnOoWFvOv2swrS3u0rvLCUeh89twx8JkkoxdOhFnMGPKZVoSM//ocRPKr +38I2gxQhZMxw91WA86nRDvevT7ulqDwHv9FSrwABz2TrlGK7nn0Akg== +-----END RSA PRIVATE KEY----- diff --git a/tests/server.pem b/tests/server.pem new file mode 100644 index 0000000..4bb9906 --- /dev/null +++ b/tests/server.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDHzCCAgmgAwIBAgIETXqrYDALBgkqhkiG9w0BAQUwKTERMA8GA1UEChMIdGxz +cHJveHkxFDASBgNVBAMTC3Rlc3Qgc2VydmVyMB4XDTExMDMxMTIzMDgxNloXDTEy +MDMxMDIzMDgxNlowKTERMA8GA1UEChMIdGxzcHJveHkxFDASBgNVBAMTC3Rlc3Qg +c2VydmVyMIIBIDALBgkqhkiG9w0BAQEDggEPADCCAQoCggEBAKw5lYyBfiMl2Hui +nA2+HGP5Uxa8XH2NGq4WPbxHCwVUg031+pmYIaJw3CQ55YcKo5vn1E8DqvMDpgWS +PIZ8tNHAkZgX/EcD+IBhQURVj/YTZN4/BR+sRM/I2195oyZ/oQBmnysdo2x92KoD +AjQcMh6i8ZZpCjppnm+KV4ojtdJkiC1FpNxMPwRE/fvYT73wcy62xSsKzmdA2cOV +Pr2sm7gm+Dbv1kJpfR1upLCNkeTDhC/TJSzBYAt37JeTQVVEL1vDjX7O/eWCe5kj +lYz8k7WjMtZbwRrB2TECssunYoeDTjcHSbpvb855tgDoX3MKIB3Q+DdGSz8f48cc +vkSG0AUCAwEAAaNVMFMwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcD +ATAPBgNVHQ8BAf8EBQMDB6AAMB0GA1UdDgQWBBSys1S/6YxytQqSEIIVmNkVpLOv +BDALBgkqhkiG9w0BAQUDggEBAAUfO6T6/RcilyMWu552Iue+yNyJDoj1wQ4WIvHZ +ZNQ9XEZReH0gkJEwCqhjeB1jPEtqHGfFt2QicCD6mjjT0zhjy+9/AtPZ1oDMVFOE +P4YkIVs0Ny6hJ1oGlT+OiUGLBfyQcaUAza6pIQmJTXj6WoSCM9TV8n4UMEwzCT1l +Df1UiTZDU8FwEdH0ezQ8UB+9QUmveBFZJQtBscgIotIW8lNARoKOCskDy1FTXNUD +1BtB0vfii/1KY6+jFf2+pZvV9wLqU0xvN/NuTnfuwzhJ88p1usnPmVqh9R/k3RXi +iidVDJLbNcxEn56PuzOmokJa6JL94tdqLRd6JhFZ1GsKmY4= +-----END CERTIFICATE----- diff --git a/tests/tests.sh b/tests/tests.sh new file mode 100755 index 0000000..21fa8f7 --- /dev/null +++ b/tests/tests.sh @@ -0,0 +1,124 @@ +#!/bin/sh + +# tlsproxy test "suite". + + +# Handle empty $srcdir. +[ "x$srcdir" = x ] && srcdir=. + +abort() { + echo abort + pkill -n gnutls-serv + pkill -n tlsproxy + exit 1 +} +server() { + gnutls-serv --http --port 4712 "$@" >/dev/null 2>/dev/null & +} +client() { + ./client ./proxy-ca.pem "$@" > tmp 2>&1 +} + +test_proxy_failure() { + grep 'proxy failure' tmp >/dev/null || abort + grep 'response: HTTP/1.0 503 Forwarding failure' tmp >/dev/null \ + || abort +} +test_proxy_successful() { + grep 'response: HTTP/1.0 200 Connection established' tmp >/dev/null \ + || abort +} +test_invalid_certificate() { + grep 'certificate invalid' tmp >/dev/null || abort +} +test_no_invalid_certificate() { + grep 'certificate invalid' tmp >/dev/null && abort +} + + +# Create necessary files. +$srcdir/../src/tlsproxy-setup >/dev/null 2>/dev/null + +# Normal tests. +../src/tlsproxy -d2 4711 >/dev/null & +server --x509certfile $srcdir/server.pem \ + --x509keyfile $srcdir/server-key.pem +sleep 1 + + +echo invalid hostname +client unknown-host 80 - && abort +test_proxy_failure +test_no_invalid_certificate + +echo missing proxy and server certificate +client localhost 4712 invalid || abort +test_proxy_successful +test_invalid_certificate + +# Create the proxy certificate. +$srcdir/../src/tlsproxy-add localhost $srcdir/server.pem \ + >/dev/null 2>/dev/null +rm -f certificate-localhost-server.pem + +echo missing server certificate +client localhost 4712 invalid || abort +test_proxy_successful +test_invalid_certificate + +# Create the proxy and server certificate. +$srcdir/../src/tlsproxy-add localhost $srcdir/server.pem \ + >/dev/null 2>/dev/null + +echo normal connection +client localhost 4712 localhost || abort +test_proxy_successful +test_no_invalid_certificate + + +# Stop server and try a "MITM" with a bad certificate. +echo +pkill -n gnutls-serv +server --x509certfile $srcdir/server-bad.pem \ + --x509keyfile $srcdir/server-key.pem +sleep 1 + + +echo mitm invalid hostname +client unknown-host 80 - && abort +test_proxy_failure +test_no_invalid_certificate + +echo mitm missing proxy and server certificate +client localhost 4712 invalid || abort +test_proxy_successful +test_invalid_certificate + +# Create the proxy certificate. +$srcdir/../src/tlsproxy-add localhost $srcdir/server.pem \ + >/dev/null 2>/dev/null +rm -f certificate-localhost-server.pem + +echo mitm missing server certificate +client localhost 4712 invalid || abort +test_proxy_successful +test_invalid_certificate + +# Create the proxy and server certificate. +$srcdir/../src/tlsproxy-add localhost $srcdir/server.pem \ + >/dev/null 2>/dev/null + +echo mitm normal connection +client localhost 4712 invalid || abort +test_proxy_successful +test_invalid_certificate + + +pkill -n gnutls-serv +pkill -n tlsproxy + +rm -f tmp \ + certificate-localhost-proxy.pem certificate-localhost-server.pem \ + proxy-ca-key.pem proxy-ca.pem proxy-invalid.pem proxy-key.pem + +exit 0 -- 2.45.2