From 158d65581dda29e6b9d85c6bc9ba612a09e73210 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Fri, 23 Jun 2017 17:23:41 -0500 Subject: [PATCH 1/2] Update to LibGit2 v0.26.0 --- base/libgit2/libgit2.jl | 51 +- .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 - .../sha512 | 1 - deps/libgit2.mk | 28 +- deps/libgit2.version | 4 +- deps/patches/libgit2-gitconfig-symlink.patch | 27 - deps/patches/libgit2-mbedtls-fixup.patch | 70 + deps/patches/libgit2-mbedtls-verify.patch | 147 +- deps/patches/libgit2-mbedtls-writer-fix.patch | 30 - deps/patches/libgit2-mbedtls.patch | 4827 +++++++++++++++-- deps/patches/libgit2-remote-push-NULL.patch | 26 - test/libgit2.jl | 18 +- 14 files changed, 4521 insertions(+), 711 deletions(-) create mode 100644 deps/checksums/libgit2-15e119375018fba121cf58e02a9f17fe22df0df8.tar.gz/md5 create mode 100644 deps/checksums/libgit2-15e119375018fba121cf58e02a9f17fe22df0df8.tar.gz/sha512 delete mode 100644 deps/checksums/libgit2-2fcb8705e584ca61f6c4657525c9d2713f6a39d2.tar.gz/md5 delete mode 100644 deps/checksums/libgit2-2fcb8705e584ca61f6c4657525c9d2713f6a39d2.tar.gz/sha512 delete mode 100644 deps/patches/libgit2-gitconfig-symlink.patch create mode 100644 deps/patches/libgit2-mbedtls-fixup.patch delete mode 100644 deps/patches/libgit2-mbedtls-writer-fix.patch delete mode 100644 deps/patches/libgit2-remote-push-NULL.patch diff --git a/base/libgit2/libgit2.jl b/base/libgit2/libgit2.jl index bfc18a85f5bbd..298f61e431464 100644 --- a/base/libgit2/libgit2.jl +++ b/base/libgit2/libgit2.jl @@ -873,33 +873,13 @@ function set_ssl_cert_locations(cert_loc) cert_file = isfile(cert_loc) ? cert_loc : Cstring(C_NULL) cert_dir = isdir(cert_loc) ? cert_loc : Cstring(C_NULL) cert_file == C_NULL && cert_dir == C_NULL && return - # TODO FIX https://github.com/libgit2/libgit2/pull/3935#issuecomment-253910017 - #ccall((:git_libgit2_opts, :libgit2), Cint, - # (Cint, Cstring, Cstring), - # Cint(Consts.SET_SSL_CERT_LOCATIONS), cert_file, cert_dir) - ENV["SSL_CERT_FILE"] = cert_file - ENV["SSL_CERT_DIR"] = cert_dir + @check ccall((:git_libgit2_opts, :libgit2), Cint, + (Cint, Cstring, Cstring), + Cint(Consts.SET_SSL_CERT_LOCATIONS), cert_file, cert_dir) end function __init__() - # Look for OpenSSL env variable for CA bundle (linux only) - # windows and macOS use the OS native security backends - old_ssl_cert_dir = Base.get(ENV, "SSL_CERT_DIR", nothing) - old_ssl_cert_file = Base.get(ENV, "SSL_CERT_FILE", nothing) - @static if is_linux() - cert_loc = if "SSL_CERT_DIR" in keys(ENV) - ENV["SSL_CERT_DIR"] - elseif "SSL_CERT_FILE" in keys(ENV) - ENV["SSL_CERT_FILE"] - else - # If we have a bundled ca cert file, point libgit2 at that so SSL connections work. - abspath(ccall(:jl_get_julia_home, Any, ()),Base.DATAROOTDIR,"julia","cert.pem") - end - set_ssl_cert_locations(cert_loc) - end - - err = ccall((:git_libgit2_init, :libgit2), Cint, ()) - err > 0 || throw(ErrorException("error initializing LibGit2 module")) + @check ccall((:git_libgit2_init, :libgit2), Cint, ()) REFCOUNT[] = 1 atexit() do @@ -909,21 +889,18 @@ function __init__() end end + # Look for OpenSSL env variable for CA bundle (linux only) + # windows and macOS use the OS native security backends @static if is_linux() - if old_ssl_cert_dir != Base.get(ENV, "SSL_CERT_DIR", "") - if old_ssl_cert_dir === nothing - delete!(ENV, "SSL_CERT_DIR") - else - ENV["SSL_CERT_DIR"] = old_ssl_cert_dir - end - end - if old_ssl_cert_file != Base.get(ENV, "SSL_CERT_FILE", "") - if old_ssl_cert_file === nothing - delete!(ENV, "SSL_CERT_FILE") - else - ENV["SSL_CERT_FILE"] = old_ssl_cert_file - end + cert_loc = if "SSL_CERT_DIR" in keys(ENV) + ENV["SSL_CERT_DIR"] + elseif "SSL_CERT_FILE" in keys(ENV) + ENV["SSL_CERT_FILE"] + else + # If we have a bundled ca cert file, point libgit2 at that so SSL connections work. + abspath(ccall(:jl_get_julia_home, Any, ()), Base.DATAROOTDIR, "julia", "cert.pem") end + set_ssl_cert_locations(cert_loc) end end diff --git a/deps/checksums/libgit2-15e119375018fba121cf58e02a9f17fe22df0df8.tar.gz/md5 b/deps/checksums/libgit2-15e119375018fba121cf58e02a9f17fe22df0df8.tar.gz/md5 new file mode 100644 index 0000000000000..f148bce28a5c8 --- /dev/null +++ b/deps/checksums/libgit2-15e119375018fba121cf58e02a9f17fe22df0df8.tar.gz/md5 @@ -0,0 +1 @@ +0d6fd3ed9265c6804349149b23ae6362 diff --git a/deps/checksums/libgit2-15e119375018fba121cf58e02a9f17fe22df0df8.tar.gz/sha512 b/deps/checksums/libgit2-15e119375018fba121cf58e02a9f17fe22df0df8.tar.gz/sha512 new file mode 100644 index 0000000000000..1e129704580c7 --- /dev/null +++ b/deps/checksums/libgit2-15e119375018fba121cf58e02a9f17fe22df0df8.tar.gz/sha512 @@ -0,0 +1 @@ +88a8a42bb8d18a5a722938404e048266d0899362ac89fdfedfa9f71aeb90408d8d98b4d9b9ea2ff46755d0a2cd8686ff04d31e85827566e1290a9536b8b36ac8 diff --git a/deps/checksums/libgit2-2fcb8705e584ca61f6c4657525c9d2713f6a39d2.tar.gz/md5 b/deps/checksums/libgit2-2fcb8705e584ca61f6c4657525c9d2713f6a39d2.tar.gz/md5 deleted file mode 100644 index ccc33a35bb056..0000000000000 --- a/deps/checksums/libgit2-2fcb8705e584ca61f6c4657525c9d2713f6a39d2.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -fb1f1140f9b55fc8499caa960382fc03 diff --git a/deps/checksums/libgit2-2fcb8705e584ca61f6c4657525c9d2713f6a39d2.tar.gz/sha512 b/deps/checksums/libgit2-2fcb8705e584ca61f6c4657525c9d2713f6a39d2.tar.gz/sha512 deleted file mode 100644 index cc5156e0c027d..0000000000000 --- a/deps/checksums/libgit2-2fcb8705e584ca61f6c4657525c9d2713f6a39d2.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -2086269728e14c0ec38f322b01f89d4d98a31a395cab3937591826a232193f00ec07bafe938ad99d70c74cb695af26c7228513019bfe6fc06427229bd2e098cf diff --git a/deps/libgit2.mk b/deps/libgit2.mk index c98528e52ae84..d83d0864c3446 100644 --- a/deps/libgit2.mk +++ b/deps/libgit2.mk @@ -37,7 +37,7 @@ LIBGIT2_OPTS += -DCURL_INCLUDE_DIRS=$(build_includedir) -DCURL_LIBRARIES="-L$(bu endif ifeq ($(OS),Linux) -LIBGIT2_OPTS += -DUSE_OPENSSL=OFF -DUSE_MBEDTLS=ON -DCMAKE_INSTALL_RPATH="\$$ORIGIN" +LIBGIT2_OPTS += -DUSE_HTTPS=ON -DTLS_BACKEND="mbedTLS" -DCMAKE_INSTALL_RPATH="\$$ORIGIN" endif ifeq ($(OS),FreeBSD) LIBGIT2_OPTS += -DCMAKE_INSTALL_RPATH="\$$ORIGIN" @@ -78,29 +78,14 @@ $(LIBGIT2_SRC_PATH)/libgit2-agent-nonfatal.patch-applied: $(LIBGIT2_SRC_PATH)/so patch -p1 -f < $(SRCDIR)/patches/libgit2-agent-nonfatal.patch echo 1 > $@ -$(LIBGIT2_SRC_PATH)/libgit2-mbedtls-writer-fix.patch-applied: $(LIBGIT2_SRC_PATH)/source-extracted | $(LIBGIT2_SRC_PATH)/libgit2-mbedtls.patch-applied - cd $(LIBGIT2_SRC_PATH) && \ - patch -p1 -f < $(SRCDIR)/patches/libgit2-mbedtls-writer-fix.patch - echo 1 > $@ - -$(LIBGIT2_SRC_PATH)/libgit2-mbedtls-verify.patch-applied: $(LIBGIT2_SRC_PATH)/source-extracted | $(LIBGIT2_SRC_PATH)/libgit2-mbedtls-writer-fix.patch-applied +$(LIBGIT2_SRC_PATH)/libgit2-mbedtls-verify.patch-applied: $(LIBGIT2_SRC_PATH)/source-extracted | $(LIBGIT2_SRC_PATH)/libgit2-agent-nonfatal.patch-applied cd $(LIBGIT2_SRC_PATH) && \ patch -p1 -f < $(SRCDIR)/patches/libgit2-mbedtls-verify.patch echo 1 > $@ -$(LIBGIT2_SRC_PATH)/libgit2-gitconfig-symlink.patch-applied: $(LIBGIT2_SRC_PATH)/source-extracted | $(LIBGIT2_SRC_PATH)/libgit2-mbedtls-verify.patch-applied - cd $(LIBGIT2_SRC_PATH) && \ - patch -p1 -f < $(SRCDIR)/patches/libgit2-gitconfig-symlink.patch - echo 1 > $@ - -$(LIBGIT2_SRC_PATH)/libgit2-free-config.patch-applied: $(LIBGIT2_SRC_PATH)/source-extracted | $(LIBGIT2_SRC_PATH)/libgit2-gitconfig-symlink.patch-applied - cd $(LIBGIT2_SRC_PATH) && \ - patch -p1 -f < $(SRCDIR)/patches/libgit2-free-config.patch - echo 1 > $@ - -$(LIBGIT2_SRC_PATH)/libgit2-remote-push-NULL.patch-applied: $(LIBGIT2_SRC_PATH)/source-extracted | $(LIBGIT2_SRC_PATH)/libgit2-free-config.patch-applied +$(LIBGIT2_SRC_PATH)/libgit2-mbedtls-fixup.patch-applied: $(LIBGIT2_SRC_PATH)/source-extracted | $(LIBGIT2_SRC_PATH)/libgit2-mbedtls-verify.patch-applied cd $(LIBGIT2_SRC_PATH) && \ - patch -p1 -f < $(SRCDIR)/patches/libgit2-remote-push-NULL.patch + patch -p1 -f < $(SRCDIR)/patches/libgit2-mbedtls-fixup.patch echo 1 > $@ $(build_datarootdir)/julia/cert.pem: $(CERTFILE) @@ -111,11 +96,8 @@ $(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured: \ $(LIBGIT2_SRC_PATH)/libgit2-mbedtls.patch-applied \ $(LIBGIT2_SRC_PATH)/libgit2-ssh.patch-applied \ $(LIBGIT2_SRC_PATH)/libgit2-agent-nonfatal.patch-applied \ - $(LIBGIT2_SRC_PATH)/libgit2-mbedtls-writer-fix.patch-applied \ $(LIBGIT2_SRC_PATH)/libgit2-mbedtls-verify.patch-applied \ - $(LIBGIT2_SRC_PATH)/libgit2-gitconfig-symlink.patch-applied \ - $(LIBGIT2_SRC_PATH)/libgit2-free-config.patch-applied \ - $(LIBGIT2_SRC_PATH)/libgit2-remote-push-NULL.patch-applied + $(LIBGIT2_SRC_PATH)/libgit2-mbedtls-fixup.patch-applied ifneq ($(CERTFILE),) $(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured: $(build_datarootdir)/julia/cert.pem diff --git a/deps/libgit2.version b/deps/libgit2.version index f5596b2fce3b9..306384f5cd768 100644 --- a/deps/libgit2.version +++ b/deps/libgit2.version @@ -1,2 +1,2 @@ -LIBGIT2_BRANCH=v0.25.1 -LIBGIT2_SHA1=2fcb8705e584ca61f6c4657525c9d2713f6a39d2 +LIBGIT2_BRANCH=v0.26.0 +LIBGIT2_SHA1=15e119375018fba121cf58e02a9f17fe22df0df8 diff --git a/deps/patches/libgit2-gitconfig-symlink.patch b/deps/patches/libgit2-gitconfig-symlink.patch deleted file mode 100644 index bd4e983045be4..0000000000000 --- a/deps/patches/libgit2-gitconfig-symlink.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 86a8cd9f6a039889801b5bec865a4bc3deb30f47 Mon Sep 17 00:00:00 2001 -From: Sven Strickroth2 -Date: Mon, 20 Mar 2017 11:21:00 +0100 -Subject: [PATCH] filebuf: fix resolving absolute symlinks - -The symlink destination is always concatenated to the original path. Fix -this by using `git_buf_sets` instead of `git_buf_puts`. ---- - src/filebuf.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/filebuf.c b/src/filebuf.c -index ef68b16f4..825b9c04c 100644 ---- a/src/filebuf.c -+++ b/src/filebuf.c -@@ -246,7 +246,7 @@ static int resolve_symlink(git_buf *out, const char *path) - - root = git_path_root(target.ptr); - if (root >= 0) { -- if ((error = git_buf_puts(&curpath, target.ptr)) < 0) -+ if ((error = git_buf_sets(&curpath, target.ptr)) < 0) - goto cleanup; - } else { - git_buf dir = GIT_BUF_INIT; --- -2.12.2 - diff --git a/deps/patches/libgit2-mbedtls-fixup.patch b/deps/patches/libgit2-mbedtls-fixup.patch new file mode 100644 index 0000000000000..2ec6fbd7167a0 --- /dev/null +++ b/deps/patches/libgit2-mbedtls-fixup.patch @@ -0,0 +1,70 @@ +commit de8721ae70dfae529fdb50224a47eadf6d29c574 +Author: Curtis Vogt +Date: Thu Jun 29 16:31:08 2017 -0500 + + Corrections to mbedtls support with LibGit2 0.26.0 + +diff --git a/src/settings.c b/src/settings.c +index 3a46f0d..4d976a0 100644 +--- a/src/settings.c ++++ b/src/settings.c +@@ -179,14 +179,18 @@ int git_libgit2_opts(int key, ...) + const char *path = va_arg(ap, const char *); + error = git_openssl_set_cert_file(file, path); + } +-#elif GIT_MBEDTLS ++#elif defined(GIT_MBEDTLS) + { + const char *file = va_arg(ap, const char *); + const char *path = va_arg(ap, const char *); +- if (file) ++ if (file) { + error = git_mbedtls_set_cert_file(file, 0); +- if (error && path) +- error = git_mbedtls_set_cert_file(path, 0); ++ } else if (path) { ++ error = git_mbedtls_set_cert_file(path, 1); ++ } else { ++ giterr_set(GITERR_NET, "cannot set certificate locations: no file or path given"); ++ error = -1; ++ } + } + #else + giterr_set(GITERR_NET, "cannot set certificate locations: OpenSSL or mbedTLS is not enabled"); +diff --git a/src/streams/mbedtls.c b/src/streams/mbedtls.c +index e456ea8..b4eb991 100644 +--- a/src/streams/mbedtls.c ++++ b/src/streams/mbedtls.c +@@ -205,12 +205,12 @@ static int ssl_set_error(mbedtls_ssl_context *ssl, int error) + break; + + case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED: +- giterr_set(GITERR_SSL, "SSL error: %x[%x] - %s", error, ssl->session_negotiate->verify_result, errbuf); ++ giterr_set(GITERR_SSL, "SSL error: 0x%04x [%x] - %s", error, ssl->session_negotiate->verify_result, errbuf); + ret = GIT_ECERTIFICATE; + break; + + default: +- giterr_set(GITERR_SSL, "SSL error: %x - %s", error, errbuf); ++ giterr_set(GITERR_SSL, "SSL error: 0x%04x - %s", error, errbuf); + } + + return ret; +@@ -236,7 +236,7 @@ static int verify_server_cert(mbedtls_ssl_context *ssl, const char *host) + if ((ret = mbedtls_ssl_get_verify_result(ssl)) != 0) { + char vrfy_buf[512]; + mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), "", ret); +- giterr_set(GITERR_SSL, "The SSL certificate is invalid: %x - %s", ret, vrfy_buf); ++ giterr_set(GITERR_SSL, "The SSL certificate is invalid: 0x%04x - %s", ret, vrfy_buf); + return GIT_ECERTIFICATE; + } + +@@ -430,7 +430,7 @@ int git_mbedtls_set_cert_file(const char *path, int is_dir) + ret = mbedtls_x509_crt_parse_file(cacert, path); + } + // mbedtls_x509_crt_parse_path returns the number of invalid certs on success +- if (ret <= 0) { ++ if (ret < 0) { + mbedtls_x509_crt_free(cacert); + git__free(cacert); + mbedtls_strerror( ret, errbuf, 512 ); diff --git a/deps/patches/libgit2-mbedtls-verify.patch b/deps/patches/libgit2-mbedtls-verify.patch index 4c454c3adc4ba..eb0b3854cbb05 100644 --- a/deps/patches/libgit2-mbedtls-verify.patch +++ b/deps/patches/libgit2-mbedtls-verify.patch @@ -1,99 +1,100 @@ -diff --git a/src/mbedtls_stream.c b/src/mbedtls_stream.c -index bcc2e8b..4706102 100644 ---- a/src/mbedtls_stream.c -+++ b/src/mbedtls_stream.c -@@ -221,82 +221,34 @@ static int ssl_teardown(mbedtls_ssl_context *ssl) - return ret; +commit eefe88eaf8c5c5b7c9a596da79e68dca3a3234d4 +Author: Curtis Vogt +Date: Thu Jun 29 16:30:53 2017 -0500 + + Use mbedtls certificate verification + + Letting mbedtls handle all certficate verification and removed the + custom alternative names and common name checking. + +diff --git a/src/streams/mbedtls.c b/src/streams/mbedtls.c +index 0376ee4..e456ea8 100644 +--- a/src/streams/mbedtls.c ++++ b/src/streams/mbedtls.c +@@ -228,82 +228,19 @@ static int ssl_teardown(mbedtls_ssl_context *ssl) + return ret; } -static int check_host_name(const char *name, const char *host) -{ -- if (!strcasecmp(name, host)) -- return 0; +- if (!strcasecmp(name, host)) +- return 0; - -- if (gitno__match_host(name, host) < 0) -- return -1; +- if (gitno__match_host(name, host) < 0) +- return -1; - -- return 0; +- return 0; -} - static int verify_server_cert(mbedtls_ssl_context *ssl, const char *host) { -- const mbedtls_x509_crt *cert; -- const mbedtls_x509_sequence *alts; -- int ret, matched = -1; -+ mbedtls_x509_crt *cert; -+ uint32_t flags; -+ int ret = -1; - size_t sn_size = 512; -- char subject_name[sn_size], alt_name[sn_size]; +- const mbedtls_x509_crt *cert; +- const mbedtls_x509_sequence *alts; +- int ret, matched = -1; +- size_t sn_size = 512; +- char subject_name[sn_size], alt_name[sn_size]; - -+ char buf[sn_size]; ++ int ret = -1; ++ (void)(host); // Suppress unused parameter warning - if (( ret = mbedtls_ssl_get_verify_result(ssl) ) != 0) { -- char vrfy_buf[512]; -- mbedtls_x509_crt_verify_info( vrfy_buf, sizeof( vrfy_buf ), " ! ", ret ); -- giterr_set(GITERR_SSL, "The SSL certificate is invalid: %s", vrfy_buf); -+ mbedtls_x509_crt_verify_info(buf, sn_size, " ! ", ret); -+ giterr_set(GITERR_SSL, "The SSL certificate is invalid: %s", buf); - return GIT_ECERTIFICATE; - } + if ((ret = mbedtls_ssl_get_verify_result(ssl)) != 0) { + char vrfy_buf[512]; +- mbedtls_x509_crt_verify_info( vrfy_buf, sizeof( vrfy_buf ), " ! ", ret ); +- giterr_set(GITERR_SSL, "The SSL certificate is invalid: %s", vrfy_buf); ++ mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), "", ret); ++ giterr_set(GITERR_SSL, "The SSL certificate is invalid: %x - %s", ret, vrfy_buf); + return GIT_ECERTIFICATE; + } -- cert = mbedtls_ssl_get_peer_cert(ssl); -+ cert = (mbedtls_x509_crt*) mbedtls_ssl_get_peer_cert(ssl); - if (!cert) { - giterr_set(GITERR_SSL, "the server did not provide a certificate"); - return -1; - } - -- /* Check the alternative names */ -- alts = &cert->subject_alt_names; -- while (alts != NULL && matched != 1) { -- // Buffer is too small -- if( alts->buf.len >= sn_size ) -- goto on_error; +- cert = mbedtls_ssl_get_peer_cert(ssl); +- if (!cert) { +- giterr_set(GITERR_SSL, "the server did not provide a certificate"); +- return -1; +- } - -- memcpy(alt_name, alts->buf.p, alts->buf.len); -- alt_name[alts->buf.len] = '\0'; +- /* Check the alternative names */ +- alts = &cert->subject_alt_names; +- while (alts != NULL && matched != 1) { +- // Buffer is too small +- if( alts->buf.len >= sn_size ) +- goto on_error; - -- if (!memchr(alt_name, '\0', alts->buf.len)) { -- if (check_host_name(alt_name, host) < 0) -- matched = 0; -- else -- matched = 1; -- } +- memcpy(alt_name, alts->buf.p, alts->buf.len); +- alt_name[alts->buf.len] = '\0'; - -- alts = alts->next; -+ if (mbedtls_x509_crt_verify(cert, git__ssl_conf->ca_chain, NULL, host, &flags, NULL, NULL) != 0) { -+ mbedtls_x509_crt_verify_info(buf, sn_size, "", flags); -+ buf[strlen(buf) - 1] = '\0'; // Remove trailing newline -+ giterr_set(GITERR_SSL, buf); -+ return GIT_ECERTIFICATE; - } -- if (matched == 0) -- goto cert_fail_name; +- if (!memchr(alt_name, '\0', alts->buf.len)) { +- if (check_host_name(alt_name, host) < 0) +- matched = 0; +- else +- matched = 1; +- } - -- if (matched == 1) -- return 0; +- alts = alts->next; +- } +- if (matched == 0) +- goto cert_fail_name; - -- /* If no alternative names are available, check the common name */ -- ret = mbedtls_x509_dn_gets(subject_name, sn_size, &cert->subject); -- if (ret == 0) -- goto on_error; -- if (memchr(subject_name, '\0', ret)) -- goto cert_fail_name; +- if (matched == 1) +- return 0; - -- if (check_host_name(subject_name, host) < 0) -- goto cert_fail_name; - - return 0; +- /* If no alternative names are available, check the common name */ +- ret = mbedtls_x509_dn_gets(subject_name, sn_size, &cert->subject); +- if (ret == 0) +- goto on_error; +- if (memchr(subject_name, '\0', ret)) +- goto cert_fail_name; +- +- if (check_host_name(subject_name, host) < 0) +- goto cert_fail_name; +- + return 0; - -on_error: -- return ssl_set_error(ssl, 0); +- return ssl_set_error(ssl, 0); - -cert_fail_name: -- giterr_set(GITERR_SSL, "hostname does not match certificate"); -- return GIT_ECERTIFICATE; +- giterr_set(GITERR_SSL, "hostname does not match certificate"); +- return GIT_ECERTIFICATE; } typedef struct { diff --git a/deps/patches/libgit2-mbedtls-writer-fix.patch b/deps/patches/libgit2-mbedtls-writer-fix.patch deleted file mode 100644 index d80a637a3996f..0000000000000 --- a/deps/patches/libgit2-mbedtls-writer-fix.patch +++ /dev/null @@ -1,30 +0,0 @@ -diff -rup libgit2-211e117a0590583a720c53172406f34186c543bd/src/mbedtls_stream.c libgit2-211e117a0590583a720c53172406f34186c543bd-fix-write/src/mbedtls_stream.c ---- libgit2-211e117a0590583a720c53172406f34186c543bd/src/mbedtls_stream.c 2016-10-24 17:10:21.000000000 -0400 -+++ libgit2-211e117a0590583a720c53172406f34186c543bd-fix-write/src/mbedtls_stream.c 2016-10-24 17:04:26.000000000 -0400 -@@ -368,16 +368,20 @@ static int mbedtls_set_proxy(git_stream - - ssize_t mbedtls_stream_write(git_stream *stream, const char *data, size_t len, int flags) - { -+ size_t read = 0; - mbedtls_stream *st = (mbedtls_stream *) stream; -- int ret; - - GIT_UNUSED(flags); - -- if ((ret = mbedtls_ssl_write(st->ssl, (const unsigned char *)data, len)) <= 0) { -- return ssl_set_error(st->ssl, ret); -- } -+ do { -+ int error = mbedtls_ssl_write(st->ssl, (const unsigned char *)data + read, len - read); -+ if (error <= 0) { -+ return ssl_set_error(st->ssl, error); -+ } -+ read += error; -+ } while (read < len); - -- return ret; -+ return read; - } - - ssize_t mbedtls_stream_read(git_stream *stream, void *data, size_t len) - diff --git a/deps/patches/libgit2-mbedtls.patch b/deps/patches/libgit2-mbedtls.patch index 955f165eab754..af12728cf8555 100644 --- a/deps/patches/libgit2-mbedtls.patch +++ b/deps/patches/libgit2-mbedtls.patch @@ -1,66 +1,282 @@ +commit 4d69ea1172fec234e0f3c8cee8574e9e13bf825e +Author: Etienne Samson +Date: Fri Jun 23 22:40:56 2017 +0000 + + mbedtls support for libgit2 v0.26.0 + + See https://github.com/libgit2/libgit2/pull/4173 + + cmake: simplify https support selection & tests + + Gather streams to src/streams + + Don't include OpenSSL from global.h so it doesn't namespace-leak + + Have clar exit immediately on initialization failure + + Generalize Travis' dependency installation + + Add USE_HTTPS as a CMake option + + It defaults to ON, e.g. "pick whatever default is appropriate for the platform". + It accepts one of SecureTransport, OpenSSL, WinHTTP, or OFF. + It errors if the backend library couldn't be found. + + mbedtls: initial support + + mbedtls: proper certificate verification + + mbedtls: use libmbedcrypto for hashing + + mbedtls: add global initialization + + mbedtls: default cipher list support + + mbedtls: load default CA certificates + + mbedtls: fix libgit2 hanging due to incomplete writes + + mbedtls: enable Travis CI tests + + mbedtls: use our own certificate validation + + Otherwise REQUIRED means that `git_stream_certificate` will always error. + We're doing the mbedtls check in verify_server_cert though. + + mbedtls: try all CA locations, stopping after any loaded + + WIP: distribution paths + +diff --git a/.travis.yml b/.travis.yml +index af38252..e1be760 100644 +--- a/.travis.yml ++++ b/.travis.yml +@@ -45,11 +45,21 @@ matrix: + - VALGRIND=1 + OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=OFF -DDEBUG_POOL=ON -DCMAKE_BUILD_TYPE=Debug" + os: linux ++ - compiler: gcc ++ env: ++ MBEDTLS=1 ++ OPTIONS="-DTHREADSAFE=ON -DCMAKE_BUILD_TYPE=Release -DUSE_TLS=mbedTLS -DMBEDTLS_ROOT_DIR=../mbedtls" ++ os: linux ++ - compiler: gcc ++ env: ++ MBEDTLS=1 ++ OPTIONS="-DTHREADSAFE=OFF -DBUILD_EXAMPLES=ON -DUSE_TLS=mbedTLS -DMBEDTLS_ROOT_DIR=../mbedtls" ++ os: linux + allow_failures: + - env: COVERITY=1 + + install: +- - if [ "$TRAVIS_OS_NAME" = "osx" ]; then ./script/install-deps-${TRAVIS_OS_NAME}.sh; fi ++ - if [ -x "./script/install-deps-${TRAVIS_OS_NAME}.sh" ]; then ./script/install-deps-${TRAVIS_OS_NAME}.sh; fi + + # Run the Build script and tests + script: diff --git a/CMakeLists.txt b/CMakeLists.txt -index 4e1104f..8110489 100644 +index 4783e3e..5d7dbe3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -113,6 +113,10 @@ IF (HAVE_STRUCT_STAT_NSEC OR WIN32) - OPTION( USE_NSEC "Care about sub-second file mtimes and ctimes" ON ) +@@ -40,6 +40,7 @@ OPTION( LIBGIT2_FILENAME "Name of the produced binary" OFF ) + OPTION( USE_SHA1DC "Use SHA-1 with collision detection" OFF ) + OPTION( USE_ICONV "Link with and use iconv library" OFF ) + OPTION( USE_SSH "Link with libssh to enable SSH support" ON ) ++OPTION( USE_HTTPS "Enable HTTPS support" ON ) + OPTION( USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF ) + OPTION( VALGRIND "Configure build for valgrind" OFF ) + OPTION( CURL "Use curl for HTTP if available" ON) +@@ -89,10 +90,6 @@ IF(MSVC) + OPTION(MSVC_CRTDBG "Enable CRTDBG memory leak reporting" OFF) ENDIF() -+IF (NOT USE_OPENSSL) -+ OPTION( USE_MBEDTLS "Link with and use mbedtls library" OFF ) -+ENDIF() -+ - # This variable will contain the libraries we need to put into - # libgit2.pc's Requires.private. That is, what we're linking to or - # what someone who's statically linking us needs to link to. -@@ -283,6 +287,10 @@ ELSE () +-IF (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") +- OPTION( USE_OPENSSL "Link with and use openssl library" ON ) +-ENDIF() +- + CHECK_STRUCT_HAS_MEMBER ("struct stat" st_mtim "sys/types.h;sys/stat.h" + HAVE_STRUCT_STAT_ST_MTIM LANGUAGE C) + CHECK_STRUCT_HAS_MEMBER ("struct stat" st_mtimespec "sys/types.h;sys/stat.h" +@@ -208,21 +205,6 @@ STRING(REGEX REPLACE "^.*LIBGIT2_SOVERSION ([0-9]+)$" "\\1" LIBGIT2_SOVERSION "$ + # Find required dependencies + INCLUDE_DIRECTORIES(src include) + +-IF (SECURITY_FOUND) +- # OS X 10.7 and older do not have some functions we use, fall back to OpenSSL there +- CHECK_LIBRARY_EXISTS("${SECURITY_DIRS}" SSLCreateContext "Security/SecureTransport.h" HAVE_NEWER_SECURITY) +- IF (HAVE_NEWER_SECURITY) +- MESSAGE("-- Found Security ${SECURITY_DIRS}") +- LIST(APPEND LIBGIT2_PC_LIBS "-framework Security") +- ELSE() +- MESSAGE("-- Security framework is too old, falling back to OpenSSL") +- SET(SECURITY_FOUND "NO") +- SET(SECURITY_DIRS "") +- SET(SECURITY_DIR "") +- SET(USE_OPENSSL "ON") +- ENDIF() +-ENDIF() +- + IF (COREFOUNDATION_FOUND) + MESSAGE("-- Found CoreFoundation ${COREFOUNDATION_DIRS}") + LIST(APPEND LIBGIT2_PC_LIBS "-framework CoreFoundation") +@@ -280,10 +262,14 @@ ELSE () + PKG_CHECK_MODULES(CURL libcurl) + ENDIF () + +- IF (NOT AMIGA AND USE_OPENSSL) ++ IF (NOT AMIGA AND (USE_HTTPS STREQUAL "OpenSSL" OR USE_HTTPS STREQUAL "ON")) FIND_PACKAGE(OpenSSL) ENDIF () -+ IF (NOT AMIGA AND USE_MBEDTLS) ++ IF (NOT AMIGA AND (USE_HTTPS STREQUAL "mbedTLS" OR USE_HTTPS STREQUAL "ON")) + FIND_PACKAGE(mbedTLS) + ENDIF () + IF (CURL_FOUND) ADD_DEFINITIONS(-DGIT_CURL) INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIRS}) -@@ -316,6 +324,9 @@ ELSEIF (OPENSSL_FOUND AND NOT SHA1_TYPE STREQUAL "builtin") +@@ -293,6 +279,69 @@ ELSE () + ENDIF() + ENDIF() + ++IF (USE_HTTPS STREQUAL "ON") ++ IF (SECURITY_FOUND) ++ # OS X 10.7 and older do not have some functions we use, fall back to OpenSSL there ++ CHECK_LIBRARY_EXISTS("${SECURITY_DIRS}" SSLCreateContext "Security/SecureTransport.h" HAVE_NEWER_SECURITY) ++ IF (HAVE_NEWER_SECURITY) ++ MESSAGE("-- Found Security ${SECURITY_DIRS}") ++ LIST(APPEND LIBGIT2_PC_LIBS "-framework Security") ++ SET(HTTPS_BACKEND "SecureTransport") ++ ELSE() ++ MESSAGE("-- Security framework is too old, falling back to OpenSSL") ++ SET(SECURITY_FOUND "NO") ++ SET(SECURITY_DIRS "") ++ SET(SECURITY_DIR "") ++ SET(HTTPS_BACKEND "OpenSSL") ++ ENDIF() ++ ELSEIF(WINHTTP) ++ SET(HTTPS_BACKEND "WinHTTP") ++ ELSEIF(MBEDTLS_FOUND) ++ SET(HTTPS_BACKEND "mbedTLS") ++ ELSE() ++ SET(HTTPS_BACKEND "OpenSSL") ++ ENDIF() ++ELSE() ++ SET(HTTPS_BACKEND ${USE_HTTPS}) ++ENDIF() ++ ++MESSAGE(STATUS "Using HTTPS backend ${HTTPS_BACKEND}") ++ ++IF (HTTPS_BACKEND STREQUAL "SecureTransport") ++ IF (NOT SECURITY_FOUND) ++ MESSAGE(FATAL_ERROR "Asked for SecureTransport HTTPS backend, but it wasn't found") ++ ENDIF() ++ ++ ADD_DEFINITIONS(-DGIT_SECURE_TRANSPORT) ++ INCLUDE_DIRECTORIES(${SECURITY_INCLUDE_DIR}) ++ELSEIF (HTTPS_BACKEND STREQUAL "OpenSSL") ++ IF (NOT OPENSSL_FOUND) ++ MESSAGE(FATAL_ERROR "Asked for OpenSSL HTTPS backend, but it wasn't found") ++ ENDIF() ++ ++ ADD_DEFINITIONS(-DGIT_OPENSSL) ++ INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR}) ++ SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES}) ++ELSEIF (TLS_BACKEND STREQUAL "mbedTLS") ++ IF (NOT MBEDTLS_FOUND) ++ MESSAGE(FATAL_ERROR "Asked for mbedTLS backend, but it wasn't found") ++ ENDIF() ++ ++ ADD_DEFINITIONS(-DGIT_MBEDTLS) ++ INCLUDE_DIRECTORIES(${MBEDTLS_INCLUDE_DIR}) ++ LINK_DIRECTORIES(${MBEDTLS_LIBRARY_DIR}) ++ SET(SSL_LIBRARIES ${MBEDTLS_LIBRARIES}) ++ELSEIF(HTTPS_BACKEND STREQUAL "WinHTTP") ++ENDIF() ++ ++IF (USE_HTTPS AND NOT HTTPS_BACKEND) ++ MESSAGE(FATAL_ERROR "Asked for backend " ${HTTPS_BACKEND} " but it wasn't found") ++ENDIF() ++ ++IF (HTTPS_BACKEND) ++ ADD_DEFINITIONS(-DGIT_HTTPS) ++ENDIF() ++ + # Specify sha1 implementation + IF (USE_SHA1DC) + ADD_DEFINITIONS(-DGIT_SHA1_COLLISIONDETECT) +@@ -303,15 +352,18 @@ IF (USE_SHA1DC) + ELSEIF (WIN32 AND NOT MINGW) + ADD_DEFINITIONS(-DGIT_SHA1_WIN32) + FILE(GLOB SRC_SHA1 src/hash/hash_win32.c) +-ELSEIF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") ++ELSEIF (TLS_BACKEND MATCHES "SecureTransport") + ADD_DEFINITIONS(-DGIT_SHA1_COMMON_CRYPTO) +-ELSEIF (OPENSSL_FOUND) ++ELSEIF (TLS_BACKEND MATCHES "OpenSSL") + ADD_DEFINITIONS(-DGIT_SHA1_OPENSSL) + IF (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + LIST(APPEND LIBGIT2_PC_LIBS "-lssl") ELSE() SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} openssl") ENDIF () -+ELSEIF (MBEDTLS_FOUND AND NOT SHA1_TYPE STREQUAL "builtin") ++ELSEIF (TLS_BACKEND STREQUAL "mbedTLS") + ADD_DEFINITIONS(-DMBEDTLS_SHA1) + FILE(GLOB SRC_SHA1 src/hash/hash_mbedtls.c) ELSE() FILE(GLOB SRC_SHA1 src/hash/hash_generic.c) ENDIF() -@@ -543,6 +554,11 @@ IF (OPENSSL_FOUND) - SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES}) +@@ -543,21 +595,6 @@ ELSE() + # that uses CMAKE_CONFIGURATION_TYPES and not CMAKE_BUILD_TYPE ENDIF() -+IF (MBEDTLS_FOUND) -+ ADD_DEFINITIONS(-DGIT_MBEDTLS) -+ INCLUDE_DIRECTORIES(${MBEDTLS_INCLUDE_DIR}) -+ SET(SSL_LIBRARIES ${MBEDTLS_LIBRARIES}) -+ENDIF() - - +-IF (SECURITY_FOUND) +- ADD_DEFINITIONS(-DGIT_SECURE_TRANSPORT) +- ADD_DEFINITIONS(-DGIT_HTTPS) +- INCLUDE_DIRECTORIES(${SECURITY_INCLUDE_DIR}) +-ENDIF () +- +-IF (OPENSSL_FOUND) +- ADD_DEFINITIONS(-DGIT_OPENSSL) +- ADD_DEFINITIONS(-DGIT_HTTPS) +- INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR}) +- SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES}) +-ENDIF() +- +- +- IF (THREADSAFE) -@@ -688,7 +704,7 @@ IF (BUILD_CLAR) + IF (NOT WIN32) + FIND_PACKAGE(Threads REQUIRED) +@@ -595,7 +632,12 @@ ELSE() + ENDIF() + FILE(GLOB SRC_OS src/unix/*.c src/unix/*.h) + ENDIF() +-FILE(GLOB SRC_GIT2 src/*.c src/*.h src/transports/*.c src/transports/*.h src/xdiff/*.c src/xdiff/*.h) ++FILE(GLOB SRC_GIT2 ++ src/*.c src/*.h ++ src/streams/*.c src/streams/*.h ++ src/transports/*.c src/transports/*.h ++ src/xdiff/*.c src/xdiff/*.h ++) + + # Determine architecture of the machine + IF (CMAKE_SIZEOF_VOID_P EQUAL 8) +@@ -703,7 +745,7 @@ IF (BUILD_CLAR) ENDIF () ENABLE_TESTING() - IF (WINHTTP OR OPENSSL_FOUND OR SECURITY_FOUND) -+ IF (WINHTTP OR OPENSSL_FOUND OR SECURITY_FOUND OR MBEDTLS_FOUND) - ADD_TEST(libgit2_clar libgit2_clar -ionline) ++ IF (HAS_HTTPS_SUPPORT) + ADD_TEST(libgit2_clar libgit2_clar -ionline -xclone::local::git_style_unc_paths -xclone::local::standard_unc_paths_are_written_git_style) ELSE () - ADD_TEST(libgit2_clar libgit2_clar -v) + ADD_TEST(libgit2_clar libgit2_clar -v -xclone::local::git_style_unc_paths -xclone::local::standard_unc_paths_are_written_git_style) diff --git a/cmake/Modules/FindmbedTLS.cmake b/cmake/Modules/FindmbedTLS.cmake new file mode 100644 -index 0000000..2f4adbc +index 0000000..9329755 --- /dev/null +++ b/cmake/Modules/FindmbedTLS.cmake -@@ -0,0 +1,64 @@ +@@ -0,0 +1,93 @@ +# - Try to find mbedTLS +# Once done this will define +# @@ -72,17 +288,46 @@ index 0000000..2f4adbc +# MBEDTLS_LIBRARY - path to mbedTLS library +# MBEDX509_LIBRARY - path to mbedTLS X.509 library +# MBEDCRYPTO_LIBRARY - path to mbedTLS Crypto library ++# ++# Hint ++# MBEDTLS_ROOT_DIR can be pointed to a local mbedTLS installation. ++ ++SET(_MBEDTLS_ROOT_HINTS ++ ${MBEDTLS_ROOT_DIR} ++ ENV MBEDTLS_ROOT_DIR ++) ++ ++SET(_MBEDTLS_ROOT_HINTS_AND_PATHS ++ HINTS ${_MBEDTLS_ROOT_HINTS} ++ PATHS ${_MBEDTLS_ROOT_PATHS} ++) + -+FIND_PATH(MBEDTLS_INCLUDE_DIR mbedtls/version.h) ++FIND_PATH(MBEDTLS_INCLUDE_DIR ++ NAMES mbedtls/version.h ++ ${_MBEDTLS_ROOT_HINTS_AND_PATHS} ++ PATH_SUFFIXES include ++) + +IF(MBEDTLS_INCLUDE_DIR AND MBEDTLS_LIBRARIES) + # Already in cache, be silent + SET(MBEDTLS_FIND_QUIETLY TRUE) +ENDIF() + -+FIND_LIBRARY(MBEDTLS_LIBRARY NAMES mbedtls libmbedtls libmbedx509) -+FIND_LIBRARY(MBEDX509_LIBRARY NAMES mbedx509 libmbedx509) -+FIND_LIBRARY(MBEDCRYPTO_LIBRARY NAMES mbedcrypto libmbedcrypto) ++FIND_LIBRARY(MBEDTLS_LIBRARY ++ NAMES mbedtls libmbedtls ++ ${_MBEDTLS_ROOT_HINTS_AND_PATHS} ++ PATH_SUFFIXES library ++) ++FIND_LIBRARY(MBEDX509_LIBRARY ++ NAMES mbedx509 libmbedx509 ++ ${_MBEDTLS_ROOT_HINTS_AND_PATHS} ++ PATH_SUFFIXES library ++) ++FIND_LIBRARY(MBEDCRYPTO_LIBRARY ++ NAMES mbedcrypto libmbedcrypto ++ ${_MBEDTLS_ROOT_HINTS_AND_PATHS} ++ PATH_SUFFIXES library ++) + +IF(MBEDTLS_INCLUDE_DIR AND MBEDTLS_LIBRARY AND MBEDX509_LIBRARY AND MBEDCRYPTO_LIBRARY) + SET(MBEDTLS_FOUND TRUE) @@ -125,68 +370,465 @@ index 0000000..2f4adbc + MBEDX509_LIBRARY + MBEDCRYPTO_LIBRARY +) +diff --git a/script/install-deps-linux.sh b/script/install-deps-linux.sh +new file mode 100755 +index 0000000..94309b0 +--- /dev/null ++++ b/script/install-deps-linux.sh +@@ -0,0 +1,12 @@ ++#!/bin/sh ++ ++echo "Installing dependencies" ++if [ "$MBEDTLS" ]; then ++ git clone https://github.com/ARMmbed/mbedtls.git ../mbedtls ++ cd ../mbedtls ++ git checkout mbedtls-2.4.2 ++ cmake -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=ON -DUSE_STATIC_MBEDTLS_LIBRARY=OFF . ++ cmake --build . ++ ++ echo "mbedTLS built in `pwd`" ++fi +diff --git a/src/curl_stream.c b/src/curl_stream.c +deleted file mode 100644 +index 4e0455c..0000000 +--- a/src/curl_stream.c ++++ /dev/null +@@ -1,362 +0,0 @@ +-/* +- * Copyright (C) the libgit2 contributors. All rights reserved. +- * +- * This file is part of libgit2, distributed under the GNU GPL v2 with +- * a Linking Exception. For full terms see the included COPYING file. +- */ +- +-#ifdef GIT_CURL +- +-#include +- +-#include "stream.h" +-#include "git2/transport.h" +-#include "buffer.h" +-#include "vector.h" +-#include "proxy.h" +- +-/* This is for backwards compatibility with curl<7.45.0. */ +-#ifndef CURLINFO_ACTIVESOCKET +-# define CURLINFO_ACTIVESOCKET CURLINFO_LASTSOCKET +-# define GIT_CURL_BADSOCKET -1 +-# define git_activesocket_t long +-#else +-# define GIT_CURL_BADSOCKET CURL_SOCKET_BAD +-# define git_activesocket_t curl_socket_t +-#endif +- +-typedef struct { +- git_stream parent; +- CURL *handle; +- curl_socket_t socket; +- char curl_error[CURL_ERROR_SIZE + 1]; +- git_cert_x509 cert_info; +- git_strarray cert_info_strings; +- git_proxy_options proxy; +- git_cred *proxy_cred; +-} curl_stream; +- +-static int seterr_curl(curl_stream *s) +-{ +- giterr_set(GITERR_NET, "curl error: %s\n", s->curl_error); +- return -1; +-} +- +-GIT_INLINE(int) error_no_credentials(void) +-{ +- giterr_set(GITERR_NET, "proxy authentication required, but no callback provided"); +- return GIT_EAUTH; +-} +- +-static int apply_proxy_creds(curl_stream *s) +-{ +- CURLcode res; +- git_cred_userpass_plaintext *userpass; +- +- if (!s->proxy_cred) +- return GIT_ENOTFOUND; +- +- userpass = (git_cred_userpass_plaintext *) s->proxy_cred; +- if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYUSERNAME, userpass->username)) != CURLE_OK) +- return seterr_curl(s); +- if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYPASSWORD, userpass->password)) != CURLE_OK) +- return seterr_curl(s); +- +- return 0; +-} +- +-static int ask_and_apply_proxy_creds(curl_stream *s) +-{ +- int error; +- git_proxy_options *opts = &s->proxy; +- +- if (!opts->credentials) +- return error_no_credentials(); +- +- /* TODO: see if PROXYAUTH_AVAIL helps us here */ +- git_cred_free(s->proxy_cred); +- s->proxy_cred = NULL; +- giterr_clear(); +- error = opts->credentials(&s->proxy_cred, opts->url, NULL, GIT_CREDTYPE_USERPASS_PLAINTEXT, opts->payload); +- if (error == GIT_PASSTHROUGH) +- return error_no_credentials(); +- if (error < 0) { +- if (!giterr_last()) +- giterr_set(GITERR_NET, "proxy authentication was aborted by the user"); +- return error; +- } +- +- if (s->proxy_cred->credtype != GIT_CREDTYPE_USERPASS_PLAINTEXT) { +- giterr_set(GITERR_NET, "credentials callback returned invalid credential type"); +- return -1; +- } +- +- return apply_proxy_creds(s); +-} +- +-static int curls_connect(git_stream *stream) +-{ +- curl_stream *s = (curl_stream *) stream; +- git_activesocket_t sockextr; +- long connect_last = 0; +- int failed_cert = 0, error; +- bool retry_connect; +- CURLcode res; +- +- /* Apply any credentials we've already established */ +- error = apply_proxy_creds(s); +- if (error < 0 && error != GIT_ENOTFOUND) +- return seterr_curl(s); +- +- do { +- retry_connect = 0; +- res = curl_easy_perform(s->handle); +- +- curl_easy_getinfo(s->handle, CURLINFO_HTTP_CONNECTCODE, &connect_last); +- +- /* HTTP 407 Proxy Authentication Required */ +- if (connect_last == 407) { +- if ((error = ask_and_apply_proxy_creds(s)) < 0) +- return error; +- +- retry_connect = true; +- } +- } while (retry_connect); +- +- if (res != CURLE_OK && res != CURLE_PEER_FAILED_VERIFICATION) +- return seterr_curl(s); +- if (res == CURLE_PEER_FAILED_VERIFICATION) +- failed_cert = 1; +- +- if ((res = curl_easy_getinfo(s->handle, CURLINFO_ACTIVESOCKET, &sockextr)) != CURLE_OK) { +- return seterr_curl(s); +- } +- +- if (sockextr == GIT_CURL_BADSOCKET) { +- giterr_set(GITERR_NET, "curl socket is no longer valid"); +- return -1; +- } +- +- s->socket = sockextr; +- +- if (s->parent.encrypted && failed_cert) +- return GIT_ECERTIFICATE; +- +- return 0; +-} +- +-static int curls_certificate(git_cert **out, git_stream *stream) +-{ +- int error; +- CURLcode res; +- struct curl_slist *slist; +- struct curl_certinfo *certinfo; +- git_vector strings = GIT_VECTOR_INIT; +- curl_stream *s = (curl_stream *) stream; +- +- if ((res = curl_easy_getinfo(s->handle, CURLINFO_CERTINFO, &certinfo)) != CURLE_OK) +- return seterr_curl(s); +- +- /* No information is available, can happen with SecureTransport */ +- if (certinfo->num_of_certs == 0) { +- s->cert_info.parent.cert_type = GIT_CERT_NONE; +- s->cert_info.data = NULL; +- s->cert_info.len = 0; +- return 0; +- } +- +- if ((error = git_vector_init(&strings, 8, NULL)) < 0) +- return error; +- +- for (slist = certinfo->certinfo[0]; slist; slist = slist->next) { +- char *str = git__strdup(slist->data); +- GITERR_CHECK_ALLOC(str); +- git_vector_insert(&strings, str); +- } +- +- /* Copy the contents of the vector into a strarray so we can expose them */ +- s->cert_info_strings.strings = (char **) strings.contents; +- s->cert_info_strings.count = strings.length; +- +- s->cert_info.parent.cert_type = GIT_CERT_STRARRAY; +- s->cert_info.data = &s->cert_info_strings; +- s->cert_info.len = strings.length; +- +- *out = &s->cert_info.parent; +- +- return 0; +-} +- +-static int curls_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts) +-{ +- int error; +- CURLcode res; +- curl_stream *s = (curl_stream *) stream; +- +- if ((error = git_proxy_options_dup(&s->proxy, proxy_opts)) < 0) +- return error; +- +- if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXY, s->proxy.url)) != CURLE_OK) +- return seterr_curl(s); +- +- if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY)) != CURLE_OK) +- return seterr_curl(s); +- +- return 0; +-} +- +-static int wait_for(curl_socket_t fd, bool reading) +-{ +- int ret; +- fd_set infd, outfd, errfd; +- +- FD_ZERO(&infd); +- FD_ZERO(&outfd); +- FD_ZERO(&errfd); +- +- assert(fd >= 0); +- FD_SET(fd, &errfd); +- if (reading) +- FD_SET(fd, &infd); +- else +- FD_SET(fd, &outfd); +- +- if ((ret = select(fd + 1, &infd, &outfd, &errfd, NULL)) < 0) { +- giterr_set(GITERR_OS, "error in select"); +- return -1; +- } +- +- return 0; +-} +- +-static ssize_t curls_write(git_stream *stream, const char *data, size_t len, int flags) +-{ +- int error; +- size_t off = 0, sent; +- CURLcode res; +- curl_stream *s = (curl_stream *) stream; +- +- GIT_UNUSED(flags); +- +- do { +- if ((error = wait_for(s->socket, false)) < 0) +- return error; +- +- res = curl_easy_send(s->handle, data + off, len - off, &sent); +- if (res == CURLE_OK) +- off += sent; +- } while ((res == CURLE_OK || res == CURLE_AGAIN) && off < len); +- +- if (res != CURLE_OK) +- return seterr_curl(s); +- +- return len; +-} +- +-static ssize_t curls_read(git_stream *stream, void *data, size_t len) +-{ +- int error; +- size_t read; +- CURLcode res; +- curl_stream *s = (curl_stream *) stream; +- +- do { +- if ((error = wait_for(s->socket, true)) < 0) +- return error; +- +- res = curl_easy_recv(s->handle, data, len, &read); +- } while (res == CURLE_AGAIN); +- +- if (res != CURLE_OK) +- return seterr_curl(s); +- +- return read; +-} +- +-static int curls_close(git_stream *stream) +-{ +- curl_stream *s = (curl_stream *) stream; +- +- if (!s->handle) +- return 0; +- +- curl_easy_cleanup(s->handle); +- s->handle = NULL; +- s->socket = 0; +- +- return 0; +-} +- +-static void curls_free(git_stream *stream) +-{ +- curl_stream *s = (curl_stream *) stream; +- +- curls_close(stream); +- git_strarray_free(&s->cert_info_strings); +- git__free(s); +-} +- +-int git_curl_stream_new(git_stream **out, const char *host, const char *port) +-{ +- curl_stream *st; +- CURL *handle; +- int iport = 0, error; +- +- st = git__calloc(1, sizeof(curl_stream)); +- GITERR_CHECK_ALLOC(st); +- +- handle = curl_easy_init(); +- if (handle == NULL) { +- giterr_set(GITERR_NET, "failed to create curl handle"); +- git__free(st); +- return -1; +- } +- +- if ((error = git__strtol32(&iport, port, NULL, 10)) < 0) { +- git__free(st); +- return error; +- } +- +- curl_easy_setopt(handle, CURLOPT_URL, host); +- curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, st->curl_error); +- curl_easy_setopt(handle, CURLOPT_PORT, iport); +- curl_easy_setopt(handle, CURLOPT_CONNECT_ONLY, 1); +- curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 1); +- curl_easy_setopt(handle, CURLOPT_CERTINFO, 1); +- curl_easy_setopt(handle, CURLOPT_HTTPPROXYTUNNEL, 1); +- curl_easy_setopt(handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY); +- +- /* curl_easy_setopt(handle, CURLOPT_VERBOSE, 1); */ +- +- st->parent.version = GIT_STREAM_VERSION; +- st->parent.encrypted = 0; /* we don't encrypt ourselves */ +- st->parent.proxy_support = 1; +- st->parent.connect = curls_connect; +- st->parent.certificate = curls_certificate; +- st->parent.set_proxy = curls_set_proxy; +- st->parent.read = curls_read; +- st->parent.write = curls_write; +- st->parent.close = curls_close; +- st->parent.free = curls_free; +- st->handle = handle; +- +- *out = (git_stream *) st; +- return 0; +-} +- +-#else +- +-#include "stream.h" +- +-int git_curl_stream_new(git_stream **out, const char *host, const char *port) +-{ +- GIT_UNUSED(out); +- GIT_UNUSED(host); +- GIT_UNUSED(port); +- +- giterr_set(GITERR_NET, "curl is not supported in this version"); +- return -1; +-} +- +- +-#endif +diff --git a/src/curl_stream.h b/src/curl_stream.h +deleted file mode 100644 +index 283f0fe..0000000 +--- a/src/curl_stream.h ++++ /dev/null +@@ -1,14 +0,0 @@ +-/* +- * Copyright (C) the libgit2 contributors. All rights reserved. +- * +- * This file is part of libgit2, distributed under the GNU GPL v2 with +- * a Linking Exception. For full terms see the included COPYING file. +- */ +-#ifndef INCLUDE_curl_stream_h__ +-#define INCLUDE_curl_stream_h__ +- +-#include "git2/sys/stream.h" +- +-extern int git_curl_stream_new(git_stream **out, const char *host, const char *port); +- +-#endif diff --git a/src/global.c b/src/global.c -index e2ad8fe..c5b4269 100644 +index afa57e1..66f1d44 100644 --- a/src/global.c +++ b/src/global.c -@@ -10,7 +10,11 @@ +@@ -10,7 +10,8 @@ #include "sysdir.h" #include "filter.h" #include "merge_driver.h" -+#ifdef GIT_OPENSSL - #include "openssl_stream.h" -+#elif GIT_MBEDTLS -+#include "mbedtls_stream.h" -+#endif +-#include "openssl_stream.h" ++#include "streams/mbedtls.h" ++#include "streams/openssl.h" #include "thread-utils.h" #include "git2/global.h" #include "transports/ssh.h" -@@ -61,8 +65,13 @@ static int init_common(void) - (ret = git_sysdir_global_init()) == 0 && +@@ -62,7 +63,8 @@ static int init_common(void) (ret = git_filter_global_init()) == 0 && (ret = git_merge_driver_global_init()) == 0 && -- (ret = git_transport_ssh_global_init()) == 0 && + (ret = git_transport_ssh_global_init()) == 0 && - (ret = git_openssl_stream_global_init()) == 0) -+ (ret = git_transport_ssh_global_init()) == 0 -+#ifdef GIT_OPENSSL -+ && (ret = git_openssl_stream_global_init()) == 0 -+#elif GIT_MBEDTLS -+ && (ret = git_mbedtls_stream_global_init()) == 0 -+#endif -+ ) ++ (ret = git_openssl_stream_global_init()) == 0 && ++ (ret = git_mbedtls_stream_global_init()) == 0) ret = git_mwindow_global_init(); GIT_MEMORY_BARRIER; diff --git a/src/global.h b/src/global.h -index 2199515..adadcd9 100644 +index 88f40aa..e4bbabf 100644 --- a/src/global.h +++ b/src/global.h -@@ -23,6 +23,12 @@ typedef struct { - extern SSL_CTX *git__ssl_ctx; - #endif +@@ -24,11 +24,6 @@ typedef struct { + git_thread *current_thread; + } git_global_st; -+#ifdef GIT_MBEDTLS -+# include "mbedtls/platform.h" -+# include "mbedtls/ssl.h" -+extern mbedtls_ssl_config *git__ssl_conf; -+#endif -+ +-#ifdef GIT_OPENSSL +-# include +-extern SSL_CTX *git__ssl_ctx; +-#endif +- git_global_st *git__global_state(void); extern git_mutex git__mwindow_mutex; diff --git a/src/hash.h b/src/hash.h -index 0bc02a8..958d23b 100644 +index 0db0339..cba4462 100644 --- a/src/hash.h +++ b/src/hash.h -@@ -20,6 +20,8 @@ void git_hash_ctx_cleanup(git_hash_ctx *ctx); - # include "hash/hash_common_crypto.h" - #elif defined(OPENSSL_SHA1) +@@ -24,6 +24,8 @@ void git_hash_ctx_cleanup(git_hash_ctx *ctx); # include "hash/hash_openssl.h" + #elif defined(GIT_SHA1_WIN32) + # include "hash/hash_win32.h" +#elif defined(MBEDTLS_SHA1) +# include "hash/hash_mbedtls.h" - #elif defined(WIN32_SHA1) - # include "hash/hash_win32.h" #else + # include "hash/hash_generic.h" + #endif diff --git a/src/hash/hash_mbedtls.c b/src/hash/hash_mbedtls.c new file mode 100644 index 0000000..a19d763 @@ -233,7 +875,7 @@ index 0000000..a19d763 +} diff --git a/src/hash/hash_mbedtls.h b/src/hash/hash_mbedtls.h new file mode 100644 -index 0000000..e50d295 +index 0000000..24196c5 --- /dev/null +++ b/src/hash/hash_mbedtls.h @@ -0,0 +1,20 @@ @@ -257,13 +899,1803 @@ index 0000000..e50d295 +#define git_hash_ctx_init(ctx) git_hash_init(ctx) + +#endif /* INCLUDE_hash_mbedtld_h__ */ -\ No newline at end of file -diff --git a/src/mbedtls_stream.c b/src/mbedtls_stream.c +diff --git a/src/openssl_stream.c b/src/openssl_stream.c +deleted file mode 100644 +index 759c501..0000000 +--- a/src/openssl_stream.c ++++ /dev/null +@@ -1,656 +0,0 @@ +-/* +- * Copyright (C) the libgit2 contributors. All rights reserved. +- * +- * This file is part of libgit2, distributed under the GNU GPL v2 with +- * a Linking Exception. For full terms see the included COPYING file. +- */ +- +-#ifdef GIT_OPENSSL +- +-#include +- +-#include "global.h" +-#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" +- +-#ifdef GIT_CURL +-# include "curl_stream.h" +-#endif +- +-#ifndef GIT_WIN32 +-# include +-# include +-# include +-#endif +- +-#include +-#include +-#include +-#include +- +-SSL_CTX *git__ssl_ctx; +- +-#define GIT_SSL_DEFAULT_CIPHERS "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES128-SHA256:DHE-DSS-AES256-SHA256:DHE-DSS-AES128-SHA:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA" +- +-#if defined(GIT_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L +- +-static git_mutex *openssl_locks; +- +-static void openssl_locking_function( +- int mode, int n, const char *file, int line) +-{ +- int lock; +- +- GIT_UNUSED(file); +- GIT_UNUSED(line); +- +- lock = mode & CRYPTO_LOCK; +- +- if (lock) { +- git_mutex_lock(&openssl_locks[n]); +- } else { +- git_mutex_unlock(&openssl_locks[n]); +- } +-} +- +-static void shutdown_ssl_locking(void) +-{ +- int num_locks, i; +- +- num_locks = CRYPTO_num_locks(); +- CRYPTO_set_locking_callback(NULL); +- +- for (i = 0; i < num_locks; ++i) +- git_mutex_free(&openssl_locks[i]); +- git__free(openssl_locks); +-} +- +-#endif /* GIT_THREADS && OPENSSL_VERSION_NUMBER < 0x10100000L */ +- +-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; +- } +-} +- +-int git_openssl_stream_global_init(void) +-{ +-#ifdef GIT_OPENSSL +- long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; +- const char *ciphers = git_libgit2__ssl_ciphers(); +- +- /* Older OpenSSL and MacOS OpenSSL doesn't have this */ +-#ifdef SSL_OP_NO_COMPRESSION +- ssl_opts |= SSL_OP_NO_COMPRESSION; +-#endif +- +-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +- SSL_load_error_strings(); +- OpenSSL_add_ssl_algorithms(); +-#else +- OPENSSL_init_ssl(0, NULL); +-#endif +- +- /* +- * Load SSLv{2,3} and TLSv1 so that we can talk with servers +- * which use the SSL hellos, which are often used for +- * compatibility. We then disable SSL so we only allow OpenSSL +- * to speak TLSv1 to perform the encryption itself. +- */ +- git__ssl_ctx = SSL_CTX_new(SSLv23_method()); +- SSL_CTX_set_options(git__ssl_ctx, ssl_opts); +- SSL_CTX_set_mode(git__ssl_ctx, SSL_MODE_AUTO_RETRY); +- SSL_CTX_set_verify(git__ssl_ctx, SSL_VERIFY_NONE, NULL); +- if (!SSL_CTX_set_default_verify_paths(git__ssl_ctx)) { +- SSL_CTX_free(git__ssl_ctx); +- git__ssl_ctx = NULL; +- return -1; +- } +- +- if (!ciphers) { +- ciphers = GIT_SSL_DEFAULT_CIPHERS; +- } +- +- if(!SSL_CTX_set_cipher_list(git__ssl_ctx, ciphers)) { +- SSL_CTX_free(git__ssl_ctx); +- 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); +- +- return 0; +-} +- +-int git_openssl_set_locking(void) +-{ +-#if defined(GIT_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L +- int num_locks, i; +- +- num_locks = CRYPTO_num_locks(); +- openssl_locks = git__calloc(num_locks, sizeof(git_mutex)); +- GITERR_CHECK_ALLOC(openssl_locks); +- +- for (i = 0; i < num_locks; i++) { +- if (git_mutex_init(&openssl_locks[i]) != 0) { +- giterr_set(GITERR_SSL, "failed to initialize openssl locks"); +- return -1; +- } +- } +- +- CRYPTO_set_locking_callback(openssl_locking_function); +- git__on_shutdown(shutdown_ssl_locking); +- return 0; +-#elif OPENSSL_VERSION_NUMBER >= 0x10100000L +- return 0; +-#else +- giterr_set(GITERR_THREAD, "libgit2 was not built with threads"); +- return -1; +-#endif +-} +- +- +-static int bio_create(BIO *b) +-{ +- BIO_set_init(b, 1); +- BIO_set_data(b, NULL); +- +- return 1; +-} +- +-static int bio_destroy(BIO *b) +-{ +- if (!b) +- return 0; +- +- BIO_set_data(b, NULL); +- +- return 1; +-} +- +-static int bio_read(BIO *b, char *buf, int len) +-{ +- git_stream *io = (git_stream *) BIO_get_data(b); +- +- return (int) git_stream_read(io, buf, len); +-} +- +-static int bio_write(BIO *b, const char *buf, int len) +-{ +- git_stream *io = (git_stream *) BIO_get_data(b); +- +- return (int) git_stream_write(io, buf, len, 0); +-} +- +-static long bio_ctrl(BIO *b, int cmd, long num, void *ptr) +-{ +- GIT_UNUSED(b); +- GIT_UNUSED(num); +- GIT_UNUSED(ptr); +- +- if (cmd == BIO_CTRL_FLUSH) +- return 1; +- +- return 0; +-} +- +-static int bio_gets(BIO *b, char *buf, int len) +-{ +- GIT_UNUSED(b); +- GIT_UNUSED(buf); +- GIT_UNUSED(len); +- return -1; +-} +- +-static int bio_puts(BIO *b, const char *str) +-{ +- return bio_write(b, str, strlen(str)); +-} +- +-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) +-{ +- int err; +- unsigned long e; +- +- err = SSL_get_error(ssl, error); +- +- assert(err != SSL_ERROR_WANT_READ); +- assert(err != SSL_ERROR_WANT_WRITE); +- +- switch (err) { +- case SSL_ERROR_WANT_CONNECT: +- case SSL_ERROR_WANT_ACCEPT: +- giterr_set(GITERR_NET, "SSL error: connection failure"); +- break; +- case SSL_ERROR_WANT_X509_LOOKUP: +- giterr_set(GITERR_NET, "SSL error: x509 error"); +- break; +- case SSL_ERROR_SYSCALL: +- e = ERR_get_error(); +- if (e > 0) { +- giterr_set(GITERR_NET, "SSL error: %s", +- ERR_error_string(e, NULL)); +- break; +- } else if (error < 0) { +- giterr_set(GITERR_OS, "SSL error: syscall failure"); +- break; +- } +- giterr_set(GITERR_NET, "SSL error: received early EOF"); +- return GIT_EEOF; +- break; +- case SSL_ERROR_SSL: +- e = ERR_get_error(); +- giterr_set(GITERR_NET, "SSL error: %s", +- ERR_error_string(e, NULL)); +- break; +- case SSL_ERROR_NONE: +- case SSL_ERROR_ZERO_RETURN: +- default: +- giterr_set(GITERR_NET, "SSL error: unknown error"); +- break; +- } +- return -1; +-} +- +-static int ssl_teardown(SSL *ssl) +-{ +- int ret; +- +- ret = SSL_shutdown(ssl); +- if (ret < 0) +- ret = ssl_set_error(ssl, ret); +- else +- ret = 0; +- +- return ret; +-} +- +-static int check_host_name(const char *name, const char *host) +-{ +- if (!strcasecmp(name, host)) +- return 0; +- +- if (gitno__match_host(name, host) < 0) +- return -1; +- +- return 0; +-} +- +-static int verify_server_cert(SSL *ssl, const char *host) +-{ +- X509 *cert; +- X509_NAME *peer_name; +- ASN1_STRING *str; +- unsigned char *peer_cn = NULL; +- int matched = -1, type = GEN_DNS; +- GENERAL_NAMES *alts; +- struct in6_addr addr6; +- struct in_addr addr4; +- void *addr; +- int i = -1,j; +- +- if (SSL_get_verify_result(ssl) != X509_V_OK) { +- giterr_set(GITERR_SSL, "the SSL certificate is invalid"); +- return GIT_ECERTIFICATE; +- } +- +- /* Try to parse the host as an IP address to see if it is */ +- if (p_inet_pton(AF_INET, host, &addr4)) { +- type = GEN_IPADD; +- addr = &addr4; +- } else { +- if(p_inet_pton(AF_INET6, host, &addr6)) { +- type = GEN_IPADD; +- addr = &addr6; +- } +- } +- +- +- cert = SSL_get_peer_certificate(ssl); +- if (!cert) { +- giterr_set(GITERR_SSL, "the server did not provide a certificate"); +- return -1; +- } +- +- /* Check the alternative names */ +- alts = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); +- if (alts) { +- int num; +- +- 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_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 */ +- if (gn->type != type) +- continue; +- +- if (type == GEN_DNS) { +- /* If it contains embedded NULs, don't even try */ +- if (memchr(name, '\0', namelen)) +- continue; +- +- if (check_host_name(name, host) < 0) +- matched = 0; +- else +- matched = 1; +- } else if (type == GEN_IPADD) { +- /* Here name isn't so much a name but a binary representation of the IP */ +- matched = !!memcmp(name, addr, namelen); +- } +- } +- } +- GENERAL_NAMES_free(alts); +- +- if (matched == 0) +- goto cert_fail_name; +- +- if (matched == 1) +- return 0; +- +- /* If no alternative names are available, check the common name */ +- peer_name = X509_get_subject_name(cert); +- if (peer_name == NULL) +- goto on_error; +- +- if (peer_name) { +- /* Get the index of the last CN entry */ +- while ((j = X509_NAME_get_index_by_NID(peer_name, NID_commonName, i)) >= 0) +- i = j; +- } +- +- if (i < 0) +- goto on_error; +- +- str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(peer_name, i)); +- if (str == NULL) +- goto on_error; +- +- /* Work around a bug in OpenSSL whereby ASN1_STRING_to_UTF8 fails if it's already in utf-8 */ +- if (ASN1_STRING_type(str) == V_ASN1_UTF8STRING) { +- int size = ASN1_STRING_length(str); +- +- if (size > 0) { +- peer_cn = OPENSSL_malloc(size + 1); +- GITERR_CHECK_ALLOC(peer_cn); +- memcpy(peer_cn, ASN1_STRING_get0_data(str), size); +- peer_cn[size] = '\0'; +- } else { +- goto cert_fail_name; +- } +- } else { +- int size = ASN1_STRING_to_UTF8(&peer_cn, str); +- GITERR_CHECK_ALLOC(peer_cn); +- if (memchr(peer_cn, '\0', size)) +- goto cert_fail_name; +- } +- +- if (check_host_name((char *)peer_cn, host) < 0) +- goto cert_fail_name; +- +- OPENSSL_free(peer_cn); +- +- return 0; +- +-on_error: +- OPENSSL_free(peer_cn); +- return ssl_set_error(ssl, 0); +- +-cert_fail_name: +- OPENSSL_free(peer_cn); +- giterr_set(GITERR_SSL, "hostname does not match certificate"); +- return GIT_ECERTIFICATE; +-} +- +-typedef struct { +- git_stream parent; +- git_stream *io; +- bool connected; +- char *host; +- SSL *ssl; +- git_cert_x509 cert_info; +-} openssl_stream; +- +-int openssl_close(git_stream *stream); +- +-int openssl_connect(git_stream *stream) +-{ +- int ret; +- BIO *bio; +- openssl_stream *st = (openssl_stream *) stream; +- +- if ((ret = git_stream_connect(st->io)) < 0) +- return ret; +- +- st->connected = true; +- +- bio = BIO_new(git_stream_bio_method); +- GITERR_CHECK_ALLOC(bio); +- +- 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); +-#endif +- +- if ((ret = SSL_connect(st->ssl)) <= 0) +- return ssl_set_error(st->ssl, ret); +- +- return verify_server_cert(st->ssl, st->host); +-} +- +-int openssl_certificate(git_cert **out, git_stream *stream) +-{ +- openssl_stream *st = (openssl_stream *) stream; +- int len; +- X509 *cert = SSL_get_peer_certificate(st->ssl); +- unsigned char *guard, *encoded_cert; +- +- /* Retrieve the length of the certificate first */ +- len = i2d_X509(cert, NULL); +- if (len < 0) { +- giterr_set(GITERR_NET, "failed to retrieve certificate information"); +- return -1; +- } +- +- encoded_cert = git__malloc(len); +- GITERR_CHECK_ALLOC(encoded_cert); +- /* i2d_X509 makes 'guard' point to just after the data */ +- guard = encoded_cert; +- +- len = i2d_X509(cert, &guard); +- if (len < 0) { +- git__free(encoded_cert); +- giterr_set(GITERR_NET, "failed to retrieve certificate information"); +- return -1; +- } +- +- st->cert_info.parent.cert_type = GIT_CERT_X509; +- st->cert_info.data = encoded_cert; +- st->cert_info.len = len; +- +- *out = &st->cert_info.parent; +- +- return 0; +-} +- +-static int openssl_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts) +-{ +- openssl_stream *st = (openssl_stream *) stream; +- +- return git_stream_set_proxy(st->io, proxy_opts); +-} +- +-ssize_t openssl_write(git_stream *stream, const char *data, size_t len, int flags) +-{ +- openssl_stream *st = (openssl_stream *) stream; +- int ret; +- +- GIT_UNUSED(flags); +- +- if ((ret = SSL_write(st->ssl, data, len)) <= 0) { +- return ssl_set_error(st->ssl, ret); +- } +- +- return ret; +-} +- +-ssize_t openssl_read(git_stream *stream, void *data, size_t len) +-{ +- openssl_stream *st = (openssl_stream *) stream; +- int ret; +- +- if ((ret = SSL_read(st->ssl, data, len)) <= 0) +- return ssl_set_error(st->ssl, ret); +- +- return ret; +-} +- +-int openssl_close(git_stream *stream) +-{ +- openssl_stream *st = (openssl_stream *) stream; +- int ret; +- +- if (st->connected && (ret = ssl_teardown(st->ssl)) < 0) +- return -1; +- +- st->connected = false; +- +- return git_stream_close(st->io); +-} +- +-void openssl_free(git_stream *stream) +-{ +- openssl_stream *st = (openssl_stream *) stream; +- +- SSL_free(st->ssl); +- git__free(st->host); +- git__free(st->cert_info.data); +- git_stream_free(st->io); +- git__free(st); +-} +- +-int git_openssl_stream_new(git_stream **out, const char *host, const char *port) +-{ +- int error; +- openssl_stream *st; +- +- st = git__calloc(1, sizeof(openssl_stream)); +- GITERR_CHECK_ALLOC(st); +- +- st->io = NULL; +-#ifdef GIT_CURL +- error = git_curl_stream_new(&st->io, host, port); +-#else +- error = git_socket_stream_new(&st->io, host, port); +-#endif +- +- if (error < 0) +- goto out_err; +- +- st->ssl = SSL_new(git__ssl_ctx); +- if (st->ssl == NULL) { +- giterr_set(GITERR_SSL, "failed to create ssl object"); +- error = -1; +- goto out_err; +- } +- +- st->host = git__strdup(host); +- GITERR_CHECK_ALLOC(st->host); +- +- st->parent.version = GIT_STREAM_VERSION; +- st->parent.encrypted = 1; +- st->parent.proxy_support = git_stream_supports_proxy(st->io); +- st->parent.connect = openssl_connect; +- st->parent.certificate = openssl_certificate; +- st->parent.set_proxy = openssl_set_proxy; +- st->parent.read = openssl_read; +- st->parent.write = openssl_write; +- st->parent.close = openssl_close; +- st->parent.free = openssl_free; +- +- *out = (git_stream *) st; +- return 0; +- +-out_err: +- git_stream_free(st->io); +- git__free(st); +- +- return error; +-} +- +-#else +- +-#include "stream.h" +-#include "git2/sys/openssl.h" +- +-int git_openssl_stream_global_init(void) +-{ +- return 0; +-} +- +-int git_openssl_set_locking(void) +-{ +- giterr_set(GITERR_SSL, "libgit2 was not built with OpenSSL support"); +- return -1; +-} +- +-int git_openssl_stream_new(git_stream **out, const char *host, const char *port) +-{ +- GIT_UNUSED(out); +- GIT_UNUSED(host); +- GIT_UNUSED(port); +- +- giterr_set(GITERR_SSL, "openssl is not supported in this version"); +- return -1; +-} +- +-#endif +diff --git a/src/openssl_stream.h b/src/openssl_stream.h +deleted file mode 100644 +index f5e59da..0000000 +--- a/src/openssl_stream.h ++++ /dev/null +@@ -1,122 +0,0 @@ +-/* +- * Copyright (C) the libgit2 contributors. All rights reserved. +- * +- * This file is part of libgit2, distributed under the GNU GPL v2 with +- * a Linking Exception. For full terms see the included COPYING file. +- */ +-#ifndef INCLUDE_openssl_stream_h__ +-#define INCLUDE_openssl_stream_h__ +- +-#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. +- */ +-#ifdef GIT_OPENSSL +-# include +-# include +-# include +-# include +- +- +- +-# if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +- +-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; +-} +- +-GIT_INLINE(const unsigned char *) ASN1_STRING_get0_data(const ASN1_STRING *x) +-{ +- return ASN1_STRING_data((ASN1_STRING *)x); +-} +- +-# endif // OpenSSL < 1.1 +-#endif // GIT_OPENSSL +- +-#endif +diff --git a/src/settings.c b/src/settings.c +index 52b861b..3a46f0d 100644 +--- a/src/settings.c ++++ b/src/settings.c +@@ -9,6 +9,10 @@ + # include + #endif + ++#ifdef GIT_MBEDTLS ++# include ++#endif ++ + #include + #include "common.h" + #include "sysdir.h" +@@ -18,6 +22,8 @@ + #include "odb.h" + #include "refs.h" + #include "transports/smart.h" ++#include "streams/openssl.h" ++#include "streams/mbedtls.h" + + void git_libgit2_version(int *major, int *minor, int *rev) + { +@@ -171,14 +177,19 @@ int git_libgit2_opts(int key, ...) + { + const char *file = va_arg(ap, const char *); + const char *path = va_arg(ap, const char *); +- if (!SSL_CTX_load_verify_locations(git__ssl_ctx, file, path)) { +- giterr_set(GITERR_NET, "SSL error: %s", +- ERR_error_string(ERR_get_error(), NULL)); +- error = -1; +- } ++ error = git_openssl_set_cert_file(file, path); ++ } ++#elif GIT_MBEDTLS ++ { ++ const char *file = va_arg(ap, const char *); ++ const char *path = va_arg(ap, const char *); ++ if (file) ++ error = git_mbedtls_set_cert_file(file, 0); ++ if (error && path) ++ error = git_mbedtls_set_cert_file(path, 0); + } + #else +- giterr_set(GITERR_NET, "cannot set certificate locations: OpenSSL is not enabled"); ++ giterr_set(GITERR_NET, "cannot set certificate locations: OpenSSL or mbedTLS is not enabled"); + error = -1; + #endif + break; +diff --git a/src/socket_stream.c b/src/socket_stream.c +deleted file mode 100644 +index c0a1684..0000000 +--- a/src/socket_stream.c ++++ /dev/null +@@ -1,210 +0,0 @@ +-/* +- * Copyright (C) the libgit2 contributors. All rights reserved. +- * +- * This file is part of libgit2, distributed under the GNU GPL v2 with +- * a Linking Exception. For full terms see the included COPYING file. +- */ +- +-#include "common.h" +-#include "posix.h" +-#include "netops.h" +-#include "stream.h" +-#include "socket_stream.h" +- +-#ifndef _WIN32 +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-#else +-# include +-# include +-# ifdef _MSC_VER +-# pragma comment(lib, "ws2_32") +-# endif +-#endif +- +-#ifdef GIT_WIN32 +-static void net_set_error(const char *str) +-{ +- int error = WSAGetLastError(); +- char * win32_error = git_win32_get_error_message(error); +- +- if (win32_error) { +- giterr_set(GITERR_NET, "%s: %s", str, win32_error); +- git__free(win32_error); +- } else { +- giterr_set(GITERR_NET, str); +- } +-} +-#else +-static void net_set_error(const char *str) +-{ +- giterr_set(GITERR_NET, "%s: %s", str, strerror(errno)); +-} +-#endif +- +-static int close_socket(GIT_SOCKET s) +-{ +- if (s == INVALID_SOCKET) +- return 0; +- +-#ifdef GIT_WIN32 +- if (SOCKET_ERROR == closesocket(s)) +- return -1; +- +- if (0 != WSACleanup()) { +- giterr_set(GITERR_OS, "winsock cleanup failed"); +- return -1; +- } +- +- return 0; +-#else +- return close(s); +-#endif +- +-} +- +-int socket_connect(git_stream *stream) +-{ +- struct addrinfo *info = NULL, *p; +- struct addrinfo hints; +- git_socket_stream *st = (git_socket_stream *) stream; +- GIT_SOCKET s = INVALID_SOCKET; +- int ret; +- +-#ifdef GIT_WIN32 +- /* on win32, the WSA context needs to be initialized +- * before any socket calls can be performed */ +- WSADATA wsd; +- +- if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) { +- giterr_set(GITERR_OS, "winsock init failed"); +- return -1; +- } +- +- if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) { +- WSACleanup(); +- giterr_set(GITERR_OS, "winsock init failed"); +- return -1; +- } +-#endif +- +- memset(&hints, 0x0, sizeof(struct addrinfo)); +- hints.ai_socktype = SOCK_STREAM; +- hints.ai_family = AF_UNSPEC; +- +- if ((ret = p_getaddrinfo(st->host, st->port, &hints, &info)) != 0) { +- giterr_set(GITERR_NET, +- "failed to resolve address for %s: %s", st->host, p_gai_strerror(ret)); +- return -1; +- } +- +- for (p = info; p != NULL; p = p->ai_next) { +- s = socket(p->ai_family, p->ai_socktype, p->ai_protocol); +- +- if (s == INVALID_SOCKET) +- continue; +- +- if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0) +- break; +- +- /* If we can't connect, try the next one */ +- close_socket(s); +- s = INVALID_SOCKET; +- } +- +- /* Oops, we couldn't connect to any address */ +- if (s == INVALID_SOCKET && p == NULL) { +- giterr_set(GITERR_OS, "failed to connect to %s", st->host); +- p_freeaddrinfo(info); +- return -1; +- } +- +- st->s = s; +- p_freeaddrinfo(info); +- return 0; +-} +- +-ssize_t socket_write(git_stream *stream, const char *data, size_t len, int flags) +-{ +- ssize_t ret; +- size_t off = 0; +- git_socket_stream *st = (git_socket_stream *) stream; +- +- while (off < len) { +- errno = 0; +- ret = p_send(st->s, data + off, len - off, flags); +- if (ret < 0) { +- net_set_error("Error sending data"); +- return -1; +- } +- +- off += ret; +- } +- +- return off; +-} +- +-ssize_t socket_read(git_stream *stream, void *data, size_t len) +-{ +- ssize_t ret; +- git_socket_stream *st = (git_socket_stream *) stream; +- +- if ((ret = p_recv(st->s, data, len, 0)) < 0) +- net_set_error("Error receiving socket data"); +- +- return ret; +-} +- +-int socket_close(git_stream *stream) +-{ +- git_socket_stream *st = (git_socket_stream *) stream; +- int error; +- +- error = close_socket(st->s); +- st->s = INVALID_SOCKET; +- +- return error; +-} +- +-void socket_free(git_stream *stream) +-{ +- git_socket_stream *st = (git_socket_stream *) stream; +- +- git__free(st->host); +- git__free(st->port); +- git__free(st); +-} +- +-int git_socket_stream_new(git_stream **out, const char *host, const char *port) +-{ +- git_socket_stream *st; +- +- assert(out && host); +- +- st = git__calloc(1, sizeof(git_socket_stream)); +- GITERR_CHECK_ALLOC(st); +- +- st->host = git__strdup(host); +- GITERR_CHECK_ALLOC(st->host); +- +- if (port) { +- st->port = git__strdup(port); +- GITERR_CHECK_ALLOC(st->port); +- } +- +- st->parent.version = GIT_STREAM_VERSION; +- st->parent.connect = socket_connect; +- st->parent.write = socket_write; +- st->parent.read = socket_read; +- st->parent.close = socket_close; +- st->parent.free = socket_free; +- st->s = INVALID_SOCKET; +- +- *out = (git_stream *) st; +- return 0; +-} +diff --git a/src/socket_stream.h b/src/socket_stream.h +deleted file mode 100644 +index 8e9949f..0000000 +--- a/src/socket_stream.h ++++ /dev/null +@@ -1,21 +0,0 @@ +-/* +- * Copyright (C) the libgit2 contributors. All rights reserved. +- * +- * This file is part of libgit2, distributed under the GNU GPL v2 with +- * a Linking Exception. For full terms see the included COPYING file. +- */ +-#ifndef INCLUDE_socket_stream_h__ +-#define INCLUDE_socket_stream_h__ +- +-#include "netops.h" +- +-typedef struct { +- git_stream parent; +- char *host; +- char *port; +- GIT_SOCKET s; +-} git_socket_stream; +- +-extern int git_socket_stream_new(git_stream **out, const char *host, const char *port); +- +-#endif +diff --git a/src/stransport_stream.c b/src/stransport_stream.c +deleted file mode 100644 +index 50ed945..0000000 +--- a/src/stransport_stream.c ++++ /dev/null +@@ -1,294 +0,0 @@ +-/* +- * Copyright (C) the libgit2 contributors. All rights reserved. +- * +- * This file is part of libgit2, distributed under the GNU GPL v2 with +- * a Linking Exception. For full terms see the included COPYING file. +- */ +- +-#ifdef GIT_SECURE_TRANSPORT +- +-#include +-#include +-#include +- +-#include "git2/transport.h" +- +-#include "socket_stream.h" +-#include "curl_stream.h" +- +-static int stransport_error(OSStatus ret) +-{ +- CFStringRef message; +- +- if (ret == noErr || ret == errSSLClosedGraceful) { +- giterr_clear(); +- return 0; +- } +- +-#if !TARGET_OS_IPHONE +- message = SecCopyErrorMessageString(ret, NULL); +- GITERR_CHECK_ALLOC(message); +- +- giterr_set(GITERR_NET, "SecureTransport error: %s", CFStringGetCStringPtr(message, kCFStringEncodingUTF8)); +- CFRelease(message); +-#else +- giterr_set(GITERR_NET, "SecureTransport error: OSStatus %d", (unsigned int)ret); +- GIT_UNUSED(message); +-#endif +- +- return -1; +-} +- +-typedef struct { +- git_stream parent; +- git_stream *io; +- SSLContextRef ctx; +- CFDataRef der_data; +- git_cert_x509 cert_info; +-} stransport_stream; +- +-static int stransport_connect(git_stream *stream) +-{ +- stransport_stream *st = (stransport_stream *) stream; +- int error; +- SecTrustRef trust = NULL; +- SecTrustResultType sec_res; +- OSStatus ret; +- +- if ((error = git_stream_connect(st->io)) < 0) +- return error; +- +- ret = SSLHandshake(st->ctx); +- if (ret != errSSLServerAuthCompleted) { +- giterr_set(GITERR_SSL, "unexpected return value from ssl handshake %d", ret); +- return -1; +- } +- +- if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr) +- goto on_error; +- +- if (!trust) +- return GIT_ECERTIFICATE; +- +- if ((ret = SecTrustEvaluate(trust, &sec_res)) != noErr) +- goto on_error; +- +- CFRelease(trust); +- +- if (sec_res == kSecTrustResultInvalid || sec_res == kSecTrustResultOtherError) { +- giterr_set(GITERR_SSL, "internal security trust error"); +- return -1; +- } +- +- if (sec_res == kSecTrustResultDeny || sec_res == kSecTrustResultRecoverableTrustFailure || +- sec_res == kSecTrustResultFatalTrustFailure) +- return GIT_ECERTIFICATE; +- +- return 0; +- +-on_error: +- if (trust) +- CFRelease(trust); +- +- return stransport_error(ret); +-} +- +-static int stransport_certificate(git_cert **out, git_stream *stream) +-{ +- stransport_stream *st = (stransport_stream *) stream; +- SecTrustRef trust = NULL; +- SecCertificateRef sec_cert; +- OSStatus ret; +- +- if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr) +- return stransport_error(ret); +- +- sec_cert = SecTrustGetCertificateAtIndex(trust, 0); +- st->der_data = SecCertificateCopyData(sec_cert); +- CFRelease(trust); +- +- if (st->der_data == NULL) { +- giterr_set(GITERR_SSL, "retrieved invalid certificate data"); +- return -1; +- } +- +- st->cert_info.parent.cert_type = GIT_CERT_X509; +- st->cert_info.data = (void *) CFDataGetBytePtr(st->der_data); +- st->cert_info.len = CFDataGetLength(st->der_data); +- +- *out = (git_cert *)&st->cert_info; +- return 0; +-} +- +-static int stransport_set_proxy( +- git_stream *stream, +- const git_proxy_options *proxy_opts) +-{ +- stransport_stream *st = (stransport_stream *) stream; +- +- return git_stream_set_proxy(st->io, proxy_opts); +-} +- +-/* +- * Contrary to typical network IO callbacks, Secure Transport write callback is +- * expected to write *all* passed data, not just as much as it can, and any +- * other case would be considered a failure. +- * +- * This behavior is actually not specified in the Apple documentation, but is +- * required for things to work correctly (and incidentally, that's also how +- * Apple implements it in its projects at opensource.apple.com). +- * +- * Libgit2 streams happen to already have this very behavior so this is just +- * passthrough. +- */ +-static OSStatus write_cb(SSLConnectionRef conn, const void *data, size_t *len) +-{ +- git_stream *io = (git_stream *) conn; +- +- if (git_stream_write(io, data, *len, 0) < 0) { +- return -36; /* "ioErr" from MacErrors.h which is not available on iOS */ +- } +- +- return noErr; +-} +- +-static ssize_t stransport_write(git_stream *stream, const char *data, size_t len, int flags) +-{ +- stransport_stream *st = (stransport_stream *) stream; +- size_t data_len, processed; +- OSStatus ret; +- +- GIT_UNUSED(flags); +- +- data_len = len; +- if ((ret = SSLWrite(st->ctx, data, data_len, &processed)) != noErr) +- return stransport_error(ret); +- +- return processed; +-} +- +-/* +- * Contrary to typical network IO callbacks, Secure Transport read callback is +- * expected to read *exactly* the requested number of bytes, not just as much +- * as it can, and any other case would be considered a failure. +- * +- * This behavior is actually not specified in the Apple documentation, but is +- * required for things to work correctly (and incidentally, that's also how +- * Apple implements it in its projects at opensource.apple.com). +- */ +-static OSStatus read_cb(SSLConnectionRef conn, void *data, size_t *len) +-{ +- git_stream *io = (git_stream *) conn; +- OSStatus error = noErr; +- size_t off = 0; +- ssize_t ret; +- +- do { +- ret = git_stream_read(io, data + off, *len - off); +- if (ret < 0) { +- error = -36; /* "ioErr" from MacErrors.h which is not available on iOS */ +- break; +- } +- if (ret == 0) { +- error = errSSLClosedGraceful; +- break; +- } +- +- off += ret; +- } while (off < *len); +- +- *len = off; +- return error; +-} +- +-static ssize_t stransport_read(git_stream *stream, void *data, size_t len) +-{ +- stransport_stream *st = (stransport_stream *) stream; +- size_t processed; +- OSStatus ret; +- +- if ((ret = SSLRead(st->ctx, data, len, &processed)) != noErr) +- return stransport_error(ret); +- +- return processed; +-} +- +-static int stransport_close(git_stream *stream) +-{ +- stransport_stream *st = (stransport_stream *) stream; +- OSStatus ret; +- +- ret = SSLClose(st->ctx); +- if (ret != noErr && ret != errSSLClosedGraceful) +- return stransport_error(ret); +- +- return git_stream_close(st->io); +-} +- +-static void stransport_free(git_stream *stream) +-{ +- stransport_stream *st = (stransport_stream *) stream; +- +- git_stream_free(st->io); +- CFRelease(st->ctx); +- if (st->der_data) +- CFRelease(st->der_data); +- git__free(st); +-} +- +-int git_stransport_stream_new(git_stream **out, const char *host, const char *port) +-{ +- stransport_stream *st; +- int error; +- OSStatus ret; +- +- assert(out && host); +- +- st = git__calloc(1, sizeof(stransport_stream)); +- GITERR_CHECK_ALLOC(st); +- +-#ifdef GIT_CURL +- error = git_curl_stream_new(&st->io, host, port); +-#else +- error = git_socket_stream_new(&st->io, host, port); +-#endif +- +- if (error < 0){ +- git__free(st); +- return error; +- } +- +- st->ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType); +- if (!st->ctx) { +- giterr_set(GITERR_NET, "failed to create SSL context"); +- git__free(st); +- return -1; +- } +- +- if ((ret = SSLSetIOFuncs(st->ctx, read_cb, write_cb)) != noErr || +- (ret = SSLSetConnection(st->ctx, st->io)) != noErr || +- (ret = SSLSetSessionOption(st->ctx, kSSLSessionOptionBreakOnServerAuth, true)) != noErr || +- (ret = SSLSetProtocolVersionMin(st->ctx, kTLSProtocol1)) != noErr || +- (ret = SSLSetProtocolVersionMax(st->ctx, kTLSProtocol12)) != noErr || +- (ret = SSLSetPeerDomainName(st->ctx, host, strlen(host))) != noErr) { +- CFRelease(st->ctx); +- git__free(st); +- return stransport_error(ret); +- } +- +- st->parent.version = GIT_STREAM_VERSION; +- st->parent.encrypted = 1; +- st->parent.proxy_support = git_stream_supports_proxy(st->io); +- st->parent.connect = stransport_connect; +- st->parent.certificate = stransport_certificate; +- st->parent.set_proxy = stransport_set_proxy; +- st->parent.read = stransport_read; +- st->parent.write = stransport_write; +- st->parent.close = stransport_close; +- st->parent.free = stransport_free; +- +- *out = (git_stream *) st; +- return 0; +-} +- +-#endif +diff --git a/src/stransport_stream.h b/src/stransport_stream.h +deleted file mode 100644 +index 714f902..0000000 +--- a/src/stransport_stream.h ++++ /dev/null +@@ -1,14 +0,0 @@ +-/* +- * Copyright (C) the libgit2 contributors. All rights reserved. +- * +- * This file is part of libgit2, distributed under the GNU GPL v2 with +- * a Linking Exception. For full terms see the included COPYING file. +- */ +-#ifndef INCLUDE_stransport_stream_h__ +-#define INCLUDE_stransport_stream_h__ +- +-#include "git2/sys/stream.h" +- +-extern int git_stransport_stream_new(git_stream **out, const char *host, const char *port); +- +-#endif +diff --git a/src/streams/curl.c b/src/streams/curl.c +new file mode 100644 +index 0000000..4e0455c +--- /dev/null ++++ b/src/streams/curl.c +@@ -0,0 +1,362 @@ ++/* ++ * Copyright (C) the libgit2 contributors. All rights reserved. ++ * ++ * This file is part of libgit2, distributed under the GNU GPL v2 with ++ * a Linking Exception. For full terms see the included COPYING file. ++ */ ++ ++#ifdef GIT_CURL ++ ++#include ++ ++#include "stream.h" ++#include "git2/transport.h" ++#include "buffer.h" ++#include "vector.h" ++#include "proxy.h" ++ ++/* This is for backwards compatibility with curl<7.45.0. */ ++#ifndef CURLINFO_ACTIVESOCKET ++# define CURLINFO_ACTIVESOCKET CURLINFO_LASTSOCKET ++# define GIT_CURL_BADSOCKET -1 ++# define git_activesocket_t long ++#else ++# define GIT_CURL_BADSOCKET CURL_SOCKET_BAD ++# define git_activesocket_t curl_socket_t ++#endif ++ ++typedef struct { ++ git_stream parent; ++ CURL *handle; ++ curl_socket_t socket; ++ char curl_error[CURL_ERROR_SIZE + 1]; ++ git_cert_x509 cert_info; ++ git_strarray cert_info_strings; ++ git_proxy_options proxy; ++ git_cred *proxy_cred; ++} curl_stream; ++ ++static int seterr_curl(curl_stream *s) ++{ ++ giterr_set(GITERR_NET, "curl error: %s\n", s->curl_error); ++ return -1; ++} ++ ++GIT_INLINE(int) error_no_credentials(void) ++{ ++ giterr_set(GITERR_NET, "proxy authentication required, but no callback provided"); ++ return GIT_EAUTH; ++} ++ ++static int apply_proxy_creds(curl_stream *s) ++{ ++ CURLcode res; ++ git_cred_userpass_plaintext *userpass; ++ ++ if (!s->proxy_cred) ++ return GIT_ENOTFOUND; ++ ++ userpass = (git_cred_userpass_plaintext *) s->proxy_cred; ++ if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYUSERNAME, userpass->username)) != CURLE_OK) ++ return seterr_curl(s); ++ if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYPASSWORD, userpass->password)) != CURLE_OK) ++ return seterr_curl(s); ++ ++ return 0; ++} ++ ++static int ask_and_apply_proxy_creds(curl_stream *s) ++{ ++ int error; ++ git_proxy_options *opts = &s->proxy; ++ ++ if (!opts->credentials) ++ return error_no_credentials(); ++ ++ /* TODO: see if PROXYAUTH_AVAIL helps us here */ ++ git_cred_free(s->proxy_cred); ++ s->proxy_cred = NULL; ++ giterr_clear(); ++ error = opts->credentials(&s->proxy_cred, opts->url, NULL, GIT_CREDTYPE_USERPASS_PLAINTEXT, opts->payload); ++ if (error == GIT_PASSTHROUGH) ++ return error_no_credentials(); ++ if (error < 0) { ++ if (!giterr_last()) ++ giterr_set(GITERR_NET, "proxy authentication was aborted by the user"); ++ return error; ++ } ++ ++ if (s->proxy_cred->credtype != GIT_CREDTYPE_USERPASS_PLAINTEXT) { ++ giterr_set(GITERR_NET, "credentials callback returned invalid credential type"); ++ return -1; ++ } ++ ++ return apply_proxy_creds(s); ++} ++ ++static int curls_connect(git_stream *stream) ++{ ++ curl_stream *s = (curl_stream *) stream; ++ git_activesocket_t sockextr; ++ long connect_last = 0; ++ int failed_cert = 0, error; ++ bool retry_connect; ++ CURLcode res; ++ ++ /* Apply any credentials we've already established */ ++ error = apply_proxy_creds(s); ++ if (error < 0 && error != GIT_ENOTFOUND) ++ return seterr_curl(s); ++ ++ do { ++ retry_connect = 0; ++ res = curl_easy_perform(s->handle); ++ ++ curl_easy_getinfo(s->handle, CURLINFO_HTTP_CONNECTCODE, &connect_last); ++ ++ /* HTTP 407 Proxy Authentication Required */ ++ if (connect_last == 407) { ++ if ((error = ask_and_apply_proxy_creds(s)) < 0) ++ return error; ++ ++ retry_connect = true; ++ } ++ } while (retry_connect); ++ ++ if (res != CURLE_OK && res != CURLE_PEER_FAILED_VERIFICATION) ++ return seterr_curl(s); ++ if (res == CURLE_PEER_FAILED_VERIFICATION) ++ failed_cert = 1; ++ ++ if ((res = curl_easy_getinfo(s->handle, CURLINFO_ACTIVESOCKET, &sockextr)) != CURLE_OK) { ++ return seterr_curl(s); ++ } ++ ++ if (sockextr == GIT_CURL_BADSOCKET) { ++ giterr_set(GITERR_NET, "curl socket is no longer valid"); ++ return -1; ++ } ++ ++ s->socket = sockextr; ++ ++ if (s->parent.encrypted && failed_cert) ++ return GIT_ECERTIFICATE; ++ ++ return 0; ++} ++ ++static int curls_certificate(git_cert **out, git_stream *stream) ++{ ++ int error; ++ CURLcode res; ++ struct curl_slist *slist; ++ struct curl_certinfo *certinfo; ++ git_vector strings = GIT_VECTOR_INIT; ++ curl_stream *s = (curl_stream *) stream; ++ ++ if ((res = curl_easy_getinfo(s->handle, CURLINFO_CERTINFO, &certinfo)) != CURLE_OK) ++ return seterr_curl(s); ++ ++ /* No information is available, can happen with SecureTransport */ ++ if (certinfo->num_of_certs == 0) { ++ s->cert_info.parent.cert_type = GIT_CERT_NONE; ++ s->cert_info.data = NULL; ++ s->cert_info.len = 0; ++ return 0; ++ } ++ ++ if ((error = git_vector_init(&strings, 8, NULL)) < 0) ++ return error; ++ ++ for (slist = certinfo->certinfo[0]; slist; slist = slist->next) { ++ char *str = git__strdup(slist->data); ++ GITERR_CHECK_ALLOC(str); ++ git_vector_insert(&strings, str); ++ } ++ ++ /* Copy the contents of the vector into a strarray so we can expose them */ ++ s->cert_info_strings.strings = (char **) strings.contents; ++ s->cert_info_strings.count = strings.length; ++ ++ s->cert_info.parent.cert_type = GIT_CERT_STRARRAY; ++ s->cert_info.data = &s->cert_info_strings; ++ s->cert_info.len = strings.length; ++ ++ *out = &s->cert_info.parent; ++ ++ return 0; ++} ++ ++static int curls_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts) ++{ ++ int error; ++ CURLcode res; ++ curl_stream *s = (curl_stream *) stream; ++ ++ if ((error = git_proxy_options_dup(&s->proxy, proxy_opts)) < 0) ++ return error; ++ ++ if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXY, s->proxy.url)) != CURLE_OK) ++ return seterr_curl(s); ++ ++ if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY)) != CURLE_OK) ++ return seterr_curl(s); ++ ++ return 0; ++} ++ ++static int wait_for(curl_socket_t fd, bool reading) ++{ ++ int ret; ++ fd_set infd, outfd, errfd; ++ ++ FD_ZERO(&infd); ++ FD_ZERO(&outfd); ++ FD_ZERO(&errfd); ++ ++ assert(fd >= 0); ++ FD_SET(fd, &errfd); ++ if (reading) ++ FD_SET(fd, &infd); ++ else ++ FD_SET(fd, &outfd); ++ ++ if ((ret = select(fd + 1, &infd, &outfd, &errfd, NULL)) < 0) { ++ giterr_set(GITERR_OS, "error in select"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static ssize_t curls_write(git_stream *stream, const char *data, size_t len, int flags) ++{ ++ int error; ++ size_t off = 0, sent; ++ CURLcode res; ++ curl_stream *s = (curl_stream *) stream; ++ ++ GIT_UNUSED(flags); ++ ++ do { ++ if ((error = wait_for(s->socket, false)) < 0) ++ return error; ++ ++ res = curl_easy_send(s->handle, data + off, len - off, &sent); ++ if (res == CURLE_OK) ++ off += sent; ++ } while ((res == CURLE_OK || res == CURLE_AGAIN) && off < len); ++ ++ if (res != CURLE_OK) ++ return seterr_curl(s); ++ ++ return len; ++} ++ ++static ssize_t curls_read(git_stream *stream, void *data, size_t len) ++{ ++ int error; ++ size_t read; ++ CURLcode res; ++ curl_stream *s = (curl_stream *) stream; ++ ++ do { ++ if ((error = wait_for(s->socket, true)) < 0) ++ return error; ++ ++ res = curl_easy_recv(s->handle, data, len, &read); ++ } while (res == CURLE_AGAIN); ++ ++ if (res != CURLE_OK) ++ return seterr_curl(s); ++ ++ return read; ++} ++ ++static int curls_close(git_stream *stream) ++{ ++ curl_stream *s = (curl_stream *) stream; ++ ++ if (!s->handle) ++ return 0; ++ ++ curl_easy_cleanup(s->handle); ++ s->handle = NULL; ++ s->socket = 0; ++ ++ return 0; ++} ++ ++static void curls_free(git_stream *stream) ++{ ++ curl_stream *s = (curl_stream *) stream; ++ ++ curls_close(stream); ++ git_strarray_free(&s->cert_info_strings); ++ git__free(s); ++} ++ ++int git_curl_stream_new(git_stream **out, const char *host, const char *port) ++{ ++ curl_stream *st; ++ CURL *handle; ++ int iport = 0, error; ++ ++ st = git__calloc(1, sizeof(curl_stream)); ++ GITERR_CHECK_ALLOC(st); ++ ++ handle = curl_easy_init(); ++ if (handle == NULL) { ++ giterr_set(GITERR_NET, "failed to create curl handle"); ++ git__free(st); ++ return -1; ++ } ++ ++ if ((error = git__strtol32(&iport, port, NULL, 10)) < 0) { ++ git__free(st); ++ return error; ++ } ++ ++ curl_easy_setopt(handle, CURLOPT_URL, host); ++ curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, st->curl_error); ++ curl_easy_setopt(handle, CURLOPT_PORT, iport); ++ curl_easy_setopt(handle, CURLOPT_CONNECT_ONLY, 1); ++ curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 1); ++ curl_easy_setopt(handle, CURLOPT_CERTINFO, 1); ++ curl_easy_setopt(handle, CURLOPT_HTTPPROXYTUNNEL, 1); ++ curl_easy_setopt(handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY); ++ ++ /* curl_easy_setopt(handle, CURLOPT_VERBOSE, 1); */ ++ ++ st->parent.version = GIT_STREAM_VERSION; ++ st->parent.encrypted = 0; /* we don't encrypt ourselves */ ++ st->parent.proxy_support = 1; ++ st->parent.connect = curls_connect; ++ st->parent.certificate = curls_certificate; ++ st->parent.set_proxy = curls_set_proxy; ++ st->parent.read = curls_read; ++ st->parent.write = curls_write; ++ st->parent.close = curls_close; ++ st->parent.free = curls_free; ++ st->handle = handle; ++ ++ *out = (git_stream *) st; ++ return 0; ++} ++ ++#else ++ ++#include "stream.h" ++ ++int git_curl_stream_new(git_stream **out, const char *host, const char *port) ++{ ++ GIT_UNUSED(out); ++ GIT_UNUSED(host); ++ GIT_UNUSED(port); ++ ++ giterr_set(GITERR_NET, "curl is not supported in this version"); ++ return -1; ++} ++ ++ ++#endif +diff --git a/src/streams/curl.h b/src/streams/curl.h +new file mode 100644 +index 0000000..283f0fe +--- /dev/null ++++ b/src/streams/curl.h +@@ -0,0 +1,14 @@ ++/* ++ * Copyright (C) the libgit2 contributors. All rights reserved. ++ * ++ * This file is part of libgit2, distributed under the GNU GPL v2 with ++ * a Linking Exception. For full terms see the included COPYING file. ++ */ ++#ifndef INCLUDE_curl_stream_h__ ++#define INCLUDE_curl_stream_h__ ++ ++#include "git2/sys/stream.h" ++ ++extern int git_curl_stream_new(git_stream **out, const char *host, const char *port); ++ ++#endif +diff --git a/src/streams/mbedtls.c b/src/streams/mbedtls.c new file mode 100644 -index 0000000..98ff808 +index 0000000..0376ee4 --- /dev/null -+++ b/src/mbedtls_stream.c -@@ -0,0 +1,483 @@ ++++ b/src/streams/mbedtls.c +@@ -0,0 +1,538 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * @@ -277,26 +2709,26 @@ index 0000000..98ff808 + +#include "global.h" +#include "stream.h" -+#include "socket_stream.h" ++#include "streams/socket.h" ++#include "netops.h" +#include "git2/transport.h" ++#include "util.h" + +#ifdef GIT_CURL -+# include "curl_stream.h" ++# include "streams/curl.h" +#endif + -+#include "mbedtls/config.h" -+#include -+#include ++#include ++#include +#include -+#include "mbedtls/net.h" -+#include "mbedtls/debug.h" -+#include "mbedtls/entropy.h" -+#include "mbedtls/ctr_drbg.h" -+#include "mbedtls/error.h" -+#include "mbedtls/certs.h" ++#include ++#include + +#ifndef OPENSSLDIR -+# define OPENSSLDIR "/usr/lib/ssl" ++//# define OPENSSLDIR ++# define OPENSSLDIR "/etc/ssl" // Debian ++//# define OPENSSLDIR "/usr/lib/ssl" //Ubuntu ++//# define OPENSSLDIR "/usr/local/etc/openssl" // mostly-Darwin +#endif +#define X509_CERT_DIR OPENSSLDIR "/certs" +#define X509_CERT_FILE OPENSSLDIR "/cert.pem" @@ -306,7 +2738,8 @@ index 0000000..98ff808 +mbedtls_ssl_config *git__ssl_conf; +mbedtls_entropy_context *mbedtls_entropy; + -+#define GIT_SSL_DEFAULT_CIPHERS "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES128-SHA256:DHE-DSS-AES256-SHA256:DHE-DSS-AES128-SHA:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA" ++#define GIT_SSL_DEFAULT_CIPHERS "TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256:TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256:TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384:TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-DSS-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-DSS-WITH-AES-256-GCM-SHA384:TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256:TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256:TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA:TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA:TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384:TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384:TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA:TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-RSA-WITH-AES-128-CBC-SHA256:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256:TLS-DHE-RSA-WITH-AES-128-CBC-SHA:TLS-DHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-DSS-WITH-AES-128-CBC-SHA256:TLS-DHE-DSS-WITH-AES-256-CBC-SHA256:TLS-DHE-DSS-WITH-AES-128-CBC-SHA:TLS-DHE-DSS-WITH-AES-256-CBC-SHA:TLS-RSA-WITH-AES-128-GCM-SHA256:TLS-RSA-WITH-AES-256-GCM-SHA384:TLS-RSA-WITH-AES-128-CBC-SHA256:TLS-RSA-WITH-AES-256-CBC-SHA256:TLS-RSA-WITH-AES-128-CBC-SHA:TLS-RSA-WITH-AES-256-CBC-SHA" ++#define GIT_SSL_DEFAULT_CIPHERS_COUNT 30 + +/** + * This function aims to clean-up the SSL context which @@ -314,565 +2747,1985 @@ index 0000000..98ff808 + */ +static void shutdown_ssl(void) +{ -+ if (git__ssl_conf) { -+ mbedtls_x509_crt_free(git__ssl_conf->ca_chain); -+ git__free(git__ssl_conf->ca_chain); -+ mbedtls_ctr_drbg_free(git__ssl_conf->p_rng); -+ git__free(git__ssl_conf->p_rng); -+ mbedtls_ssl_config_free(git__ssl_conf); -+ git__free(git__ssl_conf); -+ git__ssl_conf = NULL; -+ } -+ if (mbedtls_entropy) { -+ mbedtls_entropy_free(mbedtls_entropy); -+ git__free(mbedtls_entropy); -+ mbedtls_entropy = NULL; -+ } ++ if (git__ssl_conf) { ++ mbedtls_x509_crt_free(git__ssl_conf->ca_chain); ++ git__free(git__ssl_conf->ca_chain); ++ mbedtls_ctr_drbg_free(git__ssl_conf->p_rng); ++ git__free(git__ssl_conf->p_rng); ++ mbedtls_ssl_config_free(git__ssl_conf); ++ git__free(git__ssl_conf); ++ git__ssl_conf = NULL; ++ } ++ if (mbedtls_entropy) { ++ mbedtls_entropy_free(mbedtls_entropy); ++ git__free(mbedtls_entropy); ++ mbedtls_entropy = NULL; ++ } +} + ++int git_mbedtls_set_cert_file(const char *path, int is_dir); ++ +int git_mbedtls_stream_global_init(void) +{ -+ int ret, isdir; -+ char *crtpath; -+ struct stat statbuf; -+ // const int *cipherids; -+ // const char *ciphers = git_libgit2__ssl_ciphers(); -+ -+ mbedtls_ctr_drbg_context *ctr_drbg; -+ -+ mbedtls_entropy = git__malloc(sizeof(mbedtls_entropy_context)); -+ mbedtls_entropy_init(mbedtls_entropy); -+ -+ // Seeding the random number generator -+ ctr_drbg = git__malloc(sizeof(mbedtls_ctr_drbg_context)); -+ mbedtls_ctr_drbg_init(ctr_drbg); -+ if (mbedtls_ctr_drbg_seed(ctr_drbg, -+ mbedtls_entropy_func, -+ mbedtls_entropy, NULL, 0) != 0) { -+ mbedtls_ctr_drbg_free(ctr_drbg); -+ mbedtls_entropy_free(mbedtls_entropy); -+ git__free(ctr_drbg); -+ git__free(mbedtls_entropy); -+ return -1; -+ } -+ -+ // configure TLSv1 -+ git__ssl_conf = git__malloc(sizeof(mbedtls_ssl_config)); -+ mbedtls_ssl_config_init(git__ssl_conf); -+ if ( mbedtls_ssl_config_defaults(git__ssl_conf, -+ MBEDTLS_SSL_IS_CLIENT, -+ MBEDTLS_SSL_TRANSPORT_STREAM, -+ MBEDTLS_SSL_PRESET_DEFAULT ) != 0) { -+ mbedtls_ctr_drbg_free(ctr_drbg); -+ git__free(ctr_drbg); -+ mbedtls_ssl_config_free(git__ssl_conf); -+ git__free(git__ssl_conf); -+ git__ssl_conf = NULL; -+ return -1; -+ } -+ -+ mbedtls_ssl_conf_authmode(git__ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED); -+ mbedtls_ssl_conf_rng(git__ssl_conf, mbedtls_ctr_drbg_random, ctr_drbg); -+ -+ // find locations for which CA certificates -+ isdir = 0; -+ crtpath = getenv(X509_CERT_FILE_EVP); -+ ret = crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISREG(statbuf.st_mode) ? 0 : 1; -+ if (ret) { -+ isdir = 1; -+ crtpath = getenv(X509_CERT_DIR_EVP); -+ ret = crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISDIR(statbuf.st_mode) ? 0 : 1; -+ } -+ if (ret) { -+ isdir = 0; -+ crtpath = X509_CERT_FILE; -+ ret = crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISREG(statbuf.st_mode) ? 0 : 1; -+ } -+ if (ret) { -+ isdir = 1; -+ crtpath = X509_CERT_DIR; -+ ret = crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISDIR(statbuf.st_mode) ? 0 : 1; -+ } -+ -+ // cannot find CA certificates -+ if (ret) { -+ mbedtls_ctr_drbg_free(ctr_drbg); -+ git__free(ctr_drbg); -+ mbedtls_ssl_config_free(git__ssl_conf); -+ git__free(git__ssl_conf); -+ git__ssl_conf = NULL; -+ return -1; -+ } else { -+ // set root certificates -+ mbedtls_x509_crt *cacert = git__malloc(sizeof(mbedtls_x509_crt)); -+ mbedtls_x509_crt_init(cacert); -+ if (isdir) -+ ret = mbedtls_x509_crt_parse_path(cacert, crtpath); -+ else -+ ret = mbedtls_x509_crt_parse_file(cacert, crtpath); -+ -+ if (ret) { -+ giterr_set(GITERR_SSL, "failed to load CA certificates: %d", ret); -+ mbedtls_x509_crt_free(cacert); -+ git__free(cacert); -+ mbedtls_ctr_drbg_free(ctr_drbg); -+ git__free(ctr_drbg); -+ mbedtls_ssl_config_free(git__ssl_conf); -+ git__free(git__ssl_conf); -+ git__ssl_conf = NULL; -+ return -1; -+ } else { -+ mbedtls_ssl_conf_ca_chain(git__ssl_conf, cacert, NULL); -+ } -+ } -+ -+ // set the list of allowed ciphersuites -+ // if (!ciphers) { -+ // cipherids = mbedtls_ssl_list_ciphersuites(); -+ // } -+ // mbedtls_ssl_conf_ciphersuites(git__ssl_conf, cipherids); -+ -+ git__on_shutdown(shutdown_ssl); ++ int found = -1, loaded = 0; ++ char *crtpath; ++ struct stat statbuf; ++ mbedtls_ctr_drbg_context *ctr_drbg = NULL; + -+ return 0; ++ int *ciphers_list = NULL; ++ int ciphers_known = 0; ++ char *cipher_name = NULL; ++ char *cipher_string = NULL; ++ char *cipher_string_tmp = NULL; ++ ++ mbedtls_x509_crt *cacert = NULL; ++ ++ git__ssl_conf = git__malloc(sizeof(mbedtls_ssl_config)); ++ mbedtls_ssl_config_init(git__ssl_conf); ++ if (mbedtls_ssl_config_defaults(git__ssl_conf, ++ MBEDTLS_SSL_IS_CLIENT, ++ MBEDTLS_SSL_TRANSPORT_STREAM, ++ MBEDTLS_SSL_PRESET_DEFAULT) != 0) { ++ giterr_set(GITERR_SSL, "failed to initialize mbedTLS"); ++ goto cleanup; ++ } ++ ++ // configure TLSv1 ++ mbedtls_ssl_conf_min_version(git__ssl_conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_0); ++ // verify_server_cert is responsible for making the check. ++ // OPTIONAL because REQUIRED drops the certificate as soon as the check ++ // is made, so we can never see the certificate and override it. ++ mbedtls_ssl_conf_authmode(git__ssl_conf, MBEDTLS_SSL_VERIFY_OPTIONAL); ++ ++ // set the list of allowed ciphersuites ++ ciphers_list = calloc(GIT_SSL_DEFAULT_CIPHERS_COUNT, sizeof(int)); ++ ciphers_known = 0; ++ cipher_string = cipher_string_tmp = git__strdup(GIT_SSL_DEFAULT_CIPHERS); ++ while ((cipher_name = git__strtok(&cipher_string_tmp, ":")) != NULL) { ++ int cipherid = mbedtls_ssl_get_ciphersuite_id(cipher_name); ++ if (cipherid == 0) continue; ++ ++ ciphers_list[ciphers_known++] = cipherid; ++ } ++ git__free(cipher_string); ++ ++ if (!ciphers_known) { ++ giterr_set(GITERR_SSL, "no cipher could be enabled"); ++ goto cleanup; ++ } ++ mbedtls_ssl_conf_ciphersuites(git__ssl_conf, ciphers_list); ++ ++ // Seeding the random number generator ++ mbedtls_entropy = git__malloc(sizeof(mbedtls_entropy_context)); ++ mbedtls_entropy_init(mbedtls_entropy); ++ ++ ctr_drbg = git__malloc(sizeof(mbedtls_ctr_drbg_context)); ++ mbedtls_ctr_drbg_init(ctr_drbg); ++ if (mbedtls_ctr_drbg_seed(ctr_drbg, ++ mbedtls_entropy_func, ++ mbedtls_entropy, NULL, 0) != 0) { ++ giterr_set(GITERR_SSL, "failed to initialize mbedTLS entropy pool"); ++ goto cleanup; ++ } ++ ++ mbedtls_ssl_conf_rng(git__ssl_conf, mbedtls_ctr_drbg_random, ctr_drbg); ++ ++ // find locations for which CA certificates ++ { ++ crtpath = getenv(X509_CERT_FILE_EVP); ++ found = crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISREG(statbuf.st_mode); ++ if (found) ++ loaded = (git_mbedtls_set_cert_file(crtpath, 0) == 0); ++ } ++ if (!loaded) { ++ crtpath = getenv(X509_CERT_DIR_EVP); ++ found = crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISDIR(statbuf.st_mode); ++ if (found) ++ loaded = (git_mbedtls_set_cert_file(crtpath, 1) == 0); ++ } ++ if (!loaded) { ++ crtpath = X509_CERT_FILE; ++ found = crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISREG(statbuf.st_mode); ++ if (found) ++ loaded = (git_mbedtls_set_cert_file(crtpath, 0) == 0); ++ } ++ if (!loaded) { ++ crtpath = X509_CERT_DIR; ++ found = crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISDIR(statbuf.st_mode); ++ if (found) ++ loaded = (git_mbedtls_set_cert_file(crtpath, 1) == 0); ++ } ++ ++ git__on_shutdown(shutdown_ssl); ++ ++ return 0; ++ ++cleanup: ++ mbedtls_x509_crt_free(cacert); ++ git__free(cacert); ++ mbedtls_ctr_drbg_free(ctr_drbg); ++ git__free(ctr_drbg); ++ mbedtls_ssl_config_free(git__ssl_conf); ++ git__free(git__ssl_conf); ++ git__ssl_conf = NULL; ++ ++ return -1; +} + ++mbedtls_ssl_config *git__ssl_conf; ++ +static int bio_read(void *b, unsigned char *buf, size_t len) +{ -+ git_stream *io = (git_stream *) b; -+ return (int) git_stream_read(io, buf, len); ++ git_stream *io = (git_stream *) b; ++ return (int) git_stream_read(io, buf, len); +} + +static int bio_write(void *b, const unsigned char *buf, size_t len) +{ -+ git_stream *io = (git_stream *) b; -+ return (int) git_stream_write(io, (const char *)buf, len, 0); ++ git_stream *io = (git_stream *) b; ++ return (int) git_stream_write(io, (const char *)buf, len, 0); +} + +static int ssl_set_error(mbedtls_ssl_context *ssl, int error) +{ -+ char errbuf[512]; -+ int ret = -1; ++ char errbuf[512]; ++ int ret = -1; ++ ++ assert(error != MBEDTLS_ERR_SSL_WANT_READ); ++ assert(error != MBEDTLS_ERR_SSL_WANT_WRITE); ++ ++ if (error != 0) ++ mbedtls_strerror( error, errbuf, 512 ); + -+ assert(error != MBEDTLS_ERR_SSL_WANT_READ); -+ assert(error != MBEDTLS_ERR_SSL_WANT_WRITE); ++ switch(error){ ++ case 0: ++ giterr_set(GITERR_SSL, "SSL error: unknown error"); ++ break; + -+ if (error != 0) -+ mbedtls_strerror( error, errbuf, 512 ); ++ case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED: ++ giterr_set(GITERR_SSL, "SSL error: %x[%x] - %s", error, ssl->session_negotiate->verify_result, errbuf); ++ ret = GIT_ECERTIFICATE; ++ break; + -+ switch(error){ -+ case 0: -+ giterr_set(GITERR_SSL, "SSL error: unknown error"); -+ break; -+ case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED: -+ giterr_set(GITERR_SSL, "SSL error: %x[%x] - %s", error, ssl->session_negotiate->verify_result, errbuf); -+ ret = GIT_ECERTIFICATE; -+ break; -+ default: -+ giterr_set(GITERR_SSL, "SSL error: %x - %s", error, errbuf); -+ } ++ default: ++ giterr_set(GITERR_SSL, "SSL error: %x - %s", error, errbuf); ++ } + -+ return ret; ++ return ret; +} + +static int ssl_teardown(mbedtls_ssl_context *ssl) +{ -+ int ret = 0; ++ int ret = 0; + -+ ret = mbedtls_ssl_close_notify(ssl); -+ if (ret < 0) -+ ret = ssl_set_error(ssl, ret); ++ ret = mbedtls_ssl_close_notify(ssl); ++ if (ret < 0) ++ ret = ssl_set_error(ssl, ret); + -+ mbedtls_ssl_free(ssl); -+ return ret; ++ mbedtls_ssl_free(ssl); ++ return ret; +} + +static int check_host_name(const char *name, const char *host) +{ -+ if (!strcasecmp(name, host)) -+ return 0; ++ if (!strcasecmp(name, host)) ++ return 0; + -+ if (gitno__match_host(name, host) < 0) -+ return -1; ++ if (gitno__match_host(name, host) < 0) ++ return -1; + -+ return 0; ++ return 0; +} + +static int verify_server_cert(mbedtls_ssl_context *ssl, const char *host) +{ -+ const mbedtls_x509_crt *cert; -+ const mbedtls_x509_sequence *alts; -+ int ret, matched = -1; -+ size_t sn_size = 512; -+ char subject_name[sn_size], alt_name[sn_size]; -+ -+ -+ if (( ret = mbedtls_ssl_get_verify_result(ssl) ) != 0) { -+ char vrfy_buf[512]; -+ mbedtls_x509_crt_verify_info( vrfy_buf, sizeof( vrfy_buf ), " ! ", ret ); -+ giterr_set(GITERR_SSL, "The SSL certificate is invalid: %s", vrfy_buf); -+ return GIT_ECERTIFICATE; -+ } -+ -+ cert = mbedtls_ssl_get_peer_cert(ssl); -+ if (!cert) { -+ giterr_set(GITERR_SSL, "the server did not provide a certificate"); -+ return -1; -+ } -+ -+ /* Check the alternative names */ -+ alts = &cert->subject_alt_names; -+ while (alts != NULL && matched != 1) { -+ // Buffer is too small -+ if( alts->buf.len >= sn_size ) -+ goto on_error; -+ -+ memcpy(alt_name, alts->buf.p, alts->buf.len); -+ alt_name[alts->buf.len] = '\0'; -+ -+ if (!memchr(alt_name, '\0', alts->buf.len)) { -+ if (check_host_name(alt_name, host) < 0) -+ matched = 0; -+ else -+ matched = 1; -+ } -+ -+ alts = alts->next; -+ } -+ if (matched == 0) -+ goto cert_fail_name; -+ -+ if (matched == 1) -+ return 0; -+ -+ /* If no alternative names are available, check the common name */ -+ ret = mbedtls_x509_dn_gets(subject_name, sn_size, &cert->subject); -+ if (ret == 0) -+ goto on_error; -+ if (memchr(subject_name, '\0', ret)) -+ goto cert_fail_name; -+ -+ if (check_host_name(subject_name, host) < 0) -+ goto cert_fail_name; ++ const mbedtls_x509_crt *cert; ++ const mbedtls_x509_sequence *alts; ++ int ret, matched = -1; ++ size_t sn_size = 512; ++ char subject_name[sn_size], alt_name[sn_size]; + -+ return 0; ++ ++ if ((ret = mbedtls_ssl_get_verify_result(ssl)) != 0) { ++ char vrfy_buf[512]; ++ mbedtls_x509_crt_verify_info( vrfy_buf, sizeof( vrfy_buf ), " ! ", ret ); ++ giterr_set(GITERR_SSL, "The SSL certificate is invalid: %s", vrfy_buf); ++ return GIT_ECERTIFICATE; ++ } ++ ++ cert = mbedtls_ssl_get_peer_cert(ssl); ++ if (!cert) { ++ giterr_set(GITERR_SSL, "the server did not provide a certificate"); ++ return -1; ++ } ++ ++ /* Check the alternative names */ ++ alts = &cert->subject_alt_names; ++ while (alts != NULL && matched != 1) { ++ // Buffer is too small ++ if( alts->buf.len >= sn_size ) ++ goto on_error; ++ ++ memcpy(alt_name, alts->buf.p, alts->buf.len); ++ alt_name[alts->buf.len] = '\0'; ++ ++ if (!memchr(alt_name, '\0', alts->buf.len)) { ++ if (check_host_name(alt_name, host) < 0) ++ matched = 0; ++ else ++ matched = 1; ++ } ++ ++ alts = alts->next; ++ } ++ if (matched == 0) ++ goto cert_fail_name; ++ ++ if (matched == 1) ++ return 0; ++ ++ /* If no alternative names are available, check the common name */ ++ ret = mbedtls_x509_dn_gets(subject_name, sn_size, &cert->subject); ++ if (ret == 0) ++ goto on_error; ++ if (memchr(subject_name, '\0', ret)) ++ goto cert_fail_name; ++ ++ if (check_host_name(subject_name, host) < 0) ++ goto cert_fail_name; ++ ++ return 0; + +on_error: -+ return ssl_set_error(ssl, 0); ++ return ssl_set_error(ssl, 0); + +cert_fail_name: -+ giterr_set(GITERR_SSL, "hostname does not match certificate"); -+ return GIT_ECERTIFICATE; ++ giterr_set(GITERR_SSL, "hostname does not match certificate"); ++ return GIT_ECERTIFICATE; +} + +typedef struct { -+ git_stream parent; -+ git_stream *io; -+ bool connected; -+ char *host; -+ mbedtls_ssl_context *ssl; -+ git_cert_x509 cert_info; ++ git_stream parent; ++ git_stream *io; ++ bool connected; ++ char *host; ++ mbedtls_ssl_context *ssl; ++ git_cert_x509 cert_info; +} mbedtls_stream; + + +int mbedtls_connect(git_stream *stream) +{ -+ int ret; -+ mbedtls_stream *st = (mbedtls_stream *) stream; ++ int ret; ++ mbedtls_stream *st = (mbedtls_stream *) stream; + -+ if ((ret = git_stream_connect(st->io)) < 0) -+ return ret; ++ if ((ret = git_stream_connect(st->io)) < 0) ++ return ret; + -+ st->connected = true; ++ st->connected = true; + -+ mbedtls_ssl_set_hostname(st->ssl, st->host); ++ mbedtls_ssl_set_hostname(st->ssl, st->host); + -+ mbedtls_ssl_set_bio(st->ssl, st->io, bio_write, bio_read, NULL); ++ mbedtls_ssl_set_bio(st->ssl, st->io, bio_write, bio_read, NULL); + -+ if ((ret = mbedtls_ssl_handshake(st->ssl)) != 0) -+ return ssl_set_error(st->ssl, ret); ++ if ((ret = mbedtls_ssl_handshake(st->ssl)) != 0) ++ return ssl_set_error(st->ssl, ret); + -+ return verify_server_cert(st->ssl, st->host); ++ return verify_server_cert(st->ssl, st->host); +} + +int mbedtls_certificate(git_cert **out, git_stream *stream) +{ -+ unsigned char *encoded_cert; -+ mbedtls_stream *st = (mbedtls_stream *) stream; ++ unsigned char *encoded_cert; ++ mbedtls_stream *st = (mbedtls_stream *) stream; + -+ const mbedtls_x509_crt *cert = mbedtls_ssl_get_peer_cert(st->ssl); -+ if (!cert) { -+ giterr_set(GITERR_SSL, "the server did not provide a certificate"); -+ return -1; -+ } ++ const mbedtls_x509_crt *cert = mbedtls_ssl_get_peer_cert(st->ssl); ++ if (!cert) { ++ giterr_set(GITERR_SSL, "the server did not provide a certificate"); ++ return -1; ++ } + -+ /* Retrieve the length of the certificate first */ -+ if (cert->raw.len == 0) { -+ giterr_set(GITERR_NET, "failed to retrieve certificate information"); -+ return -1; -+ } ++ /* Retrieve the length of the certificate first */ ++ if (cert->raw.len == 0) { ++ giterr_set(GITERR_NET, "failed to retrieve certificate information"); ++ return -1; ++ } + -+ encoded_cert = git__malloc(cert->raw.len); -+ GITERR_CHECK_ALLOC(encoded_cert); -+ memcpy(encoded_cert, cert->raw.p, cert->raw.len); ++ encoded_cert = git__malloc(cert->raw.len); ++ GITERR_CHECK_ALLOC(encoded_cert); ++ memcpy(encoded_cert, cert->raw.p, cert->raw.len); + -+ st->cert_info.parent.cert_type = GIT_CERT_X509; -+ st->cert_info.data = encoded_cert; -+ st->cert_info.len = cert->raw.len; ++ st->cert_info.parent.cert_type = GIT_CERT_X509; ++ st->cert_info.data = encoded_cert; ++ st->cert_info.len = cert->raw.len; + -+ *out = &st->cert_info.parent; ++ *out = &st->cert_info.parent; + -+ return 0; ++ return 0; +} + -+static int mbedtls_set_proxy(git_stream *stream, const char *proxy_url) ++static int mbedtls_set_proxy(git_stream *stream, const git_proxy_options *proxy_options) +{ -+ mbedtls_stream *st = (mbedtls_stream *) stream; ++ mbedtls_stream *st = (mbedtls_stream *) stream; + -+ return git_stream_set_proxy(st->io, proxy_url); ++ return git_stream_set_proxy(st->io, proxy_options); +} + +ssize_t mbedtls_stream_write(git_stream *stream, const char *data, size_t len, int flags) +{ -+ mbedtls_stream *st = (mbedtls_stream *) stream; -+ int ret; ++ size_t read = 0; ++ mbedtls_stream *st = (mbedtls_stream *) stream; + -+ GIT_UNUSED(flags); ++ GIT_UNUSED(flags); + -+ if ((ret = mbedtls_ssl_write(st->ssl, (const unsigned char *)data, len)) <= 0) { -+ return ssl_set_error(st->ssl, ret); -+ } ++ do { ++ int error = mbedtls_ssl_write(st->ssl, (const unsigned char *)data + read, len - read); ++ if (error <= 0) { ++ return ssl_set_error(st->ssl, error); ++ } ++ read += error; ++ } while (read < len); + -+ return ret; ++ return read; +} + +ssize_t mbedtls_stream_read(git_stream *stream, void *data, size_t len) +{ -+ mbedtls_stream *st = (mbedtls_stream *) stream; -+ int ret; ++ mbedtls_stream *st = (mbedtls_stream *) stream; ++ int ret; + -+ if ((ret = mbedtls_ssl_read(st->ssl, (unsigned char *)data, len)) <= 0) -+ ssl_set_error(st->ssl, ret); ++ if ((ret = mbedtls_ssl_read(st->ssl, (unsigned char *)data, len)) <= 0) ++ ssl_set_error(st->ssl, ret); + -+ return ret; ++ return ret; +} + +int mbedtls_stream_close(git_stream *stream) +{ -+ mbedtls_stream *st = (mbedtls_stream *) stream; -+ int ret = 0; ++ mbedtls_stream *st = (mbedtls_stream *) stream; ++ int ret = 0; + -+ if (st->connected && (ret = ssl_teardown(st->ssl)) != 0) -+ return -1; ++ if (st->connected && (ret = ssl_teardown(st->ssl)) != 0) ++ return -1; + -+ st->connected = false; ++ st->connected = false; + -+ return git_stream_close(st->io); ++ return git_stream_close(st->io); +} + +void mbedtls_stream_free(git_stream *stream) +{ -+ mbedtls_stream *st = (mbedtls_stream *) stream; ++ mbedtls_stream *st = (mbedtls_stream *) stream; + -+ git__free(st->host); -+ git__free(st->cert_info.data); -+ git_stream_free(st->io); -+ git__free(st->ssl); -+ git__free(st); ++ git__free(st->host); ++ git__free(st->cert_info.data); ++ git_stream_free(st->io); ++ git__free(st->ssl); ++ git__free(st); +} + +int git_mbedtls_stream_new(git_stream **out, const char *host, const char *port) +{ -+ int error; -+ mbedtls_stream *st; ++ int error; ++ mbedtls_stream *st; + -+ st = git__calloc(1, sizeof(mbedtls_stream)); -+ GITERR_CHECK_ALLOC(st); ++ st = git__calloc(1, sizeof(mbedtls_stream)); ++ GITERR_CHECK_ALLOC(st); + +#ifdef GIT_CURL -+ error = git_curl_stream_new(&st->io, host, port); ++ error = git_curl_stream_new(&st->io, host, port); +#else -+ error = git_socket_stream_new(&st->io, host, port); ++ error = git_socket_stream_new(&st->io, host, port); +#endif + -+ if (error < 0) -+ return error; -+ -+ st->ssl = git__malloc(sizeof(mbedtls_ssl_context)); -+ GITERR_CHECK_ALLOC(st->ssl); -+ mbedtls_ssl_init(st->ssl); -+ if( (error = mbedtls_ssl_setup(st->ssl, git__ssl_conf)) != 0 ) { -+ mbedtls_ssl_free(st->ssl); -+ giterr_set(GITERR_SSL, "failed to create ssl object"); -+ return -1; -+ } -+ -+ st->host = git__strdup(host); -+ GITERR_CHECK_ALLOC(st->host); -+ -+ st->parent.version = GIT_STREAM_VERSION; -+ st->parent.encrypted = 1; -+ st->parent.proxy_support = git_stream_supports_proxy(st->io); -+ st->parent.connect = mbedtls_connect; -+ st->parent.certificate = mbedtls_certificate; -+ st->parent.set_proxy = mbedtls_set_proxy; -+ st->parent.read = mbedtls_stream_read; -+ st->parent.write = mbedtls_stream_write; -+ st->parent.close = mbedtls_stream_close; -+ st->parent.free = mbedtls_stream_free; -+ -+ *out = (git_stream *) st; -+ return 0; -+} ++ if (error < 0) ++ goto out_err; + -+#else ++ st->ssl = git__malloc(sizeof(mbedtls_ssl_context)); ++ GITERR_CHECK_ALLOC(st->ssl); ++ mbedtls_ssl_init(st->ssl); ++ if (mbedtls_ssl_setup(st->ssl, git__ssl_conf)) { ++ giterr_set(GITERR_SSL, "failed to create ssl object"); ++ error = -1; ++ goto out_err; ++ } + -+#include "stream.h" -+#include "git2/sys/openssl.h" ++ st->host = git__strdup(host); ++ GITERR_CHECK_ALLOC(st->host); ++ ++ st->parent.version = GIT_STREAM_VERSION; ++ st->parent.encrypted = 1; ++ st->parent.proxy_support = git_stream_supports_proxy(st->io); ++ st->parent.connect = mbedtls_connect; ++ st->parent.certificate = mbedtls_certificate; ++ st->parent.set_proxy = mbedtls_set_proxy; ++ st->parent.read = mbedtls_stream_read; ++ st->parent.write = mbedtls_stream_write; ++ st->parent.close = mbedtls_stream_close; ++ st->parent.free = mbedtls_stream_free; ++ ++ *out = (git_stream *) st; ++ return 0; ++ ++out_err: ++ mbedtls_ssl_free(st->ssl); ++ git_stream_free(st->io); ++ git__free(st); ++ ++ return error; ++} ++ ++int git_mbedtls_set_cert_file(const char *path, int is_dir) ++{ ++ int ret = 0; ++ char errbuf[512]; ++ mbedtls_x509_crt *cacert; ++ ++ assert(path != NULL); ++ ++ cacert = git__malloc(sizeof(mbedtls_x509_crt)); ++ mbedtls_x509_crt_init(cacert); ++ if (is_dir) { ++ ret = mbedtls_x509_crt_parse_path(cacert, path); ++ } else { ++ ret = mbedtls_x509_crt_parse_file(cacert, path); ++ } ++ // mbedtls_x509_crt_parse_path returns the number of invalid certs on success ++ if (ret <= 0) { ++ mbedtls_x509_crt_free(cacert); ++ git__free(cacert); ++ mbedtls_strerror( ret, errbuf, 512 ); ++ giterr_set(GITERR_SSL, "failed to load CA certificates : %s (%d)", errbuf, ret); ++ return -1; ++ } ++ ++ mbedtls_x509_crt_free(git__ssl_conf->ca_chain); ++ git__free(git__ssl_conf->ca_chain); ++ mbedtls_ssl_conf_ca_chain(git__ssl_conf, cacert, NULL); ++ ++ return 0; ++} ++ ++#else ++ ++#include "stream.h" + +int git_mbedtls_stream_global_init(void) +{ -+ return 0; ++ return 0; ++} ++ ++int git_mbedtls_stream_new(git_stream **out, const char *host, const char *port) ++{ ++ GIT_UNUSED(out); ++ GIT_UNUSED(host); ++ GIT_UNUSED(port); ++ ++ giterr_set(GITERR_SSL, "mbedTLS is not supported in this version"); ++ return -1; ++} ++ ++int git_mbedtls_set_cert_file(const char *path, int is_dir) ++{ ++ GIT_UNUSED(is_dir); ++ ++ giterr_set(GITERR_SSL, "mbedTLS is not supported in this version"); ++ return -1; ++} ++ ++#endif +diff --git a/src/streams/mbedtls.h b/src/streams/mbedtls.h +new file mode 100644 +index 0000000..7501397 +--- /dev/null ++++ b/src/streams/mbedtls.h +@@ -0,0 +1,18 @@ ++/* ++ * Copyright (C) the libgit2 contributors. All rights reserved. ++ * ++ * This file is part of libgit2, distributed under the GNU GPL v2 with ++ * a Linking Exception. For full terms see the included COPYING file. ++ */ ++#ifndef INCLUDE_mbedtls_stream_h__ ++#define INCLUDE_mbedtls_stream_h__ ++ ++#include "git2/sys/stream.h" ++ ++extern int git_mbedtls_stream_global_init(void); ++ ++extern int git_mbedtls_stream_new(git_stream **out, const char *host, const char *port); ++ ++extern int git_mbedtls_set_cert_file(const char *path, int is_dir); ++ ++#endif +diff --git a/src/streams/openssl.c b/src/streams/openssl.c +new file mode 100644 +index 0000000..8668b78 +--- /dev/null ++++ b/src/streams/openssl.c +@@ -0,0 +1,675 @@ ++/* ++ * Copyright (C) the libgit2 contributors. All rights reserved. ++ * ++ * This file is part of libgit2, distributed under the GNU GPL v2 with ++ * a Linking Exception. For full terms see the included COPYING file. ++ */ ++ ++#ifdef GIT_OPENSSL ++ ++#include ++ ++#include "global.h" ++#include "posix.h" ++#include "stream.h" ++#include "streams/socket.h" ++#include "streams/openssl.h" ++#include "netops.h" ++#include "git2/transport.h" ++#include "git2/sys/openssl.h" ++ ++#ifdef GIT_CURL ++# include "streams/curl.h" ++#endif ++ ++#ifndef GIT_WIN32 ++# include ++# include ++# include ++#endif ++ ++#include ++#include ++#include ++#include ++ ++SSL_CTX *git__ssl_ctx; ++ ++#define GIT_SSL_DEFAULT_CIPHERS "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES128-SHA256:DHE-DSS-AES256-SHA256:DHE-DSS-AES128-SHA:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA" ++ ++#if defined(GIT_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L ++ ++static git_mutex *openssl_locks; ++ ++static void openssl_locking_function( ++ int mode, int n, const char *file, int line) ++{ ++ int lock; ++ ++ GIT_UNUSED(file); ++ GIT_UNUSED(line); ++ ++ lock = mode & CRYPTO_LOCK; ++ ++ if (lock) { ++ git_mutex_lock(&openssl_locks[n]); ++ } else { ++ git_mutex_unlock(&openssl_locks[n]); ++ } ++} ++ ++static void shutdown_ssl_locking(void) ++{ ++ int num_locks, i; ++ ++ num_locks = CRYPTO_num_locks(); ++ CRYPTO_set_locking_callback(NULL); ++ ++ for (i = 0; i < num_locks; ++i) ++ git_mutex_free(&openssl_locks[i]); ++ git__free(openssl_locks); ++} ++ ++#endif /* GIT_THREADS && OPENSSL_VERSION_NUMBER < 0x10100000L */ ++ ++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; ++ } ++} ++ ++int git_openssl_stream_global_init(void) ++{ ++#ifdef GIT_OPENSSL ++ long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; ++ const char *ciphers = git_libgit2__ssl_ciphers(); ++ ++ /* Older OpenSSL and MacOS OpenSSL doesn't have this */ ++#ifdef SSL_OP_NO_COMPRESSION ++ ssl_opts |= SSL_OP_NO_COMPRESSION; ++#endif ++ ++#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) ++ SSL_load_error_strings(); ++ OpenSSL_add_ssl_algorithms(); ++#else ++ OPENSSL_init_ssl(0, NULL); ++#endif ++ ++ /* ++ * Load SSLv{2,3} and TLSv1 so that we can talk with servers ++ * which use the SSL hellos, which are often used for ++ * compatibility. We then disable SSL so we only allow OpenSSL ++ * to speak TLSv1 to perform the encryption itself. ++ */ ++ git__ssl_ctx = SSL_CTX_new(SSLv23_method()); ++ SSL_CTX_set_options(git__ssl_ctx, ssl_opts); ++ SSL_CTX_set_mode(git__ssl_ctx, SSL_MODE_AUTO_RETRY); ++ SSL_CTX_set_verify(git__ssl_ctx, SSL_VERIFY_NONE, NULL); ++ if (!SSL_CTX_set_default_verify_paths(git__ssl_ctx)) { ++ SSL_CTX_free(git__ssl_ctx); ++ git__ssl_ctx = NULL; ++ return -1; ++ } ++ ++ if (!ciphers) { ++ ciphers = GIT_SSL_DEFAULT_CIPHERS; ++ } ++ ++ if(!SSL_CTX_set_cipher_list(git__ssl_ctx, ciphers)) { ++ SSL_CTX_free(git__ssl_ctx); ++ 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); ++ ++ return 0; ++} ++ ++int git_openssl_set_locking(void) ++{ ++#if defined(GIT_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L ++ int num_locks, i; ++ ++ num_locks = CRYPTO_num_locks(); ++ openssl_locks = git__calloc(num_locks, sizeof(git_mutex)); ++ GITERR_CHECK_ALLOC(openssl_locks); ++ ++ for (i = 0; i < num_locks; i++) { ++ if (git_mutex_init(&openssl_locks[i]) != 0) { ++ giterr_set(GITERR_SSL, "failed to initialize openssl locks"); ++ return -1; ++ } ++ } ++ ++ CRYPTO_set_locking_callback(openssl_locking_function); ++ git__on_shutdown(shutdown_ssl_locking); ++ return 0; ++#elif OPENSSL_VERSION_NUMBER >= 0x10100000L ++ return 0; ++#else ++ giterr_set(GITERR_THREAD, "libgit2 was not built with threads"); ++ return -1; ++#endif ++} ++ ++ ++static int bio_create(BIO *b) ++{ ++ BIO_set_init(b, 1); ++ BIO_set_data(b, NULL); ++ ++ return 1; ++} ++ ++static int bio_destroy(BIO *b) ++{ ++ if (!b) ++ return 0; ++ ++ BIO_set_data(b, NULL); ++ ++ return 1; ++} ++ ++static int bio_read(BIO *b, char *buf, int len) ++{ ++ git_stream *io = (git_stream *) BIO_get_data(b); ++ ++ return (int) git_stream_read(io, buf, len); ++} ++ ++static int bio_write(BIO *b, const char *buf, int len) ++{ ++ git_stream *io = (git_stream *) BIO_get_data(b); ++ ++ return (int) git_stream_write(io, buf, len, 0); ++} ++ ++static long bio_ctrl(BIO *b, int cmd, long num, void *ptr) ++{ ++ GIT_UNUSED(b); ++ GIT_UNUSED(num); ++ GIT_UNUSED(ptr); ++ ++ if (cmd == BIO_CTRL_FLUSH) ++ return 1; ++ ++ return 0; ++} ++ ++static int bio_gets(BIO *b, char *buf, int len) ++{ ++ GIT_UNUSED(b); ++ GIT_UNUSED(buf); ++ GIT_UNUSED(len); ++ return -1; ++} ++ ++static int bio_puts(BIO *b, const char *str) ++{ ++ return bio_write(b, str, strlen(str)); ++} ++ ++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) ++{ ++ int err; ++ unsigned long e; ++ ++ err = SSL_get_error(ssl, error); ++ ++ assert(err != SSL_ERROR_WANT_READ); ++ assert(err != SSL_ERROR_WANT_WRITE); ++ ++ switch (err) { ++ case SSL_ERROR_WANT_CONNECT: ++ case SSL_ERROR_WANT_ACCEPT: ++ giterr_set(GITERR_NET, "SSL error: connection failure"); ++ break; ++ case SSL_ERROR_WANT_X509_LOOKUP: ++ giterr_set(GITERR_NET, "SSL error: x509 error"); ++ break; ++ case SSL_ERROR_SYSCALL: ++ e = ERR_get_error(); ++ if (e > 0) { ++ giterr_set(GITERR_NET, "SSL error: %s", ++ ERR_error_string(e, NULL)); ++ break; ++ } else if (error < 0) { ++ giterr_set(GITERR_OS, "SSL error: syscall failure"); ++ break; ++ } ++ giterr_set(GITERR_NET, "SSL error: received early EOF"); ++ return GIT_EEOF; ++ break; ++ case SSL_ERROR_SSL: ++ e = ERR_get_error(); ++ giterr_set(GITERR_NET, "SSL error: %s", ++ ERR_error_string(e, NULL)); ++ break; ++ case SSL_ERROR_NONE: ++ case SSL_ERROR_ZERO_RETURN: ++ default: ++ giterr_set(GITERR_NET, "SSL error: unknown error"); ++ break; ++ } ++ return -1; ++} ++ ++static int ssl_teardown(SSL *ssl) ++{ ++ int ret; ++ ++ ret = SSL_shutdown(ssl); ++ if (ret < 0) ++ ret = ssl_set_error(ssl, ret); ++ else ++ ret = 0; ++ ++ return ret; ++} ++ ++static int check_host_name(const char *name, const char *host) ++{ ++ if (!strcasecmp(name, host)) ++ return 0; ++ ++ if (gitno__match_host(name, host) < 0) ++ return -1; ++ ++ return 0; ++} ++ ++static int verify_server_cert(SSL *ssl, const char *host) ++{ ++ X509 *cert; ++ X509_NAME *peer_name; ++ ASN1_STRING *str; ++ unsigned char *peer_cn = NULL; ++ int matched = -1, type = GEN_DNS; ++ GENERAL_NAMES *alts; ++ struct in6_addr addr6; ++ struct in_addr addr4; ++ void *addr; ++ int i = -1,j; ++ ++ if (SSL_get_verify_result(ssl) != X509_V_OK) { ++ giterr_set(GITERR_SSL, "the SSL certificate is invalid"); ++ return GIT_ECERTIFICATE; ++ } ++ ++ /* Try to parse the host as an IP address to see if it is */ ++ if (p_inet_pton(AF_INET, host, &addr4)) { ++ type = GEN_IPADD; ++ addr = &addr4; ++ } else { ++ if(p_inet_pton(AF_INET6, host, &addr6)) { ++ type = GEN_IPADD; ++ addr = &addr6; ++ } ++ } ++ ++ ++ cert = SSL_get_peer_certificate(ssl); ++ if (!cert) { ++ giterr_set(GITERR_SSL, "the server did not provide a certificate"); ++ return -1; ++ } ++ ++ /* Check the alternative names */ ++ alts = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); ++ if (alts) { ++ int num; ++ ++ 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_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 */ ++ if (gn->type != type) ++ continue; ++ ++ if (type == GEN_DNS) { ++ /* If it contains embedded NULs, don't even try */ ++ if (memchr(name, '\0', namelen)) ++ continue; ++ ++ if (check_host_name(name, host) < 0) ++ matched = 0; ++ else ++ matched = 1; ++ } else if (type == GEN_IPADD) { ++ /* Here name isn't so much a name but a binary representation of the IP */ ++ matched = !!memcmp(name, addr, namelen); ++ } ++ } ++ } ++ GENERAL_NAMES_free(alts); ++ ++ if (matched == 0) ++ goto cert_fail_name; ++ ++ if (matched == 1) ++ return 0; ++ ++ /* If no alternative names are available, check the common name */ ++ peer_name = X509_get_subject_name(cert); ++ if (peer_name == NULL) ++ goto on_error; ++ ++ if (peer_name) { ++ /* Get the index of the last CN entry */ ++ while ((j = X509_NAME_get_index_by_NID(peer_name, NID_commonName, i)) >= 0) ++ i = j; ++ } ++ ++ if (i < 0) ++ goto on_error; ++ ++ str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(peer_name, i)); ++ if (str == NULL) ++ goto on_error; ++ ++ /* Work around a bug in OpenSSL whereby ASN1_STRING_to_UTF8 fails if it's already in utf-8 */ ++ if (ASN1_STRING_type(str) == V_ASN1_UTF8STRING) { ++ int size = ASN1_STRING_length(str); ++ ++ if (size > 0) { ++ peer_cn = OPENSSL_malloc(size + 1); ++ GITERR_CHECK_ALLOC(peer_cn); ++ memcpy(peer_cn, ASN1_STRING_get0_data(str), size); ++ peer_cn[size] = '\0'; ++ } else { ++ goto cert_fail_name; ++ } ++ } else { ++ int size = ASN1_STRING_to_UTF8(&peer_cn, str); ++ GITERR_CHECK_ALLOC(peer_cn); ++ if (memchr(peer_cn, '\0', size)) ++ goto cert_fail_name; ++ } ++ ++ if (check_host_name((char *)peer_cn, host) < 0) ++ goto cert_fail_name; ++ ++ OPENSSL_free(peer_cn); ++ ++ return 0; ++ ++on_error: ++ OPENSSL_free(peer_cn); ++ return ssl_set_error(ssl, 0); ++ ++cert_fail_name: ++ OPENSSL_free(peer_cn); ++ giterr_set(GITERR_SSL, "hostname does not match certificate"); ++ return GIT_ECERTIFICATE; ++} ++ ++typedef struct { ++ git_stream parent; ++ git_stream *io; ++ bool connected; ++ char *host; ++ SSL *ssl; ++ git_cert_x509 cert_info; ++} openssl_stream; ++ ++int openssl_close(git_stream *stream); ++ ++int openssl_connect(git_stream *stream) ++{ ++ int ret; ++ BIO *bio; ++ openssl_stream *st = (openssl_stream *) stream; ++ ++ if ((ret = git_stream_connect(st->io)) < 0) ++ return ret; ++ ++ st->connected = true; ++ ++ bio = BIO_new(git_stream_bio_method); ++ GITERR_CHECK_ALLOC(bio); ++ ++ 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); ++#endif ++ ++ if ((ret = SSL_connect(st->ssl)) <= 0) ++ return ssl_set_error(st->ssl, ret); ++ ++ return verify_server_cert(st->ssl, st->host); ++} ++ ++int openssl_certificate(git_cert **out, git_stream *stream) ++{ ++ openssl_stream *st = (openssl_stream *) stream; ++ int len; ++ X509 *cert = SSL_get_peer_certificate(st->ssl); ++ unsigned char *guard, *encoded_cert; ++ ++ /* Retrieve the length of the certificate first */ ++ len = i2d_X509(cert, NULL); ++ if (len < 0) { ++ giterr_set(GITERR_NET, "failed to retrieve certificate information"); ++ return -1; ++ } ++ ++ encoded_cert = git__malloc(len); ++ GITERR_CHECK_ALLOC(encoded_cert); ++ /* i2d_X509 makes 'guard' point to just after the data */ ++ guard = encoded_cert; ++ ++ len = i2d_X509(cert, &guard); ++ if (len < 0) { ++ git__free(encoded_cert); ++ giterr_set(GITERR_NET, "failed to retrieve certificate information"); ++ return -1; ++ } ++ ++ st->cert_info.parent.cert_type = GIT_CERT_X509; ++ st->cert_info.data = encoded_cert; ++ st->cert_info.len = len; ++ ++ *out = &st->cert_info.parent; ++ ++ return 0; ++} ++ ++static int openssl_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts) ++{ ++ openssl_stream *st = (openssl_stream *) stream; ++ ++ return git_stream_set_proxy(st->io, proxy_opts); ++} ++ ++ssize_t openssl_write(git_stream *stream, const char *data, size_t len, int flags) ++{ ++ openssl_stream *st = (openssl_stream *) stream; ++ int ret; ++ ++ GIT_UNUSED(flags); ++ ++ if ((ret = SSL_write(st->ssl, data, len)) <= 0) { ++ return ssl_set_error(st->ssl, ret); ++ } ++ ++ return ret; ++} ++ ++ssize_t openssl_read(git_stream *stream, void *data, size_t len) ++{ ++ openssl_stream *st = (openssl_stream *) stream; ++ int ret; ++ ++ if ((ret = SSL_read(st->ssl, data, len)) <= 0) ++ return ssl_set_error(st->ssl, ret); ++ ++ return ret; ++} ++ ++int openssl_close(git_stream *stream) ++{ ++ openssl_stream *st = (openssl_stream *) stream; ++ int ret; ++ ++ if (st->connected && (ret = ssl_teardown(st->ssl)) < 0) ++ return -1; ++ ++ st->connected = false; ++ ++ return git_stream_close(st->io); ++} ++ ++void openssl_free(git_stream *stream) ++{ ++ openssl_stream *st = (openssl_stream *) stream; ++ ++ SSL_free(st->ssl); ++ git__free(st->host); ++ git__free(st->cert_info.data); ++ git_stream_free(st->io); ++ git__free(st); ++} ++ ++int git_openssl_stream_new(git_stream **out, const char *host, const char *port) ++{ ++ int error; ++ openssl_stream *st; ++ ++ st = git__calloc(1, sizeof(openssl_stream)); ++ GITERR_CHECK_ALLOC(st); ++ ++ st->io = NULL; ++#ifdef GIT_CURL ++ error = git_curl_stream_new(&st->io, host, port); ++#else ++ error = git_socket_stream_new(&st->io, host, port); ++#endif ++ ++ if (error < 0) ++ goto out_err; ++ ++ st->ssl = SSL_new(git__ssl_ctx); ++ if (st->ssl == NULL) { ++ giterr_set(GITERR_SSL, "failed to create ssl object"); ++ error = -1; ++ goto out_err; ++ } ++ ++ st->host = git__strdup(host); ++ GITERR_CHECK_ALLOC(st->host); ++ ++ st->parent.version = GIT_STREAM_VERSION; ++ st->parent.encrypted = 1; ++ st->parent.proxy_support = git_stream_supports_proxy(st->io); ++ st->parent.connect = openssl_connect; ++ st->parent.certificate = openssl_certificate; ++ st->parent.set_proxy = openssl_set_proxy; ++ st->parent.read = openssl_read; ++ st->parent.write = openssl_write; ++ st->parent.close = openssl_close; ++ st->parent.free = openssl_free; ++ ++ *out = (git_stream *) st; ++ return 0; ++ ++out_err: ++ git_stream_free(st->io); ++ git__free(st); ++ ++ return error; ++} ++ ++int git_openssl_set_cert_file(const char *file, const char *path) ++{ ++ if (SSL_CTX_load_verify_locations(git__ssl_ctx, file, path) == 0) { ++ giterr_set(GITERR_SSL, "SSL error: %s", ++ ERR_error_string(ERR_get_error(), NULL)); ++ return -1; ++ } ++ return 0; ++} ++ ++#else ++ ++#include "stream.h" ++#include "git2/sys/openssl.h" ++ ++int git_openssl_stream_global_init(void) ++{ ++ return 0; ++} ++ ++int git_openssl_set_locking(void) ++{ ++ giterr_set(GITERR_SSL, "libgit2 was not built with OpenSSL support"); ++ return -1; ++} ++ ++int git_openssl_stream_new(git_stream **out, const char *host, const char *port) ++{ ++ GIT_UNUSED(out); ++ GIT_UNUSED(host); ++ GIT_UNUSED(port); ++ ++ giterr_set(GITERR_SSL, "openssl is not supported in this version"); ++ return -1; ++} ++ ++int git_openssl_set_ca_location(const char *file, const char *path) ++{ ++ GIT_UNUSED(file); ++ GIT_UNUSED(path); ++ ++ giterr_set(GITERR_SSL, "openssl is not supported in this version"); ++ return -1; ++} ++ ++#endif +diff --git a/src/streams/openssl.h b/src/streams/openssl.h +new file mode 100644 +index 0000000..8c7a84d +--- /dev/null ++++ b/src/streams/openssl.h +@@ -0,0 +1,124 @@ ++/* ++ * Copyright (C) the libgit2 contributors. All rights reserved. ++ * ++ * This file is part of libgit2, distributed under the GNU GPL v2 with ++ * a Linking Exception. For full terms see the included COPYING file. ++ */ ++#ifndef INCLUDE_openssl_stream_h__ ++#define INCLUDE_openssl_stream_h__ ++ ++#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); ++ ++extern int git_openssl_set_cert_file(const char *file, const char *path); ++ ++/* ++ * 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. ++ */ ++#ifdef GIT_OPENSSL ++# include ++# include ++# include ++# include ++ ++ ++ ++# if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) ++ ++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; +} + -+int git_mbedtls_stream_new(git_stream **out, const char *host, const char *port) ++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; ++} ++ ++GIT_INLINE(const unsigned char *) ASN1_STRING_get0_data(const ASN1_STRING *x) ++{ ++ return ASN1_STRING_data((ASN1_STRING *)x); ++} ++ ++# endif // OpenSSL < 1.1 ++#endif // GIT_OPENSSL ++ ++#endif +diff --git a/src/streams/socket.c b/src/streams/socket.c +new file mode 100644 +index 0000000..1150b40 +--- /dev/null ++++ b/src/streams/socket.c +@@ -0,0 +1,210 @@ ++/* ++ * Copyright (C) the libgit2 contributors. All rights reserved. ++ * ++ * This file is part of libgit2, distributed under the GNU GPL v2 with ++ * a Linking Exception. For full terms see the included COPYING file. ++ */ ++ ++#include "common.h" ++#include "posix.h" ++#include "netops.h" ++#include "stream.h" ++#include "streams/socket.h" ++ ++#ifndef _WIN32 ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++#else ++# include ++# include ++# ifdef _MSC_VER ++# pragma comment(lib, "ws2_32") ++# endif ++#endif ++ ++#ifdef GIT_WIN32 ++static void net_set_error(const char *str) ++{ ++ int error = WSAGetLastError(); ++ char * win32_error = git_win32_get_error_message(error); ++ ++ if (win32_error) { ++ giterr_set(GITERR_NET, "%s: %s", str, win32_error); ++ git__free(win32_error); ++ } else { ++ giterr_set(GITERR_NET, str); ++ } ++} ++#else ++static void net_set_error(const char *str) +{ -+ GIT_UNUSED(out); -+ GIT_UNUSED(host); -+ GIT_UNUSED(port); ++ giterr_set(GITERR_NET, "%s: %s", str, strerror(errno)); ++} ++#endif ++ ++static int close_socket(GIT_SOCKET s) ++{ ++ if (s == INVALID_SOCKET) ++ return 0; ++ ++#ifdef GIT_WIN32 ++ if (SOCKET_ERROR == closesocket(s)) ++ return -1; ++ ++ if (0 != WSACleanup()) { ++ giterr_set(GITERR_OS, "winsock cleanup failed"); ++ return -1; ++ } ++ ++ return 0; ++#else ++ return close(s); ++#endif + -+ giterr_set(GITERR_SSL, "mbedTLS is not supported in this version"); -+ return -1; +} + ++int socket_connect(git_stream *stream) ++{ ++ struct addrinfo *info = NULL, *p; ++ struct addrinfo hints; ++ git_socket_stream *st = (git_socket_stream *) stream; ++ GIT_SOCKET s = INVALID_SOCKET; ++ int ret; ++ ++#ifdef GIT_WIN32 ++ /* on win32, the WSA context needs to be initialized ++ * before any socket calls can be performed */ ++ WSADATA wsd; ++ ++ if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) { ++ giterr_set(GITERR_OS, "winsock init failed"); ++ return -1; ++ } ++ ++ if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) { ++ WSACleanup(); ++ giterr_set(GITERR_OS, "winsock init failed"); ++ return -1; ++ } +#endif + -diff --git a/src/mbedtls_stream.h b/src/mbedtls_stream.h ++ memset(&hints, 0x0, sizeof(struct addrinfo)); ++ hints.ai_socktype = SOCK_STREAM; ++ hints.ai_family = AF_UNSPEC; ++ ++ if ((ret = p_getaddrinfo(st->host, st->port, &hints, &info)) != 0) { ++ giterr_set(GITERR_NET, ++ "failed to resolve address for %s: %s", st->host, p_gai_strerror(ret)); ++ return -1; ++ } ++ ++ for (p = info; p != NULL; p = p->ai_next) { ++ s = socket(p->ai_family, p->ai_socktype, p->ai_protocol); ++ ++ if (s == INVALID_SOCKET) ++ continue; ++ ++ if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0) ++ break; ++ ++ /* If we can't connect, try the next one */ ++ close_socket(s); ++ s = INVALID_SOCKET; ++ } ++ ++ /* Oops, we couldn't connect to any address */ ++ if (s == INVALID_SOCKET && p == NULL) { ++ giterr_set(GITERR_OS, "failed to connect to %s", st->host); ++ p_freeaddrinfo(info); ++ return -1; ++ } ++ ++ st->s = s; ++ p_freeaddrinfo(info); ++ return 0; ++} ++ ++ssize_t socket_write(git_stream *stream, const char *data, size_t len, int flags) ++{ ++ ssize_t ret; ++ size_t off = 0; ++ git_socket_stream *st = (git_socket_stream *) stream; ++ ++ while (off < len) { ++ errno = 0; ++ ret = p_send(st->s, data + off, len - off, flags); ++ if (ret < 0) { ++ net_set_error("Error sending data"); ++ return -1; ++ } ++ ++ off += ret; ++ } ++ ++ return off; ++} ++ ++ssize_t socket_read(git_stream *stream, void *data, size_t len) ++{ ++ ssize_t ret; ++ git_socket_stream *st = (git_socket_stream *) stream; ++ ++ if ((ret = p_recv(st->s, data, len, 0)) < 0) ++ net_set_error("Error receiving socket data"); ++ ++ return ret; ++} ++ ++int socket_close(git_stream *stream) ++{ ++ git_socket_stream *st = (git_socket_stream *) stream; ++ int error; ++ ++ error = close_socket(st->s); ++ st->s = INVALID_SOCKET; ++ ++ return error; ++} ++ ++void socket_free(git_stream *stream) ++{ ++ git_socket_stream *st = (git_socket_stream *) stream; ++ ++ git__free(st->host); ++ git__free(st->port); ++ git__free(st); ++} ++ ++int git_socket_stream_new(git_stream **out, const char *host, const char *port) ++{ ++ git_socket_stream *st; ++ ++ assert(out && host); ++ ++ st = git__calloc(1, sizeof(git_socket_stream)); ++ GITERR_CHECK_ALLOC(st); ++ ++ st->host = git__strdup(host); ++ GITERR_CHECK_ALLOC(st->host); ++ ++ if (port) { ++ st->port = git__strdup(port); ++ GITERR_CHECK_ALLOC(st->port); ++ } ++ ++ st->parent.version = GIT_STREAM_VERSION; ++ st->parent.connect = socket_connect; ++ st->parent.write = socket_write; ++ st->parent.read = socket_read; ++ st->parent.close = socket_close; ++ st->parent.free = socket_free; ++ st->s = INVALID_SOCKET; ++ ++ *out = (git_stream *) st; ++ return 0; ++} +diff --git a/src/streams/socket.h b/src/streams/socket.h new file mode 100644 -index 0000000..5cb1071 +index 0000000..8e9949f --- /dev/null -+++ b/src/mbedtls_stream.h -@@ -0,0 +1,16 @@ ++++ b/src/streams/socket.h +@@ -0,0 +1,21 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ -+#ifndef INCLUDE_mbedtls_stream_h__ -+#define INCLUDE_mbedtls_stream_h__ ++#ifndef INCLUDE_socket_stream_h__ ++#define INCLUDE_socket_stream_h__ + -+#include "git2/sys/stream.h" ++#include "netops.h" + -+extern int git_mbedtls_stream_global_init(void); ++typedef struct { ++ git_stream parent; ++ char *host; ++ char *port; ++ GIT_SOCKET s; ++} git_socket_stream; + -+extern int git_mbedtls_stream_new(git_stream **out, const char *host, const char *port); ++extern int git_socket_stream_new(git_stream **out, const char *host, const char *port); + +#endif -diff --git a/src/settings.c b/src/settings.c -index 0da19ea..65bbb41 100644 ---- a/src/settings.c -+++ b/src/settings.c -@@ -9,6 +9,10 @@ - # include - #endif - -+#ifdef GIT_MBEDTLS -+# include +diff --git a/src/streams/stransport.c b/src/streams/stransport.c +new file mode 100644 +index 0000000..4c099f9 +--- /dev/null ++++ b/src/streams/stransport.c +@@ -0,0 +1,294 @@ ++/* ++ * Copyright (C) the libgit2 contributors. All rights reserved. ++ * ++ * This file is part of libgit2, distributed under the GNU GPL v2 with ++ * a Linking Exception. For full terms see the included COPYING file. ++ */ ++ ++#ifdef GIT_SECURE_TRANSPORT ++ ++#include ++#include ++#include ++ ++#include "git2/transport.h" ++ ++#include "streams/socket.h" ++#include "streams/curl.h" ++ ++static int stransport_error(OSStatus ret) ++{ ++ CFStringRef message; ++ ++ if (ret == noErr || ret == errSSLClosedGraceful) { ++ giterr_clear(); ++ return 0; ++ } ++ ++#if !TARGET_OS_IPHONE ++ message = SecCopyErrorMessageString(ret, NULL); ++ GITERR_CHECK_ALLOC(message); ++ ++ giterr_set(GITERR_NET, "SecureTransport error: %s", CFStringGetCStringPtr(message, kCFStringEncodingUTF8)); ++ CFRelease(message); ++#else ++ giterr_set(GITERR_NET, "SecureTransport error: OSStatus %d", (unsigned int)ret); ++ GIT_UNUSED(message); +#endif + - #include - #include "common.h" - #include "sysdir.h" -@@ -29,7 +33,7 @@ int git_libgit2_features() - #ifdef GIT_THREADS - | GIT_FEATURE_THREADS - #endif --#if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT) -+#if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT) || defined(GIT_MBEDTLS) - | GIT_FEATURE_HTTPS - #endif - #if defined(GIT_SSH) -@@ -174,8 +178,34 @@ int git_libgit2_opts(int key, ...) - error = -1; - } - } -+#elif GIT_MBEDTLS -+ { -+ const char *file = va_arg(ap, const char *); -+ const char *path = va_arg(ap, const char *); -+ int ret = 0; -+ char errbuf[512]; -+ mbedtls_x509_crt *cacert; -+ cacert = git__malloc(sizeof(mbedtls_x509_crt)); -+ mbedtls_x509_crt_init(cacert); -+ if (file) { -+ ret = mbedtls_x509_crt_parse_file(cacert, file); -+ } else if (path) { -+ ret = mbedtls_x509_crt_parse_path(cacert, path); -+ } -+ if (!ret) { -+ mbedtls_x509_crt_free(cacert); -+ git__free(cacert); -+ mbedtls_strerror( ret, errbuf, 512 ); -+ giterr_set(GITERR_SSL, "SSL error: failed to load CA certificates : %s (%d)", ret, errbuf); -+ error = -1; -+ } else { -+ mbedtls_x509_crt_free(git__ssl_conf->ca_chain); -+ git__free(git__ssl_conf->ca_chain); -+ mbedtls_ssl_conf_ca_chain(git__ssl_conf, cacert, NULL); -+ } ++ return -1; ++} ++ ++typedef struct { ++ git_stream parent; ++ git_stream *io; ++ SSLContextRef ctx; ++ CFDataRef der_data; ++ git_cert_x509 cert_info; ++} stransport_stream; ++ ++static int stransport_connect(git_stream *stream) ++{ ++ stransport_stream *st = (stransport_stream *) stream; ++ int error; ++ SecTrustRef trust = NULL; ++ SecTrustResultType sec_res; ++ OSStatus ret; ++ ++ if ((error = git_stream_connect(st->io)) < 0) ++ return error; ++ ++ ret = SSLHandshake(st->ctx); ++ if (ret != errSSLServerAuthCompleted) { ++ giterr_set(GITERR_SSL, "unexpected return value from ssl handshake %d", ret); ++ return -1; ++ } ++ ++ if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr) ++ goto on_error; ++ ++ if (!trust) ++ return GIT_ECERTIFICATE; ++ ++ if ((ret = SecTrustEvaluate(trust, &sec_res)) != noErr) ++ goto on_error; ++ ++ CFRelease(trust); ++ ++ if (sec_res == kSecTrustResultInvalid || sec_res == kSecTrustResultOtherError) { ++ giterr_set(GITERR_SSL, "internal security trust error"); ++ return -1; ++ } ++ ++ if (sec_res == kSecTrustResultDeny || sec_res == kSecTrustResultRecoverableTrustFailure || ++ sec_res == kSecTrustResultFatalTrustFailure) ++ return GIT_ECERTIFICATE; ++ ++ return 0; ++ ++on_error: ++ if (trust) ++ CFRelease(trust); ++ ++ return stransport_error(ret); ++} ++ ++static int stransport_certificate(git_cert **out, git_stream *stream) ++{ ++ stransport_stream *st = (stransport_stream *) stream; ++ SecTrustRef trust = NULL; ++ SecCertificateRef sec_cert; ++ OSStatus ret; ++ ++ if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr) ++ return stransport_error(ret); ++ ++ sec_cert = SecTrustGetCertificateAtIndex(trust, 0); ++ st->der_data = SecCertificateCopyData(sec_cert); ++ CFRelease(trust); ++ ++ if (st->der_data == NULL) { ++ giterr_set(GITERR_SSL, "retrieved invalid certificate data"); ++ return -1; ++ } ++ ++ st->cert_info.parent.cert_type = GIT_CERT_X509; ++ st->cert_info.data = (void *) CFDataGetBytePtr(st->der_data); ++ st->cert_info.len = CFDataGetLength(st->der_data); ++ ++ *out = (git_cert *)&st->cert_info; ++ return 0; ++} ++ ++static int stransport_set_proxy( ++ git_stream *stream, ++ const git_proxy_options *proxy_opts) ++{ ++ stransport_stream *st = (stransport_stream *) stream; ++ ++ return git_stream_set_proxy(st->io, proxy_opts); ++} ++ ++/* ++ * Contrary to typical network IO callbacks, Secure Transport write callback is ++ * expected to write *all* passed data, not just as much as it can, and any ++ * other case would be considered a failure. ++ * ++ * This behavior is actually not specified in the Apple documentation, but is ++ * required for things to work correctly (and incidentally, that's also how ++ * Apple implements it in its projects at opensource.apple.com). ++ * ++ * Libgit2 streams happen to already have this very behavior so this is just ++ * passthrough. ++ */ ++static OSStatus write_cb(SSLConnectionRef conn, const void *data, size_t *len) ++{ ++ git_stream *io = (git_stream *) conn; ++ ++ if (git_stream_write(io, data, *len, 0) < 0) { ++ return -36; /* "ioErr" from MacErrors.h which is not available on iOS */ ++ } ++ ++ return noErr; ++} ++ ++static ssize_t stransport_write(git_stream *stream, const char *data, size_t len, int flags) ++{ ++ stransport_stream *st = (stransport_stream *) stream; ++ size_t data_len, processed; ++ OSStatus ret; ++ ++ GIT_UNUSED(flags); ++ ++ data_len = len; ++ if ((ret = SSLWrite(st->ctx, data, data_len, &processed)) != noErr) ++ return stransport_error(ret); ++ ++ return processed; ++} ++ ++/* ++ * Contrary to typical network IO callbacks, Secure Transport read callback is ++ * expected to read *exactly* the requested number of bytes, not just as much ++ * as it can, and any other case would be considered a failure. ++ * ++ * This behavior is actually not specified in the Apple documentation, but is ++ * required for things to work correctly (and incidentally, that's also how ++ * Apple implements it in its projects at opensource.apple.com). ++ */ ++static OSStatus read_cb(SSLConnectionRef conn, void *data, size_t *len) ++{ ++ git_stream *io = (git_stream *) conn; ++ OSStatus error = noErr; ++ size_t off = 0; ++ ssize_t ret; ++ ++ do { ++ ret = git_stream_read(io, data + off, *len - off); ++ if (ret < 0) { ++ error = -36; /* "ioErr" from MacErrors.h which is not available on iOS */ ++ break; + } - #else -- giterr_set(GITERR_NET, "cannot set certificate locations: OpenSSL is not enabled"); -+ giterr_set(GITERR_NET, "Cannot set certificate locations: OpenSSL or mbedTLS is not enabled"); - error = -1; - #endif - break; ++ if (ret == 0) { ++ error = errSSLClosedGraceful; ++ break; ++ } ++ ++ off += ret; ++ } while (off < *len); ++ ++ *len = off; ++ return error; ++} ++ ++static ssize_t stransport_read(git_stream *stream, void *data, size_t len) ++{ ++ stransport_stream *st = (stransport_stream *) stream; ++ size_t processed; ++ OSStatus ret; ++ ++ if ((ret = SSLRead(st->ctx, data, len, &processed)) != noErr) ++ return stransport_error(ret); ++ ++ return processed; ++} ++ ++static int stransport_close(git_stream *stream) ++{ ++ stransport_stream *st = (stransport_stream *) stream; ++ OSStatus ret; ++ ++ ret = SSLClose(st->ctx); ++ if (ret != noErr && ret != errSSLClosedGraceful) ++ return stransport_error(ret); ++ ++ return git_stream_close(st->io); ++} ++ ++static void stransport_free(git_stream *stream) ++{ ++ stransport_stream *st = (stransport_stream *) stream; ++ ++ git_stream_free(st->io); ++ CFRelease(st->ctx); ++ if (st->der_data) ++ CFRelease(st->der_data); ++ git__free(st); ++} ++ ++int git_stransport_stream_new(git_stream **out, const char *host, const char *port) ++{ ++ stransport_stream *st; ++ int error; ++ OSStatus ret; ++ ++ assert(out && host); ++ ++ st = git__calloc(1, sizeof(stransport_stream)); ++ GITERR_CHECK_ALLOC(st); ++ ++#ifdef GIT_CURL ++ error = git_curl_stream_new(&st->io, host, port); ++#else ++ error = git_socket_stream_new(&st->io, host, port); ++#endif ++ ++ if (error < 0){ ++ git__free(st); ++ return error; ++ } ++ ++ st->ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType); ++ if (!st->ctx) { ++ giterr_set(GITERR_NET, "failed to create SSL context"); ++ git__free(st); ++ return -1; ++ } ++ ++ if ((ret = SSLSetIOFuncs(st->ctx, read_cb, write_cb)) != noErr || ++ (ret = SSLSetConnection(st->ctx, st->io)) != noErr || ++ (ret = SSLSetSessionOption(st->ctx, kSSLSessionOptionBreakOnServerAuth, true)) != noErr || ++ (ret = SSLSetProtocolVersionMin(st->ctx, kTLSProtocol1)) != noErr || ++ (ret = SSLSetProtocolVersionMax(st->ctx, kTLSProtocol12)) != noErr || ++ (ret = SSLSetPeerDomainName(st->ctx, host, strlen(host))) != noErr) { ++ CFRelease(st->ctx); ++ git__free(st); ++ return stransport_error(ret); ++ } ++ ++ st->parent.version = GIT_STREAM_VERSION; ++ st->parent.encrypted = 1; ++ st->parent.proxy_support = git_stream_supports_proxy(st->io); ++ st->parent.connect = stransport_connect; ++ st->parent.certificate = stransport_certificate; ++ st->parent.set_proxy = stransport_set_proxy; ++ st->parent.read = stransport_read; ++ st->parent.write = stransport_write; ++ st->parent.close = stransport_close; ++ st->parent.free = stransport_free; ++ ++ *out = (git_stream *) st; ++ return 0; ++} ++ ++#endif +diff --git a/src/streams/stransport.h b/src/streams/stransport.h +new file mode 100644 +index 0000000..714f902 +--- /dev/null ++++ b/src/streams/stransport.h +@@ -0,0 +1,14 @@ ++/* ++ * Copyright (C) the libgit2 contributors. All rights reserved. ++ * ++ * This file is part of libgit2, distributed under the GNU GPL v2 with ++ * a Linking Exception. For full terms see the included COPYING file. ++ */ ++#ifndef INCLUDE_stransport_stream_h__ ++#define INCLUDE_stransport_stream_h__ ++ ++#include "git2/sys/stream.h" ++ ++extern int git_stransport_stream_new(git_stream **out, const char *host, const char *port); ++ ++#endif diff --git a/src/tls_stream.c b/src/tls_stream.c -index 83e2d06..6fb538f 100644 +index 83e2d06..27e5cc2 100644 --- a/src/tls_stream.c +++ b/src/tls_stream.c -@@ -9,6 +9,7 @@ +@@ -8,8 +8,9 @@ + #include "git2/errors.h" #include "common.h" - #include "openssl_stream.h" -+#include "mbedtls_stream.h" - #include "stransport_stream.h" +-#include "openssl_stream.h" +-#include "stransport_stream.h" ++#include "streams/mbedtls.h" ++#include "streams/openssl.h" ++#include "streams/stransport.h" static git_stream_cb tls_ctor; + @@ -30,6 +31,8 @@ int git_tls_stream_new(git_stream **out, const char *host, const char *port) return git_stransport_stream_new(out, host, port); #elif defined(GIT_OPENSSL) return git_openssl_stream_new(out, host, port); +#elif defined(GIT_MBEDTLS) -+ return git_mbedtls_stream_new(out, host, port); ++ return git_mbedtls_stream_new(out, host, port); #else GIT_UNUSED(out); GIT_UNUSED(host); +diff --git a/src/transports/git.c b/src/transports/git.c +index 01edfdc..cae10c3 100644 +--- a/src/transports/git.c ++++ b/src/transports/git.c +@@ -10,7 +10,7 @@ + #include "netops.h" + #include "git2/sys/transport.h" + #include "stream.h" +-#include "socket_stream.h" ++#include "streams/socket.h" + + #define OWNING_SUBTRANSPORT(s) ((git_subtransport *)(s)->parent.subtransport) + +diff --git a/src/transports/http.c b/src/transports/http.c +index cb4a6d0..b602adf 100644 +--- a/src/transports/http.c ++++ b/src/transports/http.c +@@ -16,8 +16,8 @@ + #include "auth.h" + #include "auth_negotiate.h" + #include "tls_stream.h" +-#include "socket_stream.h" +-#include "curl_stream.h" ++#include "streams/socket.h" ++#include "streams/curl.h" + + git_http_auth_scheme auth_schemes[] = { + { GIT_AUTHTYPE_NEGOTIATE, "Negotiate", GIT_CREDTYPE_DEFAULT, git_http_auth_negotiate }, +diff --git a/src/transports/ssh.c b/src/transports/ssh.c +index 4c55e3f..3cb5655 100644 +--- a/src/transports/ssh.c ++++ b/src/transports/ssh.c +@@ -15,7 +15,7 @@ + #include "netops.h" + #include "smart.h" + #include "cred.h" +-#include "socket_stream.h" ++#include "streams/socket.h" + #include "ssh.h" + + #ifdef GIT_SSH diff --git a/tests/core/stream.c b/tests/core/stream.c -index 0cbf442..a4fa2b4 100644 +index 0cbf442..2da4b2f 100644 --- a/tests/core/stream.c +++ b/tests/core/stream.c -@@ -38,7 +38,7 @@ void test_core_stream__register_tls(void) +@@ -37,8 +37,7 @@ void test_core_stream__register_tls(void) + * or when openssl support is disabled (except on OSX * with Security framework). */ - #if defined(GIT_WIN32) || \ +-#if defined(GIT_WIN32) || \ - (!defined(GIT_SECURE_TRANSPORT) && !defined(GIT_OPENSSL)) -+ (!defined(GIT_SECURE_TRANSPORT) && !(defined(GIT_OPENSSL) || defined(GIT_MBEDTLS))) ++#if defined(GIT_WIN32) || !defined(GIT_HTTPS) cl_git_fail_with(-1, error); #else cl_git_pass(error); -diff --git a/tests/online/badssl.c b/tests/online/badssl.c -index 66b090d..bba31cc 100644 ---- a/tests/online/badssl.c -+++ b/tests/online/badssl.c -@@ -4,7 +4,7 @@ +diff --git a/tests/main.c b/tests/main.c +index f67c8ff..3dadc5d 100644 +--- a/tests/main.c ++++ b/tests/main.c +@@ -11,7 +11,11 @@ int main(int argc, char *argv[]) - static git_repository *g_repo; + clar_test_init(argc, argv); + +- git_libgit2_init(); ++ res = git_libgit2_init(); ++ if (res < 0) { ++ return res; ++ } ++ + cl_global_trace_register(); + cl_sandbox_set_search_path_defaults(); --#if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT) -+#if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT) || defined(GIT_MBEDTLS) - static bool g_has_ssl = true; - #else - static bool g_has_ssl = false; diff --git a/deps/patches/libgit2-remote-push-NULL.patch b/deps/patches/libgit2-remote-push-NULL.patch deleted file mode 100644 index ac84d6d1bacb2..0000000000000 --- a/deps/patches/libgit2-remote-push-NULL.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 90cdf44ffb7c78cb9d36709f8a07a216e06bd919 Mon Sep 17 00:00:00 2001 -From: Yichao Yu -Date: Sat, 29 Apr 2017 13:00:07 -0400 -Subject: [PATCH] Allow NULL refspec in git_remote_push - -Since this is allowed in `git_remote_upload` ---- - src/remote.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/remote.c b/src/remote.c -index d3132f75c..4cbc45eda 100644 ---- a/src/remote.c -+++ b/src/remote.c -@@ -2412,7 +2412,7 @@ int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_ - proxy = &opts->proxy_opts; - } - -- assert(remote && refspecs); -+ assert(remote); - - if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, proxy, custom_headers)) < 0) - return error; --- -2.12.2 - diff --git a/test/libgit2.jl b/test/libgit2.jl index f0de7c7262fe8..d7e32d21a9bdf 100644 --- a/test/libgit2.jl +++ b/test/libgit2.jl @@ -369,7 +369,9 @@ mktempdir() do dir error("unexpected") catch e @test typeof(e) == LibGit2.GitError - @test startswith(sprint(show,e),"GitError(Code:ENOTFOUND, Class:OS, Failed to resolve path") + @test startswith( + lowercase(sprint(show, e)), + lowercase("GitError(Code:ENOTFOUND, Class:OS, failed to resolve path")) end path = joinpath(dir, "Example.BareTwo") repo = LibGit2.init(path, true) @@ -1868,15 +1870,19 @@ mktempdir() do dir run(pipeline(`openssl req -new -x509 -newkey rsa:2048 -nodes -keyout $key -out $cert -days 1 -subj "/CN=$common_name"`, stderr=DevNull)) run(`openssl x509 -in $cert -out $pem -outform PEM`) + # Find an available port by listening + port, server = listenany(49152) + close(server) + # Make a fake Julia package and minimal HTTPS server with our generated # certificate. The minimal server can't actually serve a Git repository. mkdir(joinpath(root, "Example.jl")) pobj = cd(root) do - spawn(`openssl s_server -key $key -cert $cert -WWW`) + spawn(`openssl s_server -key $key -cert $cert -WWW -accept $port`) end errfile = joinpath(root, "error") - repo_url = "https://$common_name:4433/Example.jl" + repo_url = "https://$common_name:$port/Example.jl" repo_dir = joinpath(root, "dest") code = """ dest_dir = "$repo_dir" @@ -1899,6 +1905,7 @@ mktempdir() do dir deserialize(f) end @test err.code == LibGit2.Error.ECERTIFICATE + @test startswith(err.msg, "The SSL certificate is invalid") rm(errfile) @@ -1910,8 +1917,11 @@ mktempdir() do dir deserialize(f) end @test err.code == LibGit2.Error.ERROR - @test err.msg == "Invalid Content-Type: text/plain" + @test lowercase(err.msg) == lowercase("invalid Content-Type: text/plain") end + + # OpenSSL s_server should still be running + @test process_running(pobj) finally kill(pobj) end From 5492792021061ec6861564dad9575d5d6889c80d Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Sun, 2 Jul 2017 23:30:48 -0500 Subject: [PATCH 2/2] Remove libgit2-free-config patch --- deps/patches/libgit2-free-config.patch | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 deps/patches/libgit2-free-config.patch diff --git a/deps/patches/libgit2-free-config.patch b/deps/patches/libgit2-free-config.patch deleted file mode 100644 index 767aa816c3c10..0000000000000 --- a/deps/patches/libgit2-free-config.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 5552237686dae90fb9c22f0088de488b48654828 Mon Sep 17 00:00:00 2001 -From: Yichao Yu -Date: Sat, 29 Apr 2017 12:28:35 -0400 -Subject: [PATCH] Do not free config when creating remote - -The regression was introduced in 22261344de18b3cc60ee6937468d66a6a6a28875 ---- - src/remote.c | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/src/remote.c b/src/remote.c -index d3132f75c..15752ab27 100644 ---- a/src/remote.c -+++ b/src/remote.c -@@ -260,7 +260,6 @@ on_error: - if (error) - git_remote_free(remote); - -- git_config_free(config); - git_buf_free(&canonical_url); - git_buf_free(&var); - return error; --- -2.12.2 -