]> ruderich.org/simon Gitweb - tlsproxy/tlsproxy.git/commitdiff
Add -u option to pass through unknown hostnames.
authorSimon Ruderich <simon@ruderich.org>
Tue, 15 Mar 2011 20:52:43 +0000 (21:52 +0100)
committerSimon Ruderich <simon@ruderich.org>
Tue, 15 Mar 2011 20:52:43 +0000 (21:52 +0100)
Also add tests.

If -d 2 is used then use SO_REUSEADDR, necessary for the test suite as
we have to restart the proxy and otherwise we have to wait for the
timeout.

src/connection.c
src/tlsproxy.c
src/tlsproxy.h
src/verify.c
src/verify.h
tests/Makefile.am
tests/tests-passthrough.sh [new file with mode: 0755]
tests/tests.sh

index 0f491e2011c0447adbcd2dbf6c7190c38c0ea674..513be7e6d7a2d3012ae7fa1770feb16cdc0df591 100644 (file)
@@ -50,10 +50,8 @@ static void send_bad_request(FILE *client_fd);
 static void send_forwarding_failure(FILE *client_fd);
 static void tls_send_invalid_cert_message(gnutls_session_t session);
 
-#if 0
 static void transfer_data(int client, int server);
 static int read_from_write_to(int from, int to);
-#endif
 static void transfer_data_tls(int client, int server,
                               gnutls_session_t client_session,
                               gnutls_session_t server_session);
@@ -179,6 +177,35 @@ void handle_connection(int client_socket) {
 
     LOG(LOG_DEBUG, "connection to server established");
 
+    /* If the -u option is used and we don't know this hostname's server
+     * certificate then just pass through the connection and let the client
+     * verify the server certificate. */
+    if (global_passthrough_unknown) {
+        char path[1024];
+        FILE *file = NULL;
+
+        if (-2 == server_certificate_path(&file, host, path, sizeof(path))) {
+            /* We've established a connection, tell the client. */
+            fprintf(client_fd, "HTTP/1.0 200 Connection established\r\n");
+            fprintf(client_fd, "\r\n");
+            fflush(client_fd);
+
+            LOG(LOG_DEBUG, "transferring data");
+
+            /* Proxy data between client and server until one suite is done
+             * (EOF or error). */
+            transfer_data(client_socket, server_socket);
+
+            LOG(LOG_DEBUG, "finished transferring data");
+
+            goto out;
+        }
+        /* server_certificate_path() may open the file, close it. */
+        if (NULL != file) {
+            fclose(file);
+        }
+    }
+
     result = initialize_tls_session_server(server_socket, &server_session,
                                                           &server_x509_cred);
     /* Initialize TLS client credentials to talk to the server. */
@@ -253,14 +280,14 @@ void handle_connection(int client_socket) {
         goto out;
     }
 
-    LOG(LOG_DEBUG, "transferring data");
+    LOG(LOG_DEBUG, "transferring TLS data");
 
     /* Proxy data between client and server until one suite is done (EOF or
      * error). */
     transfer_data_tls(client_socket, server_socket,
                       client_session, server_session);
 
-    LOG(LOG_DEBUG, "finished transferring data");
+    LOG(LOG_DEBUG, "finished transferring TLS data");
 
 out:
     /* Close TLS sessions if necessary. Use GNUTLS_SHUT_RDWR so the data is
@@ -504,7 +531,6 @@ static void tls_send_invalid_cert_message(gnutls_session_t session) {
 }
 
 
-#if 0
 /* Transfer data between client and server sockets until one closes the
  * connection. */
 static void transfer_data(int client, int server) {
@@ -556,6 +582,8 @@ static int read_from_write_to(int from, int to) {
     ssize_t size_written;
     char buffer[4096];
 
+    LOG(LOG_DEBUG, "read_from_write_to(): %d -> %d", from, to);
+
     size_read = read(from, buffer, sizeof(buffer));
     if (0 > size_read) {
         LOG_PERROR(LOG_WARNING, "read_from_write_to(): read()");
@@ -579,7 +607,6 @@ static int read_from_write_to(int from, int to) {
 
     return 0;
 }
-#endif
 
 /* Transfer data between client and server TLS connection until one closes the
  * connection. */
index 7a27f5531335368b6e80be8fa7ee159c4ea54919..25f37043394518b1c02184fb775925ef1564d4f2 100644 (file)
@@ -168,14 +168,12 @@ int main(int argc, char **argv) {
         return EXIT_FAILURE;
     }
 
-#ifdef DEBUG
     /* Fast rebinding for debug mode, could cause invalid packets. */
-    {
+    if (LOG_DEBUG <= global_log_level) {
         int socket_option = 1;
         setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR,
                    &socket_option, sizeof(socket_option));
     }
-#endif
 
     /* Bind to the listen socket. */
     memset(&server_in, 0, sizeof(server_in));
@@ -272,8 +270,9 @@ static void parse_arguments(int argc, char **argv) {
 #else
     global_log_level = LOG_WARNING;
 #endif
+    global_passthrough_unknown = 0;
 
-    while (-1 != (option = getopt(argc, argv, "d:p:t:h?"))) {
+    while (-1 != (option = getopt(argc, argv, "d:p:t:uh?"))) {
         switch (option) {
             case 'd': {
                 if (0 > atoi(optarg)) {
@@ -324,6 +323,10 @@ static void parse_arguments(int argc, char **argv) {
                 thread_count = (size_t)atoi(optarg);
                 break;
             }
+            case 'u': {
+                global_passthrough_unknown = 1;
+                break;
+            }
             case 'h':
             default: /* '?' */
                 print_usage(argv[0]);
@@ -338,12 +341,15 @@ static void parse_arguments(int argc, char **argv) {
     }
 }
 static void print_usage(const char *argv) {
-    fprintf(stderr, "Usage: %s [-d level] [-p host:port] [-t count] port\n",
+    fprintf(stderr, "Usage: %s [-d level] [-p host:port] [-t count] [-u] port\n",
                     argv);
     fprintf(stderr, "\n");
     fprintf(stderr, "-d debug level: 0=errors only, 2=debug [default: 1]\n");
     fprintf(stderr, "-p proxy hostname and port\n");
     fprintf(stderr, "-t number of threads [default: 10]\n");
+    fprintf(stderr, "-u passthrough connection if no certificate is stored \
+[default: error]\n");
+    fprintf(stderr, "   WARNING: might be a security problem!\n");
 }
 
 static void initialize_gnutls(void) {
index bcb9c2d34255245e2918c5d492396b51ccd9e870..ae6a818f435729a2af734cda3e74eaa16206d931 100644 (file)
@@ -55,6 +55,10 @@ char *global_proxy_port;
 /* Log level, command line option. */
 int global_log_level;
 
+/* Passthrough connections if no certificate is stored for this hostname?
+ * Specified on the command line. */
+int global_passthrough_unknown;
+
 /* "Global" GnuTLS data used by all threads, read only. */
 gnutls_priority_t tls_priority_cache;
 gnutls_dh_params_t tls_dh_params;
index 0052ca61cdb73bb5d2994bbe30df22a697b982c8..6558d42866422a9a85b8f6ce4ae0fb526a20bc35 100644 (file)
@@ -109,28 +109,8 @@ int verify_tls_connection(gnutls_session_t session, const char *hostname) {
     /* We got the certificate as PEM, free it. */
     gnutls_x509_crt_deinit(cert);
 
-    /* Hostname too long. */
-    if (sizeof(path) - strlen(STORED_SERVER_CERT_FORMAT) <= strlen(hostname)) {
-        LOG(LOG_WARNING,
-            "verify_tls_connection(): hostname too long: '%s'",
-            hostname);
-        return -1;
-    }
-    /* Try to prevent path traversals in hostnames. */
-    if (NULL != strstr(hostname, "..")) {
-        LOG(LOG_WARNING,
-            "verify_tls_connection(): possible path traversal: '%s'",
-            hostname);
-        return -1;
-    }
-    snprintf(path, sizeof(path), STORED_SERVER_CERT_FORMAT, hostname);
-
-    /* Open the stored certificate file. */
-    file = fopen(path, "rb");
-    if (NULL == file) {
-        LOG(LOG_WARNING,
-            "verify_tls_connection(): failed to open '%s': %s",
-            path, strerror(errno));
+    /* Open stored server certificate file. */
+    if (0 != server_certificate_path(&file, hostname, path, sizeof(path))) {
         return -1;
     }
 
@@ -168,3 +148,33 @@ int verify_tls_connection(gnutls_session_t session, const char *hostname) {
 
     return 0;
 }
+
+int server_certificate_path(FILE **file, const char *hostname, char *path, size_t size) {
+    /* Hostname too long. */
+    if (size - strlen(STORED_SERVER_CERT_FORMAT) <= strlen(hostname)) {
+        LOG(LOG_WARNING,
+            "server_certificate_path(): hostname too long: '%s'",
+            hostname);
+        return -1;
+    }
+    /* Try to prevent path traversals in hostnames. */
+    if (NULL != strstr(hostname, "..")) {
+        LOG(LOG_WARNING,
+            "server_certificate_path(): possible path traversal: '%s'",
+            hostname);
+        return -1;
+    }
+    snprintf(path, size, STORED_SERVER_CERT_FORMAT, hostname);
+
+    /* Open the stored certificate file. */
+    *file = fopen(path, "rb");
+    if (NULL == *file) {
+        LOG(global_passthrough_unknown ? LOG_DEBUG : LOG_WARNING,
+            "server_certificate_path(): failed to open '%s': %s",
+            path, strerror(errno));
+        /* Couldn't open the file, special case. */
+        return -2;
+    }
+
+    return 0;
+}
index 8b78e7740cead315e4b198732df6bf08d9b52ed2..e39cc59ddd1f83beac88f946684f83757dad6ff1 100644 (file)
@@ -20,6 +20,7 @@
 #ifndef VERIFY_H
 #define VERIFY_H
 
+int server_certificate_path(FILE **file, const char *hostname, char *path, size_t size);
 int verify_tls_connection(gnutls_session_t session, const char *hostname);
 
 #endif
index f41c6b0705f42971a408687a0fed1420aac20f7b..d9055ae474f8d929cd6c69fb38bf392ca28352ea 100644 (file)
@@ -1,5 +1,5 @@
 TESTS = tests.sh
-dist_check_SCRIPTS = tests.sh tests-normal.sh common.sh
+dist_check_SCRIPTS = tests.sh tests-normal.sh tests-passthrough.sh common.sh
 check_PROGRAMS = client
 client_SOURCES = client.c
 
diff --git a/tests/tests-passthrough.sh b/tests/tests-passthrough.sh
new file mode 100755 (executable)
index 0000000..eb40a02
--- /dev/null
@@ -0,0 +1,113 @@
+#!/bin/sh
+
+# tlsproxy tests for the -u option.
+#
+# 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/>.
+
+
+# Handle empty $srcdir.
+[ "x$srcdir" = x ] && srcdir=.
+
+. $srcdir/common.sh
+
+
+# Create necessary files.
+cleanup
+$srcdir/../src/tlsproxy-setup >/dev/null 2>/dev/null
+
+# Normal tests.
+../src/tlsproxy -d2 -u 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 'test server' || 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 'test server' || 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
+rm -f certificate-localhost-proxy.pem certificate-localhost-server.pem
+
+
+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 'test server bad' || 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 'test server bad' || 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
+
+cleanup
+rm -f tmp
+
+exit 0
index 0dc79127ff97e86cd646491d6a0ca711fa823d27..a7b940ab348ba306c5bc7317f505cf5fcf421df0 100755 (executable)
@@ -23,3 +23,8 @@
 
 echo "RUNNING NORMAL TESTS"
 $srcdir/tests-normal.sh
+
+echo
+
+echo "RUNNING PASSTHROUGH (-u) TESTS"
+$srcdir/tests-passthrough.sh