]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
pytests/proxy: support for TLSv1.3 posthadshake reauth
authorGrigorii Demidov <grigorii.demidov@nic.cz>
Thu, 17 Jan 2019 13:01:54 +0000 (14:01 +0100)
committerTomas Krizek <tomas.krizek@nic.cz>
Wed, 23 Jan 2019 13:58:47 +0000 (14:58 +0100)
tests/pytests/proxy/tls-proxy.c
tests/pytests/proxy/tls-proxy.h
tests/pytests/proxy/tlsproxy.c

index 2e6f9ab1a5b2a608fdd3f88f91013f361166a7c3..0605fe8dc068dd0553d9844e0d2f1324e10b5b9e 100644 (file)
@@ -30,6 +30,7 @@ enum peer_state {
 enum handshake_state {
        TLS_HS_NOT_STARTED = 0,
        TLS_HS_EXPECTED,
+       TLS_HS_REAUTH_EXPECTED,
        TLS_HS_IN_PROGRESS,
        TLS_HS_DONE,
        TLS_HS_CLOSING,
@@ -85,11 +86,15 @@ static void on_upstream_close(uv_handle_t *handle);
 
 static int gnutls_references = 0;
 
-static const char * const priorities =
+static const char * const tlsv12_priorities =
        "NORMAL:" /* GnuTLS defaults */
-       "-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.3:" /* TLS 1.2 only */
+       "-VERS-TLS1.0:-VERS-TLS1.1:+VERS-TLS1.2:-VERS-TLS1.3:" /* TLS 1.2 only */
        "-VERS-SSL3.0:-ARCFOUR-128:-COMP-ALL:+COMP-NULL";
 
+static const char * const tlsv13_priorities =
+       "NORMAL:" /* GnuTLS defaults */
+       "-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:+VERS-TLS1.3:" /* TLS 1.3 only */
+       "-VERS-SSL3.0:-ARCFOUR-128:-COMP-ALL:+COMP-NULL";
 
 static struct tls_proxy_ctx *get_proxy(struct peer *peer)
 {
@@ -133,7 +138,7 @@ static int ip_addr_str(const struct sockaddr *addr, char *buf, size_t *buflen)
        }
        int len = strlen(str);
        str[len] = '#';
-       snprintf(&str[len + 1], 6, "%uh", ip_addr_port(addr));
+       snprintf(&str[len + 1], 6, "%hu", ip_addr_port(addr));
        len += 6;
        str[len] = 0;
        if (len >= *buflen) {
@@ -383,7 +388,13 @@ static void accept_connection_from_client(uv_stream_t *server)
 
        client->tls = tls;
        const char *errpos = NULL;
-       err = gnutls_init(&tls->session, GNUTLS_SERVER | GNUTLS_NONBLOCK);
+       unsigned int gnutls_flags = GNUTLS_SERVER | GNUTLS_NONBLOCK;
+#if GNUTLS_VERSION_NUMBER >= 0x030604
+       if (proxy->a->tls_13) {
+               gnutls_flags |= GNUTLS_POST_HANDSHAKE_AUTH;
+       }
+#endif
+       err = gnutls_init(&tls->session, gnutls_flags);
        if (err != GNUTLS_E_SUCCESS) {
                fprintf(stdout, "[client] gnutls_init() failed: (%d) %s\n",
                             err, gnutls_strerror_name(err));
@@ -393,17 +404,24 @@ static void accept_connection_from_client(uv_stream_t *server)
                fprintf(stdout, "[client] gnutls_priority_set() failed: (%d) %s\n",
                             err, gnutls_strerror_name(err));
        }
-       err = gnutls_priority_set_direct(tls->session, priorities, &errpos);
+
+       const char *direct_priorities = proxy->a->tls_13 ? tlsv13_priorities : tlsv12_priorities;
+       err = gnutls_priority_set_direct(tls->session, direct_priorities, &errpos);
        if (err != GNUTLS_E_SUCCESS) {
                fprintf(stdout, "[client] setting priority '%s' failed at character %zd (...'%s') with %s (%d)\n",
-                       priorities, errpos - priorities, errpos, gnutls_strerror_name(err), err);
+                       direct_priorities, errpos - direct_priorities, errpos,
+                       gnutls_strerror_name(err), err);
        }
        err = gnutls_credentials_set(tls->session, GNUTLS_CRD_CERTIFICATE, proxy->tls_credentials);
        if (err != GNUTLS_E_SUCCESS) {
                fprintf(stdout, "[client] gnutls_credentials_set() failed: (%d) %s\n",
                             err, gnutls_strerror_name(err));
        }
-       gnutls_certificate_server_set_request(tls->session, GNUTLS_CERT_IGNORE);
+       if (proxy->a->tls_13) {
+               gnutls_certificate_server_set_request(tls->session, GNUTLS_CERT_REQUEST);
+       } else  {
+               gnutls_certificate_server_set_request(tls->session, GNUTLS_CERT_IGNORE);
+       }
        gnutls_handshake_set_timeout(tls->session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
        gnutls_transport_set_pull_function(tls->session, proxy_gnutls_pull);
        gnutls_transport_set_push_function(tls->session, proxy_gnutls_push);
@@ -679,13 +697,53 @@ static int write_to_client_pending(struct peer *client)
 
        fprintf(stdout, "[client] submitted %zd bytes\n", submitted);
        if (proxy->a->rehandshake) {
+               int err = GNUTLS_E_SUCCESS;
+#if GNUTLS_VERSION_NUMBER >= 0x030604
+               if (proxy->a->tls_13) {
+                       int flags = gnutls_session_get_flags(tls_session);
+                       if ((flags & GNUTLS_SFLAGS_POST_HANDSHAKE_AUTH) == 0) {
+                               /* Client doesn't support post-handshake re-authentication,
+                                * nothing to test here */
+                               fprintf(stdout, "[client] GNUTLS_SFLAGS_POST_HANDSHAKE_AUTH flag not detected\n");
+                               assert(false);
+                       }
+                       err = gnutls_reauth(tls_session, 0);
+                       if (err != GNUTLS_E_INTERRUPTED &&
+                           err != GNUTLS_E_AGAIN &&
+                           err != GNUTLS_E_GOT_APPLICATION_DATA) {
+                               fprintf(stdout, "[client] gnutls_reauth() failed: %s (%i)\n",
+                                       gnutls_strerror_name(err), err);
+                       } else {
+                               fprintf(stdout, "[client] post-handshake authentication initiated\n");
+                       }
+                       client->tls->handshake_state = TLS_HS_REAUTH_EXPECTED;
+               } else {
+                       assert (gnutls_safe_renegotiation_status(tls_session) != 0);
+                       err = gnutls_rehandshake(tls_session);
+                       if (err != GNUTLS_E_SUCCESS) {
+                               fprintf(stdout, "[client] gnutls_rehandshake() failed: %s (%i)\n",
+                                       gnutls_strerror_name(err), err);
+                               assert(false);
+                       } else {
+                               fprintf(stdout, "[client] rehandshake started\n");
+                       }
+                       client->tls->handshake_state = TLS_HS_EXPECTED;
+               }
+#else
                assert (gnutls_safe_renegotiation_status(tls_session) != 0);
-               assert (gnutls_rehandshake(tls_session) == GNUTLS_E_SUCCESS);
+               err = gnutls_rehandshake(tls_session);
+               if (err != GNUTLS_E_SUCCESS) {
+                       fprintf(stdout, "[client] gnutls_rehandshake() failed: %s (%i)\n",
+                               gnutls_strerror_name(err), err);
+                       assert(false);
+               } else {
+                       fprintf(stdout, "[client] rehandshake started\n");
+               }
                /* Prevent write-to-client callback from sending next pending chunk.
                * At the same time tls_process_from_client() must not call gnutls_handshake()
                * as there can be application data in this direction.  */
                client->tls->handshake_state = TLS_HS_EXPECTED;
-               fprintf(stdout, "[client] rehandshake started\n");
+#endif
        }
        return submitted;
 }
@@ -746,6 +804,41 @@ int tls_process_handshake(struct peer *peer)
        return ret;
 }
 
+#if GNUTLS_VERSION_NUMBER >= 0x030604
+int tls_process_reauth(struct peer *peer)
+{
+       struct tls_ctx *tls = peer->tls;
+       int ret = 1;
+       while (tls->handshake_state == TLS_HS_REAUTH_EXPECTED) {
+               fprintf(stdout, "[tls] TLS re-authentication in progress...\n");
+               int err = gnutls_reauth(tls->session, 0);
+               if (err == GNUTLS_E_SUCCESS) {
+                       tls->handshake_state = TLS_HS_DONE;
+                       fprintf(stdout, "[tls] TLS re-authentication has completed\n");
+                       ret = 1;
+                       if (peer->pending_buf.len != 0) {
+                               write_to_client_pending(peer);
+                       }
+               } else if (err != GNUTLS_E_INTERRUPTED &&
+                          err != GNUTLS_E_AGAIN &&
+                          err != GNUTLS_E_GOT_APPLICATION_DATA) {
+                               /* these are listed as nonfatal errors there
+                                * https://www.gnutls.org/manual/gnutls.html#gnutls_005freauth  */
+                               fprintf(stdout, "[tls] gnutls_reauth failed: %s (%d)\n",
+                               gnutls_strerror_name(err), err);
+                       ret = -1;
+                       break;
+               } else {
+                       fprintf(stdout, "[tls] gnutls_reauth nonfatal error: %s (%d)\n",
+                               gnutls_strerror_name(err), err);
+                       ret = 0;
+                       break;
+               }
+       }
+       return ret;
+}
+#endif
+
 int tls_process_from_client(struct peer *client, const uint8_t *buf, ssize_t nread)
 {
        struct tls_ctx *tls = client->tls;
@@ -756,7 +849,12 @@ int tls_process_from_client(struct peer *client, const uint8_t *buf, ssize_t nre
 
        fprintf(stdout, "[client] tls_process: reading %zd bytes from client\n", nread);
 
-       int ret = tls_process_handshake(client);
+       int ret = 0;
+       if (tls->handshake_state == TLS_HS_REAUTH_EXPECTED) {
+               ret = tls_process_reauth(client);
+       } else {
+               ret = tls_process_handshake(client);
+       }
        if (ret <= 0) {
                return ret;
        }
@@ -778,9 +876,25 @@ int tls_process_from_client(struct peer *client, const uint8_t *buf, ssize_t nre
                                break;
                        }
                        continue;
-               } else if (count < 0) {
+               }
+#if GNUTLS_VERSION_NUMBER >= 0x030604
+               else if (count == GNUTLS_E_REAUTH_REQUEST) {
+                       assert(false);
+                       tls->handshake_state = TLS_HS_IN_PROGRESS;
+                       ret = tls_process_reauth(client);
+                       if (ret < 0) { /* Critical error */
+                               return ret;
+                       }
+                       if (ret == 0) { /* Non fatal, most likely GNUTLS_E_AGAIN */
+                               break;
+                       }
+                       continue;
+               }
+#endif
+               else if (count < 0) {
                        fprintf(stdout, "[client] gnutls_record_recv failed: %s (%zd)\n",
                                gnutls_strerror_name(count), count);
+                       assert(false);
                        return -1;
                } else if (count == 0) {
                        break;
index 4ec13d34ffeef3d28e2841a675b0adc3294dfbf8..783b19cd8e3d01494e6717b6631706081c8815ab 100644 (file)
@@ -13,6 +13,7 @@ struct args {
        bool rehandshake;
        bool close_connection;
        bool accept_only;
+       bool tls_13;
 
        uint64_t close_timeout;
        uint32_t max_conn_sequence;
index 36f213b5090b51a3deed9cec825d7188305ab7ac..1513cf6578465cbfa06942f005f045bd8c5e0b84 100644 (file)
@@ -30,6 +30,8 @@ void help(char *argv[], struct args *a)
               "                        sent to the client (default: no).\n"
               " -a, --acceptonly       Accept incoming connections, but don't\n"
               "                        connect to upstream (default: no).\n"
+              " -v, --tls13            Force use of TLSv1.3. If not turned on,\n"
+              "                        TLSv1.2 will be used (default: no).\n"
               ,
               a->local_addr, a->local_port,
               a->upstream, a->upstream_port,
@@ -47,6 +49,7 @@ void init_args(struct args *a)
        a->key_file = default_key_path;
        a->rehandshake = false;
        a->accept_only = false;
+       a->tls_13 = false;
        a->close_connection = false;
        a->close_timeout = 1000;
        a->max_conn_sequence = 0; /* disabled */
@@ -67,11 +70,14 @@ int main(int argc, char **argv)
                {"fail",        required_argument, 0, 'f'},
                {"rehandshake", no_argument, 0, 'r'},
                {"acceptonly",  no_argument, 0, 'a'},
+#if GNUTLS_VERSION_NUMBER >= 0x030604
+               {"tls13",       no_argument, 0, 'v'},
+#endif
                {0, 0, 0, 0}
        };
        struct args args;
        init_args(&args);
-       while ((c = getopt_long(argc, argv, "l:p:u:d:t:k:c:f:ra", opts, &li)) != -1) {
+       while ((c = getopt_long(argc, argv, "l:p:u:d:t:k:c:f:rav", opts, &li)) != -1) {
                switch (c)
                {
                case 'l':
@@ -130,6 +136,11 @@ int main(int argc, char **argv)
                case 'a':
                        args.accept_only = true;
                        break;
+               case 'v':
+#if GNUTLS_VERSION_NUMBER >= 0x030604
+                       args.tls_13 = true;
+#endif
+                       break;
                default:
                        init_args(&args);
                        help(argv, &args);
@@ -157,16 +168,26 @@ int main(int argc, char **argv)
        }
        fprintf(stdout, "Listen on                     %s#%u\n"
                        "Upstream is expected on       %s#%u\n"
+                       "Certificate file              %s\n"
+                       "Key file                      %s\n"
                        "Rehandshake                   %s\n"
                        "Close                         %s\n"
                        "Refuse incoming connections   every %ith%s\n"
-                       "Only accept, don't forward    %s\n",
+                       "Only accept, don't forward    %s\n"
+                       "Force TLSv1.3                 %s\n"
+                       ,
                        args.local_addr, args.local_port,
                        args.upstream, args.upstream_port,
+                       args.cert_file, args.key_file,
                        args.rehandshake ? "yes" : "no",
                        args.close_connection ? "yes" : "no",
                        args.max_conn_sequence, args.max_conn_sequence ? "" : " (disabled)",
-                       args.accept_only ? "yes" : "no"
+                       args.accept_only ? "yes" : "no",
+#if GNUTLS_VERSION_NUMBER >= 0x030604
+                       args.tls_13 ? "yes" : "no"
+#else
+                       "Not supported"
+#endif
                );
        res = tls_proxy_run(proxy);
        tls_proxy_free(proxy);