]> git.proxmox.com Git - libgit2.git/blame - src/streams/openssl.c
New upstream version 1.1.0+dfsg.1
[libgit2.git] / src / streams / openssl.c
CommitLineData
468d7b11
CMN
1/*
2 * Copyright (C) the libgit2 contributors. All rights reserved.
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
7
eae0bfdc
PP
8#include "streams/openssl.h"
9
24e53d2f 10#ifdef GIT_OPENSSL
468d7b11 11
468d7b11
CMN
12#include <ctype.h>
13
14#include "global.h"
15#include "posix.h"
16#include "stream.h"
eae0bfdc 17#include "streams/socket.h"
1b75c29e 18#include "netops.h"
468d7b11 19#include "git2/transport.h"
c8fe6c09 20#include "git2/sys/openssl.h"
468d7b11 21
a944c6cc
AK
22#ifndef GIT_WIN32
23# include <sys/types.h>
24# include <sys/socket.h>
25# include <netinet/in.h>
26#endif
27
ec032442
AK
28#include <openssl/ssl.h>
29#include <openssl/err.h>
30#include <openssl/x509v3.h>
e247649d
CMN
31#include <openssl/bio.h>
32
8a6d6677
ET
33SSL_CTX *git__ssl_ctx;
34
fa72d6da
DB
35#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"
36
ac3d33df
JK
37#if (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L) || \
38 (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
39# define OPENSSL_LEGACY_API
40#endif
41
42/*
43 * OpenSSL 1.1 made BIO opaque so we have to use functions to interact with it
44 * which do not exist in previous versions. We define these inline functions so
45 * we can program against the interface instead of littering the implementation
46 * with ifdefs. We do the same for OPENSSL_init_ssl.
47 */
48#if defined(OPENSSL_LEGACY_API)
49static int OPENSSL_init_ssl(int opts, void *settings)
50{
51 GIT_UNUSED(opts);
52 GIT_UNUSED(settings);
53 SSL_load_error_strings();
54 OpenSSL_add_ssl_algorithms();
55 return 0;
56}
57
58static BIO_METHOD* BIO_meth_new(int type, const char *name)
59{
60 BIO_METHOD *meth = git__calloc(1, sizeof(BIO_METHOD));
61 if (!meth) {
62 return NULL;
63 }
64
65 meth->type = type;
66 meth->name = name;
67
68 return meth;
69}
70
71static void BIO_meth_free(BIO_METHOD *biom)
72{
73 git__free(biom);
74}
75
76static int BIO_meth_set_write(BIO_METHOD *biom, int (*write) (BIO *, const char *, int))
77{
78 biom->bwrite = write;
79 return 1;
80}
81
82static int BIO_meth_set_read(BIO_METHOD *biom, int (*read) (BIO *, char *, int))
83{
84 biom->bread = read;
85 return 1;
86}
87
88static int BIO_meth_set_puts(BIO_METHOD *biom, int (*puts) (BIO *, const char *))
89{
90 biom->bputs = puts;
91 return 1;
92}
93
94static int BIO_meth_set_gets(BIO_METHOD *biom, int (*gets) (BIO *, char *, int))
95
96{
97 biom->bgets = gets;
98 return 1;
99}
100
101static int BIO_meth_set_ctrl(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *))
102{
103 biom->ctrl = ctrl;
104 return 1;
105}
106
107static int BIO_meth_set_create(BIO_METHOD *biom, int (*create) (BIO *))
108{
109 biom->create = create;
110 return 1;
111}
112
113static int BIO_meth_set_destroy(BIO_METHOD *biom, int (*destroy) (BIO *))
114{
115 biom->destroy = destroy;
116 return 1;
117}
118
119static int BIO_get_new_index(void)
120{
121 /* This exists as of 1.1 so before we'd just have 0 */
122 return 0;
123}
124
125static void BIO_set_init(BIO *b, int init)
126{
127 b->init = init;
128}
129
130static void BIO_set_data(BIO *a, void *ptr)
131{
132 a->ptr = ptr;
133}
134
135static void *BIO_get_data(BIO *a)
136{
137 return a->ptr;
138}
139
140static const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *x)
141{
142 return ASN1_STRING_data((ASN1_STRING *)x);
143}
8a6d6677 144
ac3d33df 145# if defined(GIT_THREADS)
8a6d6677
ET
146static git_mutex *openssl_locks;
147
148static void openssl_locking_function(
149 int mode, int n, const char *file, int line)
150{
151 int lock;
152
153 GIT_UNUSED(file);
154 GIT_UNUSED(line);
155
156 lock = mode & CRYPTO_LOCK;
157
158 if (lock) {
22a2d3d5 159 (void)git_mutex_lock(&openssl_locks[n]);
8a6d6677
ET
160 } else {
161 git_mutex_unlock(&openssl_locks[n]);
162 }
163}
164
165static void shutdown_ssl_locking(void)
166{
167 int num_locks, i;
168
169 num_locks = CRYPTO_num_locks();
170 CRYPTO_set_locking_callback(NULL);
171
172 for (i = 0; i < num_locks; ++i)
dd0b1e8c 173 git_mutex_free(&openssl_locks[i]);
8a6d6677
ET
174 git__free(openssl_locks);
175}
ac3d33df
JK
176# endif /* GIT_THREADS */
177#endif /* OPENSSL_LEGACY_API */
8a6d6677 178
f15eedb3
CMN
179static BIO_METHOD *git_stream_bio_method;
180static int init_bio_method(void);
181
8a6d6677
ET
182/**
183 * This function aims to clean-up the SSL context which
184 * we allocated.
185 */
186static void shutdown_ssl(void)
187{
f15eedb3
CMN
188 if (git_stream_bio_method) {
189 BIO_meth_free(git_stream_bio_method);
190 git_stream_bio_method = NULL;
191 }
192
8a6d6677
ET
193 if (git__ssl_ctx) {
194 SSL_CTX_free(git__ssl_ctx);
195 git__ssl_ctx = NULL;
196 }
197}
198
22a2d3d5
UG
199#ifdef VALGRIND
200#ifdef OPENSSL_LEGACY_API
201static void *git_openssl_malloc(size_t bytes)
202{
203 return git__calloc(1, bytes);
204}
205
206static void *git_openssl_realloc(void *mem, size_t size)
207{
208 return git__realloc(mem, size);
209}
210
211static void git_openssl_free(void *mem)
212{
213 return git__free(mem);
214}
215#else
216static void *git_openssl_malloc(size_t bytes, const char *file, int line)
217{
218 GIT_UNUSED(file);
219 GIT_UNUSED(line);
220 return git__calloc(1, bytes);
221}
222
223static void *git_openssl_realloc(void *mem, size_t size, const char *file, int line)
224{
225 GIT_UNUSED(file);
226 GIT_UNUSED(line);
227 return git__realloc(mem, size);
228}
229
230static void git_openssl_free(void *mem, const char *file, int line)
231{
232 GIT_UNUSED(file);
233 GIT_UNUSED(line);
234 return git__free(mem);
235}
236#endif
237#endif
238
8a6d6677
ET
239int git_openssl_stream_global_init(void)
240{
8a6d6677 241 long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
fa72d6da 242 const char *ciphers = git_libgit2__ssl_ciphers();
22a2d3d5
UG
243#ifdef VALGRIND
244 static bool allocators_initialized = false;
245#endif
8a6d6677
ET
246
247 /* Older OpenSSL and MacOS OpenSSL doesn't have this */
248#ifdef SSL_OP_NO_COMPRESSION
249 ssl_opts |= SSL_OP_NO_COMPRESSION;
250#endif
251
22a2d3d5
UG
252#ifdef VALGRIND
253 /* Swap in our own allocator functions that initialize allocated memory */
254 if (!allocators_initialized &&
255 CRYPTO_set_mem_functions(git_openssl_malloc,
256 git_openssl_realloc,
257 git_openssl_free) != 1)
258 goto error;
259 allocators_initialized = true;
260#endif
261
88520151 262 OPENSSL_init_ssl(0, NULL);
88520151 263
8a6d6677
ET
264 /*
265 * Load SSLv{2,3} and TLSv1 so that we can talk with servers
266 * which use the SSL hellos, which are often used for
267 * compatibility. We then disable SSL so we only allow OpenSSL
268 * to speak TLSv1 to perform the encryption itself.
269 */
ac3d33df
JK
270 if (!(git__ssl_ctx = SSL_CTX_new(SSLv23_method())))
271 goto error;
272
8a6d6677
ET
273 SSL_CTX_set_options(git__ssl_ctx, ssl_opts);
274 SSL_CTX_set_mode(git__ssl_ctx, SSL_MODE_AUTO_RETRY);
275 SSL_CTX_set_verify(git__ssl_ctx, SSL_VERIFY_NONE, NULL);
ac3d33df
JK
276 if (!SSL_CTX_set_default_verify_paths(git__ssl_ctx))
277 goto error;
fa72d6da 278
ac3d33df 279 if (!ciphers)
fa72d6da 280 ciphers = GIT_SSL_DEFAULT_CIPHERS;
fa72d6da 281
ac3d33df
JK
282 if(!SSL_CTX_set_cipher_list(git__ssl_ctx, ciphers))
283 goto error;
f15eedb3 284
ac3d33df
JK
285 if (init_bio_method() < 0)
286 goto error;
8a6d6677
ET
287
288 git__on_shutdown(shutdown_ssl);
289
290 return 0;
ac3d33df
JK
291
292error:
293 git_error_set(GIT_ERROR_NET, "could not initialize openssl: %s",
294 ERR_error_string(ERR_get_error(), NULL));
295 SSL_CTX_free(git__ssl_ctx);
296 git__ssl_ctx = NULL;
297 return -1;
8a6d6677
ET
298}
299
ac3d33df 300#if defined(GIT_THREADS) && defined(OPENSSL_LEGACY_API)
eae0bfdc
PP
301static void threadid_cb(CRYPTO_THREADID *threadid)
302{
ac3d33df
JK
303 GIT_UNUSED(threadid);
304 CRYPTO_THREADID_set_numeric(threadid, git_thread_currentid());
eae0bfdc
PP
305}
306#endif
307
8a6d6677
ET
308int git_openssl_set_locking(void)
309{
ac3d33df 310#if defined(GIT_THREADS) && defined(OPENSSL_LEGACY_API)
8a6d6677
ET
311 int num_locks, i;
312
eae0bfdc
PP
313 CRYPTO_THREADID_set_callback(threadid_cb);
314
8a6d6677
ET
315 num_locks = CRYPTO_num_locks();
316 openssl_locks = git__calloc(num_locks, sizeof(git_mutex));
ac3d33df 317 GIT_ERROR_CHECK_ALLOC(openssl_locks);
8a6d6677
ET
318
319 for (i = 0; i < num_locks; i++) {
320 if (git_mutex_init(&openssl_locks[i]) != 0) {
ac3d33df 321 git_error_set(GIT_ERROR_SSL, "failed to initialize openssl locks");
8a6d6677
ET
322 return -1;
323 }
324 }
325
326 CRYPTO_set_locking_callback(openssl_locking_function);
327 git__on_shutdown(shutdown_ssl_locking);
328 return 0;
ac3d33df 329#elif !defined(OPENSSL_LEGACY_API)
29081c2f 330 return 0;
8a6d6677 331#else
ac3d33df 332 git_error_set(GIT_ERROR_THREAD, "libgit2 was not built with threads");
8a6d6677
ET
333 return -1;
334#endif
335}
336
337
e247649d
CMN
338static int bio_create(BIO *b)
339{
feb330d5 340 BIO_set_init(b, 1);
f15eedb3 341 BIO_set_data(b, NULL);
e247649d
CMN
342
343 return 1;
344}
345
346static int bio_destroy(BIO *b)
347{
348 if (!b)
349 return 0;
350
feb330d5 351 BIO_set_data(b, NULL);
e247649d
CMN
352
353 return 1;
354}
355
356static int bio_read(BIO *b, char *buf, int len)
357{
feb330d5 358 git_stream *io = (git_stream *) BIO_get_data(b);
f15eedb3 359
e247649d
CMN
360 return (int) git_stream_read(io, buf, len);
361}
362
363static int bio_write(BIO *b, const char *buf, int len)
364{
feb330d5 365 git_stream *io = (git_stream *) BIO_get_data(b);
e247649d
CMN
366 return (int) git_stream_write(io, buf, len, 0);
367}
368
369static long bio_ctrl(BIO *b, int cmd, long num, void *ptr)
370{
371 GIT_UNUSED(b);
372 GIT_UNUSED(num);
373 GIT_UNUSED(ptr);
374
375 if (cmd == BIO_CTRL_FLUSH)
376 return 1;
377
378 return 0;
379}
380
381static int bio_gets(BIO *b, char *buf, int len)
382{
383 GIT_UNUSED(b);
384 GIT_UNUSED(buf);
385 GIT_UNUSED(len);
386 return -1;
387}
388
389static int bio_puts(BIO *b, const char *str)
390{
391 return bio_write(b, str, strlen(str));
392}
393
f15eedb3
CMN
394static int init_bio_method(void)
395{
396 /* Set up the BIO_METHOD we use for wrapping our own stream implementations */
397 git_stream_bio_method = BIO_meth_new(BIO_TYPE_SOURCE_SINK | BIO_get_new_index(), "git_stream");
ac3d33df 398 GIT_ERROR_CHECK_ALLOC(git_stream_bio_method);
f15eedb3
CMN
399
400 BIO_meth_set_write(git_stream_bio_method, bio_write);
401 BIO_meth_set_read(git_stream_bio_method, bio_read);
402 BIO_meth_set_puts(git_stream_bio_method, bio_puts);
403 BIO_meth_set_gets(git_stream_bio_method, bio_gets);
404 BIO_meth_set_ctrl(git_stream_bio_method, bio_ctrl);
405 BIO_meth_set_create(git_stream_bio_method, bio_create);
406 BIO_meth_set_destroy(git_stream_bio_method, bio_destroy);
407
408 return 0;
409}
ec032442 410
468d7b11
CMN
411static int ssl_set_error(SSL *ssl, int error)
412{
413 int err;
414 unsigned long e;
415
416 err = SSL_get_error(ssl, error);
417
418 assert(err != SSL_ERROR_WANT_READ);
419 assert(err != SSL_ERROR_WANT_WRITE);
420
421 switch (err) {
422 case SSL_ERROR_WANT_CONNECT:
423 case SSL_ERROR_WANT_ACCEPT:
ac3d33df 424 git_error_set(GIT_ERROR_SSL, "SSL error: connection failure");
468d7b11
CMN
425 break;
426 case SSL_ERROR_WANT_X509_LOOKUP:
ac3d33df 427 git_error_set(GIT_ERROR_SSL, "SSL error: x509 error");
468d7b11
CMN
428 break;
429 case SSL_ERROR_SYSCALL:
430 e = ERR_get_error();
431 if (e > 0) {
eae0bfdc
PP
432 char errmsg[256];
433 ERR_error_string_n(e, errmsg, sizeof(errmsg));
ac3d33df 434 git_error_set(GIT_ERROR_NET, "SSL error: %s", errmsg);
468d7b11
CMN
435 break;
436 } else if (error < 0) {
ac3d33df 437 git_error_set(GIT_ERROR_OS, "SSL error: syscall failure");
468d7b11
CMN
438 break;
439 }
ac3d33df 440 git_error_set(GIT_ERROR_SSL, "SSL error: received early EOF");
1396c381 441 return GIT_EEOF;
468d7b11
CMN
442 break;
443 case SSL_ERROR_SSL:
eae0bfdc
PP
444 {
445 char errmsg[256];
468d7b11 446 e = ERR_get_error();
eae0bfdc 447 ERR_error_string_n(e, errmsg, sizeof(errmsg));
ac3d33df 448 git_error_set(GIT_ERROR_SSL, "SSL error: %s", errmsg);
468d7b11 449 break;
eae0bfdc 450 }
468d7b11
CMN
451 case SSL_ERROR_NONE:
452 case SSL_ERROR_ZERO_RETURN:
453 default:
ac3d33df 454 git_error_set(GIT_ERROR_SSL, "SSL error: unknown error");
468d7b11
CMN
455 break;
456 }
457 return -1;
458}
459
460static int ssl_teardown(SSL *ssl)
461{
462 int ret;
463
464 ret = SSL_shutdown(ssl);
465 if (ret < 0)
466 ret = ssl_set_error(ssl, ret);
467 else
468 ret = 0;
469
468d7b11
CMN
470 return ret;
471}
472
473static int check_host_name(const char *name, const char *host)
474{
475 if (!strcasecmp(name, host))
476 return 0;
477
478 if (gitno__match_host(name, host) < 0)
479 return -1;
480
481 return 0;
482}
483
484static int verify_server_cert(SSL *ssl, const char *host)
485{
eae0bfdc 486 X509 *cert = NULL;
468d7b11
CMN
487 X509_NAME *peer_name;
488 ASN1_STRING *str;
489 unsigned char *peer_cn = NULL;
490 int matched = -1, type = GEN_DNS;
491 GENERAL_NAMES *alts;
492 struct in6_addr addr6;
493 struct in_addr addr4;
eae0bfdc
PP
494 void *addr = NULL;
495 int i = -1, j, error = 0;
468d7b11
CMN
496
497 if (SSL_get_verify_result(ssl) != X509_V_OK) {
ac3d33df 498 git_error_set(GIT_ERROR_SSL, "the SSL certificate is invalid");
1b75c29e 499 return GIT_ECERTIFICATE;
468d7b11
CMN
500 }
501
502 /* Try to parse the host as an IP address to see if it is */
503 if (p_inet_pton(AF_INET, host, &addr4)) {
504 type = GEN_IPADD;
505 addr = &addr4;
506 } else {
eae0bfdc 507 if (p_inet_pton(AF_INET6, host, &addr6)) {
468d7b11
CMN
508 type = GEN_IPADD;
509 addr = &addr6;
510 }
511 }
512
513
514 cert = SSL_get_peer_certificate(ssl);
515 if (!cert) {
eae0bfdc 516 error = -1;
ac3d33df 517 git_error_set(GIT_ERROR_SSL, "the server did not provide a certificate");
eae0bfdc 518 goto cleanup;
468d7b11
CMN
519 }
520
521 /* Check the alternative names */
522 alts = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
523 if (alts) {
524 int num;
525
526 num = sk_GENERAL_NAME_num(alts);
527 for (i = 0; i < num && matched != 1; i++) {
528 const GENERAL_NAME *gn = sk_GENERAL_NAME_value(alts, i);
2f3adf95 529 const char *name = (char *) ASN1_STRING_get0_data(gn->d.ia5);
468d7b11
CMN
530 size_t namelen = (size_t) ASN1_STRING_length(gn->d.ia5);
531
532 /* Skip any names of a type we're not looking for */
533 if (gn->type != type)
534 continue;
535
536 if (type == GEN_DNS) {
537 /* If it contains embedded NULs, don't even try */
538 if (memchr(name, '\0', namelen))
539 continue;
540
541 if (check_host_name(name, host) < 0)
542 matched = 0;
543 else
544 matched = 1;
545 } else if (type == GEN_IPADD) {
546 /* Here name isn't so much a name but a binary representation of the IP */
eae0bfdc 547 matched = addr && !!memcmp(name, addr, namelen);
468d7b11
CMN
548 }
549 }
550 }
551 GENERAL_NAMES_free(alts);
552
553 if (matched == 0)
554 goto cert_fail_name;
555
eae0bfdc
PP
556 if (matched == 1) {
557 goto cleanup;
558 }
468d7b11
CMN
559
560 /* If no alternative names are available, check the common name */
561 peer_name = X509_get_subject_name(cert);
562 if (peer_name == NULL)
563 goto on_error;
564
565 if (peer_name) {
566 /* Get the index of the last CN entry */
567 while ((j = X509_NAME_get_index_by_NID(peer_name, NID_commonName, i)) >= 0)
568 i = j;
569 }
570
571 if (i < 0)
572 goto on_error;
573
574 str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(peer_name, i));
575 if (str == NULL)
576 goto on_error;
577
578 /* Work around a bug in OpenSSL whereby ASN1_STRING_to_UTF8 fails if it's already in utf-8 */
579 if (ASN1_STRING_type(str) == V_ASN1_UTF8STRING) {
580 int size = ASN1_STRING_length(str);
581
582 if (size > 0) {
583 peer_cn = OPENSSL_malloc(size + 1);
ac3d33df 584 GIT_ERROR_CHECK_ALLOC(peer_cn);
2f3adf95 585 memcpy(peer_cn, ASN1_STRING_get0_data(str), size);
468d7b11 586 peer_cn[size] = '\0';
05bf67b9
PS
587 } else {
588 goto cert_fail_name;
468d7b11
CMN
589 }
590 } else {
591 int size = ASN1_STRING_to_UTF8(&peer_cn, str);
ac3d33df 592 GIT_ERROR_CHECK_ALLOC(peer_cn);
468d7b11
CMN
593 if (memchr(peer_cn, '\0', size))
594 goto cert_fail_name;
595 }
596
597 if (check_host_name((char *)peer_cn, host) < 0)
598 goto cert_fail_name;
599
eae0bfdc 600 goto cleanup;
468d7b11 601
eae0bfdc
PP
602cert_fail_name:
603 error = GIT_ECERTIFICATE;
ac3d33df 604 git_error_set(GIT_ERROR_SSL, "hostname does not match certificate");
eae0bfdc 605 goto cleanup;
468d7b11
CMN
606
607on_error:
eae0bfdc
PP
608 error = ssl_set_error(ssl, 0);
609 goto cleanup;
468d7b11 610
eae0bfdc
PP
611cleanup:
612 X509_free(cert);
468d7b11 613 OPENSSL_free(peer_cn);
eae0bfdc 614 return error;
468d7b11
CMN
615}
616
617typedef struct {
618 git_stream parent;
e247649d 619 git_stream *io;
ac3d33df 620 int owned;
146a96de 621 bool connected;
e247649d 622 char *host;
468d7b11
CMN
623 SSL *ssl;
624 git_cert_x509 cert_info;
625} openssl_stream;
626
ac3d33df 627static int openssl_connect(git_stream *stream)
468d7b11
CMN
628{
629 int ret;
e247649d 630 BIO *bio;
468d7b11
CMN
631 openssl_stream *st = (openssl_stream *) stream;
632
ac3d33df 633 if (st->owned && (ret = git_stream_connect(st->io)) < 0)
468d7b11
CMN
634 return ret;
635
feb330d5 636 bio = BIO_new(git_stream_bio_method);
ac3d33df 637 GIT_ERROR_CHECK_ALLOC(bio);
468d7b11 638
f15eedb3 639 BIO_set_data(bio, st->io);
e247649d 640 SSL_set_bio(st->ssl, bio, bio);
f15eedb3 641
987045c7 642 /* specify the host in case SNI is needed */
febc8c46 643#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
e247649d 644 SSL_set_tlsext_host_name(st->ssl, st->host);
febc8c46 645#endif
987045c7 646
468d7b11
CMN
647 if ((ret = SSL_connect(st->ssl)) <= 0)
648 return ssl_set_error(st->ssl, ret);
649
ac3d33df
JK
650 st->connected = true;
651
e247649d 652 return verify_server_cert(st->ssl, st->host);
468d7b11
CMN
653}
654
ac3d33df 655static int openssl_certificate(git_cert **out, git_stream *stream)
468d7b11
CMN
656{
657 openssl_stream *st = (openssl_stream *) stream;
468d7b11 658 X509 *cert = SSL_get_peer_certificate(st->ssl);
22a2d3d5
UG
659 unsigned char *guard, *encoded_cert = NULL;
660 int error, len;
468d7b11
CMN
661
662 /* Retrieve the length of the certificate first */
663 len = i2d_X509(cert, NULL);
664 if (len < 0) {
ac3d33df 665 git_error_set(GIT_ERROR_NET, "failed to retrieve certificate information");
22a2d3d5
UG
666 error = -1;
667 goto out;
468d7b11
CMN
668 }
669
670 encoded_cert = git__malloc(len);
ac3d33df 671 GIT_ERROR_CHECK_ALLOC(encoded_cert);
468d7b11
CMN
672 /* i2d_X509 makes 'guard' point to just after the data */
673 guard = encoded_cert;
674
675 len = i2d_X509(cert, &guard);
676 if (len < 0) {
ac3d33df 677 git_error_set(GIT_ERROR_NET, "failed to retrieve certificate information");
22a2d3d5
UG
678 error = -1;
679 goto out;
468d7b11
CMN
680 }
681
79698030 682 st->cert_info.parent.cert_type = GIT_CERT_X509;
468d7b11
CMN
683 st->cert_info.data = encoded_cert;
684 st->cert_info.len = len;
22a2d3d5 685 encoded_cert = NULL;
468d7b11 686
79698030 687 *out = &st->cert_info.parent;
22a2d3d5 688 error = 0;
79698030 689
22a2d3d5
UG
690out:
691 git__free(encoded_cert);
692 X509_free(cert);
693 return error;
468d7b11
CMN
694}
695
b373e9a6 696static int openssl_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts)
e247649d
CMN
697{
698 openssl_stream *st = (openssl_stream *) stream;
699
b373e9a6 700 return git_stream_set_proxy(st->io, proxy_opts);
e247649d
CMN
701}
702
ac3d33df 703static ssize_t openssl_write(git_stream *stream, const char *data, size_t data_len, int flags)
468d7b11
CMN
704{
705 openssl_stream *st = (openssl_stream *) stream;
ac3d33df 706 int ret, len = min(data_len, INT_MAX);
468d7b11
CMN
707
708 GIT_UNUSED(flags);
709
ac3d33df 710 if ((ret = SSL_write(st->ssl, data, len)) <= 0)
77bffc2c 711 return ssl_set_error(st->ssl, ret);
468d7b11 712
77bffc2c 713 return ret;
468d7b11
CMN
714}
715
ac3d33df 716static ssize_t openssl_read(git_stream *stream, void *data, size_t len)
468d7b11
CMN
717{
718 openssl_stream *st = (openssl_stream *) stream;
719 int ret;
720
568c5a9f 721 if ((ret = SSL_read(st->ssl, data, len)) <= 0)
4734c52a 722 return ssl_set_error(st->ssl, ret);
468d7b11
CMN
723
724 return ret;
725}
726
ac3d33df 727static int openssl_close(git_stream *stream)
468d7b11
CMN
728{
729 openssl_stream *st = (openssl_stream *) stream;
730 int ret;
731
146a96de 732 if (st->connected && (ret = ssl_teardown(st->ssl)) < 0)
468d7b11
CMN
733 return -1;
734
146a96de
CMN
735 st->connected = false;
736
ac3d33df 737 return st->owned ? git_stream_close(st->io) : 0;
468d7b11
CMN
738}
739
ac3d33df 740static void openssl_free(git_stream *stream)
468d7b11
CMN
741{
742 openssl_stream *st = (openssl_stream *) stream;
743
ac3d33df
JK
744 if (st->owned)
745 git_stream_free(st->io);
746
deecaa2e 747 SSL_free(st->ssl);
3ca84ac0 748 git__free(st->host);
468d7b11 749 git__free(st->cert_info.data);
468d7b11
CMN
750 git__free(st);
751}
752
ac3d33df
JK
753static int openssl_stream_wrap(
754 git_stream **out,
755 git_stream *in,
756 const char *host,
757 int owned)
468d7b11
CMN
758{
759 openssl_stream *st;
760
ac3d33df 761 assert(out && in && host);
468d7b11 762
ac3d33df
JK
763 st = git__calloc(1, sizeof(openssl_stream));
764 GIT_ERROR_CHECK_ALLOC(st);
e247649d 765
ac3d33df
JK
766 st->io = in;
767 st->owned = owned;
468d7b11
CMN
768
769 st->ssl = SSL_new(git__ssl_ctx);
770 if (st->ssl == NULL) {
ac3d33df
JK
771 git_error_set(GIT_ERROR_SSL, "failed to create ssl object");
772 git__free(st);
773 return -1;
468d7b11
CMN
774 }
775
e247649d 776 st->host = git__strdup(host);
ac3d33df 777 GIT_ERROR_CHECK_ALLOC(st->host);
e247649d 778
468d7b11
CMN
779 st->parent.version = GIT_STREAM_VERSION;
780 st->parent.encrypted = 1;
e247649d 781 st->parent.proxy_support = git_stream_supports_proxy(st->io);
468d7b11
CMN
782 st->parent.connect = openssl_connect;
783 st->parent.certificate = openssl_certificate;
e247649d 784 st->parent.set_proxy = openssl_set_proxy;
468d7b11
CMN
785 st->parent.read = openssl_read;
786 st->parent.write = openssl_write;
787 st->parent.close = openssl_close;
788 st->parent.free = openssl_free;
789
790 *out = (git_stream *) st;
791 return 0;
ac3d33df 792}
2baf854e 793
ac3d33df
JK
794int git_openssl_stream_wrap(git_stream **out, git_stream *in, const char *host)
795{
796 return openssl_stream_wrap(out, in, host, 0);
797}
798
799int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
800{
801 git_stream *stream = NULL;
802 int error;
803
804 assert(out && host && port);
805
806 if ((error = git_socket_stream_new(&stream, host, port)) < 0)
807 return error;
808
809 if ((error = openssl_stream_wrap(out, stream, host, 1)) < 0) {
810 git_stream_close(stream);
811 git_stream_free(stream);
812 }
2baf854e
PS
813
814 return error;
468d7b11
CMN
815}
816
eae0bfdc
PP
817int git_openssl__set_cert_location(const char *file, const char *path)
818{
819 if (SSL_CTX_load_verify_locations(git__ssl_ctx, file, path) == 0) {
820 char errmsg[256];
821
822 ERR_error_string_n(ERR_get_error(), errmsg, sizeof(errmsg));
ac3d33df 823 git_error_set(GIT_ERROR_SSL, "OpenSSL error: failed to load certificates: %s",
eae0bfdc
PP
824 errmsg);
825
826 return -1;
827 }
828 return 0;
829}
830
468d7b11
CMN
831#else
832
833#include "stream.h"
68ad3156 834#include "git2/sys/openssl.h"
468d7b11 835
8a6d6677
ET
836int git_openssl_stream_global_init(void)
837{
838 return 0;
839}
840
841int git_openssl_set_locking(void)
842{
ac3d33df 843 git_error_set(GIT_ERROR_SSL, "libgit2 was not built with OpenSSL support");
eae0bfdc
PP
844 return -1;
845}
846
468d7b11 847#endif