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/mbedtls.h"
16 #include "streams/socket.h"
18 #include "git2/transport.h"
21 #ifndef GIT_DEFAULT_CERT_LOCATION
22 #define GIT_DEFAULT_CERT_LOCATION NULL
25 /* Work around C90-conformance issues */
26 #if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L)
27 # if defined(_MSC_VER)
28 # define inline __inline
29 # elif defined(__GNUC__)
30 # define inline __inline__
36 #include <mbedtls/config.h>
37 #include <mbedtls/ssl.h>
38 #include <mbedtls/error.h>
39 #include <mbedtls/entropy.h>
40 #include <mbedtls/ctr_drbg.h>
44 #define GIT_SSL_DEFAULT_CIPHERS "TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256:TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256:TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384:TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-DSS-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-DSS-WITH-AES-256-GCM-SHA384:TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256:TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256:TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA:TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA:TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384:TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384:TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA:TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-RSA-WITH-AES-128-CBC-SHA256:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256:TLS-DHE-RSA-WITH-AES-128-CBC-SHA:TLS-DHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-DSS-WITH-AES-128-CBC-SHA256:TLS-DHE-DSS-WITH-AES-256-CBC-SHA256:TLS-DHE-DSS-WITH-AES-128-CBC-SHA:TLS-DHE-DSS-WITH-AES-256-CBC-SHA:TLS-RSA-WITH-AES-128-GCM-SHA256:TLS-RSA-WITH-AES-256-GCM-SHA384:TLS-RSA-WITH-AES-128-CBC-SHA256:TLS-RSA-WITH-AES-256-CBC-SHA256:TLS-RSA-WITH-AES-128-CBC-SHA:TLS-RSA-WITH-AES-256-CBC-SHA"
45 #define GIT_SSL_DEFAULT_CIPHERS_COUNT 30
47 static mbedtls_ssl_config
*git__ssl_conf
;
48 static int ciphers_list
[GIT_SSL_DEFAULT_CIPHERS_COUNT
];
49 static mbedtls_entropy_context
*mbedtls_entropy
;
52 * This function aims to clean-up the SSL context which
55 static void shutdown_ssl(void)
58 mbedtls_x509_crt_free(git__ssl_conf
->ca_chain
);
59 git__free(git__ssl_conf
->ca_chain
);
60 mbedtls_ctr_drbg_free(git__ssl_conf
->p_rng
);
61 git__free(git__ssl_conf
->p_rng
);
62 mbedtls_ssl_config_free(git__ssl_conf
);
63 git__free(git__ssl_conf
);
66 if (mbedtls_entropy
) {
67 mbedtls_entropy_free(mbedtls_entropy
);
68 git__free(mbedtls_entropy
);
69 mbedtls_entropy
= NULL
;
73 int git_mbedtls_stream_global_init(void)
76 char *crtpath
= GIT_DEFAULT_CERT_LOCATION
;
78 mbedtls_ctr_drbg_context
*ctr_drbg
= NULL
;
80 size_t ciphers_known
= 0;
81 char *cipher_name
= NULL
;
82 char *cipher_string
= NULL
;
83 char *cipher_string_tmp
= NULL
;
85 git__ssl_conf
= git__malloc(sizeof(mbedtls_ssl_config
));
86 GIT_ERROR_CHECK_ALLOC(git__ssl_conf
);
88 mbedtls_ssl_config_init(git__ssl_conf
);
89 if (mbedtls_ssl_config_defaults(git__ssl_conf
,
90 MBEDTLS_SSL_IS_CLIENT
,
91 MBEDTLS_SSL_TRANSPORT_STREAM
,
92 MBEDTLS_SSL_PRESET_DEFAULT
) != 0) {
93 git_error_set(GIT_ERROR_SSL
, "failed to initialize mbedTLS");
98 mbedtls_ssl_conf_min_version(git__ssl_conf
, MBEDTLS_SSL_MAJOR_VERSION_3
, MBEDTLS_SSL_MINOR_VERSION_0
);
100 /* verify_server_cert is responsible for making the check.
101 * OPTIONAL because REQUIRED drops the certificate as soon as the check
102 * is made, so we can never see the certificate and override it. */
103 mbedtls_ssl_conf_authmode(git__ssl_conf
, MBEDTLS_SSL_VERIFY_OPTIONAL
);
105 /* set the list of allowed ciphersuites */
107 cipher_string
= cipher_string_tmp
= git__strdup(GIT_SSL_DEFAULT_CIPHERS
);
108 GIT_ERROR_CHECK_ALLOC(cipher_string
);
110 while ((cipher_name
= git__strtok(&cipher_string_tmp
, ":")) != NULL
) {
111 int cipherid
= mbedtls_ssl_get_ciphersuite_id(cipher_name
);
112 if (cipherid
== 0) continue;
114 if (ciphers_known
>= ARRAY_SIZE(ciphers_list
)) {
115 git_error_set(GIT_ERROR_SSL
, "out of cipher list space");
119 ciphers_list
[ciphers_known
++] = cipherid
;
121 git__free(cipher_string
);
123 if (!ciphers_known
) {
124 git_error_set(GIT_ERROR_SSL
, "no cipher could be enabled");
127 mbedtls_ssl_conf_ciphersuites(git__ssl_conf
, ciphers_list
);
129 /* Seeding the random number generator */
130 mbedtls_entropy
= git__malloc(sizeof(mbedtls_entropy_context
));
131 GIT_ERROR_CHECK_ALLOC(mbedtls_entropy
);
133 mbedtls_entropy_init(mbedtls_entropy
);
135 ctr_drbg
= git__malloc(sizeof(mbedtls_ctr_drbg_context
));
136 GIT_ERROR_CHECK_ALLOC(ctr_drbg
);
138 mbedtls_ctr_drbg_init(ctr_drbg
);
140 if (mbedtls_ctr_drbg_seed(ctr_drbg
,
141 mbedtls_entropy_func
,
142 mbedtls_entropy
, NULL
, 0) != 0) {
143 git_error_set(GIT_ERROR_SSL
, "failed to initialize mbedTLS entropy pool");
147 mbedtls_ssl_conf_rng(git__ssl_conf
, mbedtls_ctr_drbg_random
, ctr_drbg
);
149 /* load default certificates */
150 if (crtpath
!= NULL
&& stat(crtpath
, &statbuf
) == 0 && S_ISREG(statbuf
.st_mode
))
151 loaded
= (git_mbedtls__set_cert_location(crtpath
, NULL
) == 0);
152 if (!loaded
&& crtpath
!= NULL
&& stat(crtpath
, &statbuf
) == 0 && S_ISDIR(statbuf
.st_mode
))
153 loaded
= (git_mbedtls__set_cert_location(NULL
, crtpath
) == 0);
155 return git_runtime_shutdown_register(shutdown_ssl
);
158 mbedtls_ctr_drbg_free(ctr_drbg
);
160 mbedtls_ssl_config_free(git__ssl_conf
);
161 git__free(git__ssl_conf
);
162 git__ssl_conf
= NULL
;
167 static int bio_read(void *b
, unsigned char *buf
, size_t len
)
169 git_stream
*io
= (git_stream
*) b
;
170 return (int) git_stream_read(io
, buf
, min(len
, INT_MAX
));
173 static int bio_write(void *b
, const unsigned char *buf
, size_t len
)
175 git_stream
*io
= (git_stream
*) b
;
176 return (int) git_stream_write(io
, (const char *)buf
, min(len
, INT_MAX
), 0);
179 static int ssl_set_error(mbedtls_ssl_context
*ssl
, int error
)
184 GIT_ASSERT(error
!= MBEDTLS_ERR_SSL_WANT_READ
);
185 GIT_ASSERT(error
!= MBEDTLS_ERR_SSL_WANT_WRITE
);
188 mbedtls_strerror( error
, errbuf
, 512 );
192 git_error_set(GIT_ERROR_SSL
, "SSL error: unknown error");
195 case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED
:
196 git_error_set(GIT_ERROR_SSL
, "SSL error: %#04x [%x] - %s", error
, ssl
->session_negotiate
->verify_result
, errbuf
);
197 ret
= GIT_ECERTIFICATE
;
201 git_error_set(GIT_ERROR_SSL
, "SSL error: %#04x - %s", error
, errbuf
);
207 static int ssl_teardown(mbedtls_ssl_context
*ssl
)
211 ret
= mbedtls_ssl_close_notify(ssl
);
213 ret
= ssl_set_error(ssl
, ret
);
215 mbedtls_ssl_free(ssl
);
219 static int verify_server_cert(mbedtls_ssl_context
*ssl
)
223 if ((ret
= mbedtls_ssl_get_verify_result(ssl
)) != 0) {
225 int len
= mbedtls_x509_crt_verify_info(vrfy_buf
, sizeof(vrfy_buf
), "", ret
);
226 if (len
>= 1) vrfy_buf
[len
- 1] = '\0'; /* Remove trailing \n */
227 git_error_set(GIT_ERROR_SSL
, "the SSL certificate is invalid: %#04x - %s", ret
, vrfy_buf
);
228 return GIT_ECERTIFICATE
;
240 mbedtls_ssl_context
*ssl
;
241 git_cert_x509 cert_info
;
245 static int mbedtls_connect(git_stream
*stream
)
248 mbedtls_stream
*st
= (mbedtls_stream
*) stream
;
250 if (st
->owned
&& (ret
= git_stream_connect(st
->io
)) < 0)
253 st
->connected
= true;
255 mbedtls_ssl_set_hostname(st
->ssl
, st
->host
);
257 mbedtls_ssl_set_bio(st
->ssl
, st
->io
, bio_write
, bio_read
, NULL
);
259 if ((ret
= mbedtls_ssl_handshake(st
->ssl
)) != 0)
260 return ssl_set_error(st
->ssl
, ret
);
262 return verify_server_cert(st
->ssl
);
265 static int mbedtls_certificate(git_cert
**out
, git_stream
*stream
)
267 unsigned char *encoded_cert
;
268 mbedtls_stream
*st
= (mbedtls_stream
*) stream
;
270 const mbedtls_x509_crt
*cert
= mbedtls_ssl_get_peer_cert(st
->ssl
);
272 git_error_set(GIT_ERROR_SSL
, "the server did not provide a certificate");
276 /* Retrieve the length of the certificate first */
277 if (cert
->raw
.len
== 0) {
278 git_error_set(GIT_ERROR_NET
, "failed to retrieve certificate information");
282 encoded_cert
= git__malloc(cert
->raw
.len
);
283 GIT_ERROR_CHECK_ALLOC(encoded_cert
);
284 memcpy(encoded_cert
, cert
->raw
.p
, cert
->raw
.len
);
286 st
->cert_info
.parent
.cert_type
= GIT_CERT_X509
;
287 st
->cert_info
.data
= encoded_cert
;
288 st
->cert_info
.len
= cert
->raw
.len
;
290 *out
= &st
->cert_info
.parent
;
295 static int mbedtls_set_proxy(git_stream
*stream
, const git_proxy_options
*proxy_options
)
297 mbedtls_stream
*st
= (mbedtls_stream
*) stream
;
299 return git_stream_set_proxy(st
->io
, proxy_options
);
302 static ssize_t
mbedtls_stream_write(git_stream
*stream
, const char *data
, size_t len
, int flags
)
304 mbedtls_stream
*st
= (mbedtls_stream
*) stream
;
310 * `mbedtls_ssl_write` can only represent INT_MAX bytes
311 * written via its return value. We thus need to clamp
312 * the maximum number of bytes written.
314 len
= min(len
, INT_MAX
);
316 if ((written
= mbedtls_ssl_write(st
->ssl
, (const unsigned char *)data
, len
)) <= 0)
317 return ssl_set_error(st
->ssl
, written
);
322 static ssize_t
mbedtls_stream_read(git_stream
*stream
, void *data
, size_t len
)
324 mbedtls_stream
*st
= (mbedtls_stream
*) stream
;
327 if ((ret
= mbedtls_ssl_read(st
->ssl
, (unsigned char *)data
, len
)) <= 0)
328 ssl_set_error(st
->ssl
, ret
);
333 static int mbedtls_stream_close(git_stream
*stream
)
335 mbedtls_stream
*st
= (mbedtls_stream
*) stream
;
338 if (st
->connected
&& (ret
= ssl_teardown(st
->ssl
)) != 0)
341 st
->connected
= false;
343 return st
->owned
? git_stream_close(st
->io
) : 0;
346 static void mbedtls_stream_free(git_stream
*stream
)
348 mbedtls_stream
*st
= (mbedtls_stream
*) stream
;
351 git_stream_free(st
->io
);
354 git__free(st
->cert_info
.data
);
355 mbedtls_ssl_free(st
->ssl
);
360 static int mbedtls_stream_wrap(
369 st
= git__calloc(1, sizeof(mbedtls_stream
));
370 GIT_ERROR_CHECK_ALLOC(st
);
375 st
->ssl
= git__malloc(sizeof(mbedtls_ssl_context
));
376 GIT_ERROR_CHECK_ALLOC(st
->ssl
);
377 mbedtls_ssl_init(st
->ssl
);
378 if (mbedtls_ssl_setup(st
->ssl
, git__ssl_conf
)) {
379 git_error_set(GIT_ERROR_SSL
, "failed to create ssl object");
384 st
->host
= git__strdup(host
);
385 GIT_ERROR_CHECK_ALLOC(st
->host
);
387 st
->parent
.version
= GIT_STREAM_VERSION
;
388 st
->parent
.encrypted
= 1;
389 st
->parent
.proxy_support
= git_stream_supports_proxy(st
->io
);
390 st
->parent
.connect
= mbedtls_connect
;
391 st
->parent
.certificate
= mbedtls_certificate
;
392 st
->parent
.set_proxy
= mbedtls_set_proxy
;
393 st
->parent
.read
= mbedtls_stream_read
;
394 st
->parent
.write
= mbedtls_stream_write
;
395 st
->parent
.close
= mbedtls_stream_close
;
396 st
->parent
.free
= mbedtls_stream_free
;
398 *out
= (git_stream
*) st
;
402 mbedtls_ssl_free(st
->ssl
);
403 git_stream_close(st
->io
);
404 git_stream_free(st
->io
);
410 int git_mbedtls_stream_wrap(
415 return mbedtls_stream_wrap(out
, in
, host
, 0);
418 int git_mbedtls_stream_new(
427 GIT_ASSERT_ARG(host
);
428 GIT_ASSERT_ARG(port
);
430 if ((error
= git_socket_stream_new(&stream
, host
, port
)) < 0)
433 if ((error
= mbedtls_stream_wrap(out
, stream
, host
, 1)) < 0) {
434 git_stream_close(stream
);
435 git_stream_free(stream
);
441 int git_mbedtls__set_cert_location(const char *file
, const char *path
)
445 mbedtls_x509_crt
*cacert
;
447 GIT_ASSERT_ARG(file
|| path
);
449 cacert
= git__malloc(sizeof(mbedtls_x509_crt
));
450 GIT_ERROR_CHECK_ALLOC(cacert
);
452 mbedtls_x509_crt_init(cacert
);
454 ret
= mbedtls_x509_crt_parse_file(cacert
, file
);
455 if (ret
>= 0 && path
)
456 ret
= mbedtls_x509_crt_parse_path(cacert
, path
);
457 /* mbedtls_x509_crt_parse_path returns the number of invalid certs on success */
459 mbedtls_x509_crt_free(cacert
);
461 mbedtls_strerror( ret
, errbuf
, 512 );
462 git_error_set(GIT_ERROR_SSL
, "failed to load CA certificates: %#04x - %s", ret
, errbuf
);
466 mbedtls_x509_crt_free(git__ssl_conf
->ca_chain
);
467 git__free(git__ssl_conf
->ca_chain
);
468 mbedtls_ssl_conf_ca_chain(git__ssl_conf
, cacert
, NULL
);
477 int git_mbedtls_stream_global_init(void)