File Coverage

deps/libgit2/src/streams/mbedtls.c
Criterion Covered Total %
statement 2 2 100.0
branch n/a
condition n/a
subroutine n/a
pod n/a
total 2 2 100.0


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/mbedtls.h"
9              
10             #ifdef GIT_MBEDTLS
11              
12             #include
13              
14             #include "global.h"
15             #include "stream.h"
16             #include "streams/socket.h"
17             #include "netops.h"
18             #include "git2/transport.h"
19             #include "util.h"
20              
21             #ifndef GIT_DEFAULT_CERT_LOCATION
22             #define GIT_DEFAULT_CERT_LOCATION NULL
23             #endif
24              
25             /* Work around C90-conformance issues */
26             #if defined(_MSC_VER)
27             # define inline __inline
28             #elif defined(__GNUC__)
29             # define inline __inline__
30             #else
31             # define inline
32             #endif
33              
34             #include
35             #include
36             #include
37             #include
38             #include
39              
40             #undef inline
41              
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
44              
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;
48              
49             /**
50             * This function aims to clean-up the SSL context which
51             * we allocated.
52             */
53             static void shutdown_ssl(void)
54             {
55             if (git__ssl_conf) {
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);
62             git__ssl_conf = NULL;
63             }
64             if (mbedtls_entropy) {
65             mbedtls_entropy_free(mbedtls_entropy);
66             git__free(mbedtls_entropy);
67             mbedtls_entropy = NULL;
68             }
69             }
70              
71             int git_mbedtls__set_cert_location(const char *path, int is_dir);
72              
73             int git_mbedtls_stream_global_init(void)
74             {
75             int loaded = 0;
76             char *crtpath = GIT_DEFAULT_CERT_LOCATION;
77             struct stat statbuf;
78             mbedtls_ctr_drbg_context *ctr_drbg = NULL;
79              
80             size_t ciphers_known = 0;
81             char *cipher_name = NULL;
82             char *cipher_string = NULL;
83             char *cipher_string_tmp = NULL;
84              
85             git__ssl_conf = git__malloc(sizeof(mbedtls_ssl_config));
86             GIT_ERROR_CHECK_ALLOC(git__ssl_conf);
87              
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");
94             goto cleanup;
95             }
96              
97             /* configure TLSv1 */
98             mbedtls_ssl_conf_min_version(git__ssl_conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_0);
99              
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);
104              
105             /* set the list of allowed ciphersuites */
106             ciphers_known = 0;
107             cipher_string = cipher_string_tmp = git__strdup(GIT_SSL_DEFAULT_CIPHERS);
108             GIT_ERROR_CHECK_ALLOC(cipher_string);
109              
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;
113              
114             if (ciphers_known >= ARRAY_SIZE(ciphers_list)) {
115             git_error_set(GIT_ERROR_SSL, "out of cipher list space");
116             goto cleanup;
117             }
118              
119             ciphers_list[ciphers_known++] = cipherid;
120             }
121             git__free(cipher_string);
122              
123             if (!ciphers_known) {
124             git_error_set(GIT_ERROR_SSL, "no cipher could be enabled");
125             goto cleanup;
126             }
127             mbedtls_ssl_conf_ciphersuites(git__ssl_conf, ciphers_list);
128              
129             /* Seeding the random number generator */
130             mbedtls_entropy = git__malloc(sizeof(mbedtls_entropy_context));
131             GIT_ERROR_CHECK_ALLOC(mbedtls_entropy);
132              
133             mbedtls_entropy_init(mbedtls_entropy);
134              
135             ctr_drbg = git__malloc(sizeof(mbedtls_ctr_drbg_context));
136             GIT_ERROR_CHECK_ALLOC(ctr_drbg);
137              
138             mbedtls_ctr_drbg_init(ctr_drbg);
139              
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");
144             goto cleanup;
145             }
146              
147             mbedtls_ssl_conf_rng(git__ssl_conf, mbedtls_ctr_drbg_random, ctr_drbg);
148              
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, 0) == 0);
152             if (!loaded && crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISDIR(statbuf.st_mode))
153             loaded = (git_mbedtls__set_cert_location(crtpath, 1) == 0);
154              
155             git__on_shutdown(shutdown_ssl);
156              
157             return 0;
158              
159             cleanup:
160             mbedtls_ctr_drbg_free(ctr_drbg);
161             git__free(ctr_drbg);
162             mbedtls_ssl_config_free(git__ssl_conf);
163             git__free(git__ssl_conf);
164             git__ssl_conf = NULL;
165              
166             return -1;
167             }
168              
169             static int bio_read(void *b, unsigned char *buf, size_t len)
170             {
171             git_stream *io = (git_stream *) b;
172             return (int) git_stream_read(io, buf, min(len, INT_MAX));
173             }
174              
175             static int bio_write(void *b, const unsigned char *buf, size_t len)
176             {
177             git_stream *io = (git_stream *) b;
178             return (int) git_stream_write(io, (const char *)buf, min(len, INT_MAX), 0);
179             }
180              
181             static int ssl_set_error(mbedtls_ssl_context *ssl, int error)
182             {
183             char errbuf[512];
184             int ret = -1;
185              
186             assert(error != MBEDTLS_ERR_SSL_WANT_READ);
187             assert(error != MBEDTLS_ERR_SSL_WANT_WRITE);
188              
189             if (error != 0)
190             mbedtls_strerror( error, errbuf, 512 );
191              
192             switch(error) {
193             case 0:
194             git_error_set(GIT_ERROR_SSL, "SSL error: unknown error");
195             break;
196              
197             case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED:
198             git_error_set(GIT_ERROR_SSL, "SSL error: %#04x [%x] - %s", error, ssl->session_negotiate->verify_result, errbuf);
199             ret = GIT_ECERTIFICATE;
200             break;
201              
202             default:
203             git_error_set(GIT_ERROR_SSL, "SSL error: %#04x - %s", error, errbuf);
204             }
205              
206             return ret;
207             }
208              
209             static int ssl_teardown(mbedtls_ssl_context *ssl)
210             {
211             int ret = 0;
212              
213             ret = mbedtls_ssl_close_notify(ssl);
214             if (ret < 0)
215             ret = ssl_set_error(ssl, ret);
216              
217             mbedtls_ssl_free(ssl);
218             return ret;
219             }
220              
221             static int verify_server_cert(mbedtls_ssl_context *ssl)
222             {
223             int ret = -1;
224              
225             if ((ret = mbedtls_ssl_get_verify_result(ssl)) != 0) {
226             char vrfy_buf[512];
227             int len = mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), "", ret);
228             if (len >= 1) vrfy_buf[len - 1] = '\0'; /* Remove trailing \n */
229             git_error_set(GIT_ERROR_SSL, "the SSL certificate is invalid: %#04x - %s", ret, vrfy_buf);
230             return GIT_ECERTIFICATE;
231             }
232              
233             return 0;
234             }
235              
236             typedef struct {
237             git_stream parent;
238             git_stream *io;
239             int owned;
240             bool connected;
241             char *host;
242             mbedtls_ssl_context *ssl;
243             git_cert_x509 cert_info;
244             } mbedtls_stream;
245              
246              
247             static int mbedtls_connect(git_stream *stream)
248             {
249             int ret;
250             mbedtls_stream *st = (mbedtls_stream *) stream;
251              
252             if (st->owned && (ret = git_stream_connect(st->io)) < 0)
253             return ret;
254              
255             st->connected = true;
256              
257             mbedtls_ssl_set_hostname(st->ssl, st->host);
258              
259             mbedtls_ssl_set_bio(st->ssl, st->io, bio_write, bio_read, NULL);
260              
261             if ((ret = mbedtls_ssl_handshake(st->ssl)) != 0)
262             return ssl_set_error(st->ssl, ret);
263              
264             return verify_server_cert(st->ssl);
265             }
266              
267             static int mbedtls_certificate(git_cert **out, git_stream *stream)
268             {
269             unsigned char *encoded_cert;
270             mbedtls_stream *st = (mbedtls_stream *) stream;
271              
272             const mbedtls_x509_crt *cert = mbedtls_ssl_get_peer_cert(st->ssl);
273             if (!cert) {
274             git_error_set(GIT_ERROR_SSL, "the server did not provide a certificate");
275             return -1;
276             }
277              
278             /* Retrieve the length of the certificate first */
279             if (cert->raw.len == 0) {
280             git_error_set(GIT_ERROR_NET, "failed to retrieve certificate information");
281             return -1;
282             }
283              
284             encoded_cert = git__malloc(cert->raw.len);
285             GIT_ERROR_CHECK_ALLOC(encoded_cert);
286             memcpy(encoded_cert, cert->raw.p, cert->raw.len);
287              
288             st->cert_info.parent.cert_type = GIT_CERT_X509;
289             st->cert_info.data = encoded_cert;
290             st->cert_info.len = cert->raw.len;
291              
292             *out = &st->cert_info.parent;
293              
294             return 0;
295             }
296              
297             static int mbedtls_set_proxy(git_stream *stream, const git_proxy_options *proxy_options)
298             {
299             mbedtls_stream *st = (mbedtls_stream *) stream;
300              
301             return git_stream_set_proxy(st->io, proxy_options);
302             }
303              
304             static ssize_t mbedtls_stream_write(git_stream *stream, const char *data, size_t len, int flags)
305             {
306             mbedtls_stream *st = (mbedtls_stream *) stream;
307             int written;
308              
309             GIT_UNUSED(flags);
310              
311             /*
312             * `mbedtls_ssl_write` can only represent INT_MAX bytes
313             * written via its return value. We thus need to clamp
314             * the maximum number of bytes written.
315             */
316             len = min(len, INT_MAX);
317              
318             if ((written = mbedtls_ssl_write(st->ssl, (const unsigned char *)data, len)) <= 0)
319             return ssl_set_error(st->ssl, written);
320              
321             return written;
322             }
323              
324             static ssize_t mbedtls_stream_read(git_stream *stream, void *data, size_t len)
325             {
326             mbedtls_stream *st = (mbedtls_stream *) stream;
327             int ret;
328              
329             if ((ret = mbedtls_ssl_read(st->ssl, (unsigned char *)data, len)) <= 0)
330             ssl_set_error(st->ssl, ret);
331              
332             return ret;
333             }
334              
335             static int mbedtls_stream_close(git_stream *stream)
336             {
337             mbedtls_stream *st = (mbedtls_stream *) stream;
338             int ret = 0;
339              
340             if (st->connected && (ret = ssl_teardown(st->ssl)) != 0)
341             return -1;
342              
343             st->connected = false;
344              
345             return st->owned ? git_stream_close(st->io) : 0;
346             }
347              
348             static void mbedtls_stream_free(git_stream *stream)
349             {
350             mbedtls_stream *st = (mbedtls_stream *) stream;
351              
352             if (st->owned)
353             git_stream_free(st->io);
354              
355             git__free(st->host);
356             git__free(st->cert_info.data);
357             mbedtls_ssl_free(st->ssl);
358             git__free(st->ssl);
359             git__free(st);
360             }
361              
362             static int mbedtls_stream_wrap(
363             git_stream **out,
364             git_stream *in,
365             const char *host,
366             int owned)
367             {
368             mbedtls_stream *st;
369             int error;
370              
371             st = git__calloc(1, sizeof(mbedtls_stream));
372             GIT_ERROR_CHECK_ALLOC(st);
373              
374             st->io = in;
375             st->owned = owned;
376              
377             st->ssl = git__malloc(sizeof(mbedtls_ssl_context));
378             GIT_ERROR_CHECK_ALLOC(st->ssl);
379             mbedtls_ssl_init(st->ssl);
380             if (mbedtls_ssl_setup(st->ssl, git__ssl_conf)) {
381             git_error_set(GIT_ERROR_SSL, "failed to create ssl object");
382             error = -1;
383             goto out_err;
384             }
385              
386             st->host = git__strdup(host);
387             GIT_ERROR_CHECK_ALLOC(st->host);
388              
389             st->parent.version = GIT_STREAM_VERSION;
390             st->parent.encrypted = 1;
391             st->parent.proxy_support = git_stream_supports_proxy(st->io);
392             st->parent.connect = mbedtls_connect;
393             st->parent.certificate = mbedtls_certificate;
394             st->parent.set_proxy = mbedtls_set_proxy;
395             st->parent.read = mbedtls_stream_read;
396             st->parent.write = mbedtls_stream_write;
397             st->parent.close = mbedtls_stream_close;
398             st->parent.free = mbedtls_stream_free;
399              
400             *out = (git_stream *) st;
401             return 0;
402              
403             out_err:
404             mbedtls_ssl_free(st->ssl);
405             git_stream_close(st->io);
406             git_stream_free(st->io);
407             git__free(st);
408              
409             return error;
410             }
411              
412             int git_mbedtls_stream_wrap(
413             git_stream **out,
414             git_stream *in,
415             const char *host)
416             {
417             return mbedtls_stream_wrap(out, in, host, 0);
418             }
419              
420             int git_mbedtls_stream_new(
421             git_stream **out,
422             const char *host,
423             const char *port)
424             {
425             git_stream *stream;
426             int error;
427              
428             assert(out && host && port);
429              
430             if ((error = git_socket_stream_new(&stream, host, port)) < 0)
431             return error;
432              
433             if ((error = mbedtls_stream_wrap(out, stream, host, 1)) < 0) {
434             git_stream_close(stream);
435             git_stream_free(stream);
436             }
437              
438             return error;
439             }
440              
441             int git_mbedtls__set_cert_location(const char *path, int is_dir)
442             {
443             int ret = 0;
444             char errbuf[512];
445             mbedtls_x509_crt *cacert;
446              
447             assert(path != NULL);
448              
449             cacert = git__malloc(sizeof(mbedtls_x509_crt));
450             GIT_ERROR_CHECK_ALLOC(cacert);
451              
452             mbedtls_x509_crt_init(cacert);
453             if (is_dir) {
454             ret = mbedtls_x509_crt_parse_path(cacert, path);
455             } else {
456             ret = mbedtls_x509_crt_parse_file(cacert, path);
457             }
458             /* mbedtls_x509_crt_parse_path returns the number of invalid certs on success */
459             if (ret < 0) {
460             mbedtls_x509_crt_free(cacert);
461             git__free(cacert);
462             mbedtls_strerror( ret, errbuf, 512 );
463             git_error_set(GIT_ERROR_SSL, "failed to load CA certificates: %#04x - %s", ret, errbuf);
464             return -1;
465             }
466              
467             mbedtls_x509_crt_free(git__ssl_conf->ca_chain);
468             git__free(git__ssl_conf->ca_chain);
469             mbedtls_ssl_conf_ca_chain(git__ssl_conf, cacert, NULL);
470              
471             return 0;
472             }
473              
474             #else
475              
476             #include "stream.h"
477              
478 86           int git_mbedtls_stream_global_init(void)
479             {
480 86           return 0;
481             }
482              
483             #endif