From 0205cc4eee7084e65a69995293ad584f7da21fa9 Mon Sep 17 00:00:00 2001
From: Stefan Eissing <stefan.eissing@greenbytes.de>
Date: Thu, 26 Mar 2015 17:46:18 -0400
Subject: [PATCH 15/26] Async TLSEXT servername support.

Adds support for processing the servername TLSEXT asynchronously,
using the SSL_WANT_EVENT mechansism.

(cherry picked from commit 58ef21b3d08a17db260b8c8abf058e8935c09218)

Conflicts:
	include/openssl/ssl3.h
	ssl/s3_lib.c
	ssl/s3_srvr.c
	ssl/ssl_locl.h
	ssl/t1_lib.c
---
 include/openssl/ssl.h  |  2 ++
 include/openssl/ssl3.h |  9 ++++++
 include/openssl/tls1.h |  1 +
 ssl/d1_srvr.c          | 27 +++++++++++++++---
 ssl/s3_lib.c           |  1 +
 ssl/s3_srvr.c          | 75 +++++++++++++++++++++++++++++++++++++++++++++-----
 ssl/ssl_locl.h         | 14 ++++++++++
 ssl/t1_lib.c           | 39 ++++++++++++++++++++++++--
 8 files changed, 155 insertions(+), 13 deletions(-)

diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index e4b3ba6..2cc92d2 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -925,6 +925,8 @@ __owur int SSL_extension_supported(unsigned int ext_type);
 # define SSL_EVENT_SETUP_CERT_VRFY_DONE   1002
 /* server is siging the message for key exchange */
 # define SSL_EVENT_KEY_EXCH_MSG_SIGNED    1003
