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 <agl@google.com>
diff --git a/crypto/bio/bio.c b/crypto/bio/bio.c
index 675e903..9619c22 100644
--- a/crypto/bio/bio.c
+++ b/crypto/bio/bio.c
@@ -604,3 +604,7 @@
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 6a5462c..1e2386a 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 @@
decrepit
$<TARGET_OBJECTS:bio_decrepit>
+ $<TARGET_OBJECTS:biossl_decrepit>
$<TARGET_OBJECTS:blowfish>
$<TARGET_OBJECTS:cast>
$<TARGET_OBJECTS:des_decrepit>
diff --git a/decrepit/biossl/CMakeLists.txt b/decrepit/biossl/CMakeLists.txt
new file mode 100644
index 0000000..39fe139
--- /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 0000000..4574c31
--- /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 <openssl/ssl.h>
+
+#include <openssl/bio.h>
+
+
+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 58a4747..ef7e465 100644
--- a/include/openssl/bio.h
+++ b/include/openssl/bio.h
@@ -679,6 +679,8 @@
* 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 8b443fd..5338037 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -3639,6 +3639,17 @@
/* 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.
*