Commit 382ed1e873da51cb411bd8842cc160fe4d67a666

Etienne Samson 2018-03-29T22:14:09

mbedtls: load default CA certificates

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 027e76a..fa86d9f 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -193,6 +193,43 @@ IF (USE_HTTPS)
 			MESSAGE(FATAL_ERROR "Asked for mbedTLS backend, but it wasn't found")
 		ENDIF()
 
+		IF(NOT CERT_LOCATION)
+			MESSAGE("Auto-detecting default certificates location")
+			IF(CMAKE_SYSTEM_NAME MATCHES Darwin)
+				# Check for an Homebrew installation
+				SET(OPENSSL_CMD "/usr/local/opt/openssl/bin/openssl")
+			ELSE()
+				SET(OPENSSL_CMD "openssl")
+			ENDIF()
+			EXECUTE_PROCESS(COMMAND ${OPENSSL_CMD} version -d OUTPUT_VARIABLE OPENSSL_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
+			IF(OPENSSL_DIR)
+				STRING(REGEX REPLACE "^OPENSSLDIR: \"(.*)\"$" "\\1/" OPENSSL_DIR ${OPENSSL_DIR})
+
+				SET(OPENSSL_CA_LOCATIONS
+					"ca-bundle.pem"             # OpenSUSE Leap 42.1
+					"cert.pem"                  # Ubuntu 14.04, FreeBSD
+					"certs/ca-certificates.crt" # Ubuntu 16.04
+					"certs/ca.pem"              # Debian 7
+				)
+				FOREACH(SUFFIX IN LISTS OPENSSL_CA_LOCATIONS)
+					SET(LOC "${OPENSSL_DIR}${SUFFIX}")
+					IF(NOT CERT_LOCATION AND EXISTS "${OPENSSL_DIR}${SUFFIX}")
+						SET(CERT_LOCATION ${LOC})
+					ENDIF()
+				ENDFOREACH()
+			ELSE()
+				MESSAGE("Unable to find OpenSSL executable. Please provide default certificate location via CERT_LOCATION")
+			ENDIF()
+		ENDIF()
+
+		IF(CERT_LOCATION)
+			IF(NOT EXISTS ${CERT_LOCATION})
+				MESSAGE(FATAL_ERROR "Cannot use CERT_LOCATION=${CERT_LOCATION} as it doesn't exist")
+			ENDIF()
+			ADD_FEATURE_INFO(CERT_LOCATION ON "using certificates from ${CERT_LOCATION}")
+			ADD_DEFINITIONS(-DGIT_DEFAULT_CERT_LOCATION="${CERT_LOCATION}")
+		ENDIF()
+
 		SET(GIT_MBEDTLS 1)
 		LIST(APPEND LIBGIT2_INCLUDES ${MBEDTLS_INCLUDE_DIR})
 		LIST(APPEND LIBGIT2_LIBS ${MBEDTLS_LIBRARIES})
diff --git a/src/settings.c b/src/settings.c
index 1195075..f6bc5b2 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -184,7 +184,10 @@ int git_libgit2_opts(int key, ...)
 		{
 			const char *file = va_arg(ap, const char *);
 			const char *path = va_arg(ap, const char *);
-			error = git_mbedtls__set_cert_location(file, path);
+			if (file)
+				error = git_mbedtls__set_cert_location(file, 0);
+			if (error && path)
+				error = git_mbedtls__set_cert_location(path, 1);
 		}
 #else
 		giterr_set(GITERR_SSL, "TLS backend doesn't support certificate locations");
diff --git a/src/streams/mbedtls.c b/src/streams/mbedtls.c
index ea96ae3..d134e86 100644
--- a/src/streams/mbedtls.c
+++ b/src/streams/mbedtls.c
@@ -22,13 +22,16 @@
 # include "streams/curl.h"
 #endif
 
+#ifndef GIT_DEFAULT_CERT_LOCATION
+#define GIT_DEFAULT_CERT_LOCATION NULL
+#endif
+
 #include <mbedtls/config.h>
 #include <mbedtls/ssl.h>
+#include <mbedtls/error.h>
 #include <mbedtls/entropy.h>
 #include <mbedtls/ctr_drbg.h>
 
-#define CRT_LOC "/etc/ssl/certs"
-
 mbedtls_ssl_config *git__ssl_conf;
 mbedtls_entropy_context *mbedtls_entropy;
 
@@ -57,9 +60,13 @@ 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 ret;
+	int loaded = 0;
+	char *crtpath = GIT_DEFAULT_CERT_LOCATION;
+	struct stat statbuf;
 	mbedtls_ctr_drbg_context *ctr_drbg = NULL;
 
 	int *ciphers_list = NULL;
@@ -121,16 +128,11 @@ int git_mbedtls_stream_global_init(void)
 
 	mbedtls_ssl_conf_rng(git__ssl_conf, mbedtls_ctr_drbg_random, ctr_drbg);
 
-	// set root certificates
-	cacert = git__malloc(sizeof(mbedtls_x509_crt));
-	mbedtls_x509_crt_init(cacert);
-	ret = mbedtls_x509_crt_parse_path(cacert, CRT_LOC);
-	if (ret) {
-		giterr_set(GITERR_SSL, "failed to load CA certificates: %d", ret);
-		goto cleanup;
-	}
-
-	mbedtls_ssl_conf_ca_chain(git__ssl_conf, cacert, NULL);
+	/* load default certificates */
+	if (crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISREG(statbuf.st_mode))
+		loaded = (git_mbedtls__set_cert_location(crtpath, 0) == 0);
+	if (!loaded && crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISDIR(statbuf.st_mode))
+		loaded = (git_mbedtls__set_cert_location(crtpath, 1) == 0);
 
 	git__on_shutdown(shutdown_ssl);
 
@@ -388,20 +390,34 @@ out_err:
 	return error;
 }
 
-int git_mbedtls__set_cert_location(const char *file, const char *path)
+int git_mbedtls__set_cert_location(const char *path, int is_dir)
 {
 	int ret = 0;
 	char errbuf[512];
-	if (!file) {
-		ret = mbedtls_x509_crt_parse_file(git__ssl_conf->ca_chain, file);
-	} else if (!path) {
-		ret = mbedtls_x509_crt_parse_path(git__ssl_conf->ca_chain, path);
+	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);
 	}
-	if (ret != 0) {
+	/* 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_NET, "SSL error: %d - %s", ret, errbuf);
+		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;
 }
 
@@ -424,10 +440,10 @@ int git_mbedtls_stream_new(git_stream **out, const char *host, const char *port)
 	return -1;
 }
 
-int git_mbedtls__set_cert_location(const char *file, const char *path)
+int git_mbedtls__set_cert_location(const char *path, int is_dir)
 {
-	GIT_UNUSED(file);
 	GIT_UNUSED(path);
+	GIT_UNUSED(is_dir);
 
 	giterr_set(GITERR_SSL, "mbedTLS is not supported in this version");
 	return -1;
diff --git a/src/streams/mbedtls.h b/src/streams/mbedtls.h
index 8dab2b6..7283698 100644
--- a/src/streams/mbedtls.h
+++ b/src/streams/mbedtls.h
@@ -15,6 +15,6 @@ 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_location(const char *file, const char *path);
+extern int git_mbedtls__set_cert_location(const char *path, int is_dir);
 
 #endif