From f5b30cc28c685b7744c3752a2a78cb920eae7ced Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Wed, 7 Dec 2016 10:55:27 -0800 Subject: [PATCH] Add a simplified SSL BIO for curl. A recent change to curl[1] added support for HTTPS proxies, which involves running a TLS connection inside another TLS connection. This was done by using SSL BIOs, which we removed from BoringSSL for being crazy. This change adds a stripped-down version of the SSL BIO to decrepit in order to suport curl. [1] https://github.com/curl/curl/commit/cb4e2be7c6d42ca0780f8e0a747cecf9ba45f151 Change-Id: I9cb8f2db5b28a5a70724f6f93544297c380ac124 Reviewed-on: https://boringssl-review.googlesource.com/12631 Reviewed-by: Adam Langley --- crypto/bio/bio.c | 4 + decrepit/CMakeLists.txt | 2 + decrepit/biossl/CMakeLists.txt | 9 ++ decrepit/biossl/bio_ssl.c | 185 +++++++++++++++++++++++++++++++++ include/openssl/bio.h | 2 + include/openssl/ssl.h | 11 ++ 6 files changed, 213 insertions(+) create mode 100644 decrepit/biossl/CMakeLists.txt create mode 100644 decrepit/biossl/bio_ssl.c diff --git a/crypto/bio/bio.c b/crypto/bio/bio.c index 675e903a..9619c223 100644 --- a/crypto/bio/bio.c +++ b/crypto/bio/bio.c @@ -604,3 +604,7 @@ int BIO_read_asn1(BIO *bio, uint8_t **out, size_t *out_len, size_t max_len) { return 1; } + +void BIO_set_retry_special(BIO *bio) { + bio->flags |= BIO_FLAGS_READ | BIO_FLAGS_IO_SPECIAL; +} diff --git a/decrepit/CMakeLists.txt b/decrepit/CMakeLists.txt index 6a5462c2..1e2386ab 100644 --- a/decrepit/CMakeLists.txt +++ b/decrepit/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(bio) +add_subdirectory(biossl) add_subdirectory(blowfish) add_subdirectory(cast) add_subdirectory(des) @@ -17,6 +18,7 @@ add_library( decrepit $ + $ $ $ $ diff --git a/decrepit/biossl/CMakeLists.txt b/decrepit/biossl/CMakeLists.txt new file mode 100644 index 00000000..39fe139a --- /dev/null +++ b/decrepit/biossl/CMakeLists.txt @@ -0,0 +1,9 @@ +include_directories(../../include) + +add_library( + biossl_decrepit + + OBJECT + + bio_ssl.c +) diff --git a/decrepit/biossl/bio_ssl.c b/decrepit/biossl/bio_ssl.c new file mode 100644 index 00000000..4574c310 --- /dev/null +++ b/decrepit/biossl/bio_ssl.c @@ -0,0 +1,185 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include + +#include + + +static int ssl_read(BIO *bio, char *out, int outl) { + SSL *ssl = bio->ptr; + if (ssl == NULL) { + return 0; + } + + BIO_clear_retry_flags(bio); + + const int ret = SSL_read(ssl, out, outl); + + switch (SSL_get_error(ssl, ret)) { + case SSL_ERROR_WANT_READ: + BIO_set_retry_read(bio); + break; + + case SSL_ERROR_WANT_WRITE: + BIO_set_retry_write(bio); + break; + + case SSL_ERROR_WANT_X509_LOOKUP: + BIO_set_retry_special(bio); + bio->retry_reason = BIO_RR_SSL_X509_LOOKUP; + break; + + case SSL_ERROR_WANT_ACCEPT: + BIO_set_retry_special(bio); + bio->retry_reason = BIO_RR_ACCEPT; + break; + + case SSL_ERROR_WANT_CONNECT: + BIO_set_retry_special(bio); + bio->retry_reason = BIO_RR_CONNECT; + break; + + case SSL_ERROR_NONE: + case SSL_ERROR_SYSCALL: + case SSL_ERROR_SSL: + case SSL_ERROR_ZERO_RETURN: + default: + break; + } + + return ret; +} + +static int ssl_write(BIO *bio, const char *out, int outl) { + SSL *ssl = bio->ptr; + if (ssl == NULL) { + return 0; + } + + BIO_clear_retry_flags(bio); + + const int ret = SSL_write(ssl, out, outl); + + switch (SSL_get_error(ssl, ret)) { + case SSL_ERROR_WANT_WRITE: + BIO_set_retry_write(bio); + break; + + case SSL_ERROR_WANT_READ: + BIO_set_retry_read(bio); + break; + + case SSL_ERROR_WANT_X509_LOOKUP: + BIO_set_retry_special(bio); + bio->retry_reason = BIO_RR_SSL_X509_LOOKUP; + break; + + case SSL_ERROR_WANT_CONNECT: + BIO_set_retry_special(bio); + bio->retry_reason = BIO_RR_CONNECT; + break; + + case SSL_ERROR_NONE: + case SSL_ERROR_SYSCALL: + case SSL_ERROR_SSL: + default: + break; + } + + return ret; +} + +static long ssl_ctrl(BIO *bio, int cmd, long num, void *ptr) { + SSL *ssl = bio->ptr; + if (ssl == NULL && cmd != BIO_C_SET_SSL) { + return 0; + } + + switch (cmd) { + case BIO_C_SET_SSL: + bio->shutdown = num; + bio->ptr = ptr; + bio->init = 1; + return 1; + + case BIO_CTRL_GET_CLOSE: + return bio->shutdown; + + case BIO_CTRL_SET_CLOSE: + bio->shutdown = num; + return 1; + + case BIO_CTRL_WPENDING: + return BIO_ctrl(ssl->wbio, cmd, num, ptr); + + case BIO_CTRL_PENDING: + return SSL_pending(ssl); + + case BIO_CTRL_FLUSH: { + BIO_clear_retry_flags(bio); + long ret = BIO_ctrl(ssl->wbio, cmd, num, ptr); + BIO_copy_next_retry(bio); + return ret; + } + + case BIO_CTRL_PUSH: + case BIO_CTRL_POP: + case BIO_CTRL_DUP: + return -1; + + default: + return BIO_ctrl(ssl->rbio, cmd, num, ptr); + } +} + +static int ssl_new(BIO *bio) { + return 1; +} + +static int ssl_free(BIO *bio) { + SSL *ssl = bio->ptr; + + if (ssl == NULL) { + return 1; + } + + SSL_shutdown(ssl); + if (bio->shutdown) { + SSL_free(ssl); + } + + return 1; +} + +static long ssl_callback_ctrl(BIO *bio, int cmd, bio_info_cb fp) { + SSL *ssl = bio->ptr; + if (ssl == NULL) { + return 0; + } + + switch (cmd) { + case BIO_CTRL_SET_CALLBACK: + return -1; + + default: + return BIO_callback_ctrl(ssl->rbio, cmd, fp); + } +} + +static const BIO_METHOD ssl_method = { + BIO_TYPE_SSL, "SSL", ssl_write, ssl_read, NULL, + NULL, ssl_ctrl, ssl_new, ssl_free, ssl_callback_ctrl, +}; + +const BIO_METHOD *BIO_f_ssl(void) { return &ssl_method; } + +long BIO_set_ssl(BIO *bio, SSL *ssl, int take_owership) { + return BIO_ctrl(bio, BIO_C_SET_SSL, take_owership, ssl); +} diff --git a/include/openssl/bio.h b/include/openssl/bio.h index 58a47479..ef7e4657 100644 --- a/include/openssl/bio.h +++ b/include/openssl/bio.h @@ -679,6 +679,8 @@ OPENSSL_EXPORT int BIO_shutdown_wr(BIO *bio); * on one line. */ OPENSSL_EXPORT const BIO_METHOD *BIO_f_base64(void); +OPENSSL_EXPORT void BIO_set_retry_special(BIO *bio); + /* Private functions */ diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 8b443fd1..53380378 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -3639,6 +3639,17 @@ OPENSSL_EXPORT int SSL_CTX_enable_tls_channel_id(SSL_CTX *ctx); /* SSL_enable_tls_channel_id calls |SSL_set_tls_channel_id_enabled|. */ OPENSSL_EXPORT int SSL_enable_tls_channel_id(SSL *ssl); +/* BIO_f_ssl returns a |BIO_METHOD| that can wrap an |SSL*| in a |BIO*|. Note + * that this has quite different behaviour from the version in OpenSSL (notably + * that it doesn't try to auto renegotiate). */ +OPENSSL_EXPORT const BIO_METHOD *BIO_f_ssl(void); + +/* BIO_set_ssl sets |ssl| as the underlying connection for |bio|, which must + * have been created using |BIO_f_ssl|. If |take_owership| is true, |bio| will + * call |SSL_free| on |ssl| when closed. It returns one on success or something + * other than one on error. */ +OPENSSL_EXPORT long BIO_set_ssl(BIO *bio, SSL *ssl, int take_owership); + /* Private structures. *