+/* tlsext servername has been processed */
+# define SSL_EVENT_TLSEXT_SERVERNAME_READY 1004
 
 /*
  * These will only be used when doing non-blocking IO or asynchronous
diff --git a/include/openssl/ssl3.h b/include/openssl/ssl3.h
index 199ff61..2fd8dd1 100644
--- a/include/openssl/ssl3.h
+++ b/include/openssl/ssl3.h
@@ -511,6 +511,15 @@ extern "C" {
 # define SSL3_CHANGE_CIPHER_CLIENT_READ  (SSL3_CC_CLIENT|SSL3_CC_READ)
 # define SSL3_CHANGE_CIPHER_SERVER_WRITE (SSL3_CC_SERVER|SSL3_CC_WRITE)
 
+# define SSL3_ST_SUB_0		(0x00)
+# define SSL3_ST_SUB_1		(0x01)
+# define SSL3_ST_SUB_2		(0x02)
+# define SSL3_ST_SUB_3		(0x03)
+# define SSL3_ST_SUB_4		(0x04)
+# define SSL3_ST_SUB_5		(0x05)
+# define SSL3_ST_SUB_6		(0x06)
+# define SSL3_ST_SUB_7		(0x07)
+
 #ifdef  __cplusplus
 }
 #endif
diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h
index d107396..d240c33 100644
--- a/include/openssl/tls1.h
+++ b/include/openssl/tls1.h
@@ -376,6 +376,7 @@ SSL_CTX_callback_ctrl(ctx,SSL_CTRL_SET_TLSEXT_SERVERNAME_CB,(void (*)(void))cb)
 #  define SSL_TLSEXT_ERR_ALERT_WARNING 1
 #  define SSL_TLSEXT_ERR_ALERT_FATAL 2
 #  define SSL_TLSEXT_ERR_NOACK 3
+#  define SSL_TLSEXT_ERR_WAIT_FOR_EVENT 4
 
 #  define SSL_CTX_set_tlsext_servername_arg(ctx, arg) \
 SSL_CTX_ctrl(ctx,SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG,0, (void *)arg)
diff --git a/ssl/d1_srvr.c b/ssl/d1_srvr.c
index 1e7bfb8..8a18b7e 100644
--- a/ssl/d1_srvr.c
+++ b/ssl/d1_srvr.c
@@ -313,13 +313,32 @@ int dtls1_accept(SSL *s)
             break;
 
         case SSL3_ST_SR_CLNT_HELLO_A:
+            s->s3->tmp.sub_state = SSL3_ST_SUB_0;
         case SSL3_ST_SR_CLNT_HELLO_B:
         case SSL3_ST_SR_CLNT_HELLO_C:
 
-            s->shutdown = 0;
-            ret = ssl3_get_client_hello(s);
-            if (ret <= 0)
-                goto end;
+            if (s->s3->tmp.sub_state == SSL3_ST_SUB_0) {
+                s->shutdown = 0;
+                ret = ssl3_get_client_hello(s);
+                if (ret <= 0)
+                    goto end;
+                s->s3->tmp.sub_state = SSL3_ST_SUB_1;
+            }
+            if (s->s3->tmp.sub_state == SSL3_ST_SUB_1) {
+                s->s3->tmp.sub_state = SSL3_ST_SUB_2;
+#ifndef OPENSSL_NO_TLSEXT
+                ret = ssl_check_clienthello_tlsext_async(s);
+                if (ret <= 0)
+                    goto end;
+#endif
+            }
+            if (s->s3->tmp.sub_state == SSL3_ST_SUB_2) {
+                if (!ssl_event_did_succeed(s, SSL_EVENT_TLSEXT_SERVERNAME_READY, &ret))
+                    goto end;
+                ret = ssl3_get_client_hello_post_app(s, 0);
+                if (ret <= 0)
+                    goto end;
+            }
             dtls1_stop_timer(s);
 
             if (ret == 1 && (SSL_get_options(s) & SSL_OP_COOKIE_EXCHANGE))
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index 451a554..8ca881c 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -2915,6 +2915,7 @@ void ssl3_free(SSL *s)
 #ifndef OPENSSL_NO_SRP
     SSL_SRP_CTX_free(s);
 #endif
+    sk_SSL_CIPHER_free(s->s3->tmp.ciphers);
     OPENSSL_clear_free(s->s3, sizeof(*s->s3));
     s->s3 = NULL;
 }
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index fbc8216..c651d83 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -343,14 +343,37 @@ int ssl3_accept(SSL *s)
             break;
 
         case SSL3_ST_SR_CLNT_HELLO_A:
+            s->s3->tmp.sub_state = SSL3_ST_SUB_0;
         case SSL3_ST_SR_CLNT_HELLO_B:
         case SSL3_ST_SR_CLNT_HELLO_C:
 
-            ret = ssl3_get_client_hello(s);
-            if (ret <= 0)
-                goto end;
+            if (s->s3->tmp.sub_state == SSL3_ST_SUB_0) {
+                s->shutdown = 0;
+                ret = ssl3_get_client_hello(s);
+                if (ret <= 0)
+                    goto end;
+                s->s3->tmp.sub_state = SSL3_ST_SUB_1;
+            }
+            if (s->s3->tmp.sub_state == SSL3_ST_SUB_1) {
+                s->s3->tmp.sub_state = SSL3_ST_SUB_2;
+#ifndef OPENSSL_NO_TLSEXT
+                ret = ssl_check_clienthello_tlsext_async(s);
+                if (ret <= 0)
+                    goto end;
+#endif
+            }
+            if (s->s3->tmp.sub_state == SSL3_ST_SUB_2) {
+                if (!ssl_event_did_succeed(s, SSL_EVENT_TLSEXT_SERVERNAME_READY, &ret)) {
+                    goto end;
+                }
+                ret = ssl3_get_client_hello_post_app(s, 0);
+                if (ret <= 0)
+                    goto end;
+                s->s3->tmp.sub_state = SSL3_ST_SUB_3;
+            }
 #ifndef OPENSSL_NO_SRP
-            s->state = SSL3_ST_SR_CLNT_HELLO_D;
+            if (s->s3->tmp.sub_state == SSL3_ST_SUB_3)
+                s->state = SSL3_ST_SR_CLNT_HELLO_D;
         case SSL3_ST_SR_CLNT_HELLO_D:
             {
                 int al;
@@ -901,13 +924,14 @@ int ssl3_get_client_hello(SSL *s)
     SSL_CIPHER *c;
 #ifndef OPENSSL_NO_COMP
     unsigned char *q = NULL;
-    SSL_COMP *comp = NULL;
 #endif
     STACK_OF(SSL_CIPHER) *ciphers = NULL;
     int protverr = 1;
 
-    if (s->state == SSL3_ST_SR_CLNT_HELLO_C && !s->first_packet)
-        goto retry_cert;
+    if (s->state == SSL3_ST_SR_CLNT_HELLO_C && !s->first_packet) {
+        /* head over to post_app and goto retry_cert there */
+        return ssl3_get_client_hello_post_app(s, 1);
+    }
 
     /*
      * We do this so that we will respond with our native type. If we are
@@ -1371,6 +1395,13 @@ int ssl3_get_client_hello(SSL *s)
         }
     }
 
+    /* ssl3_get_client_hello_post_app will need these when called. */
+    if (ciphers != NULL)
+        s->s3->tmp.ciphers = sk_SSL_CIPHER_dup(ciphers);
+    s->s3->tmp.q = q;
+    s->s3->tmp.i = i;
+    s->s3->tmp.complen = complen;
+
 #ifndef OPENSSL_NO_TLSEXT
     /* TLS extensions */
     if (s->version >= SSL3_VERSION) {
@@ -1379,7 +1410,37 @@ int ssl3_get_client_hello(SSL *s)
             goto err;
         }
     }
+#endif
+    if (ret < 0)
+        ret = 1;
+    if (0) {
+ f_err:
+        ssl3_send_alert(s, SSL3_AL_FATAL, al);
+    }
+ err:
+    sk_SSL_CIPHER_free(ciphers);
+    return ret < 0 ? -1 : ret;
+}
 
