From feb330d50d0fc10aceec6309131e912e152a1027 Mon Sep 17 00:00:00 2001 From: Igor Gnatenko Date: Wed, 12 Oct 2016 12:41:36 +0200 Subject: [PATCH 1/4] add support for OpenSSL 1.1.0 for BIO filter Closes: https://github.com/libgit2/libgit2/issues/3959 Signed-off-by: Igor Gnatenko --- src/openssl_stream.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/openssl_stream.c b/src/openssl_stream.c index b8ab21fef..9c5e0b1e8 100644 --- a/src/openssl_stream.c +++ b/src/openssl_stream.c @@ -156,10 +156,14 @@ int git_openssl_set_locking(void) static int bio_create(BIO *b) { +#if OPENSSL_VERSION_NUMBER < 0x10100000L b->init = 1; b->num = 0; b->ptr = NULL; b->flags = 0; +#else + BIO_set_init(b, 1); +#endif return 1; } @@ -169,23 +173,36 @@ static int bio_destroy(BIO *b) if (!b) return 0; +#if OPENSSL_VERSION_NUMBER < 0x10100000L b->init = 0; b->num = 0; b->ptr = NULL; b->flags = 0; +#else + BIO_set_init(b, 0); + BIO_set_data(b, NULL); +#endif return 1; } static int bio_read(BIO *b, char *buf, int len) { +#if OPENSSL_VERSION_NUMBER < 0x10100000L git_stream *io = (git_stream *) b->ptr; +#else + git_stream *io = (git_stream *) BIO_get_data(b); +#endif return (int) git_stream_read(io, buf, len); } static int bio_write(BIO *b, const char *buf, int len) { +#if OPENSSL_VERSION_NUMBER < 0x10100000L git_stream *io = (git_stream *) b->ptr; +#else + git_stream *io = (git_stream *) BIO_get_data(b); +#endif return (int) git_stream_write(io, buf, len, 0); } @@ -214,6 +231,7 @@ static int bio_puts(BIO *b, const char *str) return bio_write(b, str, strlen(str)); } +#if OPENSSL_VERSION_NUMBER < 0x10100000L static BIO_METHOD git_stream_bio_method = { BIO_TYPE_SOURCE_SINK, "git_stream", @@ -225,6 +243,9 @@ static BIO_METHOD git_stream_bio_method = { bio_create, bio_destroy }; +#else +static BIO_METHOD *git_stream_bio_method = NULL; +#endif static int ssl_set_error(SSL *ssl, int error) { @@ -445,9 +466,25 @@ int openssl_connect(git_stream *stream) st->connected = true; +#if OPENSSL_VERSION_NUMBER < 0x10100000L bio = BIO_new(&git_stream_bio_method); +#else + git_stream_bio_method = BIO_meth_new(BIO_TYPE_SOURCE_SINK | BIO_get_new_index(), "git_stream"); + BIO_meth_set_write(git_stream_bio_method, bio_write); + BIO_meth_set_read(git_stream_bio_method, bio_read); + BIO_meth_set_puts(git_stream_bio_method, bio_puts); + BIO_meth_set_gets(git_stream_bio_method, bio_gets); + BIO_meth_set_ctrl(git_stream_bio_method, bio_ctrl); + BIO_meth_set_create(git_stream_bio_method, bio_create); + BIO_meth_set_destroy(git_stream_bio_method, bio_destroy); + bio = BIO_new(git_stream_bio_method); +#endif GITERR_CHECK_ALLOC(bio); +#if OPENSSL_VERSION_NUMBER < 0x10100000L bio->ptr = st->io; +#else + BIO_set_data(bio, st->io); +#endif SSL_set_bio(st->ssl, bio, bio); /* specify the host in case SNI is needed */ From f15eedb3a390dcbe441cd77231c3449ff941d189 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 2 Nov 2016 12:28:25 +0100 Subject: [PATCH 2/4] openssl: recreate the OpenSSL 1.1 BIO interface for older versions We want to program against the interface, so recreate it when we compile against pre-1.1 versions. --- src/openssl_stream.c | 92 +++++++++++++++++------------------------ src/openssl_stream.h | 97 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+), 55 deletions(-) diff --git a/src/openssl_stream.c b/src/openssl_stream.c index 9c5e0b1e8..80c6b9828 100644 --- a/src/openssl_stream.c +++ b/src/openssl_stream.c @@ -13,6 +13,7 @@ #include "posix.h" #include "stream.h" #include "socket_stream.h" +#include "openssl_stream.h" #include "netops.h" #include "git2/transport.h" #include "git2/sys/openssl.h" @@ -71,12 +72,20 @@ static void shutdown_ssl_locking(void) #endif /* GIT_THREADS */ +static BIO_METHOD *git_stream_bio_method; +static int init_bio_method(void); + /** * This function aims to clean-up the SSL context which * we allocated. */ static void shutdown_ssl(void) { + if (git_stream_bio_method) { + BIO_meth_free(git_stream_bio_method); + git_stream_bio_method = NULL; + } + if (git__ssl_ctx) { SSL_CTX_free(git__ssl_ctx); git__ssl_ctx = NULL; @@ -121,6 +130,13 @@ int git_openssl_stream_global_init(void) git__ssl_ctx = NULL; return -1; } + + if (init_bio_method() < 0) { + SSL_CTX_free(git__ssl_ctx); + git__ssl_ctx = NULL; + return -1; + } + #endif git__on_shutdown(shutdown_ssl); @@ -156,14 +172,8 @@ int git_openssl_set_locking(void) static int bio_create(BIO *b) { -#if OPENSSL_VERSION_NUMBER < 0x10100000L - b->init = 1; - b->num = 0; - b->ptr = NULL; - b->flags = 0; -#else BIO_set_init(b, 1); -#endif + BIO_set_data(b, NULL); return 1; } @@ -173,36 +183,22 @@ static int bio_destroy(BIO *b) if (!b) return 0; -#if OPENSSL_VERSION_NUMBER < 0x10100000L - b->init = 0; - b->num = 0; - b->ptr = NULL; - b->flags = 0; -#else - BIO_set_init(b, 0); BIO_set_data(b, NULL); -#endif return 1; } static int bio_read(BIO *b, char *buf, int len) { -#if OPENSSL_VERSION_NUMBER < 0x10100000L - git_stream *io = (git_stream *) b->ptr; -#else git_stream *io = (git_stream *) BIO_get_data(b); -#endif + return (int) git_stream_read(io, buf, len); } static int bio_write(BIO *b, const char *buf, int len) { -#if OPENSSL_VERSION_NUMBER < 0x10100000L - git_stream *io = (git_stream *) b->ptr; -#else git_stream *io = (git_stream *) BIO_get_data(b); -#endif + return (int) git_stream_write(io, buf, len, 0); } @@ -231,21 +227,22 @@ static int bio_puts(BIO *b, const char *str) return bio_write(b, str, strlen(str)); } -#if OPENSSL_VERSION_NUMBER < 0x10100000L -static BIO_METHOD git_stream_bio_method = { - BIO_TYPE_SOURCE_SINK, - "git_stream", - bio_write, - bio_read, - bio_puts, - bio_gets, - bio_ctrl, - bio_create, - bio_destroy -}; -#else -static BIO_METHOD *git_stream_bio_method = NULL; -#endif +static int init_bio_method(void) +{ + /* Set up the BIO_METHOD we use for wrapping our own stream implementations */ + git_stream_bio_method = BIO_meth_new(BIO_TYPE_SOURCE_SINK | BIO_get_new_index(), "git_stream"); + GITERR_CHECK_ALLOC(git_stream_bio_method); + + BIO_meth_set_write(git_stream_bio_method, bio_write); + BIO_meth_set_read(git_stream_bio_method, bio_read); + BIO_meth_set_puts(git_stream_bio_method, bio_puts); + BIO_meth_set_gets(git_stream_bio_method, bio_gets); + BIO_meth_set_ctrl(git_stream_bio_method, bio_ctrl); + BIO_meth_set_create(git_stream_bio_method, bio_create); + BIO_meth_set_destroy(git_stream_bio_method, bio_destroy); + + return 0; +} static int ssl_set_error(SSL *ssl, int error) { @@ -466,27 +463,12 @@ int openssl_connect(git_stream *stream) st->connected = true; -#if OPENSSL_VERSION_NUMBER < 0x10100000L - bio = BIO_new(&git_stream_bio_method); -#else - git_stream_bio_method = BIO_meth_new(BIO_TYPE_SOURCE_SINK | BIO_get_new_index(), "git_stream"); - BIO_meth_set_write(git_stream_bio_method, bio_write); - BIO_meth_set_read(git_stream_bio_method, bio_read); - BIO_meth_set_puts(git_stream_bio_method, bio_puts); - BIO_meth_set_gets(git_stream_bio_method, bio_gets); - BIO_meth_set_ctrl(git_stream_bio_method, bio_ctrl); - BIO_meth_set_create(git_stream_bio_method, bio_create); - BIO_meth_set_destroy(git_stream_bio_method, bio_destroy); bio = BIO_new(git_stream_bio_method); -#endif GITERR_CHECK_ALLOC(bio); -#if OPENSSL_VERSION_NUMBER < 0x10100000L - bio->ptr = st->io; -#else - BIO_set_data(bio, st->io); -#endif + BIO_set_data(bio, st->io); SSL_set_bio(st->ssl, bio, bio); + /* specify the host in case SNI is needed */ #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME SSL_set_tlsext_host_name(st->ssl, st->host); diff --git a/src/openssl_stream.h b/src/openssl_stream.h index 82b5110c4..509e40428 100644 --- a/src/openssl_stream.h +++ b/src/openssl_stream.h @@ -7,10 +7,107 @@ #ifndef INCLUDE_openssl_stream_h__ #define INCLUDE_openssl_stream_h__ +#include +#include +#include +#include + #include "git2/sys/stream.h" extern int git_openssl_stream_global_init(void); extern int git_openssl_stream_new(git_stream **out, const char *host, const char *port); +/* + * OpenSSL 1.1 made BIO opaque so we have to use functions to interact with it + * which do not exist in previous versions. We define these inline functions so + * we can program against the interface instead of littering the implementation + * with ifdefs. + */ +# if OPENSSL_VERSION_NUMBER < 0x10100000L + +GIT_INLINE(BIO_METHOD*) BIO_meth_new(int type, const char *name) +{ + BIO_METHOD *meth = git__calloc(1, sizeof(BIO_METHOD)); + if (!meth) { + return NULL; + } + + meth->type = type; + meth->name = name; + + return meth; +} + +GIT_INLINE(void) BIO_meth_free(BIO_METHOD *biom) +{ + git__free(biom); +} + +GIT_INLINE(int) BIO_meth_set_write(BIO_METHOD *biom, int (*write) (BIO *, const char *, int)) +{ + biom->bwrite = write; + return 1; +} + +GIT_INLINE(int) BIO_meth_set_read(BIO_METHOD *biom, int (*read) (BIO *, char *, int)) +{ + biom->bread = read; + return 1; +} + +GIT_INLINE(int) BIO_meth_set_puts(BIO_METHOD *biom, int (*puts) (BIO *, const char *)) +{ + biom->bputs = puts; + return 1; +} + +GIT_INLINE(int) BIO_meth_set_gets(BIO_METHOD *biom, int (*gets) (BIO *, char *, int)) + +{ + biom->bgets = gets; + return 1; +} + +GIT_INLINE(int) BIO_meth_set_ctrl(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *)) +{ + biom->ctrl = ctrl; + return 1; +} + +GIT_INLINE(int) BIO_meth_set_create(BIO_METHOD *biom, int (*create) (BIO *)) +{ + biom->create = create; + return 1; +} + +GIT_INLINE(int) BIO_meth_set_destroy(BIO_METHOD *biom, int (*destroy) (BIO *)) +{ + biom->destroy = destroy; + return 1; +} + +GIT_INLINE(int) BIO_get_new_index(void) +{ + /* This exists as of 1.1 so before we'd just have 0 */ + return 0; +} + +GIT_INLINE(void) BIO_set_init(BIO *b, int init) +{ + b->init = init; +} + +GIT_INLINE(void) BIO_set_data(BIO *a, void *ptr) +{ + a->ptr = ptr; +} + +GIT_INLINE(void*) BIO_get_data(BIO *a) +{ + return a->ptr; +} + +# endif + #endif From 2f3adf9513b1579ae17489d45d032b962bba885a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 2 Nov 2016 12:35:46 +0100 Subject: [PATCH 3/4] openssl: use ASN1_STRING_get0_data when compiling against 1.1 For older versions we can fall back on the deprecated ASN1_STRING_data. --- src/openssl_stream.c | 4 ++-- src/openssl_stream.h | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/openssl_stream.c b/src/openssl_stream.c index 80c6b9828..826c0a537 100644 --- a/src/openssl_stream.c +++ b/src/openssl_stream.c @@ -357,7 +357,7 @@ static int verify_server_cert(SSL *ssl, const char *host) num = sk_GENERAL_NAME_num(alts); for (i = 0; i < num && matched != 1; i++) { const GENERAL_NAME *gn = sk_GENERAL_NAME_value(alts, i); - const char *name = (char *) ASN1_STRING_data(gn->d.ia5); + const char *name = (char *) ASN1_STRING_get0_data(gn->d.ia5); size_t namelen = (size_t) ASN1_STRING_length(gn->d.ia5); /* Skip any names of a type we're not looking for */ @@ -412,7 +412,7 @@ static int verify_server_cert(SSL *ssl, const char *host) if (size > 0) { peer_cn = OPENSSL_malloc(size + 1); GITERR_CHECK_ALLOC(peer_cn); - memcpy(peer_cn, ASN1_STRING_data(str), size); + memcpy(peer_cn, ASN1_STRING_get0_data(str), size); peer_cn[size] = '\0'; } else { goto cert_fail_name; diff --git a/src/openssl_stream.h b/src/openssl_stream.h index 509e40428..e8ce5d91b 100644 --- a/src/openssl_stream.h +++ b/src/openssl_stream.h @@ -108,6 +108,11 @@ GIT_INLINE(void*) BIO_get_data(BIO *a) return a->ptr; } +GIT_INLINE(const unsigned char *) ASN1_STRING_get0_data(const ASN1_STRING *x) +{ + return ASN1_STRING_data((ASN1_STRING *)x); +} + # endif #endif From 3b832a085b8c5dc304dd803979894b9bae05df6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 2 Nov 2016 13:11:31 +0100 Subject: [PATCH 4/4] openssl: include OpenSSL headers only when we're buliding against it We need to include the initialisation and construction functions in all backend, so we include this header when building against SecureTransport and WinHTTP as well. --- src/openssl_stream.h | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/openssl_stream.h b/src/openssl_stream.h index e8ce5d91b..b769437ae 100644 --- a/src/openssl_stream.h +++ b/src/openssl_stream.h @@ -7,11 +7,6 @@ #ifndef INCLUDE_openssl_stream_h__ #define INCLUDE_openssl_stream_h__ -#include -#include -#include -#include - #include "git2/sys/stream.h" extern int git_openssl_stream_global_init(void); @@ -24,6 +19,14 @@ extern int git_openssl_stream_new(git_stream **out, const char *host, const char * we can program against the interface instead of littering the implementation * with ifdefs. */ +#ifdef GIT_OPENSSL +# include +# include +# include +# include + + + # if OPENSSL_VERSION_NUMBER < 0x10100000L GIT_INLINE(BIO_METHOD*) BIO_meth_new(int type, const char *name) @@ -113,6 +116,7 @@ GIT_INLINE(const unsigned char *) ASN1_STRING_get0_data(const ASN1_STRING *x) return ASN1_STRING_data((ASN1_STRING *)x); } -# endif +# endif // OpenSSL < 1.1 +#endif // GIT_OPENSSL #endif