File Coverage

deps/libgit2/src/streams/openssl.c
Criterion Covered Total %
statement 60 340 17.6
branch 7 140 5.0
condition n/a
subroutine n/a
pod n/a
total 67 480 13.9


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