diff options
author | Carlos Martín Nieto <cmn@dwim.me> | 2014-11-17 17:02:56 +0100 |
---|---|---|
committer | Carlos Martín Nieto <cmn@dwim.me> | 2014-11-17 20:19:49 +0100 |
commit | 7b85608728b38aafd66931ffdcff4e8979dfe3ec (patch) | |
tree | 1612c2118142adcbef2e71f27c1da18c4fd68271 | |
parent | 92e76f40e6239474e320feb42b1dbb9a1da9346e (diff) | |
download | libgit2-cmn/io-stream-backends.tar.gz |
Tentative support for GnuTLScmn/io-stream-backends
Add GnuTLS support for the hashing functions as well as start work on a
GnuTLS IO stream.
-rw-r--r-- | CMakeLists.txt | 10 | ||||
-rw-r--r-- | src/global.c | 14 | ||||
-rw-r--r-- | src/gnutls_stream.c | 165 | ||||
-rw-r--r-- | src/hash.h | 2 | ||||
-rw-r--r-- | src/hash/hash_gnutls.h | 58 |
5 files changed, 248 insertions, 1 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 078bba636..aaa635165 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -155,6 +155,7 @@ IF (WIN32 AND WINHTTP AND NOT MINGW) FILE(GLOB SRC_HTTP deps/http-parser/*.c deps/http-parser/*.h) ELSE () IF (NOT AMIGA) + FIND_PACKAGE(GnuTLS) FIND_PACKAGE(OpenSSL) ENDIF () @@ -174,6 +175,9 @@ ENDIF() IF (WIN32 AND NOT MINGW AND NOT SHA1_TYPE STREQUAL "builtin") ADD_DEFINITIONS(-DWIN32_SHA1) FILE(GLOB SRC_SHA1 src/hash/hash_win32.c) +ELSEIF (GNUTLS_FOUND AND NOT SHA1_TYPE STREQUAL "bultin") + ADD_DEFINITIONS(-DGNUTLS_SHA1) + SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} gnutls") ELSEIF (OPENSSL_FOUND AND NOT SHA1_TYPE STREQUAL "builtin") ADD_DEFINITIONS(-DOPENSSL_SHA1) IF (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") @@ -370,7 +374,11 @@ IF (SECURITY_FOUND) INCLUDE_DIRECTORIES(${SECURITY_INCLUDE_DIR}) ENDIF () -IF (OPENSSL_FOUND) +IF (GNUTLS_FOUND) + ADD_DEFINITIONS(-DGIT_GNUTLS) + INCLUDE_DIRECTORIES(${GNUTLS_INCLUDE_DIR}) + SET(SSL_LIBRARIES ${GNUTLS_LIBRARIES}) +ELSEIF(OPENSSL_FOUND) ADD_DEFINITIONS(-DGIT_SSL) INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR}) SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES}) diff --git a/src/global.c b/src/global.c index 006202a2c..9e35ef39c 100644 --- a/src/global.c +++ b/src/global.c @@ -25,6 +25,10 @@ static git_mutex *openssl_locks; # endif #endif +#ifdef GIT_GNUTLS +# include <gnutls/gnutls.h> +#endif + static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB]; static git_atomic git__n_shutdown_callbacks; static git_atomic git__n_inits; @@ -276,6 +280,11 @@ static void init_once(void) /* OpenSSL needs to be initialized from the main thread */ init_ssl(); +#ifdef GIT_GNUTLS + gnutls_global_init(); + git__on_shutdown(gnutls_global_deinit); +#endif + GIT_MEMORY_BARRIER; } @@ -336,6 +345,11 @@ int git_libgit2_init(void) ssl_inited = 1; } +#ifdef GIT_GNUTLS + gnutls_global_init(); + git__on_shutdown(gnutls_global_deinit); +#endif + git_atomic_inc(&git__n_inits); return 0; } diff --git a/src/gnutls_stream.c b/src/gnutls_stream.c new file mode 100644 index 000000000..37fc1000d --- /dev/null +++ b/src/gnutls_stream.c @@ -0,0 +1,165 @@ +/* + * 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_GNUTLS + +#include <gnutls/gnutls.h> + +#include "stream.h" +#include "socket_stream.h" +#include "git2/transport.h" + +void set_gnutls_error(int error) +{ + giterr_set(GITERR_SSL, "gnutls: %s", gnutls_strerror(error)); +} + +typedef struct { + git_stream parent; + git_socket_stream *socket; + gnutls_session_t session; + git_cert_x509 cert_info; +} gnutls_stream; + +static int verify_server_cert(gnutls_session_t session, const char *host) +{ + int error; + unsigned int status = 0; + + if ((error = gnutls_certificate_verify_peers3(session, host, &status)) < 0) { + set_gnutls_error(error); + return -1; + } + + if (!status) + return 0; + else + return GIT_ECERTIFICATE; +} + +static ssize_t gnutls_stream_write(git_stream *stream, void *data, size_t len, int flags) +{ + ssize_t ret; + size_t off; + gnutls_stream *st = (gnutls_stream *) stream; + + GIT_UNUSED(flags); + + while (off < len) { + ret = gnutls_record_send(st->session, data + off, len - off); + if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) + continue; /* try again with the same params */ + + if (ret < 0) { + set_gnutls_error(ret); + return -1; + } + + off += ret; + } + + return ret; +} + +static ssize_t gnutls_stream_read(git_stream *stream, void *data, size_t len) +{ + ssize_t ret; + gnutls_stream *st = (gnutls_stream *) stream; + + do { + ret = gnutls_record_recv(st->session, data, len); + } while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN); + + if (ret < 0) { + set_gnutls_error(ret); + return -1; + } + + return ret; +} + +static int gnutls_connect(git_stream *stream) +{ + int error; + gnutls_stream *st = (gnutls_stream *) stream; + + if ((error = git_stream_connect((git_stream *)st->socket)) < 0) + return error; + + /* ideally we'd have functions that talk to the socket, but for now */ + gnutls_transport_set_int(st->session, st->socket->s); + + do { + error = gnutls_handshake(st->session); + } while (!gnutls_error_is_fatal(error)); + + if (error < 0) { + set_gnutls_error(error); + return -1; + } + + return verify_server_cert(st->session, st->socket->host); +} + +static int gnutls_stream_close(git_stream *stream) +{ + gnutls_stream *st = (gnutls_stream *) stream; + int error; + + do { + error = gnutls_bye(st->session, GNUTLS_SHUT_RDWR); + } while (error == GNUTLS_E_INTERRUPTED || error == GNUTLS_E_AGAIN); + + return git_stream_close((git_stream *)st->socket); +} + +static void gnutls_stream_free(git_stream *stream) +{ + gnutls_stream *st = (gnutls_stream *) stream; + + gnutls_deinit(st->session); + git__free(st); +} + +int git_gnutls_stream_new(git_stream **out, const char *host, const char *port) +{ + gnutls_stream *st; + int error; + + st = git__calloc(1, sizeof(gnutls_stream)); + GITERR_CHECK_ALLOC(st); + + if (git_socket_stream_new((git_stream **) &st->socket, host, port)) + return -1; + + if ((error = gnutls_init(&st->session, GNUTLS_CLIENT)) < 0) { + git_stream_free((git_stream *) st->socket); + git__free(st); + return -1; + } + + st->parent.encrypted = 1; + st->parent.connect = gnutls_connect; + st->parent.certificate = gnutls_certificate; + st->parent.read = gnutls_stream_read; + st->parent.write = gnutls_stream_write; + st->parent.close = gnutls_stream_close; + st->parent.free = gnutls_stream_free; + + *out = (git_stream *) st; + return 0; +} + +#else + +int git_gnutls_stream_new(git_stream **out, const char *host, const char *port) +{ + giterr_set(GITERR_SSL, "GnuTLS is not supported in this version"); + return -1; +} + +#endif diff --git a/src/hash.h b/src/hash.h index 0bc02a8a9..c373633ce 100644 --- a/src/hash.h +++ b/src/hash.h @@ -18,6 +18,8 @@ void git_hash_ctx_cleanup(git_hash_ctx *ctx); #if defined(GIT_COMMON_CRYPTO) # include "hash/hash_common_crypto.h" +#elif defined(GNUTLS_SHA1) +# include "hash/hash_gnutls.h" #elif defined(OPENSSL_SHA1) # include "hash/hash_openssl.h" #elif defined(WIN32_SHA1) diff --git a/src/hash/hash_gnutls.h b/src/hash/hash_gnutls.h new file mode 100644 index 000000000..f4a78a8f3 --- /dev/null +++ b/src/hash/hash_gnutls.h @@ -0,0 +1,58 @@ +/* + * 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_gnutls_h__ +#define INCLUDE_hash_gnutls_h__ + +#include "hash.h" + +#include <gnutls/gnutls.h> +#include <gnutls/crypto.h> + +struct git_hash_ctx { + gnutls_hash_hd_t c; +}; + +#define git_hash_global_init() 0 +#define git_hash_ctx_init(ctx) git_hash_init(ctx) +#define git_hash_ctx_cleanup(ctx) git_hash_cleanup(ctx) + +GIT_INLINE(int) git_hash_init(git_hash_ctx *ctx) +{ + int error; + assert(ctx); + if ((error = gnutls_hash_init(&ctx->c, GNUTLS_MAC_SHA1)) < 0) { + giterr_set(GITERR_SSL, "gnutls: %s", gnutls_strerror(error)); + return -1; + } + return 0; +} + +GIT_INLINE(int) git_hash_update(git_hash_ctx *ctx, const void *data, size_t len) +{ + int error; + assert(ctx); + if ((error = gnutls_hash(ctx->c, data, len)) < 0) { + giterr_set(GITERR_SSL, "gnutls: %s", gnutls_strerror(error)); + return -1; + } + return 0; +} + +GIT_INLINE(int) git_hash_final(git_oid *out, git_hash_ctx *ctx) +{ + assert(ctx); + gnutls_hash_output(ctx->c, out); + return 0; +} + +GIT_INLINE(void) git_hash_cleanup(git_hash_ctx *ctx) +{ + gnutls_hash_deinit(ctx->c, NULL); +} + +#endif |