diff --git a/include/git2/sys/stream.h b/include/git2/sys/stream.h index f773fdcd0..0f7e74c10 100644 --- a/include/git2/sys/stream.h +++ b/include/git2/sys/stream.h @@ -36,6 +36,14 @@ typedef struct { #define GIT_STREAM_CONNECT_OPTIONS_INIT \ { GIT_STREAM_CONNECT_OPTIONS_VERSION } +#ifdef GIT_WIN32 +typedef SOCKET git_socket_t; +# define GIT_SOCKET_INVALID INVALID_SOCKET +#else +typedef int git_socket_t; +# define GIT_SOCKET_INVALID -1 +#endif + /** * Every stream must have this struct as its first element, so the * API can talk to it. You'd define your stream as @@ -64,6 +72,7 @@ typedef struct git_stream { struct git_stream *, struct git_stream *in, const char *host); + git_socket_t GIT_CALLBACK(get_socket)(struct git_stream *); int GIT_CALLBACK(certificate)(git_cert **, struct git_stream *); ssize_t GIT_CALLBACK(read)(struct git_stream *, void *, size_t); ssize_t GIT_CALLBACK(write)(struct git_stream *, const char *, size_t, int); @@ -82,20 +91,6 @@ typedef struct { * @return 0 or an error code */ int GIT_CALLBACK(init)(git_stream **out); - - /** - * Called to create a new connection on top of the given stream. If - * this is a TLS stream, then this function may be used to proxy a - * TLS stream over an HTTP CONNECT session. If this is unset, then - * HTTP CONNECT proxies will not be supported. - * - * @param out The created stream - * @param in An existing stream to add TLS to - * @param host The hostname that the stream is connected to, - * for certificate validation - * @return 0 or an error code - */ - int GIT_CALLBACK(wrap)(git_stream **out, git_stream *in, const char *host); } git_stream_registration; /** diff --git a/src/libgit2/transports/http.c b/src/libgit2/transports/http.c index 62c149c7a..1e9739eeb 100644 --- a/src/libgit2/transports/http.c +++ b/src/libgit2/transports/http.c @@ -14,6 +14,7 @@ #include "smart.h" #include "http.h" #include "trace.h" +#include "transport.h" #include "streams/tls.h" #include "streams/socket.h" #include "net/auth.h" @@ -25,6 +26,9 @@ bool git_http__expect_continue = false; +extern int git_transport__timeout; +extern int git_transport__connect_timeout; + typedef enum { HTTP_STATE_NONE = 0, HTTP_STATE_SENDING_REQUEST, @@ -97,9 +101,6 @@ static const http_service receive_pack_service = { 1 }; -extern int git_transport__connect_timeout; -extern int git_transport__timeout; - #define SERVER_TYPE_REMOTE "remote" #define SERVER_TYPE_PROXY "proxy" @@ -695,8 +696,9 @@ static int http_action( GIT_ERROR_CHECK_ALLOC(stream); opts.user_agent = transport->user_agent.ptr; - opts.connect_timeout = git_transport__connect_timeout; + opts.timeout = git_transport__timeout; + opts.connect_timeout = git_transport__connect_timeout; opts.server_certificate_check_cb = connect_opts->callbacks.certificate_check; opts.server_certificate_check_payload = connect_opts->callbacks.payload; diff --git a/src/libgit2/transports/ssh.c b/src/libgit2/transports/ssh.c index 380b4e7af..d8ab68d0b 100644 --- a/src/libgit2/transports/ssh.c +++ b/src/libgit2/transports/ssh.c @@ -13,6 +13,7 @@ #include "runtime.h" #include "smart.h" +#include "transport.h" #include "streams/socket.h" #include "sysdir.h" #include "net/url.h" @@ -523,16 +524,22 @@ static int _git_ssh_session_create( LIBSSH2_KNOWNHOSTS **hosts, const char *hostname, int port, - git_stream *io) + git_stream *stream) { - git_stream_socket *socket = GIT_CONTAINER_OF(io, git_stream_socket, parent); LIBSSH2_SESSION *s; LIBSSH2_KNOWNHOSTS *known_hosts; + git_socket_t socket; git_str prefs = GIT_STR_INIT; int rc = 0; GIT_ASSERT_ARG(session); GIT_ASSERT_ARG(hosts); + GIT_ASSERT_ARG(stream); + + if ((socket = git_stream_get_socket(stream)) == GIT_SOCKET_INVALID) { + git_error_set(GIT_ERROR_NET, "could not get socket"); + return -1; + } s = libssh2_session_init(); if (!s) { @@ -559,7 +566,7 @@ static int _git_ssh_session_create( git_str_dispose(&prefs); do { - rc = libssh2_session_handshake(s, socket->s); + rc = libssh2_session_handshake(s, socket); } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc); if (rc != LIBSSH2_ERROR_NONE) { @@ -768,9 +775,10 @@ static int _git_ssh_setup_conn( int auth_methods, error = 0, port; ssh_stream *s; git_credential *cred = NULL; - LIBSSH2_SESSION *session=NULL; - LIBSSH2_CHANNEL *channel=NULL; + LIBSSH2_SESSION *session = NULL; + LIBSSH2_CHANNEL *channel = NULL; LIBSSH2_KNOWNHOSTS *known_hosts = NULL; + git_stream_connect_options opts = GIT_STREAM_CONNECT_OPTIONS_INIT; t->current_stream = NULL; @@ -782,9 +790,11 @@ static int _git_ssh_setup_conn( s->session = NULL; s->channel = NULL; + git_transport__set_connect_opts(&opts); + if ((error = git_net_url_parse_standard_or_scp(&s->url, url)) < 0 || - (error = git_stream_socket_new(&s->io, s->url.host, s->url.port)) < 0 || - (error = git_stream_connect(s->io)) < 0) + (error = git_stream_socket_new(&s->io)) < 0 || + (error = git_stream_connect(s->io, s->url.host, s->url.port, &opts)) < 0) goto done; /* diff --git a/src/util/stream.h b/src/util/stream.h index 4673ac8dd..968c63324 100644 --- a/src/util/stream.h +++ b/src/util/stream.h @@ -32,6 +32,11 @@ GIT_INLINE(int) git_stream_is_encrypted(git_stream *st) return st->encrypted; } +GIT_INLINE(GIT_SOCKET) git_stream_get_socket(git_stream *st) +{ + return st->get_socket(st); +} + GIT_INLINE(int) git_stream_certificate(git_cert **out, git_stream *st) { if (!st->encrypted) { diff --git a/src/util/streams/openssl.c b/src/util/streams/openssl.c index 55ae7c2db..cadf9f0c0 100644 --- a/src/util/streams/openssl.c +++ b/src/util/streams/openssl.c @@ -15,10 +15,9 @@ #include "git2_util.h" #include "runtime.h" -#include "settings.h" #include "posix.h" #include "stream.h" -#include "net.h" +#include "net/url.h" #include "streams/socket.h" #include "git2/transport.h" #include "git2/sys/openssl.h" @@ -36,7 +35,7 @@ # include #endif -SSL_CTX *git__ssl_ctx; +SSL_CTX *openssl_ctx; #define GIT_SSL_DEFAULT_CIPHERS "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES128-SHA256:DHE-DSS-AES256-SHA256:DHE-DSS-AES128-SHA:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA" @@ -55,9 +54,9 @@ static void shutdown_ssl(void) git_stream_bio_method = NULL; } - if (git__ssl_ctx) { - SSL_CTX_free(git__ssl_ctx); - git__ssl_ctx = NULL; + if (openssl_ctx) { + SSL_CTX_free(openssl_ctx); + openssl_ctx = NULL; } } @@ -105,7 +104,6 @@ static void git_openssl_free(void *mem) static int openssl_init(void) { long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; - const char *ciphers = git_libgit2__ssl_ciphers(); #ifdef VALGRIND static bool allocators_initialized = false; #endif @@ -138,19 +136,14 @@ static int openssl_init(void) * compatibility. We then disable SSL so we only allow OpenSSL * to speak TLSv1 to perform the encryption itself. */ - if (!(git__ssl_ctx = SSL_CTX_new(SSLv23_method()))) + if (!(openssl_ctx = SSL_CTX_new(SSLv23_method()))) goto error; - SSL_CTX_set_options(git__ssl_ctx, ssl_opts); - SSL_CTX_set_mode(git__ssl_ctx, SSL_MODE_AUTO_RETRY); - SSL_CTX_set_verify(git__ssl_ctx, SSL_VERIFY_NONE, NULL); - if (!SSL_CTX_set_default_verify_paths(git__ssl_ctx)) - goto error; + SSL_CTX_set_options(openssl_ctx, ssl_opts); + SSL_CTX_set_mode(openssl_ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_verify(openssl_ctx, SSL_VERIFY_NONE, NULL); - if (!ciphers) - ciphers = GIT_SSL_DEFAULT_CIPHERS; - - if(!SSL_CTX_set_cipher_list(git__ssl_ctx, ciphers)) + if (!SSL_CTX_set_default_verify_paths(openssl_ctx)) goto error; if (init_bio_method() < 0) @@ -161,8 +154,8 @@ static int openssl_init(void) error: git_error_set(GIT_ERROR_NET, "could not initialize openssl: %s", ERR_error_string(ERR_get_error(), NULL)); - SSL_CTX_free(git__ssl_ctx); - git__ssl_ctx = NULL; + SSL_CTX_free(openssl_ctx); + openssl_ctx = NULL; return -1; } @@ -503,14 +496,20 @@ typedef struct { git_cert_x509 cert_info; } openssl_stream; -static int openssl_connect(git_stream *stream) +static int openssl_create_session(openssl_stream *st, const char *host) { - int ret; BIO *bio; - openssl_stream *st = (openssl_stream *) stream; + int ret; - if (st->owned && (ret = git_stream_connect(st->io)) < 0) - return ret; + st->ssl = SSL_new(openssl_ctx); + + if (st->ssl == NULL) { + git_error_set(GIT_ERROR_SSL, "failed to create ssl object"); + return -1; + } + + st->host = git__strdup(host); + GIT_ERROR_CHECK_ALLOC(st->host); bio = BIO_new(git_stream_bio_method); GIT_ERROR_CHECK_ALLOC(bio); @@ -531,6 +530,33 @@ static int openssl_connect(git_stream *stream) return verify_server_cert(st->ssl, st->host); } +static int openssl_connect( + git_stream *stream, + const char *host, + const char *port, + const git_stream_connect_options *opts) +{ + openssl_stream *st = (openssl_stream *)stream; + + if (git_stream_socket_new(&st->io) < 0 || + git_stream_connect(st->io, host, port, opts) < 0) + return -1; + + st->owned = 1; + + return openssl_create_session(st, host); +} + +static int openssl_wrap(git_stream *stream, git_stream *in, const char *host) +{ + openssl_stream *st = (openssl_stream *)stream; + + st->io = in; + st->owned = 0; + + return openssl_create_session(st, host); +} + static int openssl_certificate(git_cert **out, git_stream *stream) { openssl_stream *st = (openssl_stream *) stream; @@ -622,37 +648,28 @@ static void openssl_free(git_stream *stream) git__free(st); } -static int openssl_stream_wrap( - git_stream **out, - git_stream *in, - const char *host, - int owned) +int git_stream_openssl_new(git_stream **out) { openssl_stream *st; GIT_ASSERT_ARG(out); - GIT_ASSERT_ARG(in); - GIT_ASSERT_ARG(host); + + if (openssl_ensure_initialized() < 0) + return -1; st = git__calloc(1, sizeof(openssl_stream)); GIT_ERROR_CHECK_ALLOC(st); - st->io = in; - st->owned = owned; - - st->ssl = SSL_new(git__ssl_ctx); - if (st->ssl == NULL) { + if ((st->ssl = SSL_new(openssl_ctx)) == NULL) { git_error_set(GIT_ERROR_SSL, "failed to create ssl object"); git__free(st); return -1; } - st->host = git__strdup(host); - GIT_ERROR_CHECK_ALLOC(st->host); - st->parent.version = GIT_STREAM_VERSION; st->parent.encrypted = 1; st->parent.connect = openssl_connect; + st->parent.wrap = openssl_wrap; st->parent.certificate = openssl_certificate; st->parent.read = openssl_read; st->parent.write = openssl_write; @@ -663,43 +680,12 @@ static int openssl_stream_wrap( return 0; } -int git_stream_openssl_wrap(git_stream **out, git_stream *in, const char *host) -{ - if (openssl_ensure_initialized() < 0) - return -1; - - return openssl_stream_wrap(out, in, host, 0); -} - -int git_stream_openssl_new(git_stream **out, const char *host, const char *port) -{ - git_stream *stream = NULL; - int error; - - GIT_ASSERT_ARG(out); - GIT_ASSERT_ARG(host); - GIT_ASSERT_ARG(port); - - if (openssl_ensure_initialized() < 0) - return -1; - - if ((error = git_stream_socket_new(&stream, host, port)) < 0) - return error; - - if ((error = openssl_stream_wrap(out, stream, host, 1)) < 0) { - git_stream_close(stream); - git_stream_free(stream); - } - - return error; -} - int git_openssl__set_cert_location(const char *file, const char *path) { if (openssl_ensure_initialized() < 0) return -1; - if (SSL_CTX_load_verify_locations(git__ssl_ctx, file, path) == 0) { + if (SSL_CTX_load_verify_locations(openssl_ctx, file, path) == 0) { char errmsg[256]; ERR_error_string_n(ERR_get_error(), errmsg, sizeof(errmsg)); @@ -711,6 +697,20 @@ int git_openssl__set_cert_location(const char *file, const char *path) return 0; } +int git_openssl__set_ciphers(const char *ciphers) +{ + if (!ciphers) + ciphers = GIT_SSL_DEFAULT_CIPHERS; + + if (openssl_ensure_initialized() < 0) + return -1; + + if(!SSL_CTX_set_cipher_list(openssl_ctx, GIT_SSL_DEFAULT_CIPHERS)) + return -1; + + return 0; +} + #else #include "stream.h" diff --git a/src/util/streams/openssl.h b/src/util/streams/openssl.h index 4f2ac3e2d..c5c11c1b3 100644 --- a/src/util/streams/openssl.h +++ b/src/util/streams/openssl.h @@ -23,9 +23,9 @@ extern int git_stream_openssl_global_init(void); # endif #ifdef GIT_HTTPS_OPENSSL +extern int git_openssl__set_ciphers(const char *ciphers); extern int git_openssl__set_cert_location(const char *file, const char *path); -extern int git_stream_openssl_new(git_stream **out, const char *host, const char *port); -extern int git_stream_openssl_wrap(git_stream **out, git_stream *in, const char *host); +extern int git_stream_openssl_new(git_stream **out); #endif #endif diff --git a/src/util/streams/registry.c b/src/util/streams/registry.c index 546763f7f..00d109302 100644 --- a/src/util/streams/registry.c +++ b/src/util/streams/registry.c @@ -108,7 +108,6 @@ int git_stream_register_tls( if (ctor) { registration.version = GIT_STREAM_VERSION; registration.init = ctor; - registration.wrap = NULL; return git_stream_register(GIT_STREAM_TLS, ®istration); } else { diff --git a/src/util/streams/socket.c b/src/util/streams/socket.c index 8e31868f6..a7c7d2fa4 100644 --- a/src/util/streams/socket.c +++ b/src/util/streams/socket.c @@ -243,6 +243,22 @@ done: return error; } +static int socket_wrap(git_stream *stream, git_stream *in, const char *host) +{ + GIT_UNUSED(stream); + GIT_UNUSED(in); + GIT_UNUSED(host); + + git_error_set(GIT_ERROR_NET, "cannot wrap a plaintext socket"); + return -1; +} + +static git_socket_t socket_get(git_stream *stream) +{ + git_stream_socket *st = (git_stream_socket *) stream; + return st->s; +} + static ssize_t socket_write( git_stream *stream, const char *data, @@ -350,6 +366,8 @@ static int default_socket_stream_new(git_stream **out) st->parent.version = GIT_STREAM_VERSION; st->parent.connect = socket_connect; + st->parent.wrap = socket_wrap; + st->parent.get_socket = socket_get; st->parent.write = socket_write; st->parent.read = socket_read; st->parent.close = socket_close;