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