Skip to content
Snippets Groups Projects
Unverified Commit 1a0e083b authored by Jayaram Sreevalsan's avatar Jayaram Sreevalsan Committed by GitHub
Browse files

Merge pull request #2886 from eugene7646/ct_ssl_keystore_posgres_7669

CT-7669 Using CT keystore for PosgreSQL SSL connection
parents f441c4e2 2a0262de
Branches
No related tags found
No related merge requests found
...@@ -39,17 +39,37 @@ class CaseDatabaseFactory { ...@@ -39,17 +39,37 @@ class CaseDatabaseFactory {
private static final Logger logger = Logger.getLogger(CaseDatabaseFactory.class.getName()); private static final Logger logger = Logger.getLogger(CaseDatabaseFactory.class.getName());
private final SQLHelper dbQueryHelper; private final SQLHelper dbQueryHelper;
private final DbCreationHelper dbCreationHelper; private final DbCreationHelper dbCreationHelper;
// ssl=true: enables SSL encryption.
// DefaultJavaSSLFactory: uses Java's default truststore to validate server certificate.
// sslmode=verify-ca: verifies that the server we are connecting to is trusted by CA.
final static String SSL_VERIFY_URL = "?ssl=true&sslfactory=org.postgresql.ssl.DefaultJavaSSLFactory&sslmode=verify-ca";
// ssl=true: enables SSL encryption. // ssl=true: enables SSL encryption.
// NonValidatingFactory avoids hostname verification. // NonValidatingFactory avoids hostname verification.
// sslmode=require: This mode makes the encryption mandatory and also requires the connection to fail if it can't be encrypted. // sslmode=require: This mode makes the encryption mandatory and also requires the connection to fail if it can't be encrypted.
// In this mode, the JDBC driver accepts all server certificates, including self-signed ones. // In this mode, the JDBC driver accepts all server certificates, including self-signed ones.
final static String SSL_NONVERIFY_URL = "?ssl=true&sslfactory=org.postgresql.ssl.NonValidatingFactory&sslmode=require"; final static String SSL_NONVERIFY_URL = "?ssl=true&sslfactory=org.postgresql.ssl.NonValidatingFactory&sslmode=require";
// ssl=true: enables SSL encryption.
// DefaultJavaSSLFactory: uses application's default JRE keystore to validate server certificate.
// sslmode=verify-ca: verifies that the server we are connecting to is trusted by CA.
final static String SSL_VERIFY_DEFAULT_URL = "?ssl=true&sslfactory=org.postgresql.ssl.DefaultJavaSSLFactory&sslmode=verify-ca";
/**
* Creates JDBC URL string for implementations that use custom keystore to
* validate PostgreSQL CA-signed SSL certificates. The class that performs
* SSL certificate validation must extend org.postgresql.ssl.WrappedFactory
* and generally must follow the same logic.
*
* ssl=true: enables SSL encryption.
* sslmode=verify-ca: verifies that the server we are connecting to is trusted by CA.
*
* @param customSslValidationClassName full canonical name of a Java class
* that performs custom SSL certificate
* validation.
*
* @return JDBS URL string used to connect to PosgreSQL server via CA-signed
* SSL certificate.
*/
static String getCustomPostrgesSslVerificationUrl(String customSslValidationClassName) {
return "?ssl=true&sslfactory=" + customSslValidationClassName + "&sslmode=verify-ca";
}
/** /**
* Create a new SQLite case * Create a new SQLite case
...@@ -726,7 +746,12 @@ Connection getConnection(String databaseName) throws TskCoreException { ...@@ -726,7 +746,12 @@ Connection getConnection(String databaseName) throws TskCoreException {
if (info.isSslEnabled()) { if (info.isSslEnabled()) {
if (info.isSslVerify()) { if (info.isSslVerify()) {
url.append(SSL_VERIFY_URL); if (info.getCustomSslValidationClassName().isBlank()) {
url.append(SSL_VERIFY_DEFAULT_URL);
} else {
// use custom SSL certificate validation class
url.append(getCustomPostrgesSslVerificationUrl(info.getCustomSslValidationClassName()));
}
} else { } else {
url.append(SSL_NONVERIFY_URL); url.append(SSL_NONVERIFY_URL);
} }
......
...@@ -36,6 +36,7 @@ public class CaseDbConnectionInfo { ...@@ -36,6 +36,7 @@ public class CaseDbConnectionInfo {
private DbType dbType; private DbType dbType;
private boolean sslEnabled = false; private boolean sslEnabled = false;
private boolean sslVerify = false; private boolean sslVerify = false;
private String customSslValidationClassName;
/** /**
* The intent of this class is to hold any information needed to connect to * The intent of this class is to hold any information needed to connect to
...@@ -62,13 +63,20 @@ public CaseDbConnectionInfo(String hostNameOrIP, String portNumber, String userN ...@@ -62,13 +63,20 @@ public CaseDbConnectionInfo(String hostNameOrIP, String portNumber, String userN
throw new IllegalArgumentException("SQLite database type invalid for CaseDbConnectionInfo. CaseDbConnectionInfo should be used only for remote database types."); throw new IllegalArgumentException("SQLite database type invalid for CaseDbConnectionInfo. CaseDbConnectionInfo should be used only for remote database types.");
} }
this.dbType = dbType; this.dbType = dbType;
this.customSslValidationClassName = "";
} }
/** /**
* The intent of this class is to hold any information needed to connect to * The intent of this class is to hold any information needed to connect to
* a remote database server, except for the actual database name. This constructor * a remote database server, except for the actual database name. This
* allows user to specify whether to use SSL to connect to database. This does * constructor allows user to specify whether to use SSL to connect to
* not hold information to connect to a local database such as SQLite. * database. This does not hold information to connect to a local database
* such as SQLite.
*
* This constructor allows to specify a Java class that performs custom
* PostgreQSL server SSL certificate validation (if 'sslVerify' is set to
* 'true'). If not specified, the application's default JRE keystore will be
* used.
* *
* It can be used generically to hold remote database connection * It can be used generically to hold remote database connection
* information. * information.
...@@ -80,8 +88,14 @@ public CaseDbConnectionInfo(String hostNameOrIP, String portNumber, String userN ...@@ -80,8 +88,14 @@ public CaseDbConnectionInfo(String hostNameOrIP, String portNumber, String userN
* @param dbType the database type * @param dbType the database type
* @param sslEnabled a flag whether SSL is enabled * @param sslEnabled a flag whether SSL is enabled
* @param sslVerify 'true' if SSL certificate needs to be CA verified. 'false' if self-signed certificates should be accepted. * @param sslVerify 'true' if SSL certificate needs to be CA verified. 'false' if self-signed certificates should be accepted.
* @param customSslValidationClassName full canonical name of a Java class
* that performs custom SSL certificate
* validation. If blank, the
* application's default JRE keystore
* will be used.
*/ */
public CaseDbConnectionInfo(String hostNameOrIP, String portNumber, String userName, String password, DbType dbType, boolean sslEnabled, boolean sslVerify) { public CaseDbConnectionInfo(String hostNameOrIP, String portNumber, String userName, String password, DbType dbType,
boolean sslEnabled, boolean sslVerify, String customSslValidationClassName) {
this.hostNameOrIP = hostNameOrIP; this.hostNameOrIP = hostNameOrIP;
this.portNumber = portNumber; this.portNumber = portNumber;
this.userName = userName; this.userName = userName;
...@@ -92,6 +106,7 @@ public CaseDbConnectionInfo(String hostNameOrIP, String portNumber, String userN ...@@ -92,6 +106,7 @@ public CaseDbConnectionInfo(String hostNameOrIP, String portNumber, String userN
throw new IllegalArgumentException("SQLite database type invalid for CaseDbConnectionInfo. CaseDbConnectionInfo should be used only for remote database types."); throw new IllegalArgumentException("SQLite database type invalid for CaseDbConnectionInfo. CaseDbConnectionInfo should be used only for remote database types.");
} }
this.dbType = dbType; this.dbType = dbType;
this.customSslValidationClassName = customSslValidationClassName;
} }
public DbType getDbType() { public DbType getDbType() {
...@@ -149,4 +164,12 @@ public boolean isSslVerify() { ...@@ -149,4 +164,12 @@ public boolean isSslVerify() {
public void setSslVerify(boolean sslVerify) { public void setSslVerify(boolean sslVerify) {
this.sslVerify = sslVerify; this.sslVerify = sslVerify;
} }
public String getCustomSslValidationClassName() {
return customSslValidationClassName;
}
public void setCustomSslValidationClassName(String customSslValidationClassName) {
this.customSslValidationClassName = customSslValidationClassName;
}
} }
/* /*
* Sleuth Kit Data Model * Sleuth Kit Data Model
* *
* Copyright 2011-2021 Basis Technology Corp. * Copyright 2011-2023 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
...@@ -113,8 +113,8 @@ public class SleuthkitCase { ...@@ -113,8 +113,8 @@ public class SleuthkitCase {
private static final int IS_REACHABLE_TIMEOUT_MS = 1000; private static final int IS_REACHABLE_TIMEOUT_MS = 1000;
private static final String SQL_ERROR_CONNECTION_GROUP = "08"; private static final String SQL_ERROR_CONNECTION_GROUP = "08";
// either one of these mean connection was rejected by Postgres server // either one of these mean connection was rejected by Postgres server
private static final String SQL_CONNECTION_REJECTED_1 = "08004"; private static final String SQL_CONNECTION_REJECTED = "08004";
private static final String SQL_CONNECTION_REJECTED_2 = "08006"; private static final String UNABLE_TO_VERIFY_SSL = "08006";
private static final String SQL_ERROR_AUTHENTICATION_GROUP = "28"; private static final String SQL_ERROR_AUTHENTICATION_GROUP = "28";
private static final String SQL_ERROR_PRIVILEGE_GROUP = "42"; private static final String SQL_ERROR_PRIVILEGE_GROUP = "42";
...@@ -296,7 +296,12 @@ public static void tryConnect(CaseDbConnectionInfo info) throws TskCoreException ...@@ -296,7 +296,12 @@ public static void tryConnect(CaseDbConnectionInfo info) throws TskCoreException
String connectionURL = "jdbc:postgresql://" + info.getHost() + ":" + info.getPort() + "/postgres"; String connectionURL = "jdbc:postgresql://" + info.getHost() + ":" + info.getPort() + "/postgres";
if (info.isSslEnabled()) { if (info.isSslEnabled()) {
if (info.isSslVerify()) { if (info.isSslVerify()) {
connectionURL += CaseDatabaseFactory.SSL_VERIFY_URL; if (info.getCustomSslValidationClassName().isBlank()) {
connectionURL += CaseDatabaseFactory.SSL_VERIFY_DEFAULT_URL;
} else {
// use custom SSL certificate validation class
connectionURL += CaseDatabaseFactory.getCustomPostrgesSslVerificationUrl(info.getCustomSslValidationClassName());
}
} else { } else {
connectionURL += CaseDatabaseFactory.SSL_NONVERIFY_URL; connectionURL += CaseDatabaseFactory.SSL_NONVERIFY_URL;
} }
...@@ -308,15 +313,15 @@ public static void tryConnect(CaseDbConnectionInfo info) throws TskCoreException ...@@ -308,15 +313,15 @@ public static void tryConnect(CaseDbConnectionInfo info) throws TskCoreException
} catch (SQLException ex) { } catch (SQLException ex) {
String result; String result;
String sqlState = ex.getSQLState().toLowerCase(); String sqlState = ex.getSQLState().toLowerCase();
if (sqlState.startsWith(SQL_ERROR_CONNECTION_GROUP)) { if (sqlState.startsWith(SQL_ERROR_CONNECTION_GROUP)) {
if (SQL_CONNECTION_REJECTED.equals(ex.getSQLState())) {
if (SQL_CONNECTION_REJECTED_1.equals(ex.getSQLState()) ||
SQL_CONNECTION_REJECTED_2.equals(ex.getSQLState())) {
if (info.isSslEnabled()) { if (info.isSslEnabled()) {
result = "Server rejected the SSL connection attempt. Check SSL configuration."; result = "Server rejected the SSL connection attempt. Check SSL configuration.";
} else { } else {
result = "Server rejected the connection attempt. Check server configuration."; result = "Server rejected the connection attempt. Check server configuration.";
} }
} else if (UNABLE_TO_VERIFY_SSL.equals(ex.getSQLState())) {
result = "Unable to verify SSL certificates. Check SSL configuration.";
} else { } else {
try { try {
if (InetAddress.getByName(info.getHost()).isReachable(IS_REACHABLE_TIMEOUT_MS)) { if (InetAddress.getByName(info.getHost()).isReachable(IS_REACHABLE_TIMEOUT_MS)) {
...@@ -13407,7 +13412,12 @@ private final class PostgreSQLConnections extends ConnectionPool { ...@@ -13407,7 +13412,12 @@ private final class PostgreSQLConnections extends ConnectionPool {
+ URLEncoder.encode(dbName, StandardCharsets.UTF_8.toString()); + URLEncoder.encode(dbName, StandardCharsets.UTF_8.toString());
if (info.isSslEnabled()) { if (info.isSslEnabled()) {
if (info.isSslVerify()) { if (info.isSslVerify()) {
connectionURL += CaseDatabaseFactory.SSL_VERIFY_URL; if (info.getCustomSslValidationClassName().isBlank()) {
connectionURL += CaseDatabaseFactory.SSL_VERIFY_DEFAULT_URL;
} else {
// use custom SSL certificate validation class
connectionURL += CaseDatabaseFactory.getCustomPostrgesSslVerificationUrl(info.getCustomSslValidationClassName());
}
} else { } else {
connectionURL += CaseDatabaseFactory.SSL_NONVERIFY_URL; connectionURL += CaseDatabaseFactory.SSL_NONVERIFY_URL;
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment