Commit 9f84003c229691454e7cb5b90667c180f2658e15

Edward Thomson 2021-08-29T22:34:19

Merge pull request #6009 from libgit2/ethomson/custom_cert_locations opts: test GIT_OPT_SET_SSL_CERT_LOCATIONS

diff --git a/ci/test.sh b/ci/test.sh
index 2b43ba1..4d6c41f 100755
--- a/ci/test.sh
+++ b/ci/test.sh
@@ -17,6 +17,7 @@ TMPDIR=${TMPDIR:-/tmp}
 USER=${USER:-$(whoami)}
 
 SUCCESS=1
+CONTINUE_ON_FAILURE=0
 
 cleanup() {
 	echo "Cleaning up..."
@@ -64,6 +65,10 @@ run_test() {
 	done
 
 	if [ "$FAILED" -ne 0 ]; then
+		if [ "$CONTINUE_ON_FAILURE" -ne 1 ]; then
+			exit 1
+		fi
+
 		SUCCESS=0
 	fi
 }
@@ -174,9 +179,9 @@ if [ -n "$RUN_INVASIVE_TESTS" ]; then
 fi
 
 if [ -z "$SKIP_ONLINE_TESTS" ]; then
-	# Run the various online tests.  The "online" test suite only includes the
-	# default online tests that do not require additional configuration.  The
-	# "proxy" and "ssh" test suites require further setup.
+	# Run the online tests.  The "online" test suite only includes the
+	# default online tests that do not require additional configuration.
+	# The "proxy" and "ssh" test suites require further setup.
 
 	echo ""
 	echo "##############################################################################"
@@ -186,6 +191,14 @@ if [ -z "$SKIP_ONLINE_TESTS" ]; then
 	export GITTEST_FLAKY_RETRY=5
 	run_test online
 	unset GITTEST_FLAKY_RETRY
+
+	# Run the online tests that immutably change global state separately
+	# to avoid polluting the test environment.
+	echo ""
+	echo "##############################################################################"
+	echo "## Running (online_customcert) tests"
+	echo "##############################################################################"
+	run_test online_customcert
 fi
 
 if [ -z "$SKIP_GITDAEMON_TESTS" ]; then
diff --git a/src/libgit2.c b/src/libgit2.c
index aee9cf2..09f7ab5 100644
--- a/src/libgit2.c
+++ b/src/libgit2.c
@@ -261,10 +261,7 @@ int git_libgit2_opts(int key, ...)
 		{
 			const char *file = va_arg(ap, const char *);
 			const char *path = va_arg(ap, const char *);
-			if (file)
-				error = git_mbedtls__set_cert_location(file, 0);
-			if (error && path)
-				error = git_mbedtls__set_cert_location(path, 1);
+			error = git_mbedtls__set_cert_location(file, path);
 		}
 #else
 		git_error_set(GIT_ERROR_SSL, "TLS backend doesn't support certificate locations");
diff --git a/src/streams/mbedtls.c b/src/streams/mbedtls.c
index 22b9f47..b3a35ab 100644
--- a/src/streams/mbedtls.c
+++ b/src/streams/mbedtls.c
@@ -68,8 +68,6 @@ static void shutdown_ssl(void)
 	}
 }
 
-int git_mbedtls__set_cert_location(const char *path, int is_dir);
-
 int git_mbedtls_stream_global_init(void)
 {
 	int loaded = 0;
@@ -148,9 +146,9 @@ int git_mbedtls_stream_global_init(void)
 
 	/* load default certificates */
 	if (crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISREG(statbuf.st_mode))
-		loaded = (git_mbedtls__set_cert_location(crtpath, 0) == 0);
+		loaded = (git_mbedtls__set_cert_location(crtpath, NULL) == 0);
 	if (!loaded && crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISDIR(statbuf.st_mode))
-		loaded = (git_mbedtls__set_cert_location(crtpath, 1) == 0);
+		loaded = (git_mbedtls__set_cert_location(NULL, crtpath) == 0);
 
 	return git_runtime_shutdown_register(shutdown_ssl);
 
@@ -438,23 +436,22 @@ int git_mbedtls_stream_new(
 	return error;
 }
 
-int git_mbedtls__set_cert_location(const char *path, int is_dir)
+int git_mbedtls__set_cert_location(const char *file, const char *path)
 {
 	int ret = 0;
 	char errbuf[512];
 	mbedtls_x509_crt *cacert;
 
-	GIT_ASSERT_ARG(path);
+	GIT_ASSERT_ARG(file || path);
 
 	cacert = git__malloc(sizeof(mbedtls_x509_crt));
 	GIT_ERROR_CHECK_ALLOC(cacert);
 
 	mbedtls_x509_crt_init(cacert);
-	if (is_dir) {
+	if (file)
+		ret = mbedtls_x509_crt_parse_file(cacert, file);
+	if (ret >= 0 && path)
 		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);
diff --git a/src/streams/mbedtls.h b/src/streams/mbedtls.h
index 7de94b9..bcca6dd 100644
--- a/src/streams/mbedtls.h
+++ b/src/streams/mbedtls.h
@@ -14,7 +14,7 @@
 extern int git_mbedtls_stream_global_init(void);
 
 #ifdef GIT_MBEDTLS
-extern int git_mbedtls__set_cert_location(const char *path, int is_dir);
+extern int git_mbedtls__set_cert_location(const char *file, const char *path);
 
 extern int git_mbedtls_stream_new(git_stream **out, const char *host, const char *port);
 extern int git_mbedtls_stream_wrap(git_stream **out, git_stream *in, const char *host);
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 600c617..49999f4 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -64,7 +64,8 @@ ENDFUNCTION(ADD_CLAR_TEST)
 
 ADD_CLAR_TEST(offline             -v -xonline)
 ADD_CLAR_TEST(invasive            -v -score::ftruncate -sfilter::stream::bigfile -sodb::largefiles -siterator::workdir::filesystem_gunk -srepo::init -srepo::init::at_filesystem_root)
-ADD_CLAR_TEST(online              -v -sonline)
+ADD_CLAR_TEST(online              -v -sonline -xonline::customcert)
+ADD_CLAR_TEST(online_customcert   -v -sonline::customcert)
 ADD_CLAR_TEST(gitdaemon           -v -sonline::push)
 ADD_CLAR_TEST(ssh                 -v -sonline::push -sonline::clone::ssh_cert -sonline::clone::ssh_with_paths -sonline::clone::path_whitespace_ssh)
 ADD_CLAR_TEST(proxy               -v -sonline::clone::proxy)
diff --git a/tests/online/clone.c b/tests/online/clone.c
index 6de687b..dbf45dc 100644
--- a/tests/online/clone.c
+++ b/tests/online/clone.c
@@ -114,6 +114,8 @@ void test_online_clone__cleanup(void)
 		git__free(_orig_http_proxy);
 		git__free(_orig_https_proxy);
 	}
+
+	git_libgit2_opts(GIT_OPT_SET_SSL_CERT_LOCATIONS, NULL, NULL);
 }
 
 void test_online_clone__network_full(void)
diff --git a/tests/online/customcert.c b/tests/online/customcert.c
new file mode 100644
index 0000000..c7a0de1
--- /dev/null
+++ b/tests/online/customcert.c
@@ -0,0 +1,79 @@
+#include "clar_libgit2.h"
+
+#include "path.h"
+#include "git2/clone.h"
+#include "git2/cred_helpers.h"
+#include "remote.h"
+#include "futils.h"
+#include "refs.h"
+
+/*
+ * Certificate one is in the `certs` folder; certificate two is in the
+ * `self-signed.pem` file.
+ */
+#define CUSTOM_CERT_ONE_URL "https://test.libgit2.org:1443/anonymous/test.git"
+#define CUSTOM_CERT_ONE_PATH "certs"
+
+#define CUSTOM_CERT_TWO_URL "https://test.libgit2.org:2443/anonymous/test.git"
+#define CUSTOM_CERT_TWO_FILE "self-signed.pem"
+
+#if (GIT_OPENSSL || GIT_MBEDTLS)
+static git_repository *g_repo;
+static int initialized = false;
+#endif
+
+void test_online_customcert__initialize(void)
+{
+#if (GIT_OPENSSL || GIT_MBEDTLS)
+	g_repo = NULL;
+
+	if (!initialized) {
+		git_buf path = GIT_BUF_INIT, file = GIT_BUF_INIT;
+		char cwd[GIT_PATH_MAX];
+
+		cl_fixture_sandbox(CUSTOM_CERT_ONE_PATH);
+		cl_fixture_sandbox(CUSTOM_CERT_TWO_FILE);
+
+		cl_must_pass(p_getcwd(cwd, GIT_PATH_MAX));
+		cl_git_pass(git_buf_joinpath(&path, cwd, CUSTOM_CERT_ONE_PATH));
+		cl_git_pass(git_buf_joinpath(&file, cwd, CUSTOM_CERT_TWO_FILE));
+
+		cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SSL_CERT_LOCATIONS,
+		                             file.ptr, path.ptr));
+		initialized = true;
+
+		git_buf_dispose(&file);
+		git_buf_dispose(&path);
+	}
+#endif
+}
+
+void test_online_customcert__cleanup(void)
+{
+#if (GIT_OPENSSL || GIT_MBEDTLS)
+	if (g_repo) {
+		git_repository_free(g_repo);
+		g_repo = NULL;
+	}
+
+	cl_fixture_cleanup("./cloned");
+	cl_fixture_cleanup(CUSTOM_CERT_ONE_PATH);
+	cl_fixture_cleanup(CUSTOM_CERT_TWO_FILE);
+#endif
+}
+
+void test_online_customcert__file(void)
+{
+#if (GIT_OPENSSL || GIT_MBEDTLS)
+	cl_git_pass(git_clone(&g_repo, CUSTOM_CERT_ONE_URL, "./cloned", NULL));
+	cl_assert(git_path_exists("./cloned/master.txt"));
+#endif
+}
+
+void test_online_customcert__path(void)
+{
+#if (GIT_OPENSSL || GIT_MBEDTLS)
+	cl_git_pass(git_clone(&g_repo, CUSTOM_CERT_TWO_URL, "./cloned", NULL));
+	cl_assert(git_path_exists("./cloned/master.txt"));
+#endif
+}
diff --git a/tests/resources/certs/61f2ddb6.0 b/tests/resources/certs/61f2ddb6.0
new file mode 100644
index 0000000..7d9ef6f
--- /dev/null
+++ b/tests/resources/certs/61f2ddb6.0
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFWzCCA0MCFESY816VkhBPUOsdp7djKW5q4ZVzMA0GCSqGSIb3DQEBCwUAMGox
+CzAJBgNVBAYTAlVTMRYwFAYDVQQIDA1NYXNzYWNodXNldHRzMRIwEAYDVQQHDAlD
+YW1icmlkZ2UxFDASBgNVBAoMC2xpYmdpdDIub3JnMRkwFwYDVQQDDBB0ZXN0Lmxp
+YmdpdDIub3JnMB4XDTIxMDgyNTE4NTExMVoXDTMxMDgyMzE4NTExMVowajELMAkG
+A1UEBhMCVVMxFjAUBgNVBAgMDU1hc3NhY2h1c2V0dHMxEjAQBgNVBAcMCUNhbWJy
+aWRnZTEUMBIGA1UECgwLbGliZ2l0Mi5vcmcxGTAXBgNVBAMMEHRlc3QubGliZ2l0
+Mi5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvaRUaM3IJh9N
+G6Yc7tHioUsIGU0MkzSvy/X6O/vONnuuioiJQyPIvfRSvZR2iQj8THTypDGhWn3r
+6h2wk5eOUGwJH2N9FrrlEBdpsMc7SKdiJXwTI30mkK3/qru8NzE71dgCkYp1xhKw
+edTkAFK+PkvyVLFL7K35cx8Bxfamyssdb+qGWa7g4P27CWUdvQgmurrzzPIMZiLD
+/cI1Kwer/N7nTY/6CSs9dcHTlanyZdf+mQ50+//vI4F6+OduGHJkxRF48jLUz1rz
+P3WGRMRbHjCmvWpX/9DLgqGk7XTy0hNgNUCit6kawwcv5y7SP/ii86MkynAHn5i8
+d+zhXjdrSSy8i0IbRJafnxmtrsmjGeIzraJSRqMlv7KKWEBz+alm6vlePnRUbWB7
+0po5uSsRPya6kJJCzMjIfKq1dgXq33m9jCG2wU+L4fEHVlEkFGXYTspMlIBNUjTc
+c45+e1EpamF8aHm32PP8gTF8fGZzQjOXmNW5g7t0joWMGZ+Ao2jYc1pG3SOARi36
+azrmB5/XJqbbfVZEzIue01fO/5R8RgabOP1qWUjH2KLb8zTDok+CW0ULNseU+MKf
+PHXG2OjxcR0vTqop2V6JlKTXXx3/TOD16/+mSrrPzNDejLrkvAH9oN38YpMBM8eg
+vfivHNRm0jjdGbv2OOPEBLEf1cNimQIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQBZ
+znFta24sWoqdgKXKAK5RHAh/HyOvTInwcXi9RU4XjYlbqNVs0ODR74VRZINoyAL2
+bo+x/iUuAp9+b8fjr79fpVof3nSMU7UtMcT1nvzVmaUYSkKQ0f/9vK4yg0kao1bV
+WwhIc0slKgOJjEicPVs3kd+duv5vakQeUajLPGM8SiS1F/nF67rIuZLdJn2Qp+im
+w5Q3Pjgqw5VrJxyk3AaUcntKHpWy1POLyNV79tXra6BxbtQVlRS0+h1MHELARDFx
+1ZtgyAe5YbWM7WrIiFKD4mmKZu4GMnJDXVpfUub5g0U/e7L/gg6Z1UyYZuln6axw
+RojuAHo1uAWFUsjhWLYV/7P/l/dC+7gFjvSsUqb1+U7jXObzfKjXo/FwYcy4VsVv
+xNbglbhdVjAo/YBTJuf3L0UZjSbxvQIYS+v8u1ECeWE6SH6cHRzryeo5wO4h8NJR
+n30xsvocHFbs4LWy5BVfMUo6wGUy0Y+1gSwSqVMv3JPuLwxUsv0HPdeC00Ab9cHq
+kYXPNZXg3a6orTDa4hJLdAm2V/fn/2KKJYlNj7iCL664QgoCHl7LFyLMiwFVCu5h
+4JjGL3Q+8MondaLZlq5YDmvtj979AyM/7qL4XAE2oofQ4J5dqnKKpMkWdAM/fI/9
+N5DK/4zMXJWgIED0yo2SSZHQmuqZplacOhmfjjZigQ==
+-----END CERTIFICATE-----
diff --git a/tests/resources/certs/db4f60b0.0 b/tests/resources/certs/db4f60b0.0
new file mode 100644
index 0000000..7d9ef6f
--- /dev/null
+++ b/tests/resources/certs/db4f60b0.0
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFWzCCA0MCFESY816VkhBPUOsdp7djKW5q4ZVzMA0GCSqGSIb3DQEBCwUAMGox
+CzAJBgNVBAYTAlVTMRYwFAYDVQQIDA1NYXNzYWNodXNldHRzMRIwEAYDVQQHDAlD
+YW1icmlkZ2UxFDASBgNVBAoMC2xpYmdpdDIub3JnMRkwFwYDVQQDDBB0ZXN0Lmxp
+YmdpdDIub3JnMB4XDTIxMDgyNTE4NTExMVoXDTMxMDgyMzE4NTExMVowajELMAkG
+A1UEBhMCVVMxFjAUBgNVBAgMDU1hc3NhY2h1c2V0dHMxEjAQBgNVBAcMCUNhbWJy
+aWRnZTEUMBIGA1UECgwLbGliZ2l0Mi5vcmcxGTAXBgNVBAMMEHRlc3QubGliZ2l0
+Mi5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvaRUaM3IJh9N
+G6Yc7tHioUsIGU0MkzSvy/X6O/vONnuuioiJQyPIvfRSvZR2iQj8THTypDGhWn3r
+6h2wk5eOUGwJH2N9FrrlEBdpsMc7SKdiJXwTI30mkK3/qru8NzE71dgCkYp1xhKw
+edTkAFK+PkvyVLFL7K35cx8Bxfamyssdb+qGWa7g4P27CWUdvQgmurrzzPIMZiLD
+/cI1Kwer/N7nTY/6CSs9dcHTlanyZdf+mQ50+//vI4F6+OduGHJkxRF48jLUz1rz
+P3WGRMRbHjCmvWpX/9DLgqGk7XTy0hNgNUCit6kawwcv5y7SP/ii86MkynAHn5i8
+d+zhXjdrSSy8i0IbRJafnxmtrsmjGeIzraJSRqMlv7KKWEBz+alm6vlePnRUbWB7
+0po5uSsRPya6kJJCzMjIfKq1dgXq33m9jCG2wU+L4fEHVlEkFGXYTspMlIBNUjTc
+c45+e1EpamF8aHm32PP8gTF8fGZzQjOXmNW5g7t0joWMGZ+Ao2jYc1pG3SOARi36
+azrmB5/XJqbbfVZEzIue01fO/5R8RgabOP1qWUjH2KLb8zTDok+CW0ULNseU+MKf
+PHXG2OjxcR0vTqop2V6JlKTXXx3/TOD16/+mSrrPzNDejLrkvAH9oN38YpMBM8eg
+vfivHNRm0jjdGbv2OOPEBLEf1cNimQIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQBZ
+znFta24sWoqdgKXKAK5RHAh/HyOvTInwcXi9RU4XjYlbqNVs0ODR74VRZINoyAL2
+bo+x/iUuAp9+b8fjr79fpVof3nSMU7UtMcT1nvzVmaUYSkKQ0f/9vK4yg0kao1bV
+WwhIc0slKgOJjEicPVs3kd+duv5vakQeUajLPGM8SiS1F/nF67rIuZLdJn2Qp+im
+w5Q3Pjgqw5VrJxyk3AaUcntKHpWy1POLyNV79tXra6BxbtQVlRS0+h1MHELARDFx
+1ZtgyAe5YbWM7WrIiFKD4mmKZu4GMnJDXVpfUub5g0U/e7L/gg6Z1UyYZuln6axw
+RojuAHo1uAWFUsjhWLYV/7P/l/dC+7gFjvSsUqb1+U7jXObzfKjXo/FwYcy4VsVv
+xNbglbhdVjAo/YBTJuf3L0UZjSbxvQIYS+v8u1ECeWE6SH6cHRzryeo5wO4h8NJR
+n30xsvocHFbs4LWy5BVfMUo6wGUy0Y+1gSwSqVMv3JPuLwxUsv0HPdeC00Ab9cHq
+kYXPNZXg3a6orTDa4hJLdAm2V/fn/2KKJYlNj7iCL664QgoCHl7LFyLMiwFVCu5h
+4JjGL3Q+8MondaLZlq5YDmvtj979AyM/7qL4XAE2oofQ4J5dqnKKpMkWdAM/fI/9
+N5DK/4zMXJWgIED0yo2SSZHQmuqZplacOhmfjjZigQ==
+-----END CERTIFICATE-----
diff --git a/tests/resources/self-signed.pem b/tests/resources/self-signed.pem
new file mode 100644
index 0000000..e13417e
--- /dev/null
+++ b/tests/resources/self-signed.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDUzCCAjsCFAb11im6DYQyGJ0GNQCIehXtegq6MA0GCSqGSIb3DQEBCwUAMGYx
+CzAJBgNVBAYTAlVTMRYwFAYDVQQIDA1NYXNzYWNodXNldHRzMRIwEAYDVQQHDAlD
+YW1icmlkZ2UxEDAOBgNVBAoMB2xpYmdpdDIxGTAXBgNVBAMMEHRlc3QubGliZ2l0
+Mi5vcmcwHhcNMjEwODMwMDAyMTQyWhcNMzEwODI4MDAyMTQyWjBmMQswCQYDVQQG
+EwJVUzEWMBQGA1UECAwNTWFzc2FjaHVzZXR0czESMBAGA1UEBwwJQ2FtYnJpZGdl
+MRAwDgYDVQQKDAdsaWJnaXQyMRkwFwYDVQQDDBB0ZXN0LmxpYmdpdDIub3JnMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtqe6b1vnMni+z8Z+a2bGtykI
+ITvBged15rn+0qG6Fz+sn9bYG+ceFupztFfoN3cVpUgQDBTzr3CaAx036BlV0z8i
+CrG0Oh/XGL+9TITQLumEe4iGi8NoMSujBAyXPSNgmpzDmCTGrNFfmq3HzUtO8t3x
+i8OT7d9qCVjFimLvZbgnfHGQ38xvt1XyPgYIVqDQczmMEZ5BdYWB0A1VmnWuP2dH
+BgjwPEC3HwMmm1+PL0VoPTdvE5Su092Qdt8QsiA56466DQyll1d/omnOJfrK7z0N
+OnfDmnDpARSTy6vDofEAYUQoc3dyvBUk8IIzv2UDcR7fTVvYqseQReIOTEnXmQID
+AQABMA0GCSqGSIb3DQEBCwUAA4IBAQBmUEq+JhwWTbB5ODGOKrMG1fKJ+sf6ZH6M
+c4BgLEcdoi/nOTfPuw+ols72LuhH7NKaEcqxWev0jGF0WKqMcM8AGVbywZJ3mBWo
+sKdh6rAGFNkikW4TzhjtDfFbMR45Didl28Be7ieHQL4CQ0Lse3RMOxp250WpiEYV
+W2hIKMwIqOLKGShVD7lI+eHlv+QSH4yOYKHfRHve8s82Tac5OXinc8CJm9ySOtkO
+MfLgfkHtHdFBnV6OVbf4p/596MfMXdwT/bBxT6WPkDGc1AYhoDlmLFTpRgHIDCSK
+2wgV+qHppl7Kn+p3mFQ9sW/1IaRd+jNZOrgZ8Uu5tJ00OaqR/LVG
+-----END CERTIFICATE-----