Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -328,4 +328,9 @@ public static void setupKerberos(
clientBuilder.addInterceptor(handler);
Comment thread
thepetlyon marked this conversation as resolved.
Outdated
clientBuilder.authenticator(handler);
}

public static void setupAlternateHostnameVerification(OkHttpClient.Builder clientBuilder, String alternativeHostname)
{
clientBuilder.hostnameVerifier((hostname, session) -> LegacyHostnameVerifier.INSTANCE.verify(alternativeHostname, session));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ enum SslVerificationMode
public static final ConnectionProperty<String> SOURCE = new Source();
public static final ConnectionProperty<Class<? extends DnsResolver>> DNS_RESOLVER = new Resolver();
public static final ConnectionProperty<String> DNS_RESOLVER_CONTEXT = new ResolverContext();
public static final ConnectionProperty<String> HOSTNAME_IN_CERTIFICATE = new HostnameInCertificate();

private static final Set<ConnectionProperty<?>> ALL_PROPERTIES = ImmutableSet.<ConnectionProperty<?>>builder()
.add(USER)
Expand Down Expand Up @@ -132,6 +133,7 @@ enum SslVerificationMode
.add(EXTERNAL_AUTHENTICATION_REDIRECT_HANDLERS)
.add(DNS_RESOLVER)
.add(DNS_RESOLVER_CONTEXT)
.add(HOSTNAME_IN_CERTIFICATE)
.build();

private static final Map<String, ConnectionProperty<?>> KEY_LOOKUP = unmodifiableMap(ALL_PROPERTIES.stream()
Expand Down Expand Up @@ -347,6 +349,9 @@ private static class SslVerification
static final Predicate<Properties> IF_SSL_VERIFICATION_ENABLED =
IF_SSL_ENABLED.and(checkedPredicate(properties -> !SSL_VERIFICATION.getValue(properties).orElse(SslVerificationMode.FULL).equals(SslVerificationMode.NONE)));

static final Predicate<Properties> IF_FULL_SSL_VERIFICATION_ENABLED =
IF_SSL_VERIFICATION_ENABLED.and(checkedPredicate(properties -> !SSL_VERIFICATION.getValue(properties).orElse(SslVerificationMode.FULL).equals(SslVerificationMode.CA)));

public SslVerification()
{
super("SSLVerification", NOT_REQUIRED, IF_SSL_ENABLED, SslVerificationMode::valueOf);
Expand Down Expand Up @@ -645,6 +650,15 @@ public ResolverContext()
}
}

private static class HostnameInCertificate
extends AbstractConnectionProperty<String>
{
public HostnameInCertificate()
{
super("hostnameInCertificate", NOT_REQUIRED, SslVerification.IF_FULL_SSL_VERIFICATION_ENABLED, STRING_CONVERTER);
Comment thread
thepetlyon marked this conversation as resolved.
Outdated
}
}

private static class MapPropertyParser
{
private static final CharMatcher PRINTABLE_ASCII = CharMatcher.inRange((char) 0x21, (char) 0x7E);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import static com.google.common.base.Strings.isNullOrEmpty;
import static io.trino.client.KerberosUtil.defaultCredentialCachePath;
import static io.trino.client.OkHttpUtil.basicAuth;
import static io.trino.client.OkHttpUtil.setupAlternateHostnameVerification;
import static io.trino.client.OkHttpUtil.setupCookieJar;
import static io.trino.client.OkHttpUtil.setupHttpProxy;
import static io.trino.client.OkHttpUtil.setupInsecureSsl;
Expand All @@ -65,6 +66,7 @@
import static io.trino.jdbc.ConnectionProperties.EXTERNAL_AUTHENTICATION_TIMEOUT;
import static io.trino.jdbc.ConnectionProperties.EXTERNAL_AUTHENTICATION_TOKEN_CACHE;
import static io.trino.jdbc.ConnectionProperties.EXTRA_CREDENTIALS;
import static io.trino.jdbc.ConnectionProperties.HOSTNAME_IN_CERTIFICATE;
import static io.trino.jdbc.ConnectionProperties.HTTP_PROXY;
import static io.trino.jdbc.ConnectionProperties.KERBEROS_CONFIG_PATH;
import static io.trino.jdbc.ConnectionProperties.KERBEROS_CREDENTIAL_CACHE_PATH;
Expand Down Expand Up @@ -291,6 +293,11 @@ public void setupClient(OkHttpClient.Builder builder)
SSL_USE_SYSTEM_TRUST_STORE.getValue(properties).orElse(false));
}

if (sslVerificationMode.equals(FULL)) {
HOSTNAME_IN_CERTIFICATE.getValue(properties).ifPresent(certHostname ->
setupAlternateHostnameVerification(builder, certHostname));
}

if (sslVerificationMode.equals(CA)) {
builder.hostnameVerifier((hostname, session) -> true);
}
Expand Down
217 changes: 152 additions & 65 deletions client/trino-jdbc/src/test/java/io/trino/jdbc/TestTrinoDriverAuth.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,13 @@ public void testSuccessDefaultKey()
.signWith(defaultKey)
.compact();

try (Connection connection = createConnection(ImmutableMap.of("accessToken", accessToken))) {
try (Statement statement = connection.createStatement()) {
assertTrue(statement.execute("SELECT 123"));
ResultSet rs = statement.getResultSet();
assertTrue(rs.next());
assertEquals(rs.getLong(1), 123);
assertFalse(rs.next());
}
try (Connection connection = createConnection(ImmutableMap.of("accessToken", accessToken));
Statement statement = connection.createStatement()) {
assertTrue(statement.execute("SELECT 123"));
ResultSet rs = statement.getResultSet();
assertTrue(rs.next());
assertEquals(rs.getLong(1), 123);
assertFalse(rs.next());
}
}

Expand All @@ -126,14 +125,13 @@ public void testSuccessHmac()
.signWith(hmac222)
.compact();

try (Connection connection = createConnection(ImmutableMap.of("accessToken", accessToken))) {
try (Statement statement = connection.createStatement()) {
assertTrue(statement.execute("SELECT 123"));
ResultSet rs = statement.getResultSet();
assertTrue(rs.next());
assertEquals(rs.getLong(1), 123);
assertFalse(rs.next());
}
try (Connection connection = createConnection(ImmutableMap.of("accessToken", accessToken));
Statement statement = connection.createStatement()) {
assertTrue(statement.execute("SELECT 123"));
ResultSet rs = statement.getResultSet();
assertTrue(rs.next());
assertEquals(rs.getLong(1), 123);
assertFalse(rs.next());
}
}

Expand All @@ -147,25 +145,23 @@ public void testSuccessPublicKey()
.signWith(privateKey33)
.compact();

try (Connection connection = createConnection(ImmutableMap.of("accessToken", accessToken))) {
try (Statement statement = connection.createStatement()) {
assertTrue(statement.execute("SELECT 123"));
ResultSet rs = statement.getResultSet();
assertTrue(rs.next());
assertEquals(rs.getLong(1), 123);
assertFalse(rs.next());
}
try (Connection connection = createConnection(ImmutableMap.of("accessToken", accessToken));
Statement statement = connection.createStatement()) {
assertTrue(statement.execute("SELECT 123"));
ResultSet rs = statement.getResultSet();
assertTrue(rs.next());
assertEquals(rs.getLong(1), 123);
assertFalse(rs.next());
}
}

@Test(expectedExceptions = SQLException.class, expectedExceptionsMessageRegExp = "Authentication failed: Unauthorized")
public void testFailedNoToken()
throws Exception
{
try (Connection connection = createConnection(ImmutableMap.of())) {
try (Statement statement = connection.createStatement()) {
statement.execute("SELECT 123");
}
try (Connection connection = createConnection(ImmutableMap.of());
Statement statement = connection.createStatement()) {
statement.execute("SELECT 123");
}
}

Expand All @@ -177,10 +173,9 @@ public void testFailedUnsigned()
.setSubject("test")
.compact();

try (Connection connection = createConnection(ImmutableMap.of("accessToken", accessToken))) {
try (Statement statement = connection.createStatement()) {
statement.execute("SELECT 123");
}
try (Connection connection = createConnection(ImmutableMap.of("accessToken", accessToken));
Statement statement = connection.createStatement()) {
statement.execute("SELECT 123");
}
}

Expand All @@ -194,10 +189,9 @@ public void testFailedBadHmacSignature()
.signWith(badKey)
.compact();

try (Connection connection = createConnection(ImmutableMap.of("accessToken", accessToken))) {
try (Statement statement = connection.createStatement()) {
statement.execute("SELECT 123");
}
try (Connection connection = createConnection(ImmutableMap.of("accessToken", accessToken));
Statement statement = connection.createStatement()) {
statement.execute("SELECT 123");
}
}

Expand All @@ -211,10 +205,9 @@ public void testFailedWrongPublicKey()
.signWith(privateKey33)
.compact();

try (Connection connection = createConnection(ImmutableMap.of("accessToken", accessToken))) {
try (Statement statement = connection.createStatement()) {
statement.execute("SELECT 123");
}
try (Connection connection = createConnection(ImmutableMap.of("accessToken", accessToken));
Statement statement = connection.createStatement()) {
statement.execute("SELECT 123");
}
}

Expand All @@ -228,10 +221,9 @@ public void testFailedUnknownPublicKey()
.signWith(privateKey33)
.compact();

try (Connection connection = createConnection(ImmutableMap.of("accessToken", accessToken))) {
try (Statement statement = connection.createStatement()) {
statement.execute("SELECT 123");
}
try (Connection connection = createConnection(ImmutableMap.of("accessToken", accessToken));
Statement statement = connection.createStatement()) {
statement.execute("SELECT 123");
}
}

Expand All @@ -245,17 +237,112 @@ public void testSuccessFullSslVerification()
.signWith(privateKey33)
.compact();

try (Connection connection = createConnection(ImmutableMap.of("accessToken", accessToken, "SSLVerification", "FULL"))) {
try (Statement statement = connection.createStatement()) {
assertTrue(statement.execute("SELECT 123"));
ResultSet rs = statement.getResultSet();
assertTrue(rs.next());
assertEquals(rs.getLong(1), 123);
assertFalse(rs.next());
}
try (Connection connection = createConnection(ImmutableMap.of("accessToken", accessToken, "SSLVerification", "FULL"));
Statement statement = connection.createStatement()) {
assertTrue(statement.execute("SELECT 123"));
ResultSet rs = statement.getResultSet();
assertTrue(rs.next());
assertEquals(rs.getLong(1), 123);
assertFalse(rs.next());
}
}

@Test
public void testSuccessFullSslVerificationAlternateHostname()
throws Exception
{
String accessToken = newJwtBuilder()
.setSubject("test")
.setHeaderParam(KEY_ID, "33")
.signWith(privateKey33)
.compact();

String url = format("jdbc:trino://127.0.0.1:%s", server.getHttpsAddress().getPort());
Properties properties = new Properties();
properties.setProperty("SSL", "true");
properties.setProperty("SSLVerification", "FULL");
Comment thread
thepetlyon marked this conversation as resolved.
Outdated
properties.setProperty("SSLTrustStorePath", new File(getResource("localhost.truststore").toURI()).getPath());
properties.setProperty("SSLTrustStorePassword", "changeit");
properties.setProperty("accessToken", accessToken);
properties.setProperty("hostnameInCertificate", "localhost");
Comment thread
thepetlyon marked this conversation as resolved.
Outdated

try (Connection connection = DriverManager.getConnection(url, properties);
Statement statement = connection.createStatement()) {
assertTrue(statement.execute("SELECT 123"));
ResultSet rs = statement.getResultSet();
assertTrue(rs.next());
assertEquals(rs.getLong(1), 123);
assertFalse(rs.next());
}
}

@Test
public void testFailedFullSslVerificationAlternateHostnameNotProvided()
throws Exception
{
String accessToken = newJwtBuilder()
.setSubject("test")
.setHeaderParam(KEY_ID, "33")
.signWith(privateKey33)
.compact();

String url = format("jdbc:trino://127.0.0.1:%s", server.getHttpsAddress().getPort());
Properties properties = new Properties();
properties.setProperty("SSL", "true");
properties.setProperty("SSLVerification", "FULL");
properties.setProperty("SSLTrustStorePath", new File(getResource("localhost.truststore").toURI()).getPath());
properties.setProperty("SSLTrustStorePassword", "changeit");
properties.setProperty("accessToken", accessToken);

try (Connection connection = DriverManager.getConnection(url, properties);
Statement statement = connection.createStatement()) {
assertThatThrownBy(() -> statement.execute("SELECT 123"))
.isInstanceOf(SQLException.class).hasMessageContaining("Error executing query: javax.net.ssl.SSLPeerUnverifiedException: Hostname 127.0.0.1 not verified");
}
}

@Test
public void testFailedCaSslVerificationAlternateHostname()
{
String accessToken = newJwtBuilder()
.setSubject("test")
.setHeaderParam(KEY_ID, "33")
.signWith(privateKey33)
.compact();

String url = format("jdbc:trino://127.0.0.1:%s", server.getHttpsAddress().getPort());
Properties properties = new Properties();
properties.setProperty("SSL", "true");
properties.setProperty("SSLVerification", "CA");
properties.setProperty("accessToken", accessToken);
properties.setProperty("hostnameInCertificate", "localhost");

assertThatThrownBy(() -> DriverManager.getConnection(url, properties))
.isInstanceOf(SQLException.class)
.hasMessage("Connection property 'hostnameInCertificate' is not allowed");
}

@Test
public void testFailedNoneSslVerificationAlternateHostname()
{
String accessToken = newJwtBuilder()
.setSubject("test")
.setHeaderParam(KEY_ID, "33")
.signWith(privateKey33)
.compact();

String url = format("jdbc:trino://127.0.0.1:%s", server.getHttpsAddress().getPort());
Properties properties = new Properties();
properties.setProperty("SSL", "true");
properties.setProperty("SSLVerification", "NONE");
properties.setProperty("accessToken", accessToken);
properties.setProperty("hostnameInCertificate", "localhost");

assertThatThrownBy(() -> DriverManager.getConnection(url, properties))
.isInstanceOf(SQLException.class)
.hasMessage("Connection property 'hostnameInCertificate' is not allowed");
}

@Test
public void testSuccessCaSslVerification()
throws Exception
Expand All @@ -266,14 +353,13 @@ public void testSuccessCaSslVerification()
.signWith(privateKey33)
.compact();

try (Connection connection = createConnection(ImmutableMap.of("accessToken", accessToken, "SSLVerification", "CA"))) {
try (Statement statement = connection.createStatement()) {
assertTrue(statement.execute("SELECT 123"));
ResultSet rs = statement.getResultSet();
assertTrue(rs.next());
assertEquals(rs.getLong(1), 123);
assertFalse(rs.next());
}
try (Connection connection = createConnection(ImmutableMap.of("accessToken", accessToken, "SSLVerification", "CA"));
Statement statement = connection.createStatement()) {
assertTrue(statement.execute("SELECT 123"));
ResultSet rs = statement.getResultSet();
assertTrue(rs.next());
assertEquals(rs.getLong(1), 123);
assertFalse(rs.next());
}
}

Expand Down Expand Up @@ -317,11 +403,12 @@ public void testFailedNoneSslVerificationWithSSL()
public void testFailedNoneSslVerificationWithSSLUnsigned()
throws Exception
{
Connection connection = createBasicConnection(ImmutableMap.of("SSL", "true", "SSLVerification", "NONE"));
Statement statement = connection.createStatement();
assertThatThrownBy(() -> statement.execute("SELECT 123"))
.isInstanceOf(SQLException.class)
.hasMessage("Authentication failed: Unauthorized");
try (Connection connection = createBasicConnection(ImmutableMap.of("SSL", "true", "SSLVerification", "NONE"));
Statement statement = connection.createStatement()) {
assertThatThrownBy(() -> statement.execute("SELECT 123"))
.isInstanceOf(SQLException.class)
.hasMessage("Authentication failed: Unauthorized");
}
}

private Connection createConnection(Map<String, String> additionalProperties)
Expand Down