Commit ff5a39678b77d1ccb65119314ee518b019add7aa

Carlos Martín Nieto 2016-03-14T15:29:30

Merge pull request #3683 from dbussink/dbussink/better-openssl-ciphers Setup better defaults for OpenSSL ciphers

diff --git a/include/git2/common.h b/include/git2/common.h
index c1efee3..0629abb 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -149,6 +149,7 @@ typedef enum {
 	GIT_OPT_SET_SSL_CERT_LOCATIONS,
 	GIT_OPT_SET_USER_AGENT,
 	GIT_OPT_ENABLE_STRICT_OBJECT_CREATION,
+	GIT_OPT_SET_SSL_CIPHERS,
 } git_libgit2_opt_t;
 
 /**
@@ -260,6 +261,11 @@ typedef enum {
  *		> example, when this is enabled, the parent(s) and tree inputs
  *		> will be validated when creating a new commit.  This defaults
  *		> to disabled.
+ *	* opts(GIT_OPT_SET_SSL_CIPHERS, const char *ciphers)
+ *
+ *		> Set the SSL ciphers use for HTTPS connections.
+ *		>
+ *		> - `ciphers` is the list of ciphers that are eanbled.
  *
  * @param option Option key
  * @param ... value to set the option
diff --git a/src/global.c b/src/global.c
index 0bfde1e..c725b51 100644
--- a/src/global.c
+++ b/src/global.c
@@ -27,6 +27,7 @@ static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB];
 static git_atomic git__n_shutdown_callbacks;
 static git_atomic git__n_inits;
 char *git__user_agent;
+char *git__ssl_ciphers;
 
 void git__on_shutdown(git_global_shutdown_fn callback)
 {
@@ -83,6 +84,7 @@ static void shutdown_common(void)
 	}
 
 	git__free(git__user_agent);
+	git__free(git__ssl_ciphers);
 
 #if defined(GIT_MSVC_CRTDBG)
 	git_win32__crtdbg_stacktrace_cleanup();
diff --git a/src/global.h b/src/global.h
index 9fdcee5..2199515 100644
--- a/src/global.h
+++ b/src/global.h
@@ -36,5 +36,6 @@ extern void git__on_shutdown(git_global_shutdown_fn callback);
 extern void git__free_tls_data(void);
 
 extern const char *git_libgit2__user_agent(void);
+extern const char *git_libgit2__ssl_ciphers(void);
 
 #endif
diff --git a/src/openssl_stream.c b/src/openssl_stream.c
index 97736b7..a65f558 100644
--- a/src/openssl_stream.c
+++ b/src/openssl_stream.c
@@ -34,6 +34,8 @@
 
 SSL_CTX *git__ssl_ctx;
 
+#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"
+
 #ifdef GIT_THREADS
 
 static git_mutex *openssl_locks;
@@ -85,6 +87,7 @@ int git_openssl_stream_global_init(void)
 {
 #ifdef GIT_OPENSSL
 	long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
+	const char *ciphers = git_libgit2__ssl_ciphers();
 
 	/* Older OpenSSL and MacOS OpenSSL doesn't have this */
 #ifdef SSL_OP_NO_COMPRESSION
@@ -108,6 +111,16 @@ int git_openssl_stream_global_init(void)
 		git__ssl_ctx = NULL;
 		return -1;
 	}
+
+	if (!ciphers) {
+		ciphers = GIT_SSL_DEFAULT_CIPHERS;
+	}
+
+	if(!SSL_CTX_set_cipher_list(git__ssl_ctx, ciphers)) {
+		SSL_CTX_free(git__ssl_ctx);
+		git__ssl_ctx = NULL;
+		return -1;
+	}
 #endif
 
 	git__on_shutdown(shutdown_ssl);
diff --git a/src/settings.c b/src/settings.c
index 88602ba..0da19ea 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -71,12 +71,18 @@ static int config_level_to_sysdir(int config_level)
 }
 
 extern char *git__user_agent;
+extern char *git__ssl_ciphers;
 
 const char *git_libgit2__user_agent()
 {
 	return git__user_agent;
 }
 
+const char *git_libgit2__ssl_ciphers()
+{
+	return git__ssl_ciphers;
+}
+
 int git_libgit2_opts(int key, ...)
 {
 	int error = 0;
@@ -169,7 +175,7 @@ int git_libgit2_opts(int key, ...)
 			}
 		}
 #else
-		giterr_set(GITERR_NET, "Cannot set certificate locations: OpenSSL is not enabled");
+		giterr_set(GITERR_NET, "cannot set certificate locations: OpenSSL is not enabled");
 		error = -1;
 #endif
 		break;
@@ -187,6 +193,22 @@ int git_libgit2_opts(int key, ...)
 		git_object__strict_input_validation = (va_arg(ap, int) != 0);
 		break;
 
+	case GIT_OPT_SET_SSL_CIPHERS:
+#ifdef GIT_OPENSSL
+		{
+			git__free(git__ssl_ciphers);
+			git__ssl_ciphers = git__strdup(va_arg(ap, const char *));
+			if (!git__ssl_ciphers) {
+				giterr_set_oom();
+				error = -1;
+			}
+		}
+#else
+		giterr_set(GITERR_NET, "cannot set custom ciphers: OpenSSL is not enabled");
+		error = -1;
+#endif
+		break;
+
 	default:
 		giterr_set(GITERR_INVALID, "invalid option key");
 		error = -1;
diff --git a/tests/online/badssl.c b/tests/online/badssl.c
index 12badbd..66b090d 100644
--- a/tests/online/badssl.c
+++ b/tests/online/badssl.c
@@ -36,3 +36,11 @@ void test_online_badssl__self_signed(void)
 	cl_git_fail_with(GIT_ECERTIFICATE,
 			 git_clone(&g_repo, "https://self-signed.badssl.com/fake.git", "./fake", NULL));
 }
+
+void test_online_badssl__old_cipher(void)
+{
+	if (!g_has_ssl)
+		cl_skip();
+
+	cl_git_fail(git_clone(&g_repo, "https://rc4.badssl.com/fake.git", "./fake", NULL));
+}