Commit 6bb54cbf by Carlos Martín Nieto

Add a SecureTransport TLS channel

As an alternative to OpenSSL when we're on OS X. This one can actually
take advantage of stacking the streams.
parent 69c333f9
......@@ -36,7 +36,6 @@ OPTION( LIBGIT2_FILENAME "Name of the produced binary" OFF )
OPTION( ANDROID "Build for android NDK" OFF )
OPTION( USE_OPENSSL "Link with and use openssl library" ON )
OPTION( USE_ICONV "Link with and use iconv library" OFF )
OPTION( USE_SSH "Link with libssh to enable SSH support" ON )
OPTION( USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF )
......@@ -44,6 +43,8 @@ OPTION( VALGRIND "Configure build for valgrind" OFF )
IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
SET( USE_ICONV ON )
FIND_PACKAGE(Security)
FIND_PACKAGE(CoreFoundation REQUIRED)
ENDIF()
IF(MSVC)
......@@ -68,6 +69,7 @@ IF(MSVC)
ADD_DEFINITIONS(-D_CRT_NONSTDC_NO_DEPRECATE)
ENDIF()
IF(WIN32)
# By default, libgit2 is built with WinHTTP. To use the built-in
# HTTP transport, invoke CMake with the "-DWINHTTP=OFF" argument.
......@@ -79,6 +81,10 @@ IF(MSVC)
OPTION(MSVC_CRTDBG "Enable CRTDBG memory leak reporting" OFF)
ENDIF()
IF (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
OPTION( USE_OPENSSL "Link with and use openssl library" ON )
ENDIF()
# This variable will contain the libraries we need to put into
# libgit2.pc's Requires.private. That is, what we're linking to or
# what someone who's statically linking us needs to link to.
......@@ -148,6 +154,15 @@ STRING(REGEX REPLACE "^.*LIBGIT2_SOVERSION ([0-9]+)$" "\\1" LIBGIT2_SOVERSION "$
# Find required dependencies
INCLUDE_DIRECTORIES(src include)
IF (SECURITY_FOUND)
MESSAGE("-- Found Security ${SECURITY_DIRS}")
ENDIF()
IF (COREFOUNDATION_FOUND)
MESSAGE("-- Found CoreFoundation ${COREFOUNDATION_DIRS}")
ENDIF()
IF (WIN32 AND EMBED_SSH_PATH)
FILE(GLOB SRC_SSH "${EMBED_SSH_PATH}/src/*.c")
INCLUDE_DIRECTORIES("${EMBED_SSH_PATH}/include")
......@@ -415,12 +430,19 @@ ELSE()
# that uses CMAKE_CONFIGURATION_TYPES and not CMAKE_BUILD_TYPE
ENDIF()
IF (SECURITY_FOUND)
ADD_DEFINITIONS(-DGIT_SECURE_TRANSPORT)
INCLUDE_DIRECTORIES(${SECURITY_INCLUDE_DIR})
ENDIF ()
IF (OPENSSL_FOUND)
ADD_DEFINITIONS(-DGIT_SSL)
INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES})
ENDIF()
IF (THREADSAFE)
IF (NOT WIN32)
FIND_PACKAGE(Threads REQUIRED)
......@@ -459,6 +481,8 @@ ENDIF()
# Compile and link libgit2
ADD_LIBRARY(git2 ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SSH} ${SRC_SHA1} ${WIN_RC})
TARGET_LINK_LIBRARIES(git2 ${SECURITY_DIRS})
TARGET_LINK_LIBRARIES(git2 ${COREFOUNDATION_DIRS})
TARGET_LINK_LIBRARIES(git2 ${SSL_LIBRARIES})
TARGET_LINK_LIBRARIES(git2 ${SSH_LIBRARIES})
TARGET_LINK_LIBRARIES(git2 ${GSSAPI_LIBRARIES})
......@@ -527,6 +551,8 @@ IF (BUILD_CLAR)
ADD_EXECUTABLE(libgit2_clar ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_CLAR} ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SSH} ${SRC_SHA1})
TARGET_LINK_LIBRARIES(libgit2_clar ${COREFOUNDATION_DIRS})
TARGET_LINK_LIBRARIES(libgit2_clar ${SECURITY_DIRS})
TARGET_LINK_LIBRARIES(libgit2_clar ${SSL_LIBRARIES})
TARGET_LINK_LIBRARIES(libgit2_clar ${SSH_LIBRARIES})
TARGET_LINK_LIBRARIES(libgit2_clar ${GSSAPI_LIBRARIES})
......@@ -540,7 +566,7 @@ IF (BUILD_CLAR)
ENDIF ()
ENABLE_TESTING()
IF (WINHTTP OR OPENSSL_FOUND)
IF (WINHTTP OR OPENSSL_FOUND OR SECURITY_FOUND)
ADD_TEST(libgit2_clar libgit2_clar -ionline)
ELSE ()
ADD_TEST(libgit2_clar libgit2_clar -v)
......
IF (COREFOUNDATION_INCLUDE_DIR AND COREFOUNDATION_DIRS)
SET(COREFOUNDATION_FOUND TRUE)
ELSE ()
FIND_PATH(COREFOUNDATION_INCLUDE_DIR NAMES CoreFoundation.h)
FIND_LIBRARY(COREFOUNDATION_DIRS NAMES CoreFoundation)
IF (COREFOUNDATION_INCLUDE_DIR AND COREFOUNDATION_DIRS)
SET(COREFOUNDATION_FOUND TRUE)
ENDIF ()
ENDIF ()
IF (SECURITY_INCLUDE_DIR AND SECURITY_DIRS)
SET(SECURITY_FOUND TRUE)
ELSE ()
FIND_PATH(SECURITY_INCLUDE_DIR NAMES Security/Security.h)
FIND_LIBRARY(SECURITY_DIRS NAMES Security)
IF (SECURITY_INCLUDE_DIR AND SECURITY_DIRS)
SET(SECURITY_FOUND TRUE)
ENDIF ()
ENDIF ()
......@@ -28,7 +28,7 @@ int git_libgit2_features()
#ifdef GIT_THREADS
| GIT_FEATURE_THREADS
#endif
#if defined(GIT_SSL) || defined(GIT_WINHTTP)
#if defined(GIT_SSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT)
| GIT_FEATURE_HTTPS
#endif
#if defined(GIT_SSH)
......
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifdef GIT_SECURE_TRANSPORT
#include <CoreFoundation/CoreFoundation.h>
#include <Security/SecureTransport.h>
#include <Security/SecCertificate.h>
#include "git2/transport.h"
#include "socket_stream.h"
int stransport_error(OSStatus ret)
{
switch (ret) {
case noErr:
giterr_clear();
return 0;
case errSSLXCertChainInvalid:
case errSSLBadCert:
return GIT_ECERTIFICATE;
default:
giterr_set(GITERR_NET, "SecureTransport error %d", ret);
return -1;
}
}
typedef struct {
git_stream parent;
git_stream *io;
SSLContextRef ctx;
CFDataRef der_data;
git_cert_x509 cert_info;
} stransport_stream;
int stransport_connect(git_stream *stream)
{
stransport_stream *st = (stransport_stream *) stream;
int error;
OSStatus ret;
if ((error = git_stream_connect(st->io)) < 0)
return error;
if ((ret = SSLHandshake(st->ctx)) != noErr)
return stransport_error(ret);
return 0;
}
int stransport_certificate(git_cert **out, git_stream *stream)
{
stransport_stream *st = (stransport_stream *) stream;
SecTrustRef trust = NULL;
SecCertificateRef sec_cert;
SecTrustResultType sec_res;
OSStatus ret;
if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr)
return stransport_error(ret);
if ((ret = SecTrustEvaluate(trust, &sec_res)) != noErr)
return stransport_error(ret);
sec_cert = SecTrustGetCertificateAtIndex(trust, 0);
st->der_data = SecCertificateCopyData(sec_cert);
CFRelease(trust);
if (st->der_data == NULL) {
giterr_set(GITERR_SSL, "retrieved invalid certificate data");
return -1;
}
st->cert_info.cert_type = GIT_CERT_X509;
st->cert_info.data = (void *) CFDataGetBytePtr(st->der_data);
st->cert_info.len = CFDataGetLength(st->der_data);
*out = (git_cert *)&st->cert_info;
return 0;
}
static OSStatus write_cb(SSLConnectionRef conn, const void *data, size_t *len)
{
git_stream *io = (git_stream *) conn;
ssize_t ret;
ret = git_stream_write(io, data, *len, 0);
if (ret < 0) {
*len = 0;
return -1;
}
*len = ret;
return noErr;
}
ssize_t stransport_write(git_stream *stream, const char *data, size_t len, int flags)
{
stransport_stream *st = (stransport_stream *) stream;
size_t data_len, processed;
OSStatus ret;
GIT_UNUSED(flags);
data_len = len;
if ((ret = SSLWrite(st->ctx, data, data_len, &processed)) != noErr)
return stransport_error(ret);
return processed;
}
static OSStatus read_cb(SSLConnectionRef conn, void *data, size_t *len)
{
git_stream *io = (git_stream *) conn;
ssize_t ret;
size_t left, requested;
requested = left = *len;
do {
ret = git_stream_read(io, data + (requested - left), left);
if (ret < 0) {
*len = 0;
return -1;
}
left -= ret;
} while (left);
*len = requested;
if (ret == 0)
return errSSLClosedGraceful;
return noErr;
}
ssize_t stransport_read(git_stream *stream, void *data, size_t len)
{
stransport_stream *st = (stransport_stream *) stream;
size_t processed;
OSStatus ret;
if ((ret = SSLRead(st->ctx, data, len, &processed)) != noErr)
return stransport_error(ret);
return processed;
}
int stransport_close(git_stream *stream)
{
stransport_stream *st = (stransport_stream *) stream;
OSStatus ret;
if ((ret = SSLClose(st->ctx)) != noErr)
return stransport_error(ret);
return git_stream_close(st->io);
}
void stransport_free(git_stream *stream)
{
stransport_stream *st = (stransport_stream *) stream;
git_stream_free(st->io);
CFRelease(st->ctx);
if (st->der_data)
CFRelease(st->der_data);
git__free(st);
}
int git_stransport_stream_new(git_stream **out, const char *host, const char *port)
{
stransport_stream *st;
int error;
OSStatus ret;
assert(out && host);
st = git__calloc(1, sizeof(stransport_stream));
GITERR_CHECK_ALLOC(st);
if ((error = git_socket_stream_new(&st->io, host, port)) < 0){
git__free(st);
return error;
}
st->ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType);
if (!st->ctx) {
giterr_set(GITERR_NET, "failed to create SSL context");
return -1;
}
if ((ret = SSLSetIOFuncs(st->ctx, read_cb, write_cb)) != noErr ||
(ret = SSLSetConnection(st->ctx, st->io)) != noErr ||
(ret = SSLSetPeerDomainName(st->ctx, host, strlen(host))) != noErr) {
git_stream_free((git_stream *)st);
return stransport_error(ret);
}
st->parent.version = GIT_STREAM_VERSION;
st->parent.encrypted = 1;
st->parent.connect = stransport_connect;
st->parent.certificate = stransport_certificate;
st->parent.read = stransport_read;
st->parent.write = stransport_write;
st->parent.close = stransport_close;
st->parent.free = stransport_free;
*out = (git_stream *) st;
return 0;
}
#endif
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_stransport_stream_h__
#define INCLUDE_stransport_stream_h__
#include "git2/sys/stream.h"
extern int git_stransport_stream_new(git_stream **out, const char *host, const char *port);
#endif
......@@ -29,7 +29,7 @@ static transport_definition local_transport_definition = { "file://", git_transp
static transport_definition transports[] = {
{ "git://", git_transport_smart, &git_subtransport_definition },
{ "http://", git_transport_smart, &http_subtransport_definition },
#if defined(GIT_SSL) || defined(GIT_WINHTTP)
#if defined(GIT_SSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT)
{ "https://", git_transport_smart, &http_subtransport_definition },
#endif
{ "file://", git_transport_local, NULL },
......
......@@ -557,7 +557,7 @@ static int http_connect(http_subtransport *t)
error = git_stream_connect(t->io);
#ifdef GIT_SSL
#if defined(GIT_SSL) || defined(GIT_SECURE_TRANSPORT)
if ((!error || error == GIT_ECERTIFICATE) && t->owner->certificate_check_cb != NULL &&
git_stream_is_encrypted(t->io)) {
git_cert *cert;
......
......@@ -17,7 +17,7 @@ void test_core_features__0(void)
cl_assert((caps & GIT_FEATURE_THREADS) == 0);
#endif
#if defined(GIT_SSL) || defined(GIT_WINHTTP)
#if defined(GIT_SSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT)
cl_assert((caps & GIT_FEATURE_HTTPS) != 0);
#else
cl_assert((caps & GIT_FEATURE_HTTPS) == 0);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment