openssl.c 18.8 KB
Newer Older
1 2 3 4 5 6 7
/*
 * 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.
 */

8
#include "streams/openssl.h"
9

10
#ifdef GIT_OPENSSL
11 12 13

#include <ctype.h>

14
#include "runtime.h"
15
#include "settings.h"
16 17
#include "posix.h"
#include "stream.h"
18
#include "streams/socket.h"
19
#include "netops.h"
20
#include "git2/transport.h"
21
#include "git2/sys/openssl.h"
22

23 24 25 26 27 28
#ifndef GIT_WIN32
# include <sys/types.h>
# include <sys/socket.h>
# include <netinet/in.h>
#endif

29 30 31
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/x509v3.h>
32 33
#include <openssl/bio.h>

34 35
SSL_CTX *git__ssl_ctx;

36 37
#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"

38 39 40 41 42
#if (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L) || \
     (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
# define OPENSSL_LEGACY_API
#endif

43 44 45 46
/*
 * OpenSSL 1.1 made BIO opaque so we have to use functions to interact with it
 * which do not exist in previous versions. We define these inline functions so
 * we can program against the interface instead of littering the implementation
47
 * with ifdefs. We do the same for OPENSSL_init_ssl.
48
 */
49
#if defined(OPENSSL_LEGACY_API)
50 51 52 53 54 55 56 57 58
static int OPENSSL_init_ssl(int opts, void *settings)
{
	GIT_UNUSED(opts);
	GIT_UNUSED(settings);
	SSL_load_error_strings();
	OpenSSL_add_ssl_algorithms();
	return 0;
}

59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
static BIO_METHOD* BIO_meth_new(int type, const char *name)
{
	BIO_METHOD *meth = git__calloc(1, sizeof(BIO_METHOD));
	if (!meth) {
		return NULL;
	}

	meth->type = type;
	meth->name = name;

	return meth;
}

static void BIO_meth_free(BIO_METHOD *biom)
{
	git__free(biom);
}

static int BIO_meth_set_write(BIO_METHOD *biom, int (*write) (BIO *, const char *, int))
{
	biom->bwrite = write;
	return 1;
}

static int BIO_meth_set_read(BIO_METHOD *biom, int (*read) (BIO *, char *, int))
{
	biom->bread = read;
	return 1;
}

static int BIO_meth_set_puts(BIO_METHOD *biom, int (*puts) (BIO *, const char *))
{
	biom->bputs = puts;
	return 1;
}

static int BIO_meth_set_gets(BIO_METHOD *biom, int (*gets) (BIO *, char *, int))

{
	biom->bgets = gets;
	return 1;
}

static int BIO_meth_set_ctrl(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *))
{
	biom->ctrl = ctrl;
	return 1;
}

static int BIO_meth_set_create(BIO_METHOD *biom, int (*create) (BIO *))
{
	biom->create = create;
	return 1;
}

static int BIO_meth_set_destroy(BIO_METHOD *biom, int (*destroy) (BIO *))
{
	biom->destroy = destroy;
	return 1;
}

static int BIO_get_new_index(void)
{
	/* This exists as of 1.1 so before we'd just have 0 */
	return 0;
}

static void BIO_set_init(BIO *b, int init)
{
	b->init = init;
}

static void BIO_set_data(BIO *a, void *ptr)
{
	a->ptr = ptr;
}

static void *BIO_get_data(BIO *a)
{
	return a->ptr;
}

static const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *x)
{
	return ASN1_STRING_data((ASN1_STRING *)x);
}

146
# if defined(GIT_THREADS)
147 148 149 150 151 152 153 154 155 156 157 158 159
static git_mutex *openssl_locks;

static void openssl_locking_function(
	int mode, int n, const char *file, int line)
{
	int lock;

	GIT_UNUSED(file);
	GIT_UNUSED(line);

	lock = mode & CRYPTO_LOCK;

	if (lock) {
160
		(void)git_mutex_lock(&openssl_locks[n]);
161 162 163 164 165 166 167 168 169 170 171 172 173
	} else {
		git_mutex_unlock(&openssl_locks[n]);
	}
}

static void shutdown_ssl_locking(void)
{
	int num_locks, i;

	num_locks = CRYPTO_num_locks();
	CRYPTO_set_locking_callback(NULL);

	for (i = 0; i < num_locks; ++i)
174
		git_mutex_free(&openssl_locks[i]);
175 176
	git__free(openssl_locks);
}
177 178
# endif /* GIT_THREADS */
#endif /* OPENSSL_LEGACY_API */
179

180 181 182
static BIO_METHOD *git_stream_bio_method;
static int init_bio_method(void);

183 184 185 186 187 188
/**
 * This function aims to clean-up the SSL context which
 * we allocated.
 */
static void shutdown_ssl(void)
{
189 190 191 192 193
	if (git_stream_bio_method) {
		BIO_meth_free(git_stream_bio_method);
		git_stream_bio_method = NULL;
	}

194 195 196 197 198 199
	if (git__ssl_ctx) {
		SSL_CTX_free(git__ssl_ctx);
		git__ssl_ctx = NULL;
	}
}

200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
#ifdef VALGRIND
#ifdef OPENSSL_LEGACY_API
static void *git_openssl_malloc(size_t bytes)
{
	return git__calloc(1, bytes);
}

static void *git_openssl_realloc(void *mem, size_t size)
{
	return git__realloc(mem, size);
}

static void git_openssl_free(void *mem)
{
	return git__free(mem);
}
#else
static void *git_openssl_malloc(size_t bytes, const char *file, int line)
{
	GIT_UNUSED(file);
	GIT_UNUSED(line);
	return git__calloc(1, bytes);
}

static void *git_openssl_realloc(void *mem, size_t size, const char *file, int line)
{
	GIT_UNUSED(file);
	GIT_UNUSED(line);
	return git__realloc(mem, size);
}

static void git_openssl_free(void *mem, const char *file, int line)
{
	GIT_UNUSED(file);
	GIT_UNUSED(line);
	return git__free(mem);
}
#endif
#endif

240 241 242
int git_openssl_stream_global_init(void)
{
	long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
243
	const char *ciphers = git_libgit2__ssl_ciphers();
244 245 246
#ifdef VALGRIND
	static bool allocators_initialized = false;
#endif
247 248 249 250 251 252

	/* Older OpenSSL and MacOS OpenSSL doesn't have this */
#ifdef SSL_OP_NO_COMPRESSION
	ssl_opts |= SSL_OP_NO_COMPRESSION;
#endif

253 254 255 256 257 258 259 260 261 262
#ifdef VALGRIND
	/* Swap in our own allocator functions that initialize allocated memory */
	if (!allocators_initialized &&
	    CRYPTO_set_mem_functions(git_openssl_malloc,
				     git_openssl_realloc,
				     git_openssl_free) != 1)
		goto error;
	allocators_initialized = true;
#endif

263 264
	OPENSSL_init_ssl(0, NULL);

265 266 267 268 269 270
	/*
	 * Load SSLv{2,3} and TLSv1 so that we can talk with servers
	 * which use the SSL hellos, which are often used for
	 * compatibility. We then disable SSL so we only allow OpenSSL
	 * to speak TLSv1 to perform the encryption itself.
	 */
271 272
	if (!(git__ssl_ctx = SSL_CTX_new(SSLv23_method())))
		goto error;
273

274 275 276
	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);
277 278
	if (!SSL_CTX_set_default_verify_paths(git__ssl_ctx))
		goto error;
279

280
	if (!ciphers)
281 282
		ciphers = GIT_SSL_DEFAULT_CIPHERS;

283 284
	if(!SSL_CTX_set_cipher_list(git__ssl_ctx, ciphers))
		goto error;
285

286 287
	if (init_bio_method() < 0)
		goto error;
288

289
	return git_runtime_shutdown_register(shutdown_ssl);
290 291

error:
292
	git_error_set(GIT_ERROR_NET, "could not initialize openssl: %s",
293 294 295 296
		ERR_error_string(ERR_get_error(), NULL));
	SSL_CTX_free(git__ssl_ctx);
	git__ssl_ctx = NULL;
	return -1;
297 298
}

299
#if defined(GIT_THREADS) && defined(OPENSSL_LEGACY_API)
300 301
static void threadid_cb(CRYPTO_THREADID *threadid)
{
302 303
	GIT_UNUSED(threadid);
	CRYPTO_THREADID_set_numeric(threadid, git_thread_currentid());
304 305 306
}
#endif

307 308
int git_openssl_set_locking(void)
{
309
#if defined(GIT_THREADS) && defined(OPENSSL_LEGACY_API)
310 311
	int num_locks, i;

312 313
	CRYPTO_THREADID_set_callback(threadid_cb);

314 315
	num_locks = CRYPTO_num_locks();
	openssl_locks = git__calloc(num_locks, sizeof(git_mutex));
316
	GIT_ERROR_CHECK_ALLOC(openssl_locks);
317 318 319

	for (i = 0; i < num_locks; i++) {
		if (git_mutex_init(&openssl_locks[i]) != 0) {
320
			git_error_set(GIT_ERROR_SSL, "failed to initialize openssl locks");
321 322 323 324 325
			return -1;
		}
	}

	CRYPTO_set_locking_callback(openssl_locking_function);
326 327
	return git_runtime_shutdown_register(shutdown_ssl_locking);

328
#elif !defined(OPENSSL_LEGACY_API)
329
	return 0;
330
#else
331
	git_error_set(GIT_ERROR_THREAD, "libgit2 was not built with threads");
332 333 334 335 336
	return -1;
#endif
}


337 338
static int bio_create(BIO *b)
{
339
	BIO_set_init(b, 1);
340
	BIO_set_data(b, NULL);
341 342 343 344 345 346 347 348 349

	return 1;
}

static int bio_destroy(BIO *b)
{
	if (!b)
		return 0;

350
	BIO_set_data(b, NULL);
351 352 353 354 355 356

	return 1;
}

static int bio_read(BIO *b, char *buf, int len)
{
357
	git_stream *io = (git_stream *) BIO_get_data(b);
358

359 360 361 362 363
	return (int) git_stream_read(io, buf, len);
}

static int bio_write(BIO *b, const char *buf, int len)
{
364
	git_stream *io = (git_stream *) BIO_get_data(b);
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
	return (int) git_stream_write(io, buf, len, 0);
}

static long bio_ctrl(BIO *b, int cmd, long num, void *ptr)
{
	GIT_UNUSED(b);
	GIT_UNUSED(num);
	GIT_UNUSED(ptr);

	if (cmd == BIO_CTRL_FLUSH)
		return 1;

	return 0;
}

static int bio_gets(BIO *b, char *buf, int len)
{
	GIT_UNUSED(b);
	GIT_UNUSED(buf);
	GIT_UNUSED(len);
	return -1;
}

static int bio_puts(BIO *b, const char *str)
{
	return bio_write(b, str, strlen(str));
}

393 394 395 396
static int init_bio_method(void)
{
	/* Set up the BIO_METHOD we use for wrapping our own stream implementations */
	git_stream_bio_method = BIO_meth_new(BIO_TYPE_SOURCE_SINK | BIO_get_new_index(), "git_stream");
397
	GIT_ERROR_CHECK_ALLOC(git_stream_bio_method);
398 399 400 401 402 403 404 405 406 407 408

	BIO_meth_set_write(git_stream_bio_method, bio_write);
	BIO_meth_set_read(git_stream_bio_method, bio_read);
	BIO_meth_set_puts(git_stream_bio_method, bio_puts);
	BIO_meth_set_gets(git_stream_bio_method, bio_gets);
	BIO_meth_set_ctrl(git_stream_bio_method, bio_ctrl);
	BIO_meth_set_create(git_stream_bio_method, bio_create);
	BIO_meth_set_destroy(git_stream_bio_method, bio_destroy);

	return 0;
}
409

410 411 412 413 414 415 416
static int ssl_set_error(SSL *ssl, int error)
{
	int err;
	unsigned long e;

	err = SSL_get_error(ssl, error);

417 418
	GIT_ASSERT(err != SSL_ERROR_WANT_READ);
	GIT_ASSERT(err != SSL_ERROR_WANT_WRITE);
419 420 421 422

	switch (err) {
	case SSL_ERROR_WANT_CONNECT:
	case SSL_ERROR_WANT_ACCEPT:
423
		git_error_set(GIT_ERROR_SSL, "SSL error: connection failure");
424 425
		break;
	case SSL_ERROR_WANT_X509_LOOKUP:
426
		git_error_set(GIT_ERROR_SSL, "SSL error: x509 error");
427 428 429 430
		break;
	case SSL_ERROR_SYSCALL:
		e = ERR_get_error();
		if (e > 0) {
431 432
			char errmsg[256];
			ERR_error_string_n(e, errmsg, sizeof(errmsg));
433
			git_error_set(GIT_ERROR_NET, "SSL error: %s", errmsg);
434 435
			break;
		} else if (error < 0) {
436
			git_error_set(GIT_ERROR_OS, "SSL error: syscall failure");
437 438
			break;
		}
439
		git_error_set(GIT_ERROR_SSL, "SSL error: received early EOF");
440
		return GIT_EEOF;
441 442
		break;
	case SSL_ERROR_SSL:
443 444
	{
		char errmsg[256];
445
		e = ERR_get_error();
446
		ERR_error_string_n(e, errmsg, sizeof(errmsg));
447
		git_error_set(GIT_ERROR_SSL, "SSL error: %s", errmsg);
448
		break;
449
	}
450 451 452
	case SSL_ERROR_NONE:
	case SSL_ERROR_ZERO_RETURN:
	default:
453
		git_error_set(GIT_ERROR_SSL, "SSL error: unknown error");
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
		break;
	}
	return -1;
}

static int ssl_teardown(SSL *ssl)
{
	int ret;

	ret = SSL_shutdown(ssl);
	if (ret < 0)
		ret = ssl_set_error(ssl, ret);
	else
		ret = 0;

	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(SSL *ssl, const char *host)
{
485
	X509 *cert = NULL;
486 487 488 489 490 491 492
	X509_NAME *peer_name;
	ASN1_STRING *str;
	unsigned char *peer_cn = NULL;
	int matched = -1, type = GEN_DNS;
	GENERAL_NAMES *alts;
	struct in6_addr addr6;
	struct in_addr addr4;
493
	void *addr = NULL;
494
	int i = -1, j, error = 0;
495 496

	if (SSL_get_verify_result(ssl) != X509_V_OK) {
497
		git_error_set(GIT_ERROR_SSL, "the SSL certificate is invalid");
498
		return GIT_ECERTIFICATE;
499 500 501 502 503 504 505
	}

	/* Try to parse the host as an IP address to see if it is */
	if (p_inet_pton(AF_INET, host, &addr4)) {
		type = GEN_IPADD;
		addr = &addr4;
	} else {
506
		if (p_inet_pton(AF_INET6, host, &addr6)) {
507 508 509 510 511 512 513 514
			type = GEN_IPADD;
			addr = &addr6;
		}
	}


	cert = SSL_get_peer_certificate(ssl);
	if (!cert) {
515
		error = -1;
516
		git_error_set(GIT_ERROR_SSL, "the server did not provide a certificate");
517
		goto cleanup;
518 519 520 521 522 523 524 525 526 527
	}

	/* Check the alternative names */
	alts = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
	if (alts) {
		int num;

		num = sk_GENERAL_NAME_num(alts);
		for (i = 0; i < num && matched != 1; i++) {
			const GENERAL_NAME *gn = sk_GENERAL_NAME_value(alts, i);
528
			const char *name = (char *) ASN1_STRING_get0_data(gn->d.ia5);
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
			size_t namelen = (size_t) ASN1_STRING_length(gn->d.ia5);

			/* Skip any names of a type we're not looking for */
			if (gn->type != type)
				continue;

			if (type == GEN_DNS) {
				/* If it contains embedded NULs, don't even try */
				if (memchr(name, '\0', namelen))
					continue;

				if (check_host_name(name, host) < 0)
					matched = 0;
				else
					matched = 1;
			} else if (type == GEN_IPADD) {
				/* Here name isn't so much a name but a binary representation of the IP */
546
				matched = addr && !!memcmp(name, addr, namelen);
547 548 549 550 551 552 553 554
			}
		}
	}
	GENERAL_NAMES_free(alts);

	if (matched == 0)
		goto cert_fail_name;

555 556 557
	if (matched == 1) {
		goto cleanup;
	}
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582

	/* If no alternative names are available, check the common name */
	peer_name = X509_get_subject_name(cert);
	if (peer_name == NULL)
		goto on_error;

	if (peer_name) {
		/* Get the index of the last CN entry */
		while ((j = X509_NAME_get_index_by_NID(peer_name, NID_commonName, i)) >= 0)
			i = j;
	}

	if (i < 0)
		goto on_error;

	str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(peer_name, i));
	if (str == NULL)
		goto on_error;

	/* Work around a bug in OpenSSL whereby ASN1_STRING_to_UTF8 fails if it's already in utf-8 */
	if (ASN1_STRING_type(str) == V_ASN1_UTF8STRING) {
		int size = ASN1_STRING_length(str);

		if (size > 0) {
			peer_cn = OPENSSL_malloc(size + 1);
583
			GIT_ERROR_CHECK_ALLOC(peer_cn);
584
			memcpy(peer_cn, ASN1_STRING_get0_data(str), size);
585
			peer_cn[size] = '\0';
586 587
		} else {
			goto cert_fail_name;
588 589 590
		}
	} else {
		int size = ASN1_STRING_to_UTF8(&peer_cn, str);
591
		GIT_ERROR_CHECK_ALLOC(peer_cn);
592 593 594 595 596 597 598
		if (memchr(peer_cn, '\0', size))
			goto cert_fail_name;
	}

	if (check_host_name((char *)peer_cn, host) < 0)
		goto cert_fail_name;

599
	goto cleanup;
600

601 602
cert_fail_name:
	error = GIT_ECERTIFICATE;
603
	git_error_set(GIT_ERROR_SSL, "hostname does not match certificate");
604
	goto cleanup;
605 606

on_error:
607 608
	error = ssl_set_error(ssl, 0);
	goto cleanup;
609

610
cleanup:
611
	X509_free(cert);
612
	OPENSSL_free(peer_cn);
613
	return error;
614 615 616 617
}

