2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
8 #include "streams/openssl.h"
17 #include "streams/socket.h"
19 #include "git2/transport.h"
20 #include "git2/sys/openssl.h"
23 # include <sys/types.h>
24 # include <sys/socket.h>
25 # include <netinet/in.h>
28 #include <openssl/ssl.h>
29 #include <openssl/err.h>
30 #include <openssl/x509v3.h>
31 #include <openssl/bio.h>
33 SSL_CTX
*git__ssl_ctx
;
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"
37 #if (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L) || \
38 (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
39 # define OPENSSL_LEGACY_API
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.
48 #if defined(OPENSSL_LEGACY_API)
49 static int OPENSSL_init_ssl(int opts
, void *settings
)
53 SSL_load_error_strings();
54 OpenSSL_add_ssl_algorithms();
58 static BIO_METHOD
* BIO_meth_new(int type
, const char *name
)
60 BIO_METHOD
*meth
= git__calloc(1, sizeof(BIO_METHOD
));
71 static void BIO_meth_free(BIO_METHOD
*biom
)
76 static int BIO_meth_set_write(BIO_METHOD
*biom
, int (*write
) (BIO
*, const char *, int))
82 static int BIO_meth_set_read(BIO_METHOD
*biom
, int (*read
) (BIO
*, char *, int))
88 static int BIO_meth_set_puts(BIO_METHOD
*biom
, int (*puts
) (BIO
*, const char *))
94 static int BIO_meth_set_gets(BIO_METHOD
*biom
, int (*gets
) (BIO
*, char *, int))
101 static int BIO_meth_set_ctrl(BIO_METHOD
*biom
, long (*ctrl
) (BIO
*, int, long, void *))
107 static int BIO_meth_set_create(BIO_METHOD
*biom
, int (*create
) (BIO
*))
109 biom
->create
= create
;
113 static int BIO_meth_set_destroy(BIO_METHOD
*biom
, int (*destroy
) (BIO
*))
115 biom
->destroy
= destroy
;
119 static int BIO_get_new_index(void)
121 /* This exists as of 1.1 so before we'd just have 0 */
125 static void BIO_set_init(BIO
*b
, int init
)
130 static void BIO_set_data(BIO
*a
, void *ptr
)
135 static void *BIO_get_data(BIO
*a
)
140 static const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING
*x
)
142 return ASN1_STRING_data((ASN1_STRING
*)x
);
145 # if defined(GIT_THREADS)
146 static git_mutex
*openssl_locks
;
148 static void openssl_locking_function(
149 int mode
, int n
, const char *file
, int line
)
156 lock
= mode
& CRYPTO_LOCK
;
159 (void)git_mutex_lock(&openssl_locks
[n
]);
161 git_mutex_unlock(&openssl_locks
[n
]);
165 static void shutdown_ssl_locking(void)
169 num_locks
= CRYPTO_num_locks();
170 CRYPTO_set_locking_callback(NULL
);
172 for (i
= 0; i
< num_locks
; ++i
)
173 git_mutex_free(&openssl_locks
[i
]);
174 git__free(openssl_locks
);
176 # endif /* GIT_THREADS */
177 #endif /* OPENSSL_LEGACY_API */
179 static BIO_METHOD
*git_stream_bio_method
;
180 static int init_bio_method(void);
183 * This function aims to clean-up the SSL context which
186 static void shutdown_ssl(void)
188 if (git_stream_bio_method
) {
189 BIO_meth_free(git_stream_bio_method
);
190 git_stream_bio_method
= NULL
;
194 SSL_CTX_free(git__ssl_ctx
);
200 #ifdef OPENSSL_LEGACY_API
201 static void *git_openssl_malloc(size_t bytes
)
203 return git__calloc(1, bytes
);
206 static void *git_openssl_realloc(void *mem
, size_t size
)
208 return git__realloc(mem
, size
);
211 static void git_openssl_free(void *mem
)
213 return git__free(mem
);
216 static void *git_openssl_malloc(size_t bytes
, const char *file
, int line
)
220 return git__calloc(1, bytes
);
223 static void *git_openssl_realloc(void *mem
, size_t size
, const char *file
, int line
)
227 return git__realloc(mem
, size
);
230 static void git_openssl_free(void *mem
, const char *file
, int line
)
234 return git__free(mem
);
239 int git_openssl_stream_global_init(void)
241 long ssl_opts
= SSL_OP_NO_SSLv2
| SSL_OP_NO_SSLv3
;
242 const char *ciphers
= git_libgit2__ssl_ciphers();
244 static bool allocators_initialized
= false;
247 /* Older OpenSSL and MacOS OpenSSL doesn't have this */
248 #ifdef SSL_OP_NO_COMPRESSION
249 ssl_opts
|= SSL_OP_NO_COMPRESSION
;
253 /* Swap in our own allocator functions that initialize allocated memory */
254 if (!allocators_initialized
&&
255 CRYPTO_set_mem_functions(git_openssl_malloc
,
257 git_openssl_free
) != 1)
259 allocators_initialized
= true;
262 OPENSSL_init_ssl(0, NULL
);
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.
270 if (!(git__ssl_ctx
= SSL_CTX_new(SSLv23_method())))
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
);
276 if (!SSL_CTX_set_default_verify_paths(git__ssl_ctx
))
280 ciphers
= GIT_SSL_DEFAULT_CIPHERS
;
282 if(!SSL_CTX_set_cipher_list(git__ssl_ctx
, ciphers
))
285 if (init_bio_method() < 0)
288 git__on_shutdown(shutdown_ssl
);
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
);
300 #if defined(GIT_THREADS) && defined(OPENSSL_LEGACY_API)
301 static void threadid_cb(CRYPTO_THREADID
*threadid
)
303 GIT_UNUSED(threadid
);
304 CRYPTO_THREADID_set_numeric(threadid
, git_thread_currentid());
308 int git_openssl_set_locking(void)
310 #if defined(GIT_THREADS) && defined(OPENSSL_LEGACY_API)
313 CRYPTO_THREADID_set_callback(threadid_cb
);
315 num_locks
= CRYPTO_num_locks();
316 openssl_locks
= git__calloc(num_locks
, sizeof(git_mutex
));
317 GIT_ERROR_CHECK_ALLOC(openssl_locks
);
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");
326 CRYPTO_set_locking_callback(openssl_locking_function
);
327 git__on_shutdown(shutdown_ssl_locking
);
329 #elif !defined(OPENSSL_LEGACY_API)
332 git_error_set(GIT_ERROR_THREAD
, "libgit2 was not built with threads");
338 static int bio_create(BIO
*b
)
341 BIO_set_data(b
, NULL
);
346 static int bio_destroy(BIO
*b
)
351 BIO_set_data(b
, NULL
);
356 static int bio_read(BIO
*b
, char *buf
, int len
)
358 git_stream
*io
= (git_stream
*) BIO_get_data(b
);
360 return (int) git_stream_read(io
, buf
, len
);
363 static int bio_write(BIO
*b
, const char *buf
, int len
)
365 git_stream
*io
= (git_stream
*) BIO_get_data(b
);
366 return (int) git_stream_write(io
, buf
, len
, 0);
369 static long bio_ctrl(BIO
*b
, int cmd
, long num
, void *ptr
)
375 if (cmd
== BIO_CTRL_FLUSH
)
381 static int bio_gets(BIO
*b
, char *buf
, int len
)
389 static int bio_puts(BIO
*b
, const char *str
)
391 return bio_write(b
, str
, strlen(str
));
394 static int init_bio_method(void)
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");
398 GIT_ERROR_CHECK_ALLOC(git_stream_bio_method
);
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
);
411 static int ssl_set_error(SSL
*ssl
, int error
)
416 err
= SSL_get_error(ssl
, error
);
418 assert(err
!= SSL_ERROR_WANT_READ
);
419 assert(err
!= SSL_ERROR_WANT_WRITE
);
422 case SSL_ERROR_WANT_CONNECT
:
423 case SSL_ERROR_WANT_ACCEPT
:
424 git_error_set(GIT_ERROR_SSL
, "SSL error: connection failure");
426 case SSL_ERROR_WANT_X509_LOOKUP
:
427 git_error_set(GIT_ERROR_SSL
, "SSL error: x509 error");
429 case SSL_ERROR_SYSCALL
:
433 ERR_error_string_n(e
, errmsg
, sizeof(errmsg
));
434 git_error_set(GIT_ERROR_NET
, "SSL error: %s", errmsg
);
436 } else if (error
< 0) {
437 git_error_set(GIT_ERROR_OS
, "SSL error: syscall failure");
440 git_error_set(GIT_ERROR_SSL
, "SSL error: received early EOF");
447 ERR_error_string_n(e
, errmsg
, sizeof(errmsg
));
448 git_error_set(GIT_ERROR_SSL
, "SSL error: %s", errmsg
);
452 case SSL_ERROR_ZERO_RETURN
:
454 git_error_set(GIT_ERROR_SSL
, "SSL error: unknown error");
460 static int ssl_teardown(SSL
*ssl
)
464 ret
= SSL_shutdown(ssl
);
466 ret
= ssl_set_error(ssl
, ret
);
473 static int check_host_name(const char *name
, const char *host
)
475 if (!strcasecmp(name
, host
))
478 if (gitno__match_host(name
, host
) < 0)
484 static int verify_server_cert(SSL
*ssl
, const char *host
)
487 X509_NAME
*peer_name
;
489 unsigned char *peer_cn
= NULL
;
490 int matched
= -1, type
= GEN_DNS
;
492 struct in6_addr addr6
;
493 struct in_addr addr4
;
495 int i
= -1, j
, error
= 0;
497 if (SSL_get_verify_result(ssl
) != X509_V_OK
) {
498 git_error_set(GIT_ERROR_SSL
, "the SSL certificate is invalid");
499 return GIT_ECERTIFICATE
;
502 /* Try to parse the host as an IP address to see if it is */
503 if (p_inet_pton(AF_INET
, host
, &addr4
)) {
507 if (p_inet_pton(AF_INET6
, host
, &addr6
)) {
514 cert
= SSL_get_peer_certificate(ssl
);
517 git_error_set(GIT_ERROR_SSL
, "the server did not provide a certificate");
521 /* Check the alternative names */
522 alts
= X509_get_ext_d2i(cert
, NID_subject_alt_name
, NULL
, NULL
);
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
);
529 const char *name
= (char *) ASN1_STRING_get0_data(gn
->d
.ia5
);
530 size_t namelen
= (size_t) ASN1_STRING_length(gn
->d
.ia5
);
532 /* Skip any names of a type we're not looking for */
533 if (gn
->type
!= type
)
536 if (type
== GEN_DNS
) {
537 /* If it contains embedded NULs, don't even try */
538 if (memchr(name
, '\0', namelen
))
541 if (check_host_name(name
, host
) < 0)
545 } else if (type
== GEN_IPADD
) {
546 /* Here name isn't so much a name but a binary representation of the IP */
547 matched
= addr
&& !!memcmp(name
, addr
, namelen
);
551 GENERAL_NAMES_free(alts
);
560 /* If no alternative names are available, check the common name */
561 peer_name
= X509_get_subject_name(cert
);
562 if (peer_name
== NULL
)
566 /* Get the index of the last CN entry */
567 while ((j
= X509_NAME_get_index_by_NID(peer_name
, NID_commonName
, i
)) >= 0)
574 str
= X509_NAME_ENTRY_get_data(X509_NAME_get_entry(peer_name
, i
));
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
);
583 peer_cn
= OPENSSL_malloc(size
+ 1);
584 GIT_ERROR_CHECK_ALLOC(peer_cn
);
585 memcpy(peer_cn
, ASN1_STRING_get0_data(str
), size
);
586 peer_cn
[size
] = '\0';
591 int size
= ASN1_STRING_to_UTF8(&peer_cn
, str
);
592 GIT_ERROR_CHECK_ALLOC(peer_cn
);
593 if (memchr(peer_cn
, '\0', size
))
597 if (check_host_name((char *)peer_cn
, host
) < 0)
603 error
= GIT_ECERTIFICATE
;
604 git_error_set(GIT_ERROR_SSL
, "hostname does not match certificate");
608 error
= ssl_set_error(ssl
, 0);
613 OPENSSL_free(peer_cn
);
624 git_cert_x509 cert_info
;
627 static int openssl_connect(git_stream
*stream
)
631 openssl_stream
*st
= (openssl_stream
*) stream
;
633 if (st
->owned
&& (ret
= git_stream_connect(st
->io
)) < 0)
636 bio
= BIO_new(git_stream_bio_method
);
637 GIT_ERROR_CHECK_ALLOC(bio
);
639 BIO_set_data(bio
, st
->io
);
640 SSL_set_bio(st
->ssl
, bio
, bio
);
642 /* specify the host in case SNI is needed */
643 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
644 SSL_set_tlsext_host_name(st
->ssl
, st
->host
);
647 if ((ret
= SSL_connect(st
->ssl
)) <= 0)
648 return ssl_set_error(st
->ssl
, ret
);
650 st
->connected
= true;
652 return verify_server_cert(st
->ssl
, st
->host
);
655 static int openssl_certificate(git_cert
**out
, git_stream
*stream
)
657 openssl_stream
*st
= (openssl_stream
*) stream
;
659 X509
*cert
= SSL_get_peer_certificate(st
->ssl
);
660 unsigned char *guard
, *encoded_cert
;
662 /* Retrieve the length of the certificate first */
663 len
= i2d_X509(cert
, NULL
);
665 git_error_set(GIT_ERROR_NET
, "failed to retrieve certificate information");
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
;
674 len
= i2d_X509(cert
, &guard
);
676 git__free(encoded_cert
);
677 git_error_set(GIT_ERROR_NET
, "failed to retrieve certificate information");
681 st
->cert_info
.parent
.cert_type
= GIT_CERT_X509
;
682 st
->cert_info
.data
= encoded_cert
;
683 st
->cert_info
.len
= len
;
685 *out
= &st
->cert_info
.parent
;
690 static int openssl_set_proxy(git_stream
*stream
, const git_proxy_options
*proxy_opts
)
692 openssl_stream
*st
= (openssl_stream
*) stream
;
694 return git_stream_set_proxy(st
->io
, proxy_opts
);
697 static ssize_t
openssl_write(git_stream
*stream
, const char *data
, size_t data_len
, int flags
)
699 openssl_stream
*st
= (openssl_stream
*) stream
;
700 int ret
, len
= min(data_len
, INT_MAX
);
704 if ((ret
= SSL_write(st
->ssl
, data
, len
)) <= 0)
705 return ssl_set_error(st
->ssl
, ret
);
710 static ssize_t
openssl_read(git_stream
*stream
, void *data
, size_t len
)
712 openssl_stream
*st
= (openssl_stream
*) stream
;
715 if ((ret
= SSL_read(st
->ssl
, data
, len
)) <= 0)
716 return ssl_set_error(st
->ssl
, ret
);
721 static int openssl_close(git_stream
*stream
)
723 openssl_stream
*st
= (openssl_stream
*) stream
;
726 if (st
->connected
&& (ret
= ssl_teardown(st
->ssl
)) < 0)
729 st
->connected
= false;
731 return st
->owned
? git_stream_close(st
->io
) : 0;
734 static void openssl_free(git_stream
*stream
)
736 openssl_stream
*st
= (openssl_stream
*) stream
;
739 git_stream_free(st
->io
);
743 git__free(st
->cert_info
.data
);
747 static int openssl_stream_wrap(
755 assert(out
&& in
&& host
);
757 st
= git__calloc(1, sizeof(openssl_stream
));
758 GIT_ERROR_CHECK_ALLOC(st
);
763 st
->ssl
= SSL_new(git__ssl_ctx
);
764 if (st
->ssl
== NULL
) {
765 git_error_set(GIT_ERROR_SSL
, "failed to create ssl object");
770 st
->host
= git__strdup(host
);
771 GIT_ERROR_CHECK_ALLOC(st
->host
);
773 st
->parent
.version
= GIT_STREAM_VERSION
;
774 st
->parent
.encrypted
= 1;
775 st
->parent
.proxy_support
= git_stream_supports_proxy(st
->io
);
776 st
->parent
.connect
= openssl_connect
;
777 st
->parent
.certificate
= openssl_certificate
;
778 st
->parent
.set_proxy
= openssl_set_proxy
;
779 st
->parent
.read
= openssl_read
;
780 st
->parent
.write
= openssl_write
;
781 st
->parent
.close
= openssl_close
;
782 st
->parent
.free
= openssl_free
;
784 *out
= (git_stream
*) st
;
788 int git_openssl_stream_wrap(git_stream
**out
, git_stream
*in
, const char *host
)
790 return openssl_stream_wrap(out
, in
, host
, 0);
793 int git_openssl_stream_new(git_stream
**out
, const char *host
, const char *port
)
795 git_stream
*stream
= NULL
;
798 assert(out
&& host
&& port
);
800 if ((error
= git_socket_stream_new(&stream
, host
, port
)) < 0)
803 if ((error
= openssl_stream_wrap(out
, stream
, host
, 1)) < 0) {
804 git_stream_close(stream
);
805 git_stream_free(stream
);
811 int git_openssl__set_cert_location(const char *file
, const char *path
)
813 if (SSL_CTX_load_verify_locations(git__ssl_ctx
, file
, path
) == 0) {
816 ERR_error_string_n(ERR_get_error(), errmsg
, sizeof(errmsg
));
817 git_error_set(GIT_ERROR_SSL
, "OpenSSL error: failed to load certificates: %s",
828 #include "git2/sys/openssl.h"
830 int git_openssl_stream_global_init(void)
835 int git_openssl_set_locking(void)
837 git_error_set(GIT_ERROR_SSL
, "libgit2 was not built with OpenSSL support");