streams: refactor as init / connect phase

Add a separate init and connect phase, instead of always connecting in
the constructor. This allows us to be more flexible in the future with
our streams implementations.
This commit is contained in:
Edward Thomson
2023-08-19 15:24:53 +01:00
parent b6e0866f99
commit 823d798bf4
29 changed files with 564 additions and 584 deletions

View File

@@ -3,6 +3,7 @@ include(SanitizeBool)
# 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)

View File

@@ -15,6 +15,35 @@ GIT_BEGIN_DECL
#define GIT_STREAM_VERSION 1
typedef struct {
unsigned int version;
/**
* Timeout for read and write operations; can be set to `0` to
* block indefinitely.
*/
int timeout;
/**
* Timeout to connect to the remote server; can be set to `0`
* to use the system defaults. This can be shorter than the
* system default - often 75 seconds - but cannot be longer.
*/
int connect_timeout;
} git_stream_connect_options;
#define GIT_STREAM_CONNECT_OPTIONS_VERSION 1
#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
@@ -29,22 +58,21 @@ GIT_BEGIN_DECL
typedef struct git_stream {
int version;
/**
* Nonzero if this is a TLS stream; zero if this is plain socket.
*/
int encrypted : 1;
/**
* Timeout for read and write operations; can be set to `0` to
* block indefinitely.
*/
int timeout;
/**
* Timeout to connect to the remote server; can be set to `0`
* to use the system defaults. This can be shorter than the
* system default - often 75 seconds - but cannot be longer.
*/
int connect_timeout;
int GIT_CALLBACK(connect)(struct git_stream *);
int GIT_CALLBACK(connect)(
struct git_stream *,
const char *host,
const char *port,
const git_stream_connect_options *opts);
int GIT_CALLBACK(wrap)(
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);
@@ -60,27 +88,9 @@ typedef struct {
* Called to create a new connection to a given host.
*
* @param out The created stream
* @param host The hostname to connect to; may be a hostname or
* IP address
* @param port The port to connect to; may be a port number or
* service name
* @return 0 or an error code
*/
int GIT_CALLBACK(init)(git_stream **out, const char *host, const char *port);
/**
* 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);
int GIT_CALLBACK(init)(git_stream **out);
} git_stream_registration;
/**
@@ -126,7 +136,7 @@ GIT_EXTERN(int) git_stream_register(
* @deprecated Provide a git_stream_registration to git_stream_register
* @see git_stream_registration
*/
typedef int GIT_CALLBACK(git_stream_cb)(git_stream **out, const char *host, const char *port);
typedef int GIT_CALLBACK(git_stream_cb)(git_stream **out);
/**
* Register a TLS stream constructor for the library to use. This stream

View File

@@ -45,7 +45,7 @@ set_target_properties(git2_cli PROPERTIES OUTPUT_NAME ${LIBGIT2_FILENAME})
ide_split_sources(git2_cli)
target_include_directories(git2_cli PRIVATE ${CLI_INCLUDES})
target_include_directories(git2_cli PRIVATE ${CLI_INCLUDES} ${LIBGIT2_SYSTEM_INCLUDES})
if(MSVC_IDE)
# Precompiled headers

View File

@@ -47,16 +47,14 @@ extern size_t git_indexer__max_objects;
extern bool git_disable_pack_keep_file_checks;
extern int git_odb__packed_priority;
extern int git_odb__loose_priority;
extern int git_stream_socket__connect_timeout;
extern int git_stream_socket__timeout;
char *git__user_agent;
char *git__ssl_ciphers;
int git_transport__connect_timeout;
int git_transport__timeout;
char *git_http__user_agent;
static void libgit2_settings_global_shutdown(void)
{
git__free(git__user_agent);
git__free(git__ssl_ciphers);
git__free(git_http__user_agent);
git_repository__free_extensions();
}
@@ -159,16 +157,6 @@ static int config_level_to_sysdir(int *out, int config_level)
return -1;
}
const char *git_libgit2__user_agent(void)
{
return git__user_agent;
}
const char *git_libgit2__ssl_ciphers(void)
{
return git__ssl_ciphers;
}
int git_libgit2_opts(int key, ...)
{
int error = 0;
@@ -287,9 +275,9 @@ int git_libgit2_opts(int key, ...)
#endif
break;
case GIT_OPT_SET_USER_AGENT:
git__free(git__user_agent);
git__user_agent = git__strdup(va_arg(ap, const char *));
if (!git__user_agent) {
git__free(git_http__user_agent);
git_http__user_agent = git__strdup(va_arg(ap, const char *));
if (!git_http__user_agent) {
git_error_set_oom();
error = -1;
}
@@ -305,15 +293,9 @@ int git_libgit2_opts(int key, ...)
break;
case GIT_OPT_SET_SSL_CIPHERS:
#if (GIT_OPENSSL || GIT_MBEDTLS)
{
git__free(git__ssl_ciphers);
git__ssl_ciphers = git__strdup(va_arg(ap, const char *));
if (!git__ssl_ciphers) {
git_error_set_oom();
error = -1;
}
}
#if (GIT_OPENSSL)
if (git_openssl__set_ciphers(va_arg(ap, const char *)) < 0)
error = -1;
#else
git_error_set(GIT_ERROR_SSL, "TLS backend doesn't support custom ciphers");
error = -1;
@@ -326,7 +308,7 @@ int git_libgit2_opts(int key, ...)
git_str str = GIT_STR_INIT;
if ((error = git_buf_tostr(&str, out)) < 0 ||
(error = git_str_puts(&str, git__user_agent)) < 0)
(error = git_str_puts(&str, git_http__user_agent)) < 0)
break;
error = git_buf_fromstr(out, &str);
@@ -439,7 +421,7 @@ int git_libgit2_opts(int key, ...)
break;
case GIT_OPT_GET_SERVER_CONNECT_TIMEOUT:
*(va_arg(ap, int *)) = git_stream_socket__connect_timeout;
*(va_arg(ap, int *)) = git_transport__connect_timeout;
break;
case GIT_OPT_SET_SERVER_CONNECT_TIMEOUT:
@@ -450,13 +432,13 @@ int git_libgit2_opts(int key, ...)
git_error_set(GIT_ERROR_INVALID, "invalid connect timeout");
error = -1;
} else {
git_stream_socket__connect_timeout = timeout;
git_transport__connect_timeout = timeout;
}
}
break;
case GIT_OPT_GET_SERVER_TIMEOUT:
*(va_arg(ap, int *)) = git_stream_socket__timeout;
*(va_arg(ap, int *)) = git_transport__timeout;
break;
case GIT_OPT_SET_SERVER_TIMEOUT:
@@ -467,7 +449,7 @@ int git_libgit2_opts(int key, ...)
git_error_set(GIT_ERROR_INVALID, "invalid timeout");
error = -1;
} else {
git_stream_socket__timeout = timeout;
git_transport__timeout = timeout;
}
}
break;

View File

@@ -1,11 +0,0 @@
/*
* 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.
*/
extern int git_settings_global_init(void);
extern const char *git_libgit2__user_agent(void);
extern const char *git_libgit2__ssl_ciphers(void);

22
src/libgit2/transport.h Normal file
View File

@@ -0,0 +1,22 @@
/*
* 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_transport_h__
#define INCLUDE_transport_h__
#include "common.h"
#include "git2/sys/stream.h"
extern int git_transport__timeout;
extern int git_transport__connect_timeout;
GIT_INLINE(void) git_transport__set_connect_opts(git_stream_connect_options *opts)
{
opts->timeout = git_transport__timeout;
opts->connect_timeout = git_transport__connect_timeout;
}
#endif

View File

@@ -150,8 +150,6 @@ static int git_proto_stream_alloc(
git_subtransport *t,
const char *url,
const char *cmd,
const char *host,
const char *port,
git_smart_subtransport_stream **stream)
{
git_proto_stream *s;
@@ -175,7 +173,7 @@ static int git_proto_stream_alloc(
return -1;
}
if ((git_stream_socket_new(&s->io, host, port)) < 0)
if ((git_stream_socket_new(&s->io)) < 0)
return -1;
GIT_ERROR_CHECK_VERSION(s->io, GIT_STREAM_VERSION, "git_stream");
@@ -206,24 +204,26 @@ static int _git_uploadpack_ls(
host = urldata.host;
port = urldata.port ? urldata.port : GIT_DEFAULT_PORT;
error = git_proto_stream_alloc(t, stream_url, cmd_uploadpack, host, port, stream);
git_net_url_dispose(&urldata);
error = git_proto_stream_alloc(t, stream_url, cmd_uploadpack, stream);
if (error < 0) {
git_proto_stream_free(*stream);
return error;
goto done;
}
s = (git_proto_stream *) *stream;
if ((error = git_stream_connect(s->io)) < 0) {
if ((error = git_stream_connect(s->io, host, port, NULL)) < 0) {
git_proto_stream_free(*stream);
return error;
goto done;
}
t->current_stream = s;
error = 0;
return 0;
done:
git_net_url_dispose(&urldata);
return error;
}
static int _git_uploadpack(
@@ -259,23 +259,25 @@ static int _git_receivepack_ls(
if ((error = git_net_url_parse(&urldata, url)) < 0)
return error;
error = git_proto_stream_alloc(t, stream_url, cmd_receivepack, urldata.host, urldata.port, stream);
error = git_proto_stream_alloc(t, stream_url, cmd_receivepack, stream);
git_net_url_dispose(&urldata);
if (error < 0) {
git_proto_stream_free(*stream);
return error;
goto done;
}
s = (git_proto_stream *) *stream;
if ((error = git_stream_connect(s->io)) < 0)
return error;
if ((error = git_stream_connect(s->io, urldata.host, urldata.port, NULL)) < 0)
goto done;
t->current_stream = s;
error = 0;
return 0;
done:
git_net_url_dispose(&urldata);
return error;
}
static int _git_receivepack(

View File

@@ -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,
@@ -692,6 +696,10 @@ static int http_action(
GIT_ERROR_CHECK_ALLOC(stream);
opts.user_agent = transport->user_agent.ptr;
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;
opts.proxy_certificate_check_cb = connect_opts->proxy_opts.certificate_check;
@@ -759,7 +767,7 @@ int git_smart_subtransport_http(git_smart_subtransport **out, git_transport *own
transport = git__calloc(sizeof(http_subtransport), 1);
GIT_ERROR_CHECK_ALLOC(transport);
if (git_http__user_agent(&transport->user_agent) < 0)
if (git_http__append_user_agent(&transport->user_agent) < 0)
return -1;
transport->owner = (transport_smart *)owner;

View File

@@ -8,16 +8,16 @@
#ifndef INCLUDE_transports_http_h__
#define INCLUDE_transports_http_h__
#include "settings.h"
#include "net/httpclient.h"
#define GIT_HTTP_REPLAY_MAX 15
extern bool git_http__expect_continue;
extern char *git_http__user_agent;
GIT_INLINE(int) git_http__user_agent(git_str *buf)
GIT_INLINE(int) git_http__append_user_agent(git_str *buf)
{
const char *ua = git_libgit2__user_agent();
const char *ua = git_http__user_agent;
if (!ua)
ua = "libgit2 " LIBGIT2_VERSION;

View File

@@ -13,6 +13,7 @@
#include "runtime.h"
#include "smart.h"
#include "transport.h"
#include "streams/socket.h"
#include "sysdir.h"
#include "net/url.h"
@@ -521,16 +522,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) {
@@ -557,7 +564,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) {
@@ -766,9 +773,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;
@@ -780,6 +788,8 @@ static int _git_ssh_setup_conn(
s->session = NULL;
s->channel = NULL;
git_transport__set_connect_opts(&opts);
if (git_net_str_is_url(url))
error = git_net_url_parse(&s->url, url);
else
@@ -788,8 +798,8 @@ static int _git_ssh_setup_conn(
if (error < 0)
goto done;
if ((error = git_stream_socket_new(&s->io, s->url.host, s->url.port)) < 0 ||
(error = git_stream_connect(s->io)) < 0)
if ((error = git_stream_socket_new(&s->io) < 0) ||
(error = git_stream_connect(s->io, s->url.host, s->url.port, &opts)) < 0)
goto done;
/*

View File

@@ -788,7 +788,7 @@ static int winhttp_connect(
}
if (git_http__user_agent(&ua) < 0)
if (git_http__append_user_agent(&ua) < 0)
goto on_error;
if (git_utf8_to_16_alloc(&wide_ua, git_str_cstr(&ua)) < 0) {

View File

@@ -817,12 +817,22 @@ static int check_certificate(
static int server_connect_stream(
git_http_server *server,
git_transport_certificate_check_cb cert_cb,
void *cb_payload)
git_stream *proxy_stream,
git_http_client_options *opts)
{
git_stream_connect_options connect_opts = GIT_STREAM_CONNECT_OPTIONS_INIT;
git_transport_certificate_check_cb cert_cb = opts->server_certificate_check_cb;
void *cb_payload = opts->server_certificate_check_payload;
git_net_url *url = &server->url;
int error;
error = git_stream_connect(server->stream);
connect_opts.connect_timeout = opts->connect_timeout;
connect_opts.timeout = opts->timeout;
if (proxy_stream)
error = git_stream_wrap(server->stream, proxy_stream, url->host);
else
error = git_stream_connect(server->stream, url->host, url->port, &connect_opts);
if (error && error != GIT_ECERTIFICATE)
return error;
@@ -929,9 +939,9 @@ GIT_INLINE(int) server_create_stream(git_http_server *server)
git_net_url *url = &server->url;
if (strcasecmp(url->scheme, "https") == 0)
return git_stream_tls_new(&server->stream, url->host, url->port);
return git_stream_tls_new(&server->stream);
else if (strcasecmp(url->scheme, "http") == 0)
return git_stream_socket_new(&server->stream, url->host, url->port);
return git_stream_socket_new(&server->stream);
git_error_set(GIT_ERROR_HTTP, "unknown http scheme '%s'", url->scheme);
return -1;
@@ -961,8 +971,7 @@ static int proxy_connect(
if ((error = server_create_stream(&client->proxy)) < 0 ||
(error = server_connect_stream(&client->proxy,
client->opts.proxy_certificate_check_cb,
client->opts.proxy_certificate_check_payload)) < 0)
NULL, &client->opts)) < 0)
goto done;
client->proxy_connected = 1;
@@ -1004,25 +1013,14 @@ done:
static int server_connect(git_http_client *client)
{
git_net_url *url = &client->server.url;
git_transport_certificate_check_cb cert_cb;
void *cert_payload;
int error;
client->current_server = SERVER;
if (client->proxy.stream)
error = git_stream_tls_wrap(&client->server.stream, client->proxy.stream, url->host);
else
error = server_create_stream(&client->server);
if (error < 0)
if ((error = server_create_stream(&client->server)) < 0)
goto done;
cert_cb = client->opts.server_certificate_check_cb;
cert_payload = client->opts.server_certificate_check_payload;
error = server_connect_stream(&client->server, cert_cb, cert_payload);
error = server_connect_stream(&client->server, client->proxy.stream, &client->opts);
done:
return error;

View File

@@ -72,6 +72,12 @@ typedef struct {
/** The value of the `User-Agent` header to send */
const char *user_agent;
/** Timeout for connecting */
int connect_timeout;
/** Timeout for I/O */
int timeout;
/** Certificate check callback for the remote */
git_transport_certificate_check_cb server_certificate_check_cb;
void *server_certificate_check_payload;

View File

@@ -10,9 +10,21 @@
#include "git2_util.h"
#include "git2/sys/stream.h"
GIT_INLINE(int) git_stream_connect(git_stream *st)
GIT_INLINE(int) git_stream_connect(
git_stream *st,
const char *host,
const char *port,
const git_stream_connect_options *opts)
{
return st->connect(st);
return st->connect(st, host, port, opts);
}
GIT_INLINE(int) git_stream_wrap(
git_stream *st,
git_stream *in,
const char *host)
{
return st->wrap(st, in, host);
}
GIT_INLINE(int) git_stream_is_encrypted(git_stream *st)
@@ -20,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) {

View File

@@ -220,10 +220,16 @@ 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 */
git_error_set(GIT_ERROR_SSL, "the SSL certificate is invalid: %#04x - %s", ret, vrfy_buf);
char buf[512];
int len = mbedtls_x509_crt_verify_info(buf, sizeof(buf),
"", ret);
if (len >= 1) {
buf[0] = tolower(buf[0]);
buf[len - 1] = '\0'; /* Remove trailing \n */
}
git_error_set(GIT_ERROR_SSL, "%s", buf);
return GIT_ECERTIFICATE;
}
@@ -236,29 +242,60 @@ typedef struct {
int owned;
bool connected;
char *host;
mbedtls_ssl_context *ssl;
mbedtls_ssl_context ssl;
git_cert_x509 cert_info;
} mbedtls_stream;
static int mbedtls_connect(git_stream *stream)
static int mbedtls_setup(mbedtls_stream *st, const char *host)
{
int ret;
mbedtls_ssl_init(&st->ssl);
if (mbedtls_ssl_setup(&st->ssl, git__ssl_conf)) {
git_error_set(GIT_ERROR_SSL, "failed to create ssl object");
return -1;
}
st->connected = 1;
mbedtls_ssl_set_bio(&st->ssl, st->io, bio_write, bio_read, NULL);
if ((ret = mbedtls_ssl_set_hostname(&st->ssl, host) != 0) ||
(ret = mbedtls_ssl_handshake(&st->ssl)) != 0)
return ssl_set_error(&st->ssl, ret);
return verify_server_cert(&st->ssl);
}
static int mbedtls_connect(
git_stream *stream,
const char *host,
const char *port,
const git_stream_connect_options *opts)
{
mbedtls_stream *st = (mbedtls_stream *) stream;
if (st->owned && (ret = git_stream_connect(st->io)) < 0)
return ret;
st->owned = 1;
st->connected = true;
if (git_stream_socket_new(&st->io) < 0 ||
git_stream_connect(st->io, host, port, opts) < 0)
return -1;
mbedtls_ssl_set_hostname(st->ssl, st->host);
return mbedtls_setup(st, host);
}
mbedtls_ssl_set_bio(st->ssl, st->io, bio_write, bio_read, NULL);
static int mbedtls_wrap(
git_stream *stream,
git_stream *in,
const char *host)
{
mbedtls_stream *st = (mbedtls_stream *) stream;
if ((ret = mbedtls_ssl_handshake(st->ssl)) != 0)
return ssl_set_error(st->ssl, ret);
st->io = in;
st->owned = 0;
return verify_server_cert(st->ssl);
return mbedtls_setup(st, host);
}
static int mbedtls_certificate(git_cert **out, git_stream *stream)
@@ -266,7 +303,7 @@ static 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);
const mbedtls_x509_crt *cert = mbedtls_ssl_get_peer_cert(&st->ssl);
if (!cert) {
git_error_set(GIT_ERROR_SSL, "the server did not provide a certificate");
return -1;
@@ -305,8 +342,8 @@ static ssize_t mbedtls_stream_write(git_stream *stream, const char *data, size_t
*/
len = min(len, INT_MAX);
if ((written = mbedtls_ssl_write(st->ssl, (const unsigned char *)data, len)) <= 0)
return ssl_set_error(st->ssl, written);
if ((written = mbedtls_ssl_write(&st->ssl, (const unsigned char *)data, len)) <= 0)
return ssl_set_error(&st->ssl, written);
return written;
}
@@ -316,8 +353,8 @@ static 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);
if ((ret = mbedtls_ssl_read(&st->ssl, (unsigned char *)data, len)) <= 0)
ssl_set_error(&st->ssl, ret);
return ret;
}
@@ -327,10 +364,10 @@ static 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)
if (st->connected && (ret = ssl_teardown(&st->ssl)) != 0)
return -1;
st->connected = false;
st->connected = 0;
return st->owned ? git_stream_close(st->io) : 0;
}
@@ -342,90 +379,30 @@ static void mbedtls_stream_free(git_stream *stream)
if (st->owned)
git_stream_free(st->io);
git__free(st->host);
git__free(st->cert_info.data);
mbedtls_ssl_free(st->ssl);
git__free(st->ssl);
mbedtls_ssl_free(&st->ssl);
git__free(st);
}
static int mbedtls_stream_wrap(
git_stream **out,
git_stream *in,
const char *host,
int owned)
int git_stream_mbedtls_new(git_stream **out)
{
mbedtls_stream *st;
int error;
st = git__calloc(1, sizeof(mbedtls_stream));
GIT_ERROR_CHECK_ALLOC(st);
st->io = in;
st->owned = owned;
st->ssl = git__malloc(sizeof(mbedtls_ssl_context));
GIT_ERROR_CHECK_ALLOC(st->ssl);
mbedtls_ssl_init(st->ssl);
if (mbedtls_ssl_setup(st->ssl, git__ssl_conf)) {
git_error_set(GIT_ERROR_SSL, "failed to create ssl object");
error = -1;
goto out_err;
}
st->host = git__strdup(host);
GIT_ERROR_CHECK_ALLOC(st->host);
st->parent.version = GIT_STREAM_VERSION;
st->parent.encrypted = 1;
st->parent.connect = mbedtls_connect;
st->parent.wrap = mbedtls_wrap;
st->parent.certificate = mbedtls_certificate;
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;
*out = (git_stream *)st;
return 0;
out_err:
mbedtls_ssl_free(st->ssl);
git_stream_close(st->io);
git_stream_free(st->io);
git__free(st);
return error;
}
int git_stream_mbedtls_wrap(
git_stream **out,
git_stream *in,
const char *host)
{
return mbedtls_stream_wrap(out, in, host, 0);
}
int git_stream_mbedtls_new(
git_stream **out,
const char *host,
const char *port)
{
git_stream *stream;
int error;
GIT_ASSERT_ARG(out);
GIT_ASSERT_ARG(host);
GIT_ASSERT_ARG(port);
if ((error = git_stream_socket_new(&stream, host, port)) < 0)
return error;
if ((error = mbedtls_stream_wrap(out, stream, host, 1)) < 0) {
git_stream_close(stream);
git_stream_free(stream);
}
return error;
}
int git_mbedtls__set_cert_location(const char *file, const char *path)

View File

@@ -13,11 +13,12 @@
extern int git_stream_mbedtls_global_init(void);
#ifdef GIT_HTTPS_MBEDTLS
# ifdef GIT_HTTPS_MBEDTLS
extern int git_mbedtls__set_cert_location(const char *file, const char *path);
extern int git_stream_mbedtls_new(git_stream **out, const char *host, const char *port);
extern int git_stream_mbedtls_wrap(git_stream **out, git_stream *in, const char *host);
#endif
extern int git_stream_mbedtls_new(git_stream **out);
# endif
#endif

View File

@@ -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 <openssl/bio.h>
#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,20 +136,18 @@ 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))
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 (!SSL_CTX_set_default_verify_paths(openssl_ctx))
goto error;
if (!ciphers)
ciphers = GIT_SSL_DEFAULT_CIPHERS;
if(!SSL_CTX_set_cipher_list(git__ssl_ctx, ciphers))
goto error;
if (!SSL_CTX_set_cipher_list(openssl_ctx, GIT_SSL_DEFAULT_CIPHERS))
return -1;
if (init_bio_method() < 0)
goto error;
@@ -161,8 +157,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 +499,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 +533,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 +651,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 +683,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 +700,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, ciphers))
return -1;
return 0;
}
#else
#include "stream.h"

