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 */
27 # define inline __inline
28 #elif defined(__GNUC__)
29 # define inline __inline__
34 #include <mbedtls/config.h>
35 #include <mbedtls/ssl.h>
36 #include <mbedtls/error.h>
37 #include <mbedtls/entropy.h>
38 #include <mbedtls/ctr_drbg.h>
42 #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"
43 #define GIT_SSL_DEFAULT_CIPHERS_COUNT 30
45 static mbedtls_ssl_config
*git__ssl_conf
;
46 static int ciphers_list
[GIT_SSL_DEFAULT_CIPHERS_COUNT
];
47 static mbedtls_entropy_context
*mbedtls_entropy
;
50 * This function aims to clean-up the SSL context which
53 static void shutdown_ssl(void)
56 mbedtls_x509_crt_free(git__ssl_conf
->ca_chain
);
57 git__free(git__ssl_conf
->ca_chain
);
58 mbedtls_ctr_drbg_free(git__ssl_conf
->p_rng
);
59 git__free(git__ssl_conf
->p_rng
);
60 mbedtls_ssl_config_free(git__ssl_conf
);
61 git__free(git__ssl_conf
);
64 if (mbedtls_entropy
) {
65 mbedtls_entropy_free(mbedtls_entropy
);
66 git__free(mbedtls_entropy
);
67 mbedtls_entropy
= NULL
;
71 int git_mbedtls_stream_global_init(void)
74 char *crtpath
= GIT_DEFAULT_CERT_LOCATION
;
76 mbedtls_ctr_drbg_context
*ctr_drbg
= NULL
;
78 size_t ciphers_known
= 0;
79 char *cipher_name
= NULL
;
80 char *cipher_string
= NULL
;
81 char *cipher_string_tmp
= NULL
;
83 git__ssl_conf
= git__malloc(sizeof(mbedtls_ssl_config
));
84 GIT_ERROR_CHECK_ALLOC(git__ssl_conf
);
86 mbedtls_ssl_config_init(git__ssl_conf
);
87 if (mbedtls_ssl_config_defaults(git__ssl_conf
,
88 MBEDTLS_SSL_IS_CLIENT
,
89 MBEDTLS_SSL_TRANSPORT_STREAM
,
90 MBEDTLS_SSL_PRESET_DEFAULT
) != 0) {
91 git_error_set(GIT_ERROR_SSL
, "failed to initialize mbedTLS");
96 mbedtls_ssl_conf_min_version(git__ssl_conf
, MBEDTLS_SSL_MAJOR_VERSION_3
, MBEDTLS_SSL_MINOR_VERSION_0
);
98 /* verify_server_cert is responsible for making the check.
99 * OPTIONAL because REQUIRED drops the certificate as soon as the check
100 * is made, so we can never see the certificate and override it. */
101 mbedtls_ssl_conf_authmode(git__ssl_conf
, MBEDTLS_SSL_VERIFY_OPTIONAL
);
103 /* set the list of allowed ciphersuites */
105 cipher_string
= cipher_string_tmp
= git__strdup(GIT_SSL_DEFAULT_CIPHERS
);
106 GIT_ERROR_CHECK_ALLOC(cipher_string
);
108 while ((cipher_name
= git__strtok(&cipher_string_tmp
, ":")) != NULL
) {
109 int cipherid
= mbedtls_ssl_get_ciphersuite_id(cipher_name
);
110 if (cipherid
== 0) continue;
112 if (ciphers_known
>= ARRAY_SIZE(ciphers_list
)) {
113 git_error_set(GIT_ERROR_SSL
, "out of cipher list space");
117 ciphers_list
[ciphers_known
++] = cipherid
;
119 git__free(cipher_string
);
121 if (!ciphers_known
) {
122 git_error_set(GIT_ERROR_SSL
, "no cipher could be enabled");
125 mbedtls_ssl_conf_ciphersuites(git__ssl_conf
, ciphers_list
);
127 /* Seeding the random number generator */
128 mbedtls_entropy
= git__malloc(sizeof(mbedtls_entropy_context
));
129 GIT_ERROR_CHECK_ALLOC(mbedtls_entropy
);
131 mbedtls_entropy_init(mbedtls_entropy
);
133 ctr_drbg
= git__malloc(sizeof(mbedtls_ctr_drbg_context
));
134 GIT_ERROR_CHECK_ALLOC(ctr_drbg
);
136 mbedtls_ctr_drbg_init(ctr_drbg
);
138 if (mbedtls_ctr_drbg_seed(ctr_drbg
,
139 mbedtls_entropy_func
,
140 mbedtls_entropy
, NULL
, 0) != 0) {
141 git_error_set(GIT_ERROR_SSL
, "failed to initialize mbedTLS entropy pool");
145 mbedtls_ssl_conf_rng(git__ssl_conf
, mbedtls_ctr_drbg_random
, ctr_drbg
);
147 /* load default certificates */
148 if (crtpath
!= NULL
&& stat(crtpath
, &statbuf
) == 0 && S_ISREG(statbuf
.st_mode
))
149 loaded
= (git_mbedtls__set_cert_location(crtpath
, NULL
) == 0);
150 if (!loaded
&& crtpath
!= NULL
&& stat(crtpath
, &statbuf
) == 0 && S_ISDIR(statbuf
.st_mode
))
151 loaded
= (git_mbedtls__set_cert_location(NULL
, crtpath
) == 0);
153 return git_runtime_shutdown_register(shutdown_ssl
);
156 mbedtls_ctr_drbg_free(ctr_drbg
);
158 mbedtls_ssl_config_free(git__ssl_conf
);
159 git__free(git__ssl_conf
);
160 git__ssl_conf
= NULL
;
165 static int bio_read(void *b
, unsigned char *buf
, size_t len
)
167 git_stream
*io
= (git_stream
*) b
;
168 return (int) git_stream_read(io
, buf
, min(len
, INT_MAX
));
171 static int bio_write(void *b
, const unsigned char *buf
, size_t len
)
173 git_stream
*io
= (git_stream
*) b
;
174 return (int) git_stream_write(io
, (const char *)buf
, min(len
, INT_MAX
), 0);
177 static int ssl_set_error(mbedtls_ssl_context
*ssl
, int error
)
182 GIT_ASSERT(error
!= MBEDTLS_ERR_SSL_WANT_READ
);
183 GIT_ASSERT(error
!= MBEDTLS_ERR_SSL_WANT_WRITE
);
186 mbedtls_strerror( error
, errbuf
, 512 );
190 git_error_set(GIT_ERROR_SSL
, "SSL error: unknown error");
193 case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED
:
194 git_error_set(GIT_ERROR_SSL
, "SSL error: %#04x [%x] - %s", error
, ssl
->session_negotiate
->verify_result
, errbuf
);
195 ret
= GIT_ECERTIFICATE
;
199 git_error_set(GIT_ERROR_SSL
, "SSL error: %#04x - %s", error
, errbuf
);
205 static int ssl_teardown(mbedtls_ssl_context
*ssl
)
209 ret
= mbedtls_ssl_close_notify(ssl
);
211 ret
= ssl_set_error(ssl
, ret
);
213 mbedtls_ssl_free(ssl
);
217 static int verify_server_cert(mbedtls_ssl_context
*ssl
)
221 if ((ret
= mbedtls_ssl_get_verify_result(ssl
)) != 0) {
223 int len
= mbedtls_x509_crt_verify_info(vrfy_buf
, sizeof(vrfy_buf
), "", ret
);
224 if (len
>= 1) vrfy_buf
[len
- 1] = '\0'; /* Remove trailing \n */
225 git_error_set(GIT_ERROR_SSL
, "the SSL certificate is invalid: %#04x - %s", ret
, vrfy_buf
);
226 return GIT_ECERTIFICATE
;
238 mbedtls_ssl_context
*ssl
;
239 git_cert_x509 cert_info
;
243 static int mbedtls_connect(git_stream
*stream
)
246 mbedtls_stream
*st
= (mbedtls_stream
*) stream
;
248 if (st
->owned
&& (ret
= git_stream_connect(st
->io
)) < 0)
251 st
->connected
= true;
253 mbedtls_ssl_set_hostname(st
->ssl
, st
->host
);
255 mbedtls_ssl_set_bio(st
->ssl
, st
->io
, bio_write
, bio_read
, NULL
);
257 if ((ret
= mbedtls_ssl_handshake(st
->ssl
)) != 0)
258 return ssl_set_error(st
->ssl
, ret
);
260 return verify_server_cert(st
->ssl
);
263 static int mbedtls_certificate(git_cert
**out
, git_stream
*stream
)
265 unsigned char *encoded_cert
;
266 mbedtls_stream
*st
= (mbedtls_stream
*) stream
;
268 const mbedtls_x509_crt
*cert
= mbedtls_ssl_get_peer_cert(st
->ssl
);
270 git_error_set(GIT_ERROR_SSL
, "the server did not provide a certificate");
274 /* Retrieve the length of the certificate first */
275 if (cert
->raw
.len
== 0) {
276 git_error_set(GIT_ERROR_NET
, "failed to retrieve certificate information");
280 encoded_cert
= git__malloc(cert
->raw
.len
);
281 GIT_ERROR_CHECK_ALLOC(encoded_cert
);
282 memcpy(encoded_cert
, cert
->raw
.p
, cert
->raw
.len
);
284 st
->cert_info
.parent
.cert_type
= GIT_CERT_X509
;
285 st
->cert_info
.data
= encoded_cert
;
286 st
->cert_info
.len
= cert
->raw
.len
;
288 *out
= &st
->cert_info
.parent
;
293 static int mbedtls_set_proxy(git_stream
*stream
, const git_proxy_options
*proxy_options
)
295 mbedtls_stream
*st
= (mbedtls_stream
*) stream
;
297 return git_stream_set_proxy(st
->io
, proxy_options
);
300 static ssize_t
mbedtls_stream_write(git_stream
*stream
, const char *data
, size_t len
, int flags
)
302 mbedtls_stream
*st
= (mbedtls_stream
*) stream
;
308 * `mbedtls_ssl_write` can only represent INT_MAX bytes
309 * written via its return value. We thus need to clamp
310 * the maximum number of bytes written.
312 len
= min(len
, INT_MAX
);
314 if ((written
= mbedtls_ssl_write(st
->ssl
, (const unsigned char *)data
, len
)) <= 0)
315 return ssl_set_error(st
->ssl
, written
);
320 static ssize_t
mbedtls_stream_read(git_stream
*stream
, void *data
, size_t len
)
322 mbedtls_stream
*st
= (mbedtls_stream
*) stream
;
325 if ((ret
= mbedtls_ssl_read(st
->ssl
, (unsigned char *)data
, len
)) <= 0)
326 ssl_set_error(st
->ssl
, ret
);
331 static int mbedtls_stream_close(git_stream
*stream
)
333 mbedtls_stream
*st
= (mbedtls_stream
*) stream
;
336 if (st
->connected
&& (ret
= ssl_teardown(st
->ssl
)) != 0)
339 st
->connected
= false;
341 return st
->owned
? git_stream_close(st
->io
) : 0;
344 static void mbedtls_stream_free(git_stream
*stream
)
346 mbedtls_stream
*st
= (mbedtls_stream
*) stream
;
349 git_stream_free(st
->io
);
352 git__free(st
->cert_info
.data
);
353 mbedtls_ssl_free(st
->ssl
);
358 static int mbedtls_stream_wrap(
367 st
= git__calloc(1, sizeof(mbedtls_stream
));
368 GIT_ERROR_CHECK_ALLOC(st
);
373 st
->ssl
= git__malloc(sizeof(mbedtls_ssl_context
));
374 GIT_ERROR_CHECK_ALLOC(st
->ssl
);
375 mbedtls_ssl_init(st
->ssl
);
376 if (mbedtls_ssl_setup(st
->ssl
, git__ssl_conf
)) {
377 git_error_set(GIT_ERROR_SSL
, "failed to create ssl object");
382 st
->host
= git__strdup(host
);
383 GIT_ERROR_CHECK_ALLOC(st
->host
);
385 st
->parent
.version
= GIT_STREAM_VERSION
;
386 st
->parent
.encrypted
= 1;
387 st
->parent
.proxy_support
= git_stream_supports_proxy(st
->io
);
388 st
->parent
.connect
= mbedtls_connect
;
389 st
->parent
.certificate
= mbedtls_certificate
;
390 st
->parent
.set_proxy
= mbedtls_set_proxy
;
391 st
->parent
.read
= mbedtls_stream_read
;
392 st
->parent
.write
= mbedtls_stream_write
;
393 st
->parent
.close
= mbedtls_stream_close
;
394 st
->parent
.free
= mbedtls_stream_free
;
396 *out
= (git_stream
*) st
;
400 mbedtls_ssl_free(st
->ssl
);
401 git_stream_close(st
->io
);
402 git_stream_free(st
->io
);
408 int git_mbedtls_stream_wrap(
413 return mbedtls_stream_wrap(out
, in
, host
, 0);
416 int git_mbedtls_stream_new(
425 GIT_ASSERT_ARG(host
);
426 GIT_ASSERT_ARG(port
);
428 if ((error
= git_socket_stream_new(&stream
, host
, port
)) < 0)
431 if ((error
= mbedtls_stream_wrap(out
, stream
, host
, 1)) < 0) {
432 git_stream_close(stream
);
433 git_stream_free(stream
);
439 int git_mbedtls__set_cert_location(const char *file
, const char *path
)
443 mbedtls_x509_crt
*cacert
;
445 GIT_ASSERT_ARG(file
|| path
);
447 cacert
= git__malloc(sizeof(mbedtls_x509_crt
));
448 GIT_ERROR_CHECK_ALLOC(cacert
);
450 mbedtls_x509_crt_init(cacert
);
452 ret
= mbedtls_x509_crt_parse_file(cacert
, file
);
453 if (ret
>= 0 && path
)
454 ret
= mbedtls_x509_crt_parse_path(cacert
, path
);
455 /* mbedtls_x509_crt_parse_path returns the number of invalid certs on success */
457 mbedtls_x509_crt_free(cacert
);
459 mbedtls_strerror( ret
, errbuf
, 512 );
460 git_error_set(GIT_ERROR_SSL
, "failed to load CA certificates: %#04x - %s", ret
, errbuf
);
464 mbedtls_x509_crt_free(git__ssl_conf
->ca_chain
);
465 git__free(git__ssl_conf
->ca_chain
);
466 mbedtls_ssl_conf_ca_chain(git__ssl_conf
, cacert
, NULL
);
475 int git_mbedtls_stream_global_init(void)