+int ssl3_get_client_hello_post_app(SSL *s, int retry_cert)
+{
+    int al = SSL_AD_INTERNAL_ERROR, ret= -1;
+    int complen = s->s3->tmp.complen;
+    unsigned char *q = s->s3->tmp.q;
+    SSL_CIPHER *c;
+    STACK_OF(SSL_CIPHER) *ciphers = s->s3->tmp.ciphers;
+
+#ifndef OPENSSL_NO_COMP
+    SSL_COMP *comp = NULL;
+#endif
+
+    /* we have already copied tmp_ciphers into ciphers. */
+    s->s3->tmp.ciphers = NULL;
+    if (retry_cert) {
+        /* we likely came through the regular function in this state */
+        goto retry_cert;
+    }
+#ifndef OPENSSL_NO_TLSEXT
     /*
      * Check if we want to use external pre-shared secret for this handshake
      * for not reused session only. We need to generate server_random before
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 7ef6baa..ad8218c 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -1361,6 +1361,18 @@ typedef struct ssl3_state_st {
         unsigned long mask_ssl;
 
         int skip_client_verify;
+        /* managing micro state inside SSL3_ST_xxx states */
+        int sub_state;
+        /*
+         * Next three variables are temporary variables that store various information
+         * between call to ssl3_get_client_hello and ssl3_get_client_hello_post_app.
+         * I couldn't figure out a better way to break ssl3_get_client_hello into
+         * two parts without having to store this information.
+         */
+        STACK_OF(SSL_CIPHER) *ciphers;
+        unsigned char *q;
+        int i;
+        int complen;
     } tmp;
 
     /* Connection binding to prevent renegotiation attacks */
@@ -2128,6 +2140,7 @@ int dtls1_client_hello(SSL *s);
 
 /* some server-only functions */
 __owur int ssl3_get_client_hello(SSL *s);
+__owur int ssl3_get_client_hello_post_app(SSL *s, int retry_cert);
 __owur int ssl3_send_server_hello(SSL *s);
 __owur int ssl3_send_hello_request(SSL *s);
 __owur int ssl3_send_server_key_exchange(SSL *s);
@@ -2204,6 +2217,7 @@ __owur unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *buf,
 __owur int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **data,
                                  unsigned char *d, int n);
 __owur int tls1_set_server_sigalgs(SSL *s);
+__owur int ssl_check_clienthello_tlsext_async(SSL *s);
 __owur int ssl_check_clienthello_tlsext_late(SSL *s);
 __owur int ssl_parse_serverhello_tlsext(SSL *s, unsigned char **data,
                                  unsigned char *d, int n);
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index f485224..c395450 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -2672,7 +2672,6 @@ static int ssl_check_clienthello_tlsext_early(SSL *s)
      * in ssl3_choose_cipher in s3_lib.c.
      */
 # endif
-
     if (s->ctx != NULL && s->ctx->tlsext_servername_callback != 0)
         ret =
             s->ctx->tlsext_servername_callback(s, &al,
@@ -2683,7 +2682,6 @@ static int ssl_check_clienthello_tlsext_early(SSL *s)
             s->initial_ctx->tlsext_servername_callback(s, &al,
                                                        s->
                                                        initial_ctx->tlsext_servername_arg);
-
     switch (ret) {
     case SSL_TLSEXT_ERR_ALERT_FATAL:
         ssl3_send_alert(s, SSL3_AL_FATAL, al);
@@ -2715,6 +2713,43 @@ static void ssl_set_default_md(SSL *s)
 #endif
 }
 
+int ssl_check_clienthello_tlsext_async(SSL *s)
+{
+    int ret = SSL_TLSEXT_ERR_NOACK;
+    int al = SSL_AD_UNRECOGNIZED_NAME;
+
+    s->rwstate = SSL_EVENT_TLSEXT_SERVERNAME_READY;
+    if (s->ctx != NULL && s->ctx->tlsext_servername_callback != 0)
+        ret =
+            s->ctx->tlsext_servername_callback(s, &al,
+                                               s->ctx->tlsext_servername_arg);
+    else if (s->initial_ctx != NULL
+             && s->initial_ctx->tlsext_servername_callback != 0)
+        ret =
+            s->initial_ctx->tlsext_servername_callback(s, &al,
+                                                       s->
+                                                       initial_ctx->tlsext_servername_arg);
+    if (ret == SSL_TLSEXT_ERR_WAIT_FOR_EVENT)
+        /* we shall wait for event SSL_EVENT_TLSEXT_SERVERNAME_READY */
+        return -1;
+    SSL_signal_event(s, SSL_EVENT_TLSEXT_SERVERNAME_READY, 1);
+
+    switch (ret) {
+    case SSL_TLSEXT_ERR_ALERT_FATAL:
+        ssl3_send_alert(s, SSL3_AL_FATAL, al);
+        return 0;
+
+    case SSL_TLSEXT_ERR_ALERT_WARNING:
+        ssl3_send_alert(s, SSL3_AL_WARNING, al);
+        return 1;
+
+    case SSL_TLSEXT_ERR_NOACK:
+        s->servername_done = 0;
+    default:
+        return 1;
+    }
+}
+
 int tls1_set_server_sigalgs(SSL *s)
 {
     int al;
-- 
2.3.2 (Apple Git-55)

