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