View File

@@ -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

View File

@@ -101,14 +101,13 @@ int git_stream_register(git_stream_t type, git_stream_registration *registration
#ifndef GIT_DEPRECATE_HARD
int git_stream_register_tls(
int GIT_CALLBACK(ctor)(git_stream **out, const char *host, const char *port))
int GIT_CALLBACK(ctor)(git_stream **out))
{
git_stream_registration registration = {0};
if (ctor) {
registration.version = GIT_STREAM_VERSION;
registration.init = ctor;
registration.wrap = NULL;
return git_stream_register(GIT_STREAM_TLS, &registration);
} else {

View File

@@ -42,8 +42,8 @@ typedef enum {
typedef struct {
git_stream parent;
git_stream *io;
int owned;
bool connected;
int owned : 1,
connected : 1;
wchar_t *host_w;
schannel_state state;
@@ -70,9 +70,6 @@ static int connect_context(schannel_stream *st)
ssize_t read_len;
int error = 0;
if (st->owned && (error = git_stream_connect(st->io)) < 0)
return error;
cred.dwVersion = SCHANNEL_CRED_VERSION;
cred.dwFlags = SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
SCH_CRED_IGNORE_REVOCATION_OFFLINE |
@@ -357,14 +354,23 @@ static int check_certificate(schannel_stream* st)
return 0;
}
static int schannel_connect(git_stream *stream)
static int schannel_connect(
git_stream *stream,
const char *host,
const char *port,
const git_stream_connect_options *opts)
{
schannel_stream *st = (schannel_stream *)stream;
int error;
GIT_ASSERT(st->state == STATE_NONE);
if ((error = connect_context(st)) < 0 ||
st->owned = 1;
if ((error = git_stream_socket_new(&st->io)) < 0 ||
(error = git_stream_connect(st->io, host, port, opts)) < 0 ||
(error = git_utf8_to_16_alloc(&st->host_w, host)) < 0 ||
(error = connect_context(st)) < 0 ||
(error = check_certificate(st)) < 0)
return error;
@@ -372,6 +378,24 @@ static int schannel_connect(git_stream *stream)
return 0;
}
static int schannel_wrap(git_stream *stream, git_stream *in, const char *host)
{
schannel_stream *st = (schannel_stream *)stream;
GIT_ASSERT(st->state == STATE_NONE);
st->io = in;
st->owned = 0;
if (git_utf8_to_16_alloc(&st->host_w, host) < 0 ||
connect_context(st) < 0 ||
check_certificate(st) < 0)
return -1;
st->connected = 1;
return 0;
}
static int schannel_certificate(git_cert **out, git_stream *stream)
{
schannel_stream *st = (schannel_stream *)stream;
@@ -603,7 +627,7 @@ static int schannel_close(git_stream *stream)
}
}
st->connected = false;
st->connected = 0;
if (st->owned && git_stream_close(st->io) < 0)
error = -1;
@@ -639,28 +663,19 @@ static void schannel_free(git_stream *stream)
git__free(st);
}
static int schannel_stream_wrap(
git_stream **out,
git_stream *in,
const char *host,
int owned)
extern int git_stream_schannel_new(git_stream **out)
{
schannel_stream *st;
GIT_ASSERT_ARG(out);
st = git__calloc(1, sizeof(schannel_stream));
GIT_ERROR_CHECK_ALLOC(st);
st->io = in;
st->owned = owned;
if (git_utf8_to_16_alloc(&st->host_w, host) < 0) {
git__free(st);
return -1;
}
st->parent.version = GIT_STREAM_VERSION;
st->parent.encrypted = 1;
st->parent.connect = schannel_connect;
st->parent.wrap = schannel_wrap;
st->parent.certificate = schannel_certificate;
st->parent.read = schannel_read;
st->parent.write = schannel_write;
@@ -671,35 +686,4 @@ static int schannel_stream_wrap(
return 0;
}
extern int git_stream_schannel_new(
git_stream **out,
const char *host,
const char *port)
{
git_stream *stream;
int error;
GIT_ASSERT_ARG(out);
GIT_ASSERT_ARG(host);
GIT_ASSERT_ARG(port);
if ((error = git_stream_socket_new(&stream, host, port)) < 0)
return error;
if ((error = schannel_stream_wrap(out, stream, host, 1)) < 0) {
git_stream_close(stream);
git_stream_free(stream);
}
return error;
}
extern int git_stream_schannel_wrap(
git_stream **out,
git_stream *in,
const char *host)
{
return schannel_stream_wrap(out, in, host, 0);
}
#endif

View File

@@ -13,15 +13,7 @@
#ifdef GIT_HTTPS_SCHANNEL
extern int git_stream_schannel_new(
git_stream **out,
const char *host,
const char *port);
extern int git_stream_schannel_wrap(
git_stream **out,
git_stream *in,
const char *host);
extern int git_stream_schannel_new(git_stream **out);
#endif

View File

@@ -50,57 +50,6 @@ typedef struct {
git_cert_x509 cert_info;
} securetransport_stream;
static int securetransport_connect(git_stream *stream)
{
securetransport_stream *st = (securetransport_stream *) stream;
int error;
SecTrustRef trust = NULL;
SecTrustResultType sec_res;
OSStatus ret;
if (st->owned && (error = git_stream_connect(st->io)) < 0)
return error;
ret = SSLHandshake(st->ctx);
if (ret != errSSLServerAuthCompleted && st->error != 0)
return -1;
else if (ret != errSSLServerAuthCompleted) {
git_error_set(GIT_ERROR_SSL, "unexpected return value from ssl handshake %d", (int)ret);
return -1;
}
if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr)
goto on_error;
if (!trust)
return GIT_ECERTIFICATE;
if ((ret = SecTrustEvaluate(trust, &sec_res)) != noErr)
goto on_error;
CFRelease(trust);
if (sec_res == kSecTrustResultInvalid || sec_res == kSecTrustResultOtherError) {
git_error_set(GIT_ERROR_SSL, "internal security trust error");
return -1;
}
if (sec_res == kSecTrustResultDeny || sec_res == kSecTrustResultRecoverableTrustFailure ||
sec_res == kSecTrustResultFatalTrustFailure) {
git_error_set(GIT_ERROR_SSL, "untrusted connection error");
return GIT_ECERTIFICATE;
}
return 0;
on_error:
if (trust)
CFRelease(trust);
return securetransport_error(ret);
}
static int securetransport_certificate(git_cert **out, git_stream *stream)
{
securetransport_stream *st = (securetransport_stream *) stream;
@@ -160,7 +109,11 @@ static OSStatus write_cb(SSLConnectionRef conn, const void *data, size_t *len)
return noErr;
}
static ssize_t securetransport_write(git_stream *stream, const char *data, size_t len, int flags)
static ssize_t securetransport_write(
git_stream *stream,
const char *data,
size_t len,
int flags)
{
securetransport_stream *st = (securetransport_stream *) stream;
size_t data_len, processed;
@@ -220,7 +173,10 @@ static OSStatus read_cb(SSLConnectionRef conn, void *data, size_t *len)
return error;
}
static ssize_t securetransport_read(git_stream *stream, void *data, size_t len)
static ssize_t securetransport_read(
git_stream *stream,
void *data,
size_t len)
{
securetransport_stream *st = (securetransport_stream *)stream;
size_t processed;
@@ -236,12 +192,137 @@ static ssize_t securetransport_read(git_stream *stream, void *data, size_t len)
return processed;
}
static int securetransport_create_context(
securetransport_stream *st,
const char *host)
{
SecTrustRef trust = NULL;
SecTrustResultType sec_res;
OSStatus ret;
int error = -1;
st->ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType);
if (!st->ctx) {
git_error_set(GIT_ERROR_NET, "failed to create SSL context");
return -1;
}
/* Set up context */
if ((ret = SSLSetIOFuncs(st->ctx, read_cb, write_cb)) != noErr ||
(ret = SSLSetConnection(st->ctx, st)) != noErr ||
(ret = SSLSetSessionOption(st->ctx, kSSLSessionOptionBreakOnServerAuth, true)) != noErr ||
(ret = SSLSetProtocolVersionMin(st->ctx, kTLSProtocol1)) != noErr ||
(ret = SSLSetProtocolVersionMax(st->ctx, kTLSProtocol12)) != noErr ||
(ret = SSLSetPeerDomainName(st->ctx, host, strlen(host))) != noErr) {
error = securetransport_error(ret);
goto on_error;
}
/* Connect */
ret = SSLHandshake(st->ctx);
if (ret != errSSLServerAuthCompleted && st->error != 0) {
error = -1;
goto on_error;
} else if (ret != errSSLServerAuthCompleted) {
git_error_set(GIT_ERROR_SSL, "unexpected return value from ssl handshake %d", (int)ret);
error = -1;
goto on_error;
}
if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr) {
error = securetransport_error(ret);
goto on_error;
}
if (!trust)
return GIT_ECERTIFICATE;
if ((ret = SecTrustEvaluate(trust, &sec_res)) != noErr) {
error = securetransport_error(ret);
goto on_error;
}
CFRelease(trust);
if (sec_res == kSecTrustResultInvalid ||
sec_res == kSecTrustResultOtherError) {
git_error_set(GIT_ERROR_SSL, "internal security trust error");
error = -1;
goto on_error;
}
if (sec_res == kSecTrustResultDeny ||
sec_res == kSecTrustResultRecoverableTrustFailure ||
sec_res == kSecTrustResultFatalTrustFailure) {
git_error_set(GIT_ERROR_SSL, "untrusted connection error");
return GIT_ECERTIFICATE;
}
return 0;
on_error:
if (trust)
CFRelease(trust);
if (st->ctx) {
CFRelease(st->ctx);
st->ctx = NULL;
}
return error;
}
int securetransport_connect(
git_stream *stream,
const char *host,
const char *port,
const git_stream_connect_options *opts)
{
securetransport_stream *st = (securetransport_stream *)stream;
GIT_ASSERT_ARG(stream);
GIT_ASSERT_ARG(host);
GIT_ASSERT_ARG(port);
if (git_stream_socket_new(&st->io) < 0)
return -1;
st->owned = 1;
if (git_stream_connect(st->io, host, port, opts) < 0)
return -1;
return securetransport_create_context(st, host);
}
int securetransport_wrap(
git_stream *stream,
git_stream *in,
const char *host)
{
securetransport_stream *st = (securetransport_stream *)stream;
GIT_ASSERT_ARG(stream);
GIT_ASSERT_ARG(in);
GIT_ASSERT_ARG(host);
st->io = in;
st->owned = 0;
return securetransport_create_context(st, host);
}
static int securetransport_close(git_stream *stream)
{
securetransport_stream *st = (securetransport_stream *) stream;
securetransport_stream *st = (securetransport_stream *)stream;
OSStatus ret;
ret = SSLClose(st->ctx);
if (ret != noErr && ret != errSSLClosedGraceful)
return securetransport_error(ret);
@@ -250,94 +331,41 @@ static int securetransport_close(git_stream *stream)
static void securetransport_free(git_stream *stream)
{
securetransport_stream *st = (securetransport_stream *) stream;
securetransport_stream *st = (securetransport_stream *)stream;
if (st->owned)
git_stream_free(st->io);
CFRelease(st->ctx);
if (st->ctx)
CFRelease(st->ctx);
if (st->der_data)
CFRelease(st->der_data);
git__free(st);
}
static int securetransport_wrap(
git_stream **out,
git_stream *in,
const char *host,
int owned)
int git_stream_securetransport_new(git_stream **out)
{
securetransport_stream *st;
OSStatus ret;
GIT_ASSERT_ARG(out);
GIT_ASSERT_ARG(in);
GIT_ASSERT_ARG(host);
st = git__calloc(1, sizeof(securetransport_stream));
GIT_ERROR_CHECK_ALLOC(st);
st->io = in;
st->owned = owned;
st->ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType);
if (!st->ctx) {
git_error_set(GIT_ERROR_NET, "failed to create SSL context");
git__free(st);
return -1;
}
if ((ret = SSLSetIOFuncs(st->ctx, read_cb, write_cb)) != noErr ||
(ret = SSLSetConnection(st->ctx, st)) != noErr ||
(ret = SSLSetSessionOption(st->ctx, kSSLSessionOptionBreakOnServerAuth, true)) != noErr ||
(ret = SSLSetProtocolVersionMin(st->ctx, kTLSProtocol1)) != noErr ||
(ret = SSLSetProtocolVersionMax(st->ctx, kTLSProtocol12)) != noErr ||
(ret = SSLSetPeerDomainName(st->ctx, host, strlen(host))) != noErr) {
CFRelease(st->ctx);
git__free(st);
return securetransport_error(ret);
}
st->parent.version = GIT_STREAM_VERSION;
st->parent.encrypted = 1;
st->parent.connect = securetransport_connect;
st->parent.wrap = securetransport_wrap;
st->parent.certificate = securetransport_certificate;
st->parent.read = securetransport_read;
st->parent.write = securetransport_write;
st->parent.close = securetransport_close;
st->parent.free = securetransport_free;
*out = (git_stream *) st;
*out = (git_stream *)st;
return 0;
}
int git_stream_securetransport_wrap(
git_stream **out,
git_stream *in,
const char *host)
{
return securetransport_wrap(out, in, host, 0);
}
int git_stream_securetransport_new(git_stream **out, const char *host, const char *port)
{
git_stream *stream = NULL;
int error;
GIT_ASSERT_ARG(out);
GIT_ASSERT_ARG(host);
error = git_stream_socket_new(&stream, host, port);
if (!error)
error = securetransport_wrap(out, stream, host, 1);
if (error < 0 && stream) {
git_stream_close(stream);
git_stream_free(stream);
}
return error;
}
#endif

View File

@@ -13,8 +13,7 @@
#ifdef GIT_HTTPS_SECURETRANSPORT
extern int git_stream_securetransport_new(git_stream **out, const char *host, const char *port);
extern int git_stream_securetransport_wrap(git_stream **out, git_stream *in, const char *host);
extern int git_stream_securetransport_new(git_stream **out);
#endif

View File

@@ -28,11 +28,10 @@
# endif
#endif
int git_stream_socket__connect_timeout = 0;
int git_stream_socket__timeout = 0;
typedef struct {
git_stream parent;
int connect_timeout;
int timeout;
char *host;
char *port;
GIT_SOCKET s;
@@ -173,7 +172,11 @@ static int connect_with_timeout(
return 0;
}
static int socket_connect(git_stream *stream)
static int socket_connect(
git_stream *stream,
const char *host,
const char *port,
const git_stream_connect_options *opts)
{
git_stream_socket *st = (git_stream_socket *) stream;
GIT_SOCKET s = INVALID_SOCKET;
@@ -181,14 +184,23 @@ static int socket_connect(git_stream *stream)
struct addrinfo hints;
int error;
GIT_ASSERT_ARG(stream);
GIT_ASSERT_ARG(host);
GIT_ASSERT_ARG(port);
if (opts) {
st->timeout = opts->timeout;
st->connect_timeout = opts->connect_timeout;
}
memset(&hints, 0x0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_UNSPEC;
if ((error = p_getaddrinfo(st->host, st->port, &hints, &info)) != 0) {
if ((error = p_getaddrinfo(host, port, &hints, &info)) != 0) {
git_error_set(GIT_ERROR_NET,
"failed to resolve address for %s: %s",
st->host, p_gai_strerror(error));
host, p_gai_strerror(error));
return -1;
}
@@ -200,7 +212,7 @@ static int socket_connect(git_stream *stream)
error = connect_with_timeout(s, p->ai_addr,
(socklen_t)p->ai_addrlen,
st->parent.connect_timeout);
st->connect_timeout);
if (error == 0)
break;
@@ -216,14 +228,14 @@ static int socket_connect(git_stream *stream)
/* Oops, we couldn't connect to any address */
if (s == INVALID_SOCKET) {
if (error == GIT_TIMEOUT)
git_error_set(GIT_ERROR_NET, "failed to connect to %s: Operation timed out", st->host);
git_error_set(GIT_ERROR_NET, "failed to connect to %s: Operation timed out", host);
else
git_error_set(GIT_ERROR_OS, "failed to connect to %s", st->host);
git_error_set(GIT_ERROR_OS, "failed to connect to %s", host);
error = -1;
goto done;
}
if (st->parent.timeout && !st->parent.connect_timeout &&
if (st->timeout && !st->connect_timeout &&
(error = set_nonblocking(s)) < 0)
return error;
@@ -235,6 +247,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,
@@ -250,13 +278,13 @@ static ssize_t socket_write(
ret = p_send(st->s, data, len, 0);
if (st->parent.timeout && ret < 0 &&
if (st->timeout && ret < 0 &&
(errno == EAGAIN || errno != EWOULDBLOCK)) {
fd.fd = st->s;
fd.events = POLLOUT;
fd.revents = 0;
ret = p_poll(&fd, 1, st->parent.timeout);
ret = p_poll(&fd, 1, st->timeout);
if (ret == 1) {
ret = p_send(st->s, data, len, 0);
@@ -286,13 +314,13 @@ static ssize_t socket_read(
ret = p_recv(st->s, data, len, 0);
if (st->parent.timeout && ret < 0 &&
if (st->timeout && ret < 0 &&
(errno == EAGAIN || errno != EWOULDBLOCK)) {
fd.fd = st->s;
fd.events = POLLIN;
fd.revents = 0;
ret = p_poll(&fd, 1, st->parent.timeout);
ret = p_poll(&fd, 1, st->timeout);
if (ret == 1) {
ret = p_recv(st->s, data, len, 0);
@@ -331,32 +359,19 @@ static void socket_free(git_stream *stream)
git__free(st);
}
static int default_socket_stream_new(
git_stream **out,
const char *host,
const char *port)
static int default_socket_stream_new(git_stream **out)
{
git_stream_socket *st;
GIT_ASSERT_ARG(out);
GIT_ASSERT_ARG(host);
GIT_ASSERT_ARG(port);
st = git__calloc(1, sizeof(git_stream_socket));
GIT_ERROR_CHECK_ALLOC(st);
st->host = git__strdup(host);
GIT_ERROR_CHECK_ALLOC(st->host);
if (port) {
st->port = git__strdup(port);
GIT_ERROR_CHECK_ALLOC(st->port);
}
st->parent.version = GIT_STREAM_VERSION;
st->parent.timeout = git_stream_socket__timeout;
st->parent.connect_timeout = git_stream_socket__connect_timeout;
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;
@@ -367,18 +382,13 @@ static int default_socket_stream_new(
return 0;
}
int git_stream_socket_new(
git_stream **out,
const char *host,
const char *port)
int git_stream_socket_new(git_stream **out)
{
int (*init)(git_stream **, const char *, const char *) = NULL;
int (*init)(git_stream **) = NULL;
git_stream_registration custom = {0};
int error;
GIT_ASSERT_ARG(out);
GIT_ASSERT_ARG(host);
GIT_ASSERT_ARG(port);
if ((error = git_stream_registry_lookup(&custom, GIT_STREAM_STANDARD)) == 0)
init = custom.init;
@@ -392,7 +402,7 @@ int git_stream_socket_new(
return -1;
}
return init(out, host, port);
return init(out);
}
#ifdef GIT_WIN32

View File

@@ -11,7 +11,7 @@
#include "stream.h"
extern int git_stream_socket_new(git_stream **out, const char *host, const char *port);
extern int git_stream_socket_new(git_stream **out);
extern int git_stream_socket_global_init(void);

View File

@@ -15,15 +15,13 @@
#include "streams/securetransport.h"
#include "streams/schannel.h"
int git_stream_tls_new(git_stream **out, const char *host, const char *port)
int git_stream_tls_new(git_stream **out)
{
int (*init)(git_stream **, const char *, const char *) = NULL;
int (*init)(git_stream **) = NULL;
git_stream_registration custom = {0};
int error;
GIT_ASSERT_ARG(out);
GIT_ASSERT_ARG(host);
GIT_ASSERT_ARG(port);
if ((error = git_stream_registry_lookup(&custom, GIT_STREAM_TLS)) == 0) {
init = custom.init;
@@ -46,35 +44,5 @@ int git_stream_tls_new(git_stream **out, const char *host, const char *port)
return -1;
}
return init(out, host, port);
}
int git_stream_tls_wrap(git_stream **out, git_stream *in, const char *host)
{
int (*wrap)(git_stream **, git_stream *, const char *) = NULL;
git_stream_registration custom = {0};
GIT_ASSERT_ARG(out);
GIT_ASSERT_ARG(in);
if (git_stream_registry_lookup(&custom, GIT_STREAM_TLS) == 0) {
wrap = custom.wrap;
} else {
#if defined(GIT_HTTPS_SECURETRANSPORT)
wrap = git_stream_securetransport_wrap;
#elif defined(GIT_HTTPS_OPENSSL)
wrap = git_stream_openssl_wrap;
#elif defined(GIT_HTTPS_MBEDTLS)
wrap = git_stream_mbedtls_wrap;
#elif defined(GIT_HTTPS_SCHANNEL)
wrap = git_stream_schannel_wrap;
#endif
}
if (!wrap) {
git_error_set(GIT_ERROR_SSL, "there is no TLS stream available");
return -1;
}
return wrap(out, in, host);
return init(out);
}

View File

@@ -16,16 +16,6 @@
* the current platform, whether that's SecureTransport on macOS,
* OpenSSL or mbedTLS on other Unixes, or something else entirely.
*/
extern int git_stream_tls_new(git_stream **out, const char *host, const char *port);
/**
* Create a TLS stream on top of an existing insecure stream, using
* the most appropriate backend available for the current platform.
*
* This allows us to upgrade an existing socket to add TLS -- for
* example, creating a CONNECT stream on top of an existing HTTP
* connection.
*/
extern int git_stream_tls_wrap(git_stream **out, git_stream *in, const char *host);
extern int git_stream_tls_new(git_stream **out);
#endif

View File

@@ -1,14 +1,15 @@
#include "clar_libgit2.h"
#include "settings.h"
extern char *git_http__user_agent;
void test_core_useragent__get(void)
{
const char *custom_name = "super duper git";
git_str buf = GIT_STR_INIT;
cl_assert_equal_p(NULL, git_libgit2__user_agent());
cl_assert_equal_p(NULL, git_http__user_agent);
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT, custom_name));
cl_assert_equal_s(custom_name, git_libgit2__user_agent());
cl_assert_equal_s(custom_name, git_http__user_agent);
cl_git_pass(git_libgit2_opts(GIT_OPT_GET_USER_AGENT, &buf));
cl_assert_equal_s(custom_name, buf.ptr);

View File

@@ -12,22 +12,8 @@ void test_stream_registration__cleanup(void)
cl_git_pass(git_stream_register(GIT_STREAM_TLS | GIT_STREAM_STANDARD, NULL));
}
static int test_stream_init(git_stream **out, const char *host, const char *port)
static int test_stream_init(git_stream **out)
{
GIT_UNUSED(host);
GIT_UNUSED(port);
ctor_called = 1;
*out = &test_stream;
return 0;
}
static int test_stream_wrap(git_stream **out, git_stream *in, const char *host)
{
GIT_UNUSED(in);
GIT_UNUSED(host);
ctor_called = 1;
*out = &test_stream;
@@ -41,18 +27,17 @@ void test_stream_registration__insecure(void)
registration.version = 1;
registration.init = test_stream_init;
registration.wrap = test_stream_wrap;
ctor_called = 0;
cl_git_pass(git_stream_register(GIT_STREAM_STANDARD, &registration));
cl_git_pass(git_stream_socket_new(&stream, "localhost", "80"));
cl_git_pass(git_stream_socket_new(&stream));
cl_assert_equal_i(1, ctor_called);
cl_assert_equal_p(&test_stream, stream);
ctor_called = 0;
stream = NULL;
cl_git_pass(git_stream_register(GIT_STREAM_STANDARD, NULL));
cl_git_pass(git_stream_socket_new(&stream, "localhost", "80"));
cl_git_pass(git_stream_socket_new(&stream));
cl_assert_equal_i(0, ctor_called);
cl_assert(&test_stream != stream);
@@ -68,18 +53,17 @@ void test_stream_registration__tls(void)
registration.version = 1;
registration.init = test_stream_init;
registration.wrap = test_stream_wrap;
ctor_called = 0;
cl_git_pass(git_stream_register(GIT_STREAM_TLS, &registration));
cl_git_pass(git_stream_tls_new(&stream, "localhost", "443"));
cl_git_pass(git_stream_tls_new(&stream));
cl_assert_equal_i(1, ctor_called);
cl_assert_equal_p(&test_stream, stream);
ctor_called = 0;
stream = NULL;
cl_git_pass(git_stream_register(GIT_STREAM_TLS, NULL));
error = git_stream_tls_new(&stream, "localhost", "443");
error = git_stream_tls_new(&stream);
/* We don't have TLS support enabled, or we're on Windows
* with WinHTTP, which is not actually TLS stream support.
@@ -103,17 +87,16 @@ void test_stream_registration__both(void)
registration.version = 1;
registration.init = test_stream_init;
registration.wrap = test_stream_wrap;
cl_git_pass(git_stream_register(GIT_STREAM_STANDARD | GIT_STREAM_TLS, &registration));
ctor_called = 0;
cl_git_pass(git_stream_tls_new(&stream, "localhost", "443"));
cl_git_pass(git_stream_tls_new(&stream));
cl_assert_equal_i(1, ctor_called);
cl_assert_equal_p(&test_stream, stream);
ctor_called = 0;
cl_git_pass(git_stream_socket_new(&stream, "localhost", "80"));
cl_git_pass(git_stream_socket_new(&stream));
cl_assert_equal_i(1, ctor_called);
cl_assert_equal_p(&test_stream, stream);
}