typedef struct {
	git_stream parent;
618
	git_stream *io;
619
	int owned;
620
	bool connected;
621
	char *host;
622 623 624 625
	SSL *ssl;
	git_cert_x509 cert_info;
} openssl_stream;

626
static int openssl_connect(git_stream *stream)
627 628
{
	int ret;
629
	BIO *bio;
630 631
	openssl_stream *st = (openssl_stream *) stream;

632
	if (st->owned && (ret = git_stream_connect(st->io)) < 0)
633 634
		return ret;

635
	bio = BIO_new(git_stream_bio_method);
636
	GIT_ERROR_CHECK_ALLOC(bio);
637

638
	BIO_set_data(bio, st->io);
639
	SSL_set_bio(st->ssl, bio, bio);
640

641
	/* specify the host in case SNI is needed */
642
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
643
	SSL_set_tlsext_host_name(st->ssl, st->host);
644
#endif
645

646 647 648
	if ((ret = SSL_connect(st->ssl)) <= 0)
		return ssl_set_error(st->ssl, ret);

649 650
	st->connected = true;

651
	return verify_server_cert(st->ssl, st->host);
652 653
}

654
static int openssl_certificate(git_cert **out, git_stream *stream)
655 656 657
{
	openssl_stream *st = (openssl_stream *) stream;
	X509 *cert = SSL_get_peer_certificate(st->ssl);
658 659
	unsigned char *guard, *encoded_cert = NULL;
	int error, len;
660 661 662 663

	/* Retrieve the length of the certificate first */
	len = i2d_X509(cert, NULL);
	if (len < 0) {
664
		git_error_set(GIT_ERROR_NET, "failed to retrieve certificate information");
665 666
		error = -1;
		goto out;
667 668 669
	}

	encoded_cert = git__malloc(len);
670
	GIT_ERROR_CHECK_ALLOC(encoded_cert);
671 672 673 674 675
	/* i2d_X509 makes 'guard' point to just after the data */
	guard = encoded_cert;

	len = i2d_X509(cert, &guard);
	if (len < 0) {
676
		git_error_set(GIT_ERROR_NET, "failed to retrieve certificate information");
677 678
		error = -1;
		goto out;
679 680
	}

681
	st->cert_info.parent.cert_type = GIT_CERT_X509;
682 683
	st->cert_info.data = encoded_cert;
	st->cert_info.len = len;
684
	encoded_cert = NULL;
685

686
	*out = &st->cert_info.parent;
687
	error = 0;
688

689 690 691 692
out:
	git__free(encoded_cert);
	X509_free(cert);
	return error;
693 694
}

695
static int openssl_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts)
696 697 698
{
	openssl_stream *st = (openssl_stream *) stream;

699
	return git_stream_set_proxy(st->io, proxy_opts);
700 701
}

702
static ssize_t openssl_write(git_stream *stream, const char *data, size_t data_len, int flags)
703 704
{
	openssl_stream *st = (openssl_stream *) stream;
705
	int ret, len = min(data_len, INT_MAX);
706 707 708

	GIT_UNUSED(flags);

709
	if ((ret = SSL_write(st->ssl, data, len)) <= 0)
710
		return ssl_set_error(st->ssl, ret);
711

712
	return ret;
713 714
}

