summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@gnutls.org>2018-04-23 11:41:53 +0000
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2018-04-23 11:41:53 +0000
commite097a7320ddd61e0bd6081bec5aad2a255875d75 (patch)
treeacab28dffa772b479803b50e5346f9de9353f331
parentf1aa58d3b3fa80531052e16c28ba9683484125f4 (diff)
parent43a8dd12a842d3ee0ee6c13d346f128c77dad9e4 (diff)
downloadgnutls-e097a7320ddd61e0bd6081bec5aad2a255875d75.tar.gz
Merge branch 'gnutls_3_3_x' into 'gnutls_3_3_x'
Backport --sni-hostname option from master See merge request gnutls/gnutls!622
-rw-r--r--src/cli-args.def7
-rw-r--r--src/cli.c5
-rw-r--r--src/serv-args.def13
-rw-r--r--src/serv.c104
-rw-r--r--tests/Makefile.am2
-rwxr-xr-xtests/sni-hostname.sh66
6 files changed, 193 insertions, 4 deletions
diff --git a/src/cli-args.def b/src/cli-args.def
index c661f458b0..11d66ae8ab 100644
--- a/src/cli-args.def
+++ b/src/cli-args.def
@@ -81,6 +81,13 @@ flag = {
};
flag = {
+ name = sni-hostname;
+ descrip = "Server's hostname for server name indication extension";
+ arg-type = string;
+ doc = "Set explicitly the server name used in the TLS server name indication extension. That is useful when testing with servers setup on different DNS name than the intended. If not specified, the provided hostname is used.";
+};
+
+flag = {
name = starttls;
value = s;
descrip = "Connect, establish a plain session and start TLS";
diff --git a/src/cli.c b/src/cli.c
index 82d8e11660..f3d159a297 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -638,7 +638,10 @@ static gnutls_session_t init_tls_session(const char *hostname)
/* allow the use of private ciphersuites.
*/
if (disable_extensions == 0 && disable_sni == 0) {
- if (hostname != NULL && is_ip(hostname) == 0)
+ if (HAVE_OPT(SNI_HOSTNAME)) {
+ gnutls_server_name_set(session, GNUTLS_NAME_DNS,
+ OPT_ARG(SNI_HOSTNAME), strlen(OPT_ARG(SNI_HOSTNAME)));
+ } else if (hostname != NULL && is_ip(hostname) == 0)
gnutls_server_name_set(session, GNUTLS_NAME_DNS,
hostname, strlen(hostname));
}
diff --git a/src/serv-args.def b/src/serv-args.def
index 44b67f1abe..027737772e 100644
--- a/src/serv-args.def
+++ b/src/serv-args.def
@@ -9,6 +9,19 @@ detail = "Server program that listens to incoming TLS connections.";
#include args-std.def
flag = {
+ name = sni-hostname;
+ descrip = "Server's hostname for server name extension";
+ arg-type = string;
+ doc = "Server name of type host_name that the server will recognise as its own. If the server receives client hello with different name, it will send a warning-level unrecognized_name alert.";
+};
+
+flag = {
+ name = sni-hostname-fatal;
+ descrip = "Send fatal alert on sni-hostname mismatch";
+ doc = "";
+};
+
+flag = {
name = noticket;
descrip = "Don't accept session tickets";
doc = "";
diff --git a/src/serv.c b/src/serv.c
index a1f9adfa8e..8c7c92a921 100644
--- a/src/serv.c
+++ b/src/serv.c
@@ -49,6 +49,8 @@
#include "sockets.h"
#include "udp-serv.h"
+#define _GNUTLS_E_UNRECOGNIZED_NAME -294
+
/* konqueror cannot handle sending the page in multiple
* pieces.
*/
@@ -81,6 +83,8 @@ const char *dh_params_file = NULL;
const char *x509_crlfile = NULL;
const char *priorities = NULL;
const char *status_response_ocsp = NULL;
+const char *sni_hostname = NULL;
+int sni_hostname_fatal = 0;
gnutls_datum_t session_ticket_key;
static void tcp_server(const char *name, int port);
@@ -312,6 +316,83 @@ int ret;
return 0;
}
+/* callback used to verify if the host name advertised in client hello matches
+ * the one configured in server
+ */
+static int
+post_client_hello(gnutls_session_t session)
+{
+ int ret;
+ /* DNS names (only type supported) may be at most 256 byte long */
+ char *name;
+ size_t len = 256;
+ unsigned int type;
+ int i;
+
+ name = malloc(len);
+ if (name == NULL)
+ return GNUTLS_E_MEMORY_ERROR;
+
+ for (i=0; ; ) {
+ ret = gnutls_server_name_get(session, name, &len, &type, i);
+ if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ char *new_name;
+ new_name = realloc(name, len);
+ if (new_name == NULL) {
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto end;
+ }
+ name = new_name;
+ continue; /* retry call with same index */
+ }
+
+ /* check if it is the last entry in list */
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+ i++;
+ if (ret != GNUTLS_E_SUCCESS)
+ goto end;
+ /* unknown types need to be ignored */
+ if (type != GNUTLS_NAME_DNS)
+ continue;
+
+ if (strlen(sni_hostname) != len)
+ continue;
+ /* API guarantees that the name of type DNS will be null terminated */
+ if (!strncmp(name, sni_hostname, len)) {
+ ret = GNUTLS_E_SUCCESS;
+ goto end;
+ }
+ };
+ /* when there is no extension, we can't send the extension specific alert */
+ if (i == 0) {
+ fprintf(stderr, "Warning: client did not include SNI extension, using default host\n");
+ ret = GNUTLS_E_SUCCESS;
+ goto end;
+ }
+
+ if (sni_hostname_fatal == 1) {
+ /* abort the connection, propagate error up the stack */
+ ret = _GNUTLS_E_UNRECOGNIZED_NAME;
+ goto end;
+ }
+
+ fprintf(stderr, "Warning: client provided unrecognized host name\n");
+ /* since we just want to send an alert, not abort the connection, we
+ * need to send it ourselves
+ */
+ do {
+ ret = gnutls_alert_send(session,
+ GNUTLS_AL_WARNING,
+ GNUTLS_A_UNRECOGNIZED_NAME);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ /* continue handshake, fall through */
+end:
+ free(name);
+ return ret;
+}
+
gnutls_session_t initialize_session(int dtls)
{
gnutls_session_t session;
@@ -343,6 +424,10 @@ gnutls_session_t initialize_session(int dtls)
&session_ticket_key);
#endif
+ if (sni_hostname != NULL)
+ gnutls_handshake_set_post_client_hello_function(session,
+ &post_client_hello);
+
if (gnutls_priority_set_direct(session, priorities, &err) < 0) {
fprintf(stderr, "Syntax error at: %s\n", err);
exit(1);
@@ -1193,6 +1278,15 @@ int main(int argc, char **argv)
return 0;
}
+int _gnutls_alert_send_appropriate (gnutls_session_t session, int err)
+{
+ if (err == _GNUTLS_E_UNRECOGNIZED_NAME)
+ return gnutls_alert_send(session,
+ GNUTLS_AL_FATAL,
+ GNUTLS_A_UNRECOGNIZED_NAME);
+ return gnutls_alert_send_appropriate(session, err);
+}
+
static void retry_handshake(listener_item *j)
{
int r, ret;
@@ -1208,7 +1302,7 @@ static void retry_handshake(listener_item *j)
GERR(r);
do {
- ret = gnutls_alert_send_appropriate(j->tls_session, r);
+ ret = _gnutls_alert_send_appropriate(j->tls_session, r);
} while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
} else if (r == 0) {
if (gnutls_session_is_resumed(j->tls_session) != 0 && verbose != 0)
@@ -1241,7 +1335,7 @@ int r, ret;
if (r < 0) {
do {
- ret = gnutls_alert_send_appropriate(j->tls_session, r);
+ ret = _gnutls_alert_send_appropriate(j->tls_session, r);
} while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
GERR(r);
j->http_state = HTTP_STATE_CLOSING;
@@ -1629,6 +1723,12 @@ static void cmd_parser(int argc, char **argv)
if (HAVE_OPT(OCSP_RESPONSE))
status_response_ocsp = OPT_ARG(OCSP_RESPONSE);
+ if (HAVE_OPT(SNI_HOSTNAME))
+ sni_hostname = OPT_ARG(SNI_HOSTNAME);
+
+ if (HAVE_OPT(SNI_HOSTNAME_FATAL))
+ sni_hostname_fatal = 1;
+
}
/* session resuming support */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index bafb12ae0b..d249d405fd 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -156,7 +156,7 @@ endif
endif
check_PROGRAMS = $(ctests)
-dist_check_SCRIPTS = rfc2253-escape-test
+dist_check_SCRIPTS = rfc2253-escape-test sni-hostname.sh
TESTS = $(ctests) $(dist_check_SCRIPTS)
diff --git a/tests/sni-hostname.sh b/tests/sni-hostname.sh
new file mode 100755
index 0000000000..4fb51be68e
--- /dev/null
+++ b/tests/sni-hostname.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+# Copyright (C) 2017 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.
+
+srcdir="${srcdir:-.}"
+SERV="${SERV:-../src/gnutls-serv${EXEEXT}}"
+CLI="${CLI:-../src/gnutls-cli${EXEEXT}}"
+unset RETCODE
+
+if ! test -x "${SERV}"; then
+ exit 77
+fi
+
+if ! test -x "${CLI}"; then
+ exit 77
+fi
+
+if test "${WINDIR}" != ""; then
+ exit 77
+fi
+
+if ! test -z "${VALGRIND}"; then
+ VALGRIND="${LIBTOOL:-libtool} --mode=execute ${VALGRIND} --error-exitcode=15"
+fi
+
+
+SERV="${SERV} -q"
+
+. "${srcdir}/scripts/common.sh"
+
+echo "Checking SNI hostname in gnutls-cli"
+
+eval "${GETPORT}"
+launch_server $$ --echo --priority "NORMAL:+ANON-ECDH" --sni-hostname-fatal --sni-hostname example.com
+PID=$!
+wait_server ${PID}
+
+${VALGRIND} "${CLI}" -p "${PORT}" 127.0.0.1 --sni-hostname example.com --priority "NORMAL:+ANON-ECDH:+ANON-DH" </dev/null >/dev/null || \
+ fail ${PID} "1. rehandshake should have succeeded!"
+
+${VALGRIND} "${CLI}" -p "${PORT}" 127.0.0.1 --sni-hostname noexample.com --priority "NORMAL:+ANON-ECDH:+ANON-DH" </dev/null >/dev/null && \
+ fail ${PID} "2. rehandshake should have failed!"
+
+
+kill ${PID}
+wait
+
+exit 0