From 487e24faf0792a05579370a089ee6dec1f205430 Mon Sep 17 00:00:00 2001 From: Alessandro Ghedini Date: Tue, 13 Sep 2016 23:47:48 +0100 Subject: [PATCH] Add support for asynchronous session lookup The whole SSL_magic_pending_session_ptr() machinery was copied from BoringSSL so projects already using it (e.g. OpenResty) can be built with OpenSSL 1.1 without changes. --- doc/man3/SSL_CTX_sess_set_get_cb.pod | 9 ++++++++- doc/man3/SSL_want.pod | 7 +++++++ include/openssl/bio.h | 2 ++ include/openssl/ssl.h | 5 +++++ ssl/bio_ssl.c | 12 ++++++++++++ ssl/ssl_lib.c | 3 +++ ssl/ssl_sess.c | 11 +++++++++++ ssl/statem/statem.c | 13 +++++++++---- ssl/statem/statem.h | 1 + ssl/statem/statem_srvr.c | 5 +++++ util/libssl.num | 1 + 11 files changed, 64 insertions(+), 5 deletions(-) diff --git a/doc/man3/SSL_CTX_sess_set_get_cb.pod b/doc/man3/SSL_CTX_sess_set_get_cb.pod index ebea4c54cd240..bbd34557249e4 100644 --- a/doc/man3/SSL_CTX_sess_set_get_cb.pod +++ b/doc/man3/SSL_CTX_sess_set_get_cb.pod @@ -15,6 +15,8 @@ SSL_CTX_sess_set_new_cb, SSL_CTX_sess_set_remove_cb, SSL_CTX_sess_set_get_cb, SS void SSL_CTX_sess_set_get_cb(SSL_CTX *ctx, SSL_SESSION (*get_session_cb)(SSL *, const unsigned char *, int, int *)); + SSL_SESSION *SSL_magic_pending_session_ptr(void); + int (*SSL_CTX_sess_get_new_cb(SSL_CTX *ctx))(struct ssl_st *ssl, SSL_SESSION *sess); void (*SSL_CTX_sess_get_remove_cb(SSL_CTX *ctx))(struct ssl_ctx_st *ctx, SSL_SESSION *sess); SSL_SESSION *(*SSL_CTX_sess_get_get_cb(SSL_CTX *ctx))(struct ssl_st *ssl, const unsigned char *data, int len, int *copy); @@ -74,7 +76,12 @@ B. With the parameter B the callback can require the SSL engine to increment the reference count of the SSL_SESSION object, Normally the reference count is not incremented and therefore the session must not be explicitly freed with -L. +L. get_session_cb() can also return the value returned by +SSL_magic_pending_session_ptr() instead of a real SSL_SESSION object, in which +case the handshake is suspended. The next call to the handshake function will +again lead to the call of get_session_cb(). To indicate that the handshake was +suspended, L will return SSL_ERROR_WANT_SESSION_LOOKUP to the +application. =head1 SEE ALSO diff --git a/doc/man3/SSL_want.pod b/doc/man3/SSL_want.pod index c86344eeceb6e..5c4c0aea9e847 100644 --- a/doc/man3/SSL_want.pod +++ b/doc/man3/SSL_want.pod @@ -17,6 +17,7 @@ operation int SSL_want_x509_lookup(const SSL *ssl); int SSL_want_async(const SSL *ssl); int SSL_want_async_job(const SSL *ssl); + int SSL_want_sess_lookup(const SSL *ssl); =head1 DESCRIPTION @@ -81,6 +82,12 @@ The asynchronous job could not be started because there were no async jobs available in the pool (see ASYNC_init_thread(3)). A call to L should return SSL_ERROR_WANT_ASYNC_JOB. +=item SSL_SESS_LOOKUP + +The operation did not complete because an application callback set by +SSL_CTX_sess_set_get_cb() has asked to be called again. +A call to L should return SSL_ERROR_WANT_SESSION_LOOKUP. + =back SSL_want_nothing(), SSL_want_read(), SSL_want_write(), SSL_want_x509_lookup(), diff --git a/include/openssl/bio.h b/include/openssl/bio.h index 2fb9023eac027..4a3c0a61a50eb 100644 --- a/include/openssl/bio.h +++ b/include/openssl/bio.h @@ -220,6 +220,8 @@ void BIO_clear_flags(BIO *b, int flags); /* Returned from the accept BIO when an accept would have blocked */ # define BIO_RR_ACCEPT 0x03 +# define BIO_RR_SSL_SESSION_LOOKUP 0x04 + /* These are passed by the BIO callback */ # define BIO_CB_FREE 0x01 # define BIO_CB_READ 0x02 diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index b61a992e16098..2105ef501a2eb 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -791,6 +791,7 @@ __owur int SSL_extension_supported(unsigned int ext_type); # define SSL_X509_LOOKUP 4 # define SSL_ASYNC_PAUSED 5 # define SSL_ASYNC_NO_JOBS 6 +# define SSL_SESS_LOOKUP 7 /* These will only be used when doing non-blocking IO */ # define SSL_want_nothing(s) (SSL_want(s) == SSL_NOTHING) @@ -799,6 +800,7 @@ __owur int SSL_extension_supported(unsigned int ext_type); # define SSL_want_x509_lookup(s) (SSL_want(s) == SSL_X509_LOOKUP) # define SSL_want_async(s) (SSL_want(s) == SSL_ASYNC_PAUSED) # define SSL_want_async_job(s) (SSL_want(s) == SSL_ASYNC_NO_JOBS) +# define SSL_want_sess_lookup(s) (SSL_want(s) == SSL_SESS_LOOKUP) # define SSL_MAC_FLAG_READ_MAC_STREAM 1 # define SSL_MAC_FLAG_WRITE_MAC_STREAM 2 @@ -1031,6 +1033,8 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION) # define SSL_ERROR_WANT_ACCEPT 8 # define SSL_ERROR_WANT_ASYNC 9 # define SSL_ERROR_WANT_ASYNC_JOB 10 +# define SSL_ERROR_WANT_SESSION_LOOKUP 11 +# define SSL_ERROR_PENDING_SESSION 11 /* BoringSSL compatibility */ # define SSL_CTRL_SET_TMP_DH 3 # define SSL_CTRL_SET_TMP_ECDH 4 # define SSL_CTRL_SET_TMP_DH_CB 6 @@ -1440,6 +1444,7 @@ int SSL_SESSION_print(BIO *fp, const SSL_SESSION *ses); int SSL_SESSION_print_keylog(BIO *bp, const SSL_SESSION *x); int SSL_SESSION_up_ref(SSL_SESSION *ses); void SSL_SESSION_free(SSL_SESSION *ses); +SSL_SESSION *SSL_magic_pending_session_ptr(void); __owur int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp); __owur int SSL_set_session(SSL *to, SSL_SESSION *session); __owur int SSL_CTX_add_session(SSL_CTX *s, SSL_SESSION *c); diff --git a/ssl/bio_ssl.c b/ssl/bio_ssl.c index e48b90fceaa0c..3ab3a0294167e 100644 --- a/ssl/bio_ssl.c +++ b/ssl/bio_ssl.c @@ -138,6 +138,10 @@ static int ssl_read(BIO *b, char *buf, size_t size, size_t *readbytes) BIO_set_retry_special(b); retry_reason = BIO_RR_SSL_X509_LOOKUP; break; + case SSL_ERROR_WANT_SESSION_LOOKUP: + BIO_set_retry_special(b); + retry_reason = BIO_RR_SSL_SESSION_LOOKUP; + break; case SSL_ERROR_WANT_ACCEPT: BIO_set_retry_special(b); retry_reason = BIO_RR_ACCEPT; @@ -206,6 +210,10 @@ static int ssl_write(BIO *b, const char *buf, size_t size, size_t *written) BIO_set_retry_special(b); retry_reason = BIO_RR_SSL_X509_LOOKUP; break; + case SSL_ERROR_WANT_SESSION_LOOKUP: + BIO_set_retry_special(b); + retry_reason = BIO_RR_SSL_SESSION_LOOKUP; + break; case SSL_ERROR_WANT_CONNECT: BIO_set_retry_special(b); retry_reason = BIO_RR_CONNECT; @@ -360,6 +368,10 @@ static long ssl_ctrl(BIO *b, int cmd, long num, void *ptr) BIO_set_retry_special(b); BIO_set_retry_reason(b, BIO_RR_SSL_X509_LOOKUP); break; + case SSL_ERROR_WANT_SESSION_LOOKUP: + BIO_set_retry_special(b); + BIO_set_retry_reason(b, BIO_RR_SSL_SESSION_LOOKUP); + break; default: break; } diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index a6360accea7a7..e4446aeb570b3 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -3066,6 +3066,9 @@ int SSL_get_error(const SSL *s, int i) if (SSL_want_async_job(s)) { return SSL_ERROR_WANT_ASYNC_JOB; } + if (SSL_want_sess_lookup(s)) { + return SSL_ERROR_WANT_SESSION_LOOKUP; + } if ((s->shutdown & SSL_RECEIVED_SHUTDOWN) && (s->s3->warn_alert == SSL_AD_CLOSE_NOTIFY)) diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c index 825e706561567..f495c66f6413b 100644 --- a/ssl/ssl_sess.c +++ b/ssl/ssl_sess.c @@ -40,6 +40,8 @@ #include #include "ssl_locl.h" +static const char g_pending_session_magic = 0; + static void SSL_SESSION_list_remove(SSL_CTX *ctx, SSL_SESSION *s); static void SSL_SESSION_list_add(SSL_CTX *ctx, SSL_SESSION *s); static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *c, int lck); @@ -503,6 +505,10 @@ int ssl_get_prev_session(SSL *s, CLIENTHELLO_MSG *hello) hello->session_id_len, ©); + if (ret == SSL_magic_pending_session_ptr()) { + return -2; /* Retry later */ + } + if (ret != NULL) { s->session_ctx->stats.sess_cb_hit++; @@ -878,6 +884,11 @@ X509 *SSL_SESSION_get0_peer(SSL_SESSION *s) return s->peer; } +SSL_SESSION *SSL_magic_pending_session_ptr(void) +{ + return (SSL_SESSION *) &g_pending_session_magic; +} + int SSL_SESSION_set1_id_context(SSL_SESSION *s, const unsigned char *sid_ctx, unsigned int sid_ctx_len) { diff --git a/ssl/statem/statem.c b/ssl/statem/statem.c index a1da2a4418d11..2bd055b5b02ff 100644 --- a/ssl/statem/statem.c +++ b/ssl/statem/statem.c @@ -567,16 +567,18 @@ static SUB_STATE_RETURN read_state_machine(SSL *s) } s->first_packet = 0; - if (!PACKET_buf_init(&pkt, s->init_msg, len)) { + + st->read_state = READ_STATE_PROCESS; + /* Fall through */ + + case READ_STATE_PROCESS: + if (!PACKET_buf_init(&pkt, s->init_msg, s->init_num)) { ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); SSLerr(SSL_F_READ_STATE_MACHINE, ERR_R_INTERNAL_ERROR); return SUB_STATE_ERROR; } ret = process_message(s, &pkt); - /* Discard the packet data */ - s->init_num = 0; - switch (ret) { case MSG_PROCESS_ERROR: return SUB_STATE_ERROR; @@ -596,6 +598,9 @@ static SUB_STATE_RETURN read_state_machine(SSL *s) st->read_state = READ_STATE_HEADER; break; } + + /* Discard the packet data */ + s->init_num = 0; break; case READ_STATE_POST_PROCESS: diff --git a/ssl/statem/statem.h b/ssl/statem/statem.h index 2fca39b0db3a2..b106f4d069a61 100644 --- a/ssl/statem/statem.h +++ b/ssl/statem/statem.h @@ -60,6 +60,7 @@ typedef enum { typedef enum { READ_STATE_HEADER, READ_STATE_BODY, + READ_STATE_PROCESS, READ_STATE_POST_PROCESS } READ_STATE; diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c index 142c637dc934b..a971cec8e2a4d 100644 --- a/ssl/statem/statem_srvr.c +++ b/ssl/statem/statem_srvr.c @@ -1202,11 +1202,16 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt) s->hit = 1; } else if (i == -1) { goto err; + } else if (i == -2) { + s->rwstate = SSL_SESS_LOOKUP; + s->statem.read_state_work = WORK_MORE_A; + return MSG_PROCESS_ERROR; } else { /* i == 0 */ if (!ssl_get_new_session(s, 1)) goto err; } + s->rwstate = SSL_NOTHING; } if (ssl_bytes_to_cipher_list(s, &clienthello.ciphersuites, &ciphers, diff --git a/util/libssl.num b/util/libssl.num index 7d1e8d88dd44b..dcf1c6041ea42 100644 --- a/util/libssl.num +++ b/util/libssl.num @@ -408,3 +408,4 @@ DTLS_get_data_mtu 408 1_1_1 EXIST::FUNCTION: SSL_read_ex 409 1_1_1 EXIST::FUNCTION: SSL_peek_ex 410 1_1_1 EXIST::FUNCTION: SSL_write_ex 411 1_1_1 EXIST::FUNCTION: +SSL_magic_pending_session_ptr 412 1_1_1 EXIST::FUNCTION: