From 6955c18e9d957433a9d281e5f0f6b7ffd7d35d91 Mon Sep 17 00:00:00 2001 From: Barys Yakavita Date: Mon, 28 Sep 2020 18:56:29 +0300 Subject: [PATCH 1/2] CS-1562 Trust self-signed SSL certificates --- .../api/connected/ServerConfiguration.java | 14 ++++ .../connected/SonarLintWsClient.java | 1 + .../sonarlint/core/util/ws/HttpConnector.java | 11 +++ .../core/util/ws/OkHttpClientBuilder.java | 18 +++-- .../SonarLintLanguageServer.java | 69 ++++++++++++++++--- 5 files changed, 98 insertions(+), 15 deletions(-) diff --git a/client-api/src/main/java/org/sonarsource/sonarlint/core/client/api/connected/ServerConfiguration.java b/client-api/src/main/java/org/sonarsource/sonarlint/core/client/api/connected/ServerConfiguration.java index 6c21e428f0..70e738aa70 100644 --- a/client-api/src/main/java/org/sonarsource/sonarlint/core/client/api/connected/ServerConfiguration.java +++ b/client-api/src/main/java/org/sonarsource/sonarlint/core/client/api/connected/ServerConfiguration.java @@ -24,6 +24,7 @@ import javax.annotation.CheckForNull; import javax.annotation.Nullable; +import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509TrustManager; @@ -44,6 +45,7 @@ public class ServerConfiguration { private final int readTimeoutMs; private SSLSocketFactory sslSocketFactory = null; private X509TrustManager sslTrustManager = null; + private HostnameVerifier hostnameVerifier = null; private ServerConfiguration(Builder builder) { this.url = builder.url; @@ -58,6 +60,7 @@ private ServerConfiguration(Builder builder) { this.readTimeoutMs = builder.readTimeoutMs; this.sslSocketFactory = builder.sslSocketFactory; this.sslTrustManager = builder.sslTrustManager; + this.hostnameVerifier = builder.hostnameVerifier; } @Override @@ -116,6 +119,11 @@ public SSLSocketFactory getSSLSocketFactory() { return sslSocketFactory; } + @CheckForNull + public HostnameVerifier getHostnameVerifier() { + return hostnameVerifier; + } + @CheckForNull public String getPassword() { return password; @@ -161,6 +169,7 @@ public static class Builder { private int readTimeoutMs = DEFAULT_READ_TIMEOUT_MILLISECONDS; private SSLSocketFactory sslSocketFactory = null; private X509TrustManager sslTrustManager = null; + private HostnameVerifier hostnameVerifier = null; private Builder() { } @@ -200,6 +209,11 @@ public Builder trustManager(X509TrustManager sslTrustManager) { return this; } + public Builder hostnameVerifier(HostnameVerifier hostnameVerifier) { + this.hostnameVerifier = hostnameVerifier; + return this; + } + /** * Optional login/password, for example "admin" */ diff --git a/core/src/main/java/org/sonarsource/sonarlint/core/container/connected/SonarLintWsClient.java b/core/src/main/java/org/sonarsource/sonarlint/core/container/connected/SonarLintWsClient.java index 73bfa842e8..c99839f922 100644 --- a/core/src/main/java/org/sonarsource/sonarlint/core/container/connected/SonarLintWsClient.java +++ b/core/src/main/java/org/sonarsource/sonarlint/core/container/connected/SonarLintWsClient.java @@ -78,6 +78,7 @@ private static WsConnector buildClient(ServerConfiguration serverConfig) { .connectTimeoutMilliseconds(serverConfig.getConnectTimeoutMs()) .setSSLSocketFactory(serverConfig.getSSLSocketFactory()) .setTrustManager(serverConfig.getTrustManager()) + .setHostnameVerifier(serverConfig.getHostnameVerifier()) .build(); } diff --git a/core/src/main/java/org/sonarsource/sonarlint/core/util/ws/HttpConnector.java b/core/src/main/java/org/sonarsource/sonarlint/core/util/ws/HttpConnector.java index 75b59af861..a599b9362b 100644 --- a/core/src/main/java/org/sonarsource/sonarlint/core/util/ws/HttpConnector.java +++ b/core/src/main/java/org/sonarsource/sonarlint/core/util/ws/HttpConnector.java @@ -23,6 +23,7 @@ import java.net.Proxy; import java.util.Map; import javax.annotation.Nullable; +import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509TrustManager; import okhttp3.Call; @@ -83,6 +84,7 @@ private HttpConnector(Builder builder) { okHttpClientBuilder.setReadTimeoutMs(builder.readTimeoutMs); okHttpClientBuilder.setSSLSocketFactory(builder.sslSocketFactory); okHttpClientBuilder.setTrustManager(builder.sslTrustManager); + okHttpClientBuilder.setHostnameVerifier(builder.hostnameVerifier); this.okHttpClient = okHttpClientBuilder.build(); this.noRedirectOkHttpClient = newClientWithoutRedirect(this.okHttpClient); } @@ -251,6 +253,7 @@ public static class Builder { private int readTimeoutMs = DEFAULT_READ_TIMEOUT_MILLISECONDS; private SSLSocketFactory sslSocketFactory = null; private X509TrustManager sslTrustManager = null; + private HostnameVerifier hostnameVerifier = null; /** * Private since 5.5. @@ -320,6 +323,14 @@ public Builder setTrustManager(@Nullable X509TrustManager sslTrustManager) { return this; } + /** + * Optional hostname verifier. + */ + public Builder setHostnameVerifier(@Nullable HostnameVerifier hostnameVerifier) { + this.hostnameVerifier = hostnameVerifier; + return this; + } + /** * Sets the read timeout to a specified timeout, in milliseconds. * A timeout of zero is interpreted as an infinite timeout. Default value is {@link #DEFAULT_READ_TIMEOUT_MILLISECONDS} diff --git a/core/src/main/java/org/sonarsource/sonarlint/core/util/ws/OkHttpClientBuilder.java b/core/src/main/java/org/sonarsource/sonarlint/core/util/ws/OkHttpClientBuilder.java index 9ad77b085b..7fe2586287 100644 --- a/core/src/main/java/org/sonarsource/sonarlint/core/util/ws/OkHttpClientBuilder.java +++ b/core/src/main/java/org/sonarsource/sonarlint/core/util/ws/OkHttpClientBuilder.java @@ -19,6 +19,9 @@ */ package org.sonarsource.sonarlint.core.util.ws; +import static java.util.Arrays.asList; +import static org.apache.commons.lang.StringUtils.defaultString; + import java.io.FileInputStream; import java.io.IOException; import java.net.Proxy; @@ -32,6 +35,7 @@ import java.util.Arrays; import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; +import javax.net.ssl.HostnameVerifier; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; @@ -46,9 +50,6 @@ import okhttp3.Request; import okhttp3.Response; -import static java.util.Arrays.asList; -import static org.apache.commons.lang.StringUtils.defaultString; - /** * Helper to build an instance of {@link okhttp3.OkHttpClient} that * correctly supports HTTPS and proxy authentication. It also handles @@ -69,6 +70,7 @@ public class OkHttpClientBuilder { private long readTimeoutMs = -1; private SSLSocketFactory sslSocketFactory = null; private X509TrustManager sslTrustManager = null; + private HostnameVerifier hostnameVerifier = null; /** * Optional User-Agent. If set, then all the requests sent by the @@ -97,6 +99,14 @@ public OkHttpClientBuilder setTrustManager(@Nullable X509TrustManager sslTrustMa return this; } + /** + * Optional Hostname Verifier. + */ + public OkHttpClientBuilder setHostnameVerifier(@Nullable HostnameVerifier hostnameVerifier) { + this.hostnameVerifier = hostnameVerifier; + return this; + } + /** * Optional proxy. If set, then all the requests sent by the * {@link OkHttpClient} will reach the proxy. If not set, @@ -189,7 +199,7 @@ public OkHttpClient build() { X509TrustManager trustManager = sslTrustManager != null ? sslTrustManager : systemDefaultTrustManager(); SSLSocketFactory sslFactory = sslSocketFactory != null ? sslSocketFactory : systemDefaultSslSocketFactory(trustManager); builder.sslSocketFactory(sslFactory, trustManager); - + builder.hostnameVerifier(hostnameVerifier); return builder.build(); } diff --git a/language-server/src/main/java/org/sonarlint/languageserver/SonarLintLanguageServer.java b/language-server/src/main/java/org/sonarlint/languageserver/SonarLintLanguageServer.java index 39a43f6b25..251d467839 100644 --- a/language-server/src/main/java/org/sonarlint/languageserver/SonarLintLanguageServer.java +++ b/language-server/src/main/java/org/sonarlint/languageserver/SonarLintLanguageServer.java @@ -19,6 +19,10 @@ */ package org.sonarlint.languageserver; +import static java.util.Collections.singleton; +import static java.util.Objects.nonNull; +import static org.apache.commons.lang.StringUtils.isBlank; + import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonSyntaxException; @@ -34,6 +38,9 @@ import java.nio.file.Path; import java.nio.file.PathMatcher; import java.nio.file.Paths; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -51,6 +58,10 @@ import java.util.function.Function; import java.util.stream.Collectors; import javax.annotation.Nullable; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.X509TrustManager; import org.eclipse.lsp4j.CodeAction; import org.eclipse.lsp4j.CodeActionParams; import org.eclipse.lsp4j.CodeLens; @@ -108,7 +119,6 @@ import org.eclipse.lsp4j.services.TextDocumentService; import org.eclipse.lsp4j.services.WorkspaceService; import org.sonar.api.internal.apachecommons.lang.StringUtils; -import org.sonar.api.rule.RuleKey; import org.sonarsource.sonarlint.core.client.api.common.RuleDetails; import org.sonarsource.sonarlint.core.client.api.common.analysis.AnalysisResults; import org.sonarsource.sonarlint.core.client.api.common.analysis.ClientInputFile; @@ -128,10 +138,6 @@ import org.sonarsource.sonarlint.core.client.api.util.FileUtils; import org.sonarsource.sonarlint.core.telemetry.TelemetryPathManager; -import static java.util.Collections.singleton; -import static java.util.Objects.nonNull; -import static org.apache.commons.lang.StringUtils.isBlank; - public class SonarLintLanguageServer implements LanguageServer, WorkspaceService, TextDocumentService { private static final String USER_AGENT = "CodeScan Language Server"; @@ -329,13 +335,34 @@ private void updateServerStorage(ConnectedSonarLintEngine engine, ServerInfo ser } private static ServerConfiguration getServerConfiguration(ServerInfo serverInfo) { + X509TrustManager trustManager = null; + SSLSocketFactory socketFactory = null; + HostnameVerifier hostnameVerifier = null; + + if (Boolean.parseBoolean(System.getProperty("http.allowUntrustedSsl"))) { + trustManager = new SelfSignedSslTrustManager(); + + try { + SSLContext sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, new X509TrustManager[] {trustManager}, new java.security.SecureRandom()); + socketFactory = sslContext.getSocketFactory(); + } catch (KeyManagementException | NoSuchAlgorithmException e) { + throw new IllegalStateException(e.getMessage(), e); + } + + hostnameVerifier = (hostname, session) -> true; + } + return ServerConfiguration.builder() - .url(serverInfo.serverUrl) - .token(serverInfo.token) - .organizationKey(serverInfo.organizationKey) - .userAgent(USER_AGENT) - .proxyCredentials(System.getProperty("http.proxyUser"), System.getProperty("http.proxyPassword")) - .build(); + .url(serverInfo.serverUrl) + .token(serverInfo.token) + .organizationKey(serverInfo.organizationKey) + .userAgent(USER_AGENT) + .proxyCredentials(System.getProperty("http.proxyUser"), System.getProperty("http.proxyPassword")) + .trustManager(trustManager) + .sslSocketFactory(socketFactory) + .hostnameVerifier(hostnameVerifier) + .build(); } private void updateBinding(@Nullable Map connectedModeProject) { @@ -981,4 +1008,24 @@ static class ServerProjectBinding { this.projectKey = projectKey; } } + + /** + * Custom trust manager which helps to accept self-signed untrusted certificates. + */ + private static class SelfSignedSslTrustManager implements X509TrustManager { + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) { + } + } + } From e46eb121691b29e5cbf8987b3f8d17f0069e6ad7 Mon Sep 17 00:00:00 2001 From: Barys Yakavita Date: Mon, 28 Sep 2020 19:16:48 +0300 Subject: [PATCH 2/2] Configure OkHttp3 to use proxy credentials --- .../sonarlint/core/util/ws/OkHttpClientBuilder.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/sonarsource/sonarlint/core/util/ws/OkHttpClientBuilder.java b/core/src/main/java/org/sonarsource/sonarlint/core/util/ws/OkHttpClientBuilder.java index 7fe2586287..3784b78621 100644 --- a/core/src/main/java/org/sonarsource/sonarlint/core/util/ws/OkHttpClientBuilder.java +++ b/core/src/main/java/org/sonarsource/sonarlint/core/util/ws/OkHttpClientBuilder.java @@ -199,7 +199,9 @@ public OkHttpClient build() { X509TrustManager trustManager = sslTrustManager != null ? sslTrustManager : systemDefaultTrustManager(); SSLSocketFactory sslFactory = sslSocketFactory != null ? sslSocketFactory : systemDefaultSslSocketFactory(trustManager); builder.sslSocketFactory(sslFactory, trustManager); - builder.hostnameVerifier(hostnameVerifier); + if (hostnameVerifier != null) { + builder.hostnameVerifier(hostnameVerifier); + } return builder.build(); }