From ca3b2234dc7f1bd0d0f81488d3e29980b47a85b4 Mon Sep 17 00:00:00 2001 From: Etienne Samson 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 diff --git a/cmake/Modules/FindmbedTLS.cmake b/cmake/Modules/FindmbedTLS.cmake new file mode 100644 index 0000000000..93297555e8 --- /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 +) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b03b96af93..cc6f5c961e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -133,6 +133,7 @@ ELSE () ENDIF() IF (USE_HTTPS) + FIND_PACKAGE(mbedTLS) IF (CMAKE_SYSTEM_NAME MATCHES "Darwin") FIND_PACKAGE(Security) FIND_PACKAGE(CoreFoundation) @@ -149,6 +150,8 @@ IF (USE_HTTPS) ENDIF() ELSEIF (WINHTTP) SET(HTTPS_BACKEND "WinHTTP") + ELSEIF(MBEDTLS_FOUND) + SET(HTTPS_BACKEND "mbedTLS") ELSE() SET(HTTPS_BACKEND "OpenSSL") ENDIF() @@ -185,6 +188,16 @@ IF (USE_HTTPS) 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() + + 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() diff --git a/src/features.h.in b/src/features.h.in index e03b7a2517..f7f162c2a7 100644 --- a/src/features.h.in +++ b/src/features.h.in @@ -27,6 +27,7 @@ #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 diff --git a/src/settings.c b/src/settings.c index 2a52ffbf63..13ae6d4892 100644 --- a/src/settings.c +++ b/src/settings.c @@ -11,6 +11,10 @@ # include #endif +#ifdef GIT_MBEDTLS +# include +#endif + #include #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,12 @@ int git_libgit2_opts(int key, ...) 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 *); + error = git_mbedtls__set_cert_location(file, path); + } #else giterr_set(GITERR_SSL, "TLS backend doesn't support certificate locations"); error = -1; diff --git a/src/streams/mbedtls.c b/src/streams/mbedtls.c new file mode 100644 index 0000000000..fbef31e81d --- /dev/null +++ b/src/streams/mbedtls.c @@ -0,0 +1,344 @@ +/* + * 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 + +#include "global.h" +#include "stream.h" +#include "streams/socket.h" +#include "netops.h" +#include "git2/transport.h" + +#ifdef GIT_CURL +# include "streams/curl.h" +#endif + +#include +#include +#include +#include + +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: %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); + } + + 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, const char *host) +{ + const mbedtls_x509_crt *cert; + int flags; + struct in6_addr addr6; + struct in_addr addr4; + void *addr; + + if( ( flags = mbedtls_ssl_get_verify_result(ssl) ) != 0 ) + { + char vrfy_buf[512]; + mbedtls_x509_crt_verify_info( vrfy_buf, sizeof( vrfy_buf ), " ! ", flags ); + giterr_set(GITERR_SSL, "The SSL certificate is invalid: %s", vrfy_buf); + 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)) { + addr = &addr4; + } else { + if(p_inet_pton(AF_INET6, host, &addr6)) { + addr = &addr6; + } + } + + 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 */ + //TODO: cert->subject_alt_names + + /* If no alternative names are available, check the common name */ + /*TODO + mbedtls_x509_name peer_name = cert->subject; + if (peer_name == NULL) + goto on_error; + */ + + return 0; + +on_error: + return ssl_set_error(ssl, 0); + +cert_fail_name: + 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; +} 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_bio(st->ssl, st->io, bio_write, bio_read, NULL); + + /* specify the host in case SNI is needed */ +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + mbedtls_ssl_set_hostname(st->ssl, st->host); +#endif + + if ((ret = mbedtls_ssl_handshake(st->ssl)) != 0) + return ssl_set_error(st->ssl, ret); + + 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; + + 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) +{ + 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); + } + + return ret; +} + +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 *file, const char *path) +{ + 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); + } + if (ret != 0) { + mbedtls_strerror( ret, errbuf, 512 ); + giterr_set(GITERR_NET, "SSL error: %d - %s", ret, errbuf); + return -1; + } + return 0; +} + +#else + +#include "stream.h" + +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 *file, const char *path) +{ + GIT_UNUSED(file); + GIT_UNUSED(path); + + 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 0000000000..0572442014 --- /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_steams_mbedtls_h__ +#define INCLUDE_steams_mbedtls_h__ + +#include "common.h" + +#include "git2/sys/stream.h" + +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); + +#endif diff --git a/src/streams/tls.c b/src/streams/tls.c index d6ca7d40de..1bcb0d9841 100644 --- 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 @@ 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); #else GIT_UNUSED(out); GIT_UNUSED(host); From 1a1875f3227c3edb6ae0d5da3a5ffe4699aae63b Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Thu, 29 Mar 2018 22:13:58 +0200 Subject: [PATCH 02/15] mbedtls: proper certificate verification --- src/streams/mbedtls.c | 76 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/src/streams/mbedtls.c b/src/streams/mbedtls.c index fbef31e81d..ecf219412b 100644 --- a/src/streams/mbedtls.c +++ b/src/streams/mbedtls.c @@ -80,31 +80,33 @@ 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 (gitno__match_host(name, host) < 0) + return -1; + + return 0; +} + static int verify_server_cert(mbedtls_ssl_context *ssl, const char *host) { const mbedtls_x509_crt *cert; - int flags; - struct in6_addr addr6; - struct in_addr addr4; - void *addr; + const mbedtls_x509_sequence *alts; + int ret, matched = -1; + size_t sn_size = 512; + char subject_name[sn_size], alt_name[sn_size]; + - if( ( flags = mbedtls_ssl_get_verify_result(ssl) ) != 0 ) - { + if ((ret = mbedtls_ssl_get_verify_result(ssl)) != 0) { char vrfy_buf[512]; - mbedtls_x509_crt_verify_info( vrfy_buf, sizeof( vrfy_buf ), " ! ", flags ); + 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; } - /* Try to parse the host as an IP address to see if it is */ - if (p_inet_pton(AF_INET, host, &addr4)) { - addr = &addr4; - } else { - if(p_inet_pton(AF_INET6, host, &addr6)) { - addr = &addr6; - } - } - cert = mbedtls_ssl_get_peer_cert(ssl); if (!cert) { giterr_set(GITERR_SSL, "the server did not provide a certificate"); @@ -112,14 +114,39 @@ static int verify_server_cert(mbedtls_ssl_context *ssl, const char *host) } /* Check the alternative names */ - //TODO: cert->subject_alt_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 */ - /*TODO - mbedtls_x509_name peer_name = cert->subject; - if (peer_name == NULL) + 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; @@ -151,12 +178,9 @@ int mbedtls_connect(git_stream *stream) st->connected = true; - mbedtls_ssl_set_bio(st->ssl, st->io, bio_write, bio_read, NULL); - - /* specify the host in case SNI is needed */ -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME mbedtls_ssl_set_hostname(st->ssl, st->host); -#endif + + 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); From 6c6be3ce6ff23429089c0211870971b17246a992 Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Thu, 29 Mar 2018 22:13:59 +0200 Subject: [PATCH 03/15] mbedtls: use libmbedcrypto for hashing --- CMakeLists.txt | 2 +- src/CMakeLists.txt | 5 +++++ src/features.h.in | 1 + src/hash.h | 2 ++ src/hash/hash_mbedtls.c | 38 ++++++++++++++++++++++++++++++++++++++ src/hash/hash_mbedtls.h | 20 ++++++++++++++++++++ 6 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/hash/hash_mbedtls.c create mode 100644 src/hash/hash_mbedtls.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ca5354a7f..9176eee04a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,7 +48,7 @@ OPTION( PROFILE "Generate profiling information" OFF ) 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 ) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cc6f5c961e..027e76a324 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -243,6 +243,11 @@ ELSEIF(SHA1_BACKEND STREQUAL "Win32") 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() diff --git a/src/features.h.in b/src/features.h.in index f7f162c2a7..f414c58433 100644 --- a/src/features.h.in +++ b/src/features.h.in @@ -33,5 +33,6 @@ #cmakedefine GIT_SHA1_WIN32 1 #cmakedefine GIT_SHA1_COMMON_CRYPTO 1 #cmakedefine GIT_SHA1_OPENSSL 1 +#cmakedefine GIT_SHA1_MBEDTLS 1 #endif diff --git a/src/hash.h b/src/hash.h index 31eaf88897..93765adf33 100644 --- a/src/hash.h +++ b/src/hash.h @@ -26,6 +26,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(GIT_SHA1_MBEDTLS) +# include "hash/hash_mbedtls.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 0000000000..a19d763082 --- /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; +} diff --git a/src/hash/hash_mbedtls.h b/src/hash/hash_mbedtls.h new file mode 100644 index 0000000000..24196c5bff --- /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 + +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__ */ From 60e1ad927b7f64ad33681fc162b512e2cf12b5b8 Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Thu, 29 Mar 2018 22:14:01 +0200 Subject: [PATCH 04/15] mbedtls: add global initialization --- src/global.c | 4 +- src/streams/mbedtls.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/streams/mbedtls.h | 2 + 3 files changed, 102 insertions(+), 4 deletions(-) diff --git a/src/global.c b/src/global.c index 2f9b45bcda..02aedf57d1 100644 --- 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 @@ static int init_common(void) (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; diff --git a/src/streams/mbedtls.c b/src/streams/mbedtls.c index ecf219412b..ea0989caaf 100644 --- a/src/streams/mbedtls.c +++ b/src/streams/mbedtls.c @@ -21,10 +21,99 @@ # include "streams/curl.h" #endif +#include #include -#include -#include -#include +#include +#include + +#define CRT_LOC "/etc/ssl/certs" + +mbedtls_ssl_config *git__ssl_conf; +mbedtls_entropy_context *mbedtls_entropy; + +/** + * 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_stream_global_init(void) +{ + int ret; + mbedtls_ctr_drbg_context *ctr_drbg = 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); + mbedtls_ssl_conf_authmode(git__ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED); + + /* 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); + + // 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); + + 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; @@ -346,6 +435,11 @@ int git_mbedtls__set_cert_location(const char *file, const char *path) #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); diff --git a/src/streams/mbedtls.h b/src/streams/mbedtls.h index 0572442014..8dab2b6c99 100644 --- a/src/streams/mbedtls.h +++ b/src/streams/mbedtls.h @@ -11,6 +11,8 @@ #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 *file, const char *path); From 2419cccd16ae6585e2b58360dc6a53304a3baee8 Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Thu, 29 Mar 2018 22:14:02 +0200 Subject: [PATCH 05/15] mbedtls: default cipher list support --- src/settings.c | 2 +- src/streams/mbedtls.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/settings.c b/src/settings.c index 13ae6d4892..1195075ee9 100644 --- a/src/settings.c +++ b/src/settings.c @@ -210,7 +210,7 @@ int git_libgit2_opts(int key, ...) 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 *)); diff --git a/src/streams/mbedtls.c b/src/streams/mbedtls.c index ea0989caaf..42ac8aa174 100644 --- a/src/streams/mbedtls.c +++ b/src/streams/mbedtls.c @@ -16,6 +16,7 @@ #include "streams/socket.h" #include "netops.h" #include "git2/transport.h" +#include "util.h" #ifdef GIT_CURL # include "streams/curl.h" @@ -31,6 +32,9 @@ 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. @@ -57,6 +61,13 @@ int git_mbedtls_stream_global_init(void) { int ret; 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)); @@ -73,6 +84,24 @@ int git_mbedtls_stream_global_init(void) mbedtls_ssl_conf_min_version(git__ssl_conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_0); mbedtls_ssl_conf_authmode(git__ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED); + /* 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); From ec79b0fdd6236647349a67010f1a8fdcc5edfae7 Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Thu, 29 Mar 2018 22:14:04 +0200 Subject: [PATCH 06/15] mbedtls: fix libgit2 hanging due to incomplete writes --- src/streams/mbedtls.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/streams/mbedtls.c b/src/streams/mbedtls.c index 42ac8aa174..fa7d31e71a 100644 --- a/src/streams/mbedtls.c +++ b/src/streams/mbedtls.c @@ -345,16 +345,20 @@ static int mbedtls_set_proxy(git_stream *stream, const git_proxy_options *proxy_ 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) From 262dfcf0bf7416ddac5012737dfa1686e013824f Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Thu, 29 Mar 2018 22:14:05 +0200 Subject: [PATCH 07/15] mbedtls: enable Travis CI tests --- .travis.yml | 10 ++++++++++ script/install-deps-linux.sh | 13 +++++++++++++ 2 files changed, 23 insertions(+) create mode 100755 script/install-deps-linux.sh diff --git a/.travis.yml b/.travis.yml index a4c8e91dfb..b5f1c6ff68 100644 --- a/.travis.yml +++ b/.travis.yml @@ -55,6 +55,16 @@ matrix: OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=OFF -DDEBUG_POOL=ON -DCMAKE_BUILD_TYPE=Debug" os: linux dist: trusty + - compiler: gcc + env: + MBEDTLS=1 + OPTIONS="-DTHREADSAFE=ON -DCMAKE_BUILD_TYPE=Release -DUSE_HTTPS=mbedTLS -DMBEDTLS_ROOT_DIR=../deps/mbedtls" + os: linux + - compiler: gcc + env: + MBEDTLS=1 + OPTIONS="-DTHREADSAFE=OFF -DBUILD_EXAMPLES=ON -DUSE_HTTPS=mbedTLS -DMBEDTLS_ROOT_DIR=../deps/mbedtls" + os: linux allow_failures: - env: COVERITY=1 diff --git a/script/install-deps-linux.sh b/script/install-deps-linux.sh new file mode 100755 index 0000000000..c084ca2e7c --- /dev/null +++ b/script/install-deps-linux.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +set -x + +if [ "$MBEDTLS" ]; then + git clone https://github.com/ARMmbed/mbedtls.git ./deps/mbedtls + cd ./deps/mbedtls + git checkout mbedtls-2.6.1 + 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 From 4165bb7f4b767801c2142ec521e275f6560197a5 Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Thu, 29 Mar 2018 22:14:06 +0200 Subject: [PATCH 08/15] 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. --- src/streams/mbedtls.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/streams/mbedtls.c b/src/streams/mbedtls.c index fa7d31e71a..b2f02ccd3f 100644 --- a/src/streams/mbedtls.c +++ b/src/streams/mbedtls.c @@ -82,7 +82,11 @@ int git_mbedtls_stream_global_init(void) /* configure TLSv1 */ mbedtls_ssl_conf_min_version(git__ssl_conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_0); - mbedtls_ssl_conf_authmode(git__ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED); + + /* 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)); From 1edde0bcea160f5629d733b1aae26a1fa8215177 Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Thu, 29 Mar 2018 22:14:08 +0200 Subject: [PATCH 09/15] mbedtls: use mbedTLS certificate verification Taken from https://github.com/JuliaLang/julia/blob/8d47a314537779c8fb86642c54925613628a91b0/deps/patches/libgit2-mbedtls-verify.patch, with some modifications. --- src/streams/mbedtls.c | 75 +++++---------------------------------------------- 1 file changed, 6 insertions(+), 69 deletions(-) diff --git a/src/streams/mbedtls.c b/src/streams/mbedtls.c index b2f02ccd3f..ea96ae3de2 100644 --- a/src/streams/mbedtls.c +++ b/src/streams/mbedtls.c @@ -202,82 +202,19 @@ static int ssl_teardown(mbedtls_ssl_context *ssl) return ret; } -static int check_host_name(const char *name, const char *host) +static int verify_server_cert(mbedtls_ssl_context *ssl) { - if (!strcasecmp(name, host)) - return 0; - - if (gitno__match_host(name, host) < 0) - return -1; - - 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]; - + int ret = -1; 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); + 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: %x - %s", ret, 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); - -cert_fail_name: - giterr_set(GITERR_SSL, "hostname does not match certificate"); - return GIT_ECERTIFICATE; } typedef struct { @@ -307,7 +244,7 @@ int mbedtls_connect(git_stream *stream) 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); } int mbedtls_certificate(git_cert **out, git_stream *stream) From 382ed1e873da51cb411bd8842cc160fe4d67a666 Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Thu, 29 Mar 2018 22:14:09 +0200 Subject: [PATCH 10/15] mbedtls: load default CA certificates --- src/CMakeLists.txt | 37 +++++++++++++++++++++++++++++++ src/settings.c | 5 ++++- src/streams/mbedtls.c | 60 ++++++++++++++++++++++++++++++++------------------- src/streams/mbedtls.h | 2 +- 4 files changed, 80 insertions(+), 24 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 027e76a324..fa86d9fb44 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 1195075ee9..f6bc5b270a 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 ea96ae3de2..d134e865f6 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 #include +#include #include #include -#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 @@ int git_mbedtls_stream_new(git_stream **out, const char *host, const char *port) 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 8dab2b6c99..7283698ffa 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 From b3e0280d0b76891cae9f9abb8650938f86b6ef2c Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Thu, 29 Mar 2018 22:14:11 +0200 Subject: [PATCH 11/15] mbedtls: display error codes as hex for consistency with mbedTLS docs Remaining parts of https://github.com/JuliaLang/julia/blob/8d47a314537779c8fb86642c54925613628a91b0/deps/patches/libgit2-mbedtls-fixup.patch --- src/streams/mbedtls.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/streams/mbedtls.c b/src/streams/mbedtls.c index d134e865f6..0a49a36a6f 100644 --- a/src/streams/mbedtls.c +++ b/src/streams/mbedtls.c @@ -181,12 +181,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: %#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: %#04x - %s", error, errbuf); } return ret; @@ -212,7 +212,7 @@ static int verify_server_cert(mbedtls_ssl_context *ssl) 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: %x - %s", ret, vrfy_buf); + giterr_set(GITERR_SSL, "the SSL certificate is invalid: %#04x - %s", ret, vrfy_buf); return GIT_ECERTIFICATE; } @@ -410,7 +410,7 @@ int git_mbedtls__set_cert_location(const char *path, int is_dir) 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); + giterr_set(GITERR_SSL, "failed to load CA certificates: %#04x - %s", ret, errbuf); return -1; } From e3d764a46b41886d8700e6d2c70c7dd33b255f96 Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Thu, 29 Mar 2018 22:14:12 +0200 Subject: [PATCH 12/15] tests: clarify comment --- tests/core/stream.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/core/stream.c b/tests/core/stream.c index 9bed4ae278..262888b10d 100644 --- a/tests/core/stream.c +++ b/tests/core/stream.c @@ -33,9 +33,8 @@ void test_core_stream__register_tls(void) 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); From 54554757c0595f5b3705f15d1212b34984bb35d1 Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Thu, 29 Mar 2018 22:14:14 +0200 Subject: [PATCH 13/15] cmake: make our preferred backend ordering consistent --- src/CMakeLists.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fa86d9fb44..0f5d78547b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -133,6 +133,8 @@ ELSE () 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) @@ -150,10 +152,13 @@ IF (USE_HTTPS) ENDIF() ELSEIF (WINHTTP) SET(HTTPS_BACKEND "WinHTTP") + ELSEIF(OPENSSL_FOUND) + SET(HTTPS_BACKEND "OpenSSL") ELSEIF(MBEDTLS_FOUND) SET(HTTPS_BACKEND "mbedTLS") ELSE() - SET(HTTPS_BACKEND "OpenSSL") + 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 @@ -177,8 +182,6 @@ IF (USE_HTTPS) 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() From 10aff3d5236c390155d247c9c366a9e4ff133825 Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Thu, 29 Mar 2018 22:14:15 +0200 Subject: [PATCH 14/15] travis: just grab what we need from mbedtls --- script/install-deps-linux.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/script/install-deps-linux.sh b/script/install-deps-linux.sh index c084ca2e7c..68b3cc2756 100755 --- a/script/install-deps-linux.sh +++ b/script/install-deps-linux.sh @@ -3,9 +3,8 @@ set -x if [ "$MBEDTLS" ]; then - git clone https://github.com/ARMmbed/mbedtls.git ./deps/mbedtls + git clone --depth 10 --single-branch --branch mbedtls-2.6.1 https://github.com/ARMmbed/mbedtls.git ./deps/mbedtls cd ./deps/mbedtls - git checkout mbedtls-2.6.1 cmake -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=ON -DUSE_STATIC_MBEDTLS_LIBRARY=OFF . cmake --build . From cb2da47e56159faaaf143943c74ffb8f60a988b1 Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Thu, 29 Mar 2018 22:14:17 +0200 Subject: [PATCH 15/15] travis: pass -fPIC when configuring mbedtls --- script/install-deps-linux.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/script/install-deps-linux.sh b/script/install-deps-linux.sh index 68b3cc2756..99cbde4e0a 100755 --- a/script/install-deps-linux.sh +++ b/script/install-deps-linux.sh @@ -5,7 +5,8 @@ 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 - cmake -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=ON -DUSE_STATIC_MBEDTLS_LIBRARY=OFF . + # 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`"