715
static ssize_t openssl_read(git_stream *stream, void *data, size_t len)
716 717 718 719
{
	openssl_stream *st = (openssl_stream *) stream;
	int ret;

720
	if ((ret = SSL_read(st->ssl, data, len)) <= 0)
721
		return ssl_set_error(st->ssl, ret);
722 723 724 725

	return ret;
}

726
static int openssl_close(git_stream *stream)
727 728 729 730
{
	openssl_stream *st = (openssl_stream *) stream;
	int ret;

731
	if (st->connected && (ret = ssl_teardown(st->ssl)) < 0)
732 733
		return -1;

734 735
	st->connected = false;

736
	return st->owned ? git_stream_close(st->io) : 0;
737 738
}

739
static void openssl_free(git_stream *stream)
740 741 742
{
	openssl_stream *st = (openssl_stream *) stream;

743 744 745
	if (st->owned)
		git_stream_free(st->io);

746
	SSL_free(st->ssl);
747
	git__free(st->host);
748 749 750 751
	git__free(st->cert_info.data);
	git__free(st);
}

752 753 754 755 756
static int openssl_stream_wrap(
	git_stream **out,
	git_stream *in,
	const char *host,
	int owned)
757 758 759
{
	openssl_stream *st;

760 761 762
	GIT_ASSERT_ARG(out);
	GIT_ASSERT_ARG(in);
	GIT_ASSERT_ARG(host);
763

764
	st = git__calloc(1, sizeof(openssl_stream));
765
	GIT_ERROR_CHECK_ALLOC(st);
766

767 768
	st->io = in;
	st->owned = owned;
769 770 771

	st->ssl = SSL_new(git__ssl_ctx);
	if (st->ssl == NULL) {
772
		git_error_set(GIT_ERROR_SSL, "failed to create ssl object");
773 774
		git__free(st);
		return -1;
775 776
	}

777
	st->host = git__strdup(host);
778
	GIT_ERROR_CHECK_ALLOC(st->host);
779

780 781
	st->parent.version = GIT_STREAM_VERSION;
	st->parent.encrypted = 1;
782
	st->parent.proxy_support = git_stream_supports_proxy(st->io);
783 784
	st->parent.connect = openssl_connect;
	st->parent.certificate = openssl_certificate;
785
	st->parent.set_proxy = openssl_set_proxy;
786 787 788 789 790 791 792
	st->parent.read = openssl_read;
	st->parent.write = openssl_write;
	st->parent.close = openssl_close;
	st->parent.free = openssl_free;

	*out = (git_stream *) st;
	return 0;
793
}
794

795 796 797 798 799 800 801 802 803 804
int git_openssl_stream_wrap(git_stream **out, git_stream *in, const char *host)
{
	return openssl_stream_wrap(out, in, host, 0);
}

int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
{
	git_stream *stream = NULL;
	int error;

805 806 807
	GIT_ASSERT_ARG(out);
	GIT_ASSERT_ARG(host);
	GIT_ASSERT_ARG(port);
808

Edward Thomson committed
809
	if ((error = git_socket_stream_new(&stream, host, port)) < 0)
810 811 812 813 814 815
		return error;

	if ((error = openssl_stream_wrap(out, stream, host, 1)) < 0) {
		git_stream_close(stream);
		git_stream_free(stream);
	}
816 817

	return error;
818 819
}

820 821 822
int git_openssl__set_cert_location(const char *file, const char *path)
{
	if (SSL_CTX_load_verify_locations(git__ssl_ctx, file, path) == 0) {
823 824 825
		char errmsg[256];

		ERR_error_string_n(ERR_get_error(), errmsg, sizeof(errmsg));
826
		git_error_set(GIT_ERROR_SSL, "OpenSSL error: failed to load certificates: %s",
827 828
			errmsg);

829 830 831 832 833
		return -1;
	}
	return 0;
}

834 835 836
#else

#include "stream.h"
837
#include "git2/sys/openssl.h"
838

839 840 841 842 843 844 845
int git_openssl_stream_global_init(void)
{
	return 0;
}

int git_openssl_set_locking(void)
{
846
	git_error_set(GIT_ERROR_SSL, "libgit2 was not built with OpenSSL support");
847 848 849
	return -1;
}

850
#endif