libgit2/debian/patches/use-mbedtls.patch
2018-12-27 11:06:11 +05:30

911 lines
27 KiB
Diff
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From ca3b2234dc7f1bd0d0f81488d3e29980b47a85b4 Mon Sep 17 00:00:00 2001
From: Etienne Samson <samson.etienne@gmail.com>
Date: Thu, 29 Mar 2018 22:13:56 +0200
Subject: [PATCH 01/15] mbedtls: initial support
---
cmake/Modules/FindmbedTLS.cmake | 93 +++++++++++
src/CMakeLists.txt | 13 ++
src/features.h.in | 1 +
src/settings.c | 11 ++
src/streams/mbedtls.c | 344 ++++++++++++++++++++++++++++++++++++++++
src/streams/mbedtls.h | 18 +++
src/streams/tls.c | 3 +
7 files changed, 483 insertions(+)
create mode 100644 cmake/Modules/FindmbedTLS.cmake
create mode 100644 src/streams/mbedtls.c
create mode 100644 src/streams/mbedtls.h
--- /dev/null
+++ b/cmake/Modules/FindmbedTLS.cmake
@@ -0,0 +1,93 @@
+# - Try to find mbedTLS
+# Once done this will define
+#
+# Read-Only variables
+# MBEDTLS_FOUND - system has mbedTLS
+# MBEDTLS_INCLUDE_DIR - the mbedTLS include directory
+# MBEDTLS_LIBRARY_DIR - the mbedTLS library directory
+# MBEDTLS_LIBRARIES - Link these to use mbedTLS
+# 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
+ 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
+ ${_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)
+ENDIF()
+
+IF(MBEDTLS_FOUND)
+ # split mbedTLS into -L and -l linker options, so we can set them for pkg-config
+ GET_FILENAME_COMPONENT(MBEDTLS_LIBRARY_DIR ${MBEDTLS_LIBRARY} PATH)
+ GET_FILENAME_COMPONENT(MBEDTLS_LIBRARY_FILE ${MBEDTLS_LIBRARY} NAME_WE)
+ GET_FILENAME_COMPONENT(MBEDX509_LIBRARY_FILE ${MBEDX509_LIBRARY} NAME_WE)
+ GET_FILENAME_COMPONENT(MBEDCRYPTO_LIBRARY_FILE ${MBEDCRYPTO_LIBRARY} NAME_WE)
+ STRING(REGEX REPLACE "^lib" "" MBEDTLS_LIBRARY_FILE ${MBEDTLS_LIBRARY_FILE})
+ STRING(REGEX REPLACE "^lib" "" MBEDX509_LIBRARY_FILE ${MBEDX509_LIBRARY_FILE})
+ STRING(REGEX REPLACE "^lib" "" MBEDCRYPTO_LIBRARY_FILE ${MBEDCRYPTO_LIBRARY_FILE})
+ SET(MBEDTLS_LIBRARIES "-L${MBEDTLS_LIBRARY_DIR} -l${MBEDTLS_LIBRARY_FILE} -l${MBEDX509_LIBRARY_FILE} -l${MBEDCRYPTO_LIBRARY_FILE}")
+
+ IF(NOT MBEDTLS_FIND_QUIETLY)
+ MESSAGE(STATUS "Found mbedTLS:")
+ FILE(READ ${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h MBEDTLSCONTENT)
+ STRING(REGEX MATCH "MBEDTLS_VERSION_STRING +\"[0-9|.]+\"" MBEDTLSMATCH ${MBEDTLSCONTENT})
+ IF (MBEDTLSMATCH)
+ STRING(REGEX REPLACE "MBEDTLS_VERSION_STRING +\"([0-9|.]+)\"" "\\1" MBEDTLS_VERSION ${MBEDTLSMATCH})
+ MESSAGE(STATUS " version ${MBEDTLS_VERSION}")
+ ENDIF(MBEDTLSMATCH)
+ MESSAGE(STATUS " TLS: ${MBEDTLS_LIBRARY}")
+ MESSAGE(STATUS " X509: ${MBEDX509_LIBRARY}")
+ MESSAGE(STATUS " Crypto: ${MBEDCRYPTO_LIBRARY}")
+ ENDIF(NOT MBEDTLS_FIND_QUIETLY)
+ELSE(MBEDTLS_FOUND)
+ IF(MBEDTLS_FIND_REQUIRED)
+ MESSAGE(FATAL_ERROR "Could not find mbedTLS")
+ ENDIF(MBEDTLS_FIND_REQUIRED)
+ENDIF(MBEDTLS_FOUND)
+
+MARK_AS_ADVANCED(
+ MBEDTLS_INCLUDE_DIR
+ MBEDTLS_LIBRARY_DIR
+ MBEDTLS_LIBRARIES
+ MBEDTLS_LIBRARY
+ MBEDX509_LIBRARY
+ MBEDCRYPTO_LIBRARY
+)
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -138,6 +138,9 @@
ENDIF()
IF (USE_HTTPS)
+ # We try to find any packages our backends might use
+ FIND_PACKAGE(OpenSSL)
+ FIND_PACKAGE(mbedTLS)
IF (CMAKE_SYSTEM_NAME MATCHES "Darwin")
FIND_PACKAGE(Security)
FIND_PACKAGE(CoreFoundation)
@@ -154,8 +157,13 @@
ENDIF()
ELSEIF (WINHTTP)
SET(HTTPS_BACKEND "WinHTTP")
- ELSE()
+ ELSEIF(OPENSSL_FOUND)
SET(HTTPS_BACKEND "OpenSSL")
+ ELSEIF(MBEDTLS_FOUND)
+ SET(HTTPS_BACKEND "mbedTLS")
+ ELSE()
+ MESSAGE(FATAL_ERROR "Unable to autodetect a usable HTTPS backend."
+ "Please pass the backend name explicitly (-DUSE_HTTPS=backend)")
ENDIF()
ELSE()
# Backend was explicitly set
@@ -179,8 +187,6 @@
LIST(APPEND LIBGIT2_LIBS ${COREFOUNDATION_LIBRARIES} ${SECURITY_LIBRARIES})
LIST(APPEND LIBGIT2_PC_LIBS ${COREFOUNDATION_LDFLAGS} ${SECURITY_LDFLAGS})
ELSEIF (HTTPS_BACKEND STREQUAL "OpenSSL")
- FIND_PACKAGE(OpenSSL)
-
IF (NOT OPENSSL_FOUND)
MESSAGE(FATAL_ERROR "Asked for OpenSSL TLS backend, but it wasn't found")
ENDIF()
@@ -190,6 +196,53 @@
LIST(APPEND LIBGIT2_LIBS ${OPENSSL_LIBRARIES})
LIST(APPEND LIBGIT2_PC_LIBS ${OPENSSL_LDFLAGS})
LIST(APPEND LIBGIT2_PC_REQUIRES "openssl")
+ ELSEIF(HTTPS_BACKEND STREQUAL "mbedTLS")
+ IF (NOT MBEDTLS_FOUND)
+ 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})
+ LIST(APPEND LIBGIT2_PC_LIBS ${MBEDTLS_LDFLAGS})
+ LIST(APPEND LIBGIT2_PC_REQUIRES "mbedtls")
ELSEIF (HTTPS_BACKEND STREQUAL "WinHTTP")
# WinHTTP setup was handled in the WinHTTP-specific block above
ELSE()
@@ -235,6 +288,11 @@
ELSEIF(SHA1_BACKEND STREQUAL "CommonCrypto")
ADD_FEATURE_INFO(SHA ON "using CommonCrypto")
SET(GIT_SHA1_COMMON_CRYPTO 1)
+ELSEIF (SHA1_BACKEND STREQUAL "mbedTLS")
+ ADD_FEATURE_INFO(SHA ON "using mbedTLS")
+ SET(GIT_SHA1_MBEDTLS 1)
+ FILE(GLOB SRC_SHA1 src/hash/hash_mbedtls.c)
+ LIST(APPEND LIBGIT2_PC_REQUIRES "mbedtls")
ELSE()
MESSAGE(FATAL_ERROR "Asked for unknown SHA1 backend ${SHA1_BACKEND}")
ENDIF()
--- a/src/features.h.in
+++ b/src/features.h.in
@@ -27,10 +27,12 @@
#cmakedefine GIT_HTTPS 1
#cmakedefine GIT_OPENSSL 1
#cmakedefine GIT_SECURE_TRANSPORT 1
+#cmakedefine GIT_MBEDTLS 1
#cmakedefine GIT_SHA1_COLLISIONDETECT 1
#cmakedefine GIT_SHA1_WIN32 1
#cmakedefine GIT_SHA1_COMMON_CRYPTO 1
#cmakedefine GIT_SHA1_OPENSSL 1
+#cmakedefine GIT_SHA1_MBEDTLS 1
#endif
--- a/src/settings.c
+++ b/src/settings.c
@@ -11,6 +11,10 @@
# include <openssl/err.h>
#endif
+#ifdef GIT_MBEDTLS
+# include <mbedtls/error.h>
+#endif
+
#include <git2.h>
#include "sysdir.h"
#include "cache.h"
@@ -20,6 +24,7 @@
#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)
{
@@ -175,6 +180,15 @@
const char *path = va_arg(ap, const char *);
error = git_openssl__set_cert_location(file, path);
}
+#elif defined(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_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");
error = -1;
@@ -199,7 +213,7 @@
break;
case GIT_OPT_SET_SSL_CIPHERS:
-#ifdef GIT_OPENSSL
+#if (GIT_OPENSSL || GIT_MBEDTLS)
{
git__free(git__ssl_ciphers);
git__ssl_ciphers = git__strdup(va_arg(ap, const char *));
--- /dev/null
+++ b/src/streams/mbedtls.c
@@ -0,0 +1,452 @@
+/*
+ * 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 "streams/mbedtls.h"
+
+#ifdef GIT_MBEDTLS
+
+#include <ctype.h>
+
+#include "global.h"
+#include "stream.h"
+#include "streams/socket.h"
+#include "netops.h"
+#include "git2/transport.h"
+#include "util.h"
+
+#ifdef GIT_CURL
+# 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>
+
+mbedtls_ssl_config *git__ssl_conf;
+mbedtls_entropy_context *mbedtls_entropy;
+
+#define GIT_SSL_DEFAULT_CIPHERS "TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256:TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256:TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384:TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-DSS-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-DSS-WITH-AES-256-GCM-SHA384:TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256:TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256:TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA:TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA:TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384:TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384:TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA:TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-RSA-WITH-AES-128-CBC-SHA256:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256:TLS-DHE-RSA-WITH-AES-128-CBC-SHA:TLS-DHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-DSS-WITH-AES-128-CBC-SHA256:TLS-DHE-DSS-WITH-AES-256-CBC-SHA256:TLS-DHE-DSS-WITH-AES-128-CBC-SHA:TLS-DHE-DSS-WITH-AES-256-CBC-SHA:TLS-RSA-WITH-AES-128-GCM-SHA256:TLS-RSA-WITH-AES-256-GCM-SHA384:TLS-RSA-WITH-AES-128-CBC-SHA256:TLS-RSA-WITH-AES-256-CBC-SHA256:TLS-RSA-WITH-AES-128-CBC-SHA:TLS-RSA-WITH-AES-256-CBC-SHA"
+#define GIT_SSL_DEFAULT_CIPHERS_COUNT 30
+
+/**
+ * This function aims to clean-up the SSL context which
+ * we allocated.
+ */
+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;
+ }
+}
+
+int git_mbedtls__set_cert_location(const char *path, int is_dir);
+
+int git_mbedtls_stream_global_init(void)
+{
+ int loaded = 0;
+ char *crtpath = GIT_DEFAULT_CERT_LOCATION;
+ struct stat statbuf;
+ mbedtls_ctr_drbg_context *ctr_drbg = NULL;
+
+ 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);
+
+ /* 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);
+
+ 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);
+}
+
+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);
+}
+
+static int ssl_set_error(mbedtls_ssl_context *ssl, int error)
+{
+ 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 );
+
+ 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: %#04x [%x] - %s", error, ssl->session_negotiate->verify_result, errbuf);
+ ret = GIT_ECERTIFICATE;
+ break;
+
+ default:
+ giterr_set(GITERR_SSL, "SSL error: %#04x - %s", error, errbuf);
+ }
+
+ return ret;
+}
+
+static int ssl_teardown(mbedtls_ssl_context *ssl)
+{
+ int ret = 0;
+
+ ret = mbedtls_ssl_close_notify(ssl);
+ if (ret < 0)
+ ret = ssl_set_error(ssl, ret);
+
+ mbedtls_ssl_free(ssl);
+ return ret;
+}
+
+static int verify_server_cert(mbedtls_ssl_context *ssl)
+{
+ int ret = -1;
+
+ if ((ret = mbedtls_ssl_get_verify_result(ssl)) != 0) {
+ char vrfy_buf[512];
+ int len = mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), "", ret);
+ if (len >= 1) vrfy_buf[len - 1] = '\0'; /* Remove trailing \n */
+ giterr_set(GITERR_SSL, "the SSL certificate is invalid: %#04x - %s", ret, vrfy_buf);
+ return GIT_ECERTIFICATE;
+ }
+
+ return 0;
+}
+
+typedef struct {
+ 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;
+
+ if ((ret = git_stream_connect(st->io)) < 0)
+ return ret;
+
+ st->connected = true;
+
+ mbedtls_ssl_set_hostname(st->ssl, st->host);
+
+ 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);
+
+ return verify_server_cert(st->ssl);
+}
+
+int mbedtls_certificate(git_cert **out, git_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;
+ }
+
+ /* 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);
+
+ 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;
+
+ return 0;
+}
+
+static int mbedtls_set_proxy(git_stream *stream, const git_proxy_options *proxy_options)
+{
+ mbedtls_stream *st = (mbedtls_stream *) stream;
+
+ 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)
+{
+ size_t read = 0;
+ mbedtls_stream *st = (mbedtls_stream *) stream;
+
+ GIT_UNUSED(flags);
+
+ 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 read;
+}
+
+ssize_t mbedtls_stream_read(git_stream *stream, void *data, size_t len)
+{
+ 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);
+
+ return ret;
+}
+
+int mbedtls_stream_close(git_stream *stream)
+{
+ mbedtls_stream *st = (mbedtls_stream *) stream;
+ int ret = 0;
+
+ if (st->connected && (ret = ssl_teardown(st->ssl)) != 0)
+ return -1;
+
+ st->connected = false;
+
+ return git_stream_close(st->io);
+}
+
+void mbedtls_stream_free(git_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);
+}
+
+int git_mbedtls_stream_new(git_stream **out, const char *host, const char *port)
+{
+ int error;
+ mbedtls_stream *st;
+
+ st = git__calloc(1, sizeof(mbedtls_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)
+ goto out_err;
+
+ 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;
+ }
+
+ 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_location(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: %#04x - %s", ret, errbuf);
+ 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;
+}
+
+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_location(const char *path, int is_dir)
+{
+ GIT_UNUSED(path);
+ GIT_UNUSED(is_dir);
+
+ giterr_set(GITERR_SSL, "mbedTLS is not supported in this version");
+ return -1;
+}
+
+#endif
--- /dev/null
+++ b/src/streams/mbedtls.h
@@ -0,0 +1,20 @@
+/*
+ * 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_steams_mbedtls_h__
+#define INCLUDE_steams_mbedtls_h__
+
+#include "common.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_location(const char *path, int is_dir);
+
+#endif
--- a/src/streams/tls.c
+++ b/src/streams/tls.c
@@ -9,6 +9,7 @@
#include "git2/errors.h"
+#include "streams/mbedtls.h"
#include "streams/openssl.h"
#include "streams/stransport.h"
@@ -31,6 +32,8 @@
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);
#else
GIT_UNUSED(out);
GIT_UNUSED(host);
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -49,7 +49,7 @@
OPTION( ENABLE_TRACE "Enables tracing support" OFF )
OPTION( LIBGIT2_FILENAME "Name of the produced binary" OFF )
-SET(SHA1_BACKEND "CollisionDetection" CACHE STRING "Backend to use for SHA1. One of Generic, OpenSSL, Win32, CommonCrypto, CollisionDetection. ")
+SET(SHA1_BACKEND "CollisionDetection" CACHE STRING "Backend to use for SHA1. One of Generic, OpenSSL, Win32, CommonCrypto, mbedTLS, CollisionDetection. ")
OPTION( USE_SSH "Link with libssh to enable SSH support" ON )
OPTION( USE_HTTPS "Enable HTTPS support. Can be set to a specific backend" ON )
OPTION( USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF )
--- a/src/hash.h
+++ b/src/hash.h
@@ -26,6 +26,8 @@
# include "hash/hash_openssl.h"
#elif defined(GIT_SHA1_WIN32)
# include "hash/hash_win32.h"
+#elif defined(GIT_SHA1_MBEDTLS)
+# include "hash/hash_mbedtls.h"
#else
# include "hash/hash_generic.h"
#endif
--- /dev/null
+++ b/src/hash/hash_mbedtls.c
@@ -0,0 +1,38 @@
+/*
+ * 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 "hash.h"
+#include "hash/hash_mbedtls.h"
+
+void git_hash_ctx_cleanup(git_hash_ctx *ctx)
+{
+ assert(ctx);
+ mbedtls_sha1_free(&ctx->c);
+}
+
+int git_hash_init(git_hash_ctx *ctx)
+{
+ assert(ctx);
+ mbedtls_sha1_init(&ctx->c);
+ mbedtls_sha1_starts(&ctx->c);
+ return 0;
+}
+
+int git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)
+{
+ assert(ctx);
+ mbedtls_sha1_update(&ctx->c, data, len);
+ return 0;
+}
+
+int git_hash_final(git_oid *out, git_hash_ctx *ctx)
+{
+ assert(ctx);
+ mbedtls_sha1_finish(&ctx->c, out->id);
+ return 0;
+}
--- /dev/null
+++ b/src/hash/hash_mbedtls.h
@@ -0,0 +1,20 @@
+/*
+ * 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_hash_mbedtld_h__
+#define INCLUDE_hash_mbedtld_h__
+
+#include <mbedtls/sha1.h>
+
+struct git_hash_ctx {
+ mbedtls_sha1_context c;
+};
+
+#define git_hash_global_init() 0
+#define git_hash_ctx_init(ctx) git_hash_init(ctx)
+
+#endif /* INCLUDE_hash_mbedtld_h__ */
--- a/src/global.c
+++ b/src/global.c
@@ -12,6 +12,7 @@
#include "filter.h"
#include "merge_driver.h"
#include "streams/curl.h"
+#include "streams/mbedtls.h"
#include "streams/openssl.h"
#include "thread-utils.h"
#include "git2/global.h"
@@ -65,7 +66,8 @@
(ret = git_merge_driver_global_init()) == 0 &&
(ret = git_transport_ssh_global_init()) == 0 &&
(ret = git_openssl_stream_global_init()) == 0 &&
- (ret = git_curl_stream_global_init()) == 0)
+ (ret = git_curl_stream_global_init()) == 0 &&
+ (ret = git_mbedtls_stream_global_init()) == 0)
ret = git_mwindow_global_init();
GIT_MEMORY_BARRIER;
--- /dev/null
+++ b/script/install-deps-linux.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+set -x
+
+if [ "$MBEDTLS" ]; then
+ git clone --depth 10 --single-branch --branch mbedtls-2.6.1 https://github.com/ARMmbed/mbedtls.git ./deps/mbedtls
+ cd ./deps/mbedtls
+ # We pass -fPIC explicitely because we'll include it in libgit2.so
+ CFLAGS=-fPIC cmake -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=OFF -DUSE_STATIC_MBEDTLS_LIBRARY=ON .
+ cmake --build .
+
+ echo "mbedTLS built in `pwd`"
+fi
--- a/tests/core/stream.c
+++ b/tests/core/stream.c
@@ -33,9 +33,8 @@
cl_git_pass(git_stream_register_tls(NULL));
error = git_tls_stream_new(&stream, "localhost", "443");
- /* We don't have arbitrary TLS stream support on Windows
- * or when openssl support is disabled (except on OSX
- * with Security framework).
+ /* We don't have TLS support enabled, or we're on Windows,
+ * which has no arbitrary TLS stream support.
*/
#if defined(GIT_WIN32) || !defined(GIT_HTTPS)
cl_git_fail_with(-1, error);