]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
gnutls-serv: sending alerts on mismatched SNI names
authorHubert Kario <hkario@redhat.com>
Fri, 6 May 2016 09:12:29 +0000 (11:12 +0200)
committerNikos Mavrogiannopoulos <nmav@redhat.com>
Fri, 6 May 2016 09:45:37 +0000 (11:45 +0200)
Extend serv utility to be able to send alerts when the name advertised
by client does not match the name expected by server.

src/serv-args.def
src/serv.c

index e0dd2bb4a548810a538320403f2b9f513fb5d917..07c3bbf3886869b1efa97fc54f75d1464120f287 100644 (file)
@@ -8,6 +8,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";
index b34254d38eb4a2cdf4edf30a10ce2fa6ff079580..1695725f553806087be963395a71ed57cf6301a7 100644 (file)
@@ -81,6 +81,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);
@@ -318,6 +320,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;
@@ -349,6 +428,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);
@@ -1642,6 +1725,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 */