]> ruderich.org/simon Gitweb - tlsproxy/tlsproxy.git/commitdiff
tests: Add. Test basic behavior.
authorSimon Ruderich <simon@ruderich.org>
Fri, 11 Mar 2011 23:12:29 +0000 (00:12 +0100)
committerSimon Ruderich <simon@ruderich.org>
Fri, 11 Mar 2011 23:12:29 +0000 (00:12 +0100)
Makefile.am
configure.ac
tests/Makefile.am [new file with mode: 0644]
tests/client.c [new file with mode: 0644]
tests/server-bad.pem [new file with mode: 0644]
tests/server-key.pem [new file with mode: 0644]
tests/server.pem [new file with mode: 0644]
tests/tests.sh [new file with mode: 0755]

index af437a64d6d80a47ea903fd842a648fc59cbcd9a..a4593771bd43752305d1819c2c4e381fd497c186 100644 (file)
@@ -1 +1 @@
-SUBDIRS = src
+SUBDIRS = src tests
index 9ca7e3950256102807c43a98b206cadc17c0872a..4a3786a9b70297e6a0cad0e2b87c3bacf7f3ea50 100644 (file)
@@ -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 (file)
index 0000000..6ec752e
--- /dev/null
@@ -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 (file)
index 0000000..3f2dc4b
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+/* socket(), connect() */
+#include <sys/types.h>
+#include <sys/socket.h>
+/* close() */
+#include <unistd.h>
+/* getaddrinfo() */
+#include <netdb.h>
+/* htons() */
+#include <arpa/inet.h>
+/* errno */
+#include <errno.h>
+
+/* GnuTLS */
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+
+#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 <ca-file> <hostname> <port> <hostname-verify>\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 (file)
index 0000000..ad9b553
--- /dev/null
@@ -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 (file)
index 0000000..4f3c791
--- /dev/null
@@ -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 (file)
index 0000000..4bb9906
--- /dev/null
@@ -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 (executable)
index 0000000..21fa8f7
--- /dev/null
@@ -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