X-Original-To: alpine-aports@lists.alpinelinux.org Received: from mail.cmpwn.com (mail.cmpwn.com [45.56.77.53]) by lists.alpinelinux.org (Postfix) with ESMTP id 799245C5B69 for ; Thu, 4 Jan 2018 03:29:40 +0000 (GMT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=cmpwn.com; s=cmpwn; t=1515036713; bh=faakZuFQi4yEbiUd/Csc3B3F9YkE2A4ybN/gUJssEpE=; h=From:To:Cc:Subject:Date; b=rSZHUKLRQNc0Ug7+Q/yMshbXfXR9yzL0mc1tIxkKZa/RJDP0BVY86Pjb/qMym0aow CIpMUWv1mGeB137fANy5JswmxOA8RGBfQYNyHevgBqkNEC2ZqMRLojU2uWuADfOsZB ur8AD+396HSi/bGTXKzJ7WjpH7eLVyMGn3YreqXs= From: Drew DeVault To: alpine-aports@lists.alpinelinux.org Cc: Drew DeVault , Shiz Subject: [alpine-aports] [PATCH] [WIP] community/julia: update to 0.6.2 Date: Wed, 3 Jan 2018 22:29:24 -0500 Message-Id: <20180104032924.6662-1-sir@cmpwn.com> X-Mailer: git-send-email 2.15.0 X-Mailinglist: alpine-aports Precedence: list List-Id: Alpine Development List-Unsubscribe: List-Post: List-Help: List-Subscribe: --- This is necessary to get the libgit2 patch I submitted through. I noted this as [WIP] because I'm able to compile this, but it fails in the check() - however, I get the same errors when building the latest version of Julia as well. CC'd the current maintainer of this package, can you take a look? community/julia/APKBUILD | 12 +- community/julia/update-libgit2-0.26.0.patch | 5642 +++++++++++++++++++++++++++ 2 files changed, 5649 insertions(+), 5 deletions(-) create mode 100644 community/julia/update-libgit2-0.26.0.patch diff --git a/community/julia/APKBUILD b/community/julia/APKBUILD index 973a811f27..1e2a74c03b 100644 --- a/community/julia/APKBUILD +++ b/community/julia/APKBUILD @@ -2,9 +2,9 @@ # Contributor: Shiz # Maintainer: Shiz pkgname=julia -pkgver=0.6.0 +pkgver=0.6.2 # Keep in sync with deps/libuv.version. -_libuv_ver=52d72a52cc7ccd570929990f010ed16e2ec604c8 +_libuv_ver=d8ab1c6a33e77bf155facb54215dd8798e13825d _llvm_ver=3.9 pkgrel=0 pkgdesc="A high-level, high-performance dynamic language for technical computing" @@ -63,6 +63,7 @@ source="https://github.com/JuliaLang/julia/releases/download/v$pkgver/$pkgname-$ test-disable-tests-using-fake_pty.patch test-libgit2-skip-SSL_CERT_FILE.patch test-repl-disable-22176-20482.patch + update-libgit2-0.26.0.patch " builddir="$srcdir/$pkgname-$pkgver" ldpath="/usr/lib/julia" @@ -171,8 +172,8 @@ dbg() { mv "$pkgdir"/usr/lib/julia/*-debug.* "$subpkgdir"/usr/lib/julia/ } -sha512sums="da21b35eb2c682c0fb8720974a76759c51fe993f10e6af5ca4864fc7f0bb6c90d880b706eb798476c7228bae2db19ff0825add6a2abde2961f16a93a3050cb69 julia-0.6.0.tar.gz -4bf9c8b16617691b70ea43c667ecc575d2a06b3d0c347b949d10360c012138585a13ecea5e18de81b1585a96cf7734b7cc3d6072490898f1d5531c702cf5afab libuv-52d72a52cc7ccd570929990f010ed16e2ec604c8.tar.gz +sha512sums="679050463ed5825d34c477bd44cd91cfecc7d1735a2f52cc70b509afd9883cb60902861643266ecac5ba6cbb03f8fcdd12fc2e87dc41b9ed0eef813fa40610f1 julia-0.6.2.tar.gz +272e3cc7b1290ef19cc941c3b3e6dd39dba7dcb26f0aea8e667c48c56288aa9266020504e0cc90074f02881521b3352079416f564c7eb24ab444326a8f04ca64 libuv-d8ab1c6a33e77bf155facb54215dd8798e13825d.tar.gz 43eaf66d9cb3748012b2dfd77da1b41f667c5c7602a56bea8186b796b215bde82d555d79ab053378c2222521396354dcce5cf23a78fa3b1456062c47771c8433 UnicodeData.txt 0283c7b0ff7dd99ae79f6ddac63513ce7d58ba31de506a3fee07af9b8882ddc275d0f9cb002381ba1e304bcacf252612fa16b21b85667422477e6b945b725899 find-syslibs f812b05b7712975eaef0bf071dd2c1cd0b21c68b18d6a513878bb8fe877bfcadf9f4b9dd8cd27b6809378de5b250a010301e7a90169677d155ad2ba377e8b428 0001-hardened.patch @@ -183,4 +184,5 @@ a66526aee8745875b254f0d27db78b10e76cd8489f4601c77b82674a31ee7a0cf417af8b24a520e0 3f2a6cf439aaada90872b869972f17b506d2f39758f3e2407c3e5f6b28082e7797ed6f2d7e4bace56e02809fe94f879c2a34e22216aa19b08a452371b81e7462 fix-or-disable-broken-tests.patch be3eea9d7506796a48171d244e61e8ee3f69ee659aa65ee738a7ec08499566a014bd6ee21ba39ec97e74d734a8b45b7ec1f5a630a96a31a6d4e17b274107b640 test-disable-tests-using-fake_pty.patch a97a16e3b36c7c192c1133139c87b7b3a719cef645dd2435828b5b5468f4f9beb3bba0e02bb68935319dc150d0c4ccd4728c9eaedc9e2ddee2d823e881f36e2c test-libgit2-skip-SSL_CERT_FILE.patch -fc5eef406d84b60fdf1c837aa1f5bd0e7a205c311853d0ae8b2b237e2eb750d7da2152632d866d372174918a3953746620f2bf130596ab2b027ca389a6816edd test-repl-disable-22176-20482.patch" +fc5eef406d84b60fdf1c837aa1f5bd0e7a205c311853d0ae8b2b237e2eb750d7da2152632d866d372174918a3953746620f2bf130596ab2b027ca389a6816edd test-repl-disable-22176-20482.patch +fb9b4ea938284a31f6f062ecafc977f6c85a3550125857f6b2b138b84222b3cdeb4ed4739ac77e7c254644921e3ce203b83dc9bb62fbb96755ca2740ada68f77 update-libgit2-0.26.0.patch" diff --git a/community/julia/update-libgit2-0.26.0.patch b/community/julia/update-libgit2-0.26.0.patch new file mode 100644 index 0000000000..940d5314e4 --- /dev/null +++ b/community/julia/update-libgit2-0.26.0.patch @@ -0,0 +1,5642 @@ +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 + +--- + .../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 - + 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/deps/checksums/libgit2-15e119375018fba121cf58e02a9f17fe22df0df8.tar.gz/md5 b/deps/checksums/libgit2-15e119375018fba121cf58e02a9f17fe22df0df8.tar.gz/md5 +new file mode 100644 +index 000000000000..f148bce28a5c +--- /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 000000000000..1e129704580c +--- /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 ccc33a35bb05..000000000000 +--- 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 cc5156e0c027..000000000000 +--- 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 c98528e52ae8..d83d0864c344 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 f5596b2fce3b..306384f5cd76 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 bd4e983045be..000000000000 +--- 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 000000000000..2ec6fbd7167a +--- /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 4c454c3adc4b..eb0b3854cbb0 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 d80a637a3996..000000000000 +--- 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 955f165eab75..af12728cf855 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-C BC-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 ac84d6d1bacb..000000000000 +--- 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 +- + +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 767aa816c3c1..000000000000 +--- 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 +- -- 2.15.0 --- Unsubscribe: alpine-aports+unsubscribe@lists.alpinelinux.org Help: alpine-aports+help@lists.alpinelinux.org ---