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.
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);
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. */
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
}
-#if 0
/* Transfer data between client and server sockets until one closes the
* connection. */
static void transfer_data(int client, int server) {
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()");
return 0;
}
-#endif
/* Transfer data between client and server TLS connection until one closes the
* connection. */
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));
#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)) {
thread_count = (size_t)atoi(optarg);
break;
}
+ case 'u': {
+ global_passthrough_unknown = 1;
+ break;
+ }
case 'h':
default: /* '?' */
print_usage(argv[0]);
}
}
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) {
/* 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;
/* 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;
}
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;
+}
#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
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
--- /dev/null
+#!/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
echo "RUNNING NORMAL TESTS"
$srcdir/tests-normal.sh
+
+echo
+
+echo "RUNNING PASSTHROUGH (-u) TESTS"
+$srcdir/tests-passthrough.sh