Skip to content

Commit

Permalink
TDSS Support (#1757)
Browse files Browse the repository at this point in the history
  • Loading branch information
lilgreenbird authored Mar 16, 2022
1 parent 9a24ba1 commit 887cf6f
Show file tree
Hide file tree
Showing 112 changed files with 317 additions and 249 deletions.
29 changes: 16 additions & 13 deletions src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -761,7 +761,6 @@ final InetSocketAddress open(String host, int port, int timeoutMillis, boolean u
channelSocket = tcpSocket = socketFinder.findSocket(host, port, timeoutMillis, useParallel, useTnir,
isTnirFirstAttempt, timeoutMillisForFullTimeout);
try {

// Set socket options
tcpSocket.setTcpNoDelay(true);
tcpSocket.setKeepAlive(true);
Expand Down Expand Up @@ -1828,8 +1827,8 @@ enum SSLHandhsakeState {
* Private key file's password
* @throws SQLServerException
*/
void enableSSL(String host, int port, String clientCertificate, String clientKey,
String clientKeyPassword) throws SQLServerException {
void enableSSL(String host, int port, String clientCertificate, String clientKey, String clientKeyPassword,
boolean isTDSS) throws SQLServerException {
// If enabling SSL fails, which it can for a number of reasons, the following items
// are used in logging information to the TDS channel logger to help diagnose the problem.
Provider tmfProvider = null; // TrustManagerFactory provider
Expand Down Expand Up @@ -1870,12 +1869,11 @@ void enableSSL(String host, int port, String clientCertificate, String clientKey
validateFips(trustStoreType, trustStoreFileName);
}

assert TDS.ENCRYPT_OFF == con.getRequestedEncryptionLevel() || // Login only SSL
TDS.ENCRYPT_ON == con.getRequestedEncryptionLevel(); // Full SSL

assert TDS.ENCRYPT_OFF == con.getNegotiatedEncryptionLevel() || // Login only SSL
TDS.ENCRYPT_ON == con.getNegotiatedEncryptionLevel() || // Full SSL
TDS.ENCRYPT_REQ == con.getNegotiatedEncryptionLevel(); // Full SSL
byte requestedEncryptLevel = con.getRequestedEncryptionLevel();
assert TDS.ENCRYPT_OFF == requestedEncryptLevel || // Login only SSL
TDS.ENCRYPT_ON == requestedEncryptLevel || // Full SSL
TDS.ENCRYPT_REQ == requestedEncryptLevel || // Full SSL
(isTDSS && TDS.ENCRYPT_NOT_SUP == requestedEncryptLevel); // TDSS

// If encryption wasn't negotiated or trust server certificate is specified,
// then we'll "validate" the server certificate using a naive TrustManager that trusts
Expand Down Expand Up @@ -2004,13 +2002,17 @@ else if (con.getTrustManagerClass() != null) {
// Got the SSL context. Now create an SSL socket over our own proxy socket
// which we can toggle between TDS-encapsulated and raw communications.
// Initially, the proxy is set to encapsulate the SSL handshake in TDS packets.
proxySocket = new ProxySocket(this);

if (logger.isLoggable(Level.FINEST))
logger.finest(toString() + " Creating SSL socket");

// don't close proxy when SSL socket is closed
sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(proxySocket, host, port, false);
proxySocket = new ProxySocket(this);

if (isTDSS) {
sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(host, port);
} else {
// don't close proxy when SSL socket is closed
sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(proxySocket, host, port, false);
}

// At long last, start the SSL handshake ...
if (logger.isLoggable(Level.FINER))
Expand Down Expand Up @@ -2053,6 +2055,7 @@ else if (con.getTrustManagerClass() != null) {

if (logger.isLoggable(Level.FINER))
logger.finer(toString() + " SSL enabled");

} catch (Exception e) {
// Log the original exception and its source at FINER level
if (logger.isLoggable(Level.FINER))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,23 +101,32 @@ public interface ISQLServerDataSource extends javax.sql.CommonDataSource {
boolean getLastUpdateCount();

/**
* Sets a Boolean value that indicates if the encrypt property is enabled.
* Sets the option whether TLS encryption is used.
*
* @param encrypt
* true if the Secure Sockets Layer (SSL) encryption is enabled between the client and the SQL Server.
* Otherwise, false.
* @param encryptOption
* TLS encrypt option. Default is "true"
*/
void setEncrypt(boolean encrypt);
void setEncrypt(String encryptOption);

/**
* Returns a Boolean value that indicates if the encrypt property is enabled.
* Sets the option whether TLS encryption is used.
*
* @return true if encrypt is enabled. Otherwise, false.
* @deprecated Use {@link ISQLServerDataSource#setEncrypt(String encryptOption)} instead
* @param encryptOption
* TLS encrypt option. Default is true
*/
boolean getEncrypt();
@Deprecated
void setEncrypt(boolean encryptOption);

/**
* Returns the TLS encryption option.
*
* @return the TLS encrypt option
*/
String getEncrypt();

/**
* Sets the value to enable/disable Transparent Netowrk IP Resolution (TNIR). Beginning in version 6.0 of the
* Sets the value to enable/disable Transparent Network IP Resolution (TNIR). Beginning in version 6.0 of the
* Microsoft JDBC Driver for SQL Server, a new connection property transparentNetworkIPResolution (TNIR) is added
* for transparent connection to Always On availability groups or to a server which has multiple IP addresses
* associated. When transparentNetworkIPResolution is true, the driver attempts to connect to the first IP address
Expand All @@ -143,18 +152,20 @@ public interface ISQLServerDataSource extends javax.sql.CommonDataSource {
boolean getTransparentNetworkIPResolution();

/**
* Sets a Boolean value that indicates if the trustServerCertificate property is enabled.
* Sets a boolean value that indicates if the trustServerCertificate property is enabled.
*
* @param e
* true, if the server Secure Sockets Layer (SSL) certificate should be automatically trusted when the
* communication layer is encrypted using SSL. Otherwise, false.
* communication layer is encrypted using SSL. false, if server SLL certificate should not be trusted
* certificate location, if encrypt=strict
*/
void setTrustServerCertificate(boolean e);

/**
* Returns a Boolean value that indicates if the trustServerCertificate property is enabled.
* Returns a boolean value that indicates if the trustServerCertificate property is enabled.
*
* @return true if trustServerCertificate is enabled. Otherwise, false.
* @return true if trustServerCertificate is enabled. Otherwise, false. If encrypt=strict, returns server
* certificate location
*/
boolean getTrustServerCertificate();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* Abstract class that implements ISQLServerBulkRecord
*
*/
@SuppressWarnings("deprecation")
abstract class SQLServerBulkRecord implements ISQLServerBulkRecord {

/**
Expand Down
86 changes: 61 additions & 25 deletions src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,13 @@ public class SQLServerConnection implements ISQLServerConnection, java.io.Serial
/** Engine Edition 11 = Azure Synapse serverless SQL pool */
private final int ENGINE_EDITION_SQL_AZURE_SYNAPSE_SERVERLESS_SQL_POOL = 11;

/** flag indicated whether server is Azure */
/** flag indicating whether server is Azure */
private Boolean isAzure = null;

/** flag indicated whether server is Azure DW */
/** flag indicating whether server is Azure DW */
private Boolean isAzureDW = null;

/** flag indicated whether server is Azure MI */
/** flag indicating whether server is Azure MI */
private Boolean isAzureMI = null;

/** shared timer */
Expand All @@ -214,6 +214,9 @@ public class SQLServerConnection implements ISQLServerConnection, java.io.Serial
/** connect retry interval */
private int connectRetryInterval = 0;

/** flag indicating whether prelogin TLS handshake is required */
private boolean isTDSS = false;

/**
* Return an existing cached SharedTimer associated with this Connection or create a new one.
*
Expand Down Expand Up @@ -743,9 +746,10 @@ final byte getRequestedEncryptionLevel() {
return requestedEncryptionLevel;
}

/** trust server certificate */
/** flag indicating whether to trust server certificate */
private boolean trustServerCertificate;

/** return whether to trust server certificate */
final boolean trustServerCertificate() {
return trustServerCertificate;
}
Expand All @@ -754,7 +758,7 @@ final boolean trustServerCertificate() {
private byte negotiatedEncryptionLevel = TDS.ENCRYPT_INVALID;

final byte getNegotiatedEncryptionLevel() {
assert TDS.ENCRYPT_INVALID != negotiatedEncryptionLevel;
assert (!isTDSS ? TDS.ENCRYPT_INVALID != negotiatedEncryptionLevel : true);
return negotiatedEncryptionLevel;
}

Expand Down Expand Up @@ -795,6 +799,9 @@ final String getTrustManagerConstructorArg() {
/** column encryption setting */
String columnEncryptionSetting = null;

/** encrypt option */
String encryptOption = null;

boolean isColumnEncryptionSettingEnabled() {
return (columnEncryptionSetting.equalsIgnoreCase(ColumnEncryptionSetting.Enabled.toString()));
}
Expand Down Expand Up @@ -2045,6 +2052,7 @@ Connection connectInternal(Properties propsIn,
}
transparentNetworkIPResolution = isBooleanPropertyOn(sPropKey, sPropValue);

sPropKey = SQLServerDriverStringProperty.ENCRYPT.toString();
sPropKey = SQLServerDriverStringProperty.PREPARE_METHOD.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null == sPropValue) {
Expand All @@ -2053,20 +2061,13 @@ Connection connectInternal(Properties propsIn,
}
setPrepareMethod(PrepareMethod.valueOfString(sPropValue).toString());

sPropKey = SQLServerDriverBooleanProperty.ENCRYPT.toString();
sPropKey = SQLServerDriverStringProperty.ENCRYPT.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null == sPropValue) {
sPropValue = Boolean.toString(SQLServerDriverBooleanProperty.ENCRYPT.getDefaultValue());
sPropValue = SQLServerDriverStringProperty.ENCRYPT.getDefaultValue();
activeConnectionProperties.setProperty(sPropKey, sPropValue);
}

socketFactoryClass = activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.SOCKET_FACTORY_CLASS.toString());
socketFactoryConstructorArg = activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.SOCKET_FACTORY_CONSTRUCTOR_ARG.toString());

// Set requestedEncryptionLevel according to the value of the encrypt connection property
requestedEncryptionLevel = isBooleanPropertyOn(sPropKey, sPropValue) ? TDS.ENCRYPT_ON : TDS.ENCRYPT_OFF;
encryptOption = EncryptOption.valueOfString(sPropValue).toString();

sPropKey = SQLServerDriverBooleanProperty.TRUST_SERVER_CERTIFICATE.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
Expand All @@ -2075,8 +2076,33 @@ Connection connectInternal(Properties propsIn,
.toString(SQLServerDriverBooleanProperty.TRUST_SERVER_CERTIFICATE.getDefaultValue());
activeConnectionProperties.setProperty(sPropKey, sPropValue);
}

trustServerCertificate = isBooleanPropertyOn(sPropKey, sPropValue);

// Set requestedEncryptionLevel according to the value of the encrypt connection property
if (encryptOption.compareToIgnoreCase(EncryptOption.False.toString()) == 0) {
requestedEncryptionLevel = TDS.ENCRYPT_OFF;
} else if (encryptOption.compareToIgnoreCase(EncryptOption.True.toString()) == 0) {
requestedEncryptionLevel = TDS.ENCRYPT_ON;
} else if (encryptOption.compareToIgnoreCase(EncryptOption.Strict.toString()) == 0) {
// this is necessary so we don't encrypt again
requestedEncryptionLevel = TDS.ENCRYPT_NOT_SUP;

if (trustServerCertificate) {
if (loggerExternal.isLoggable(Level.FINER))
loggerExternal.finer(toString() + " ignore trustServerCertificate for strict");
}
// do not trust server cert for strict
trustServerCertificate = false;

// prelogin TLS handshake is required
isTDSS = true;
} else {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_InvalidConnectionSetting"));
Object[] msgArgs = {"encrypt", encryptOption};
throw new SQLServerException(null, form.format(msgArgs), null, 0, false);
}

trustManagerClass = activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.TRUST_MANAGER_CLASS.toString());
trustManagerConstructorArg = activeConnectionProperties
Expand All @@ -2088,6 +2114,11 @@ Connection connectInternal(Properties propsIn,
sPropValue = SQLServerDriverStringProperty.SELECT_METHOD.getDefaultValue();
}

socketFactoryClass = activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.SOCKET_FACTORY_CLASS.toString());
socketFactoryConstructorArg = activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.SOCKET_FACTORY_CONSTRUCTOR_ARG.toString());

if ("cursor".equalsIgnoreCase(sPropValue) || "direct".equalsIgnoreCase(sPropValue)) {
sPropValue = sPropValue.toLowerCase(Locale.ENGLISH);
activeConnectionProperties.setProperty(sPropKey, sPropValue);
Expand Down Expand Up @@ -2721,9 +2752,7 @@ else if (0 == requestedPacketSize)

activeConnectionProperties.remove(SQLServerDriverStringProperty.TRUST_STORE_PASSWORD.toString());
}

return this;

}

/**
Expand Down Expand Up @@ -3185,6 +3214,7 @@ private InetSocketAddress connectHelper(ServerPortPlaceHolder serverInfo, int ti

// if the timeout is infinite slices are infinite too.
tdsChannel = new TDSChannel(this);

InetSocketAddress inetSocketAddress = tdsChannel.open(serverInfo.getParsedServerName(),
serverInfo.getPortNumber(), (0 == timeOutFullInSeconds) ? 0 : timeOutSliceInMillis, useParallel,
useTnir, isTnirFirstAttempt, timeOutsliceInMillisForFullTimeout);
Expand All @@ -3202,15 +3232,20 @@ private InetSocketAddress connectHelper(ServerPortPlaceHolder serverInfo, int ti
}
clientConnectionId = UUID.randomUUID();
}

assert null != clientConnectionId;

Prelogin(serverInfo.getServerName(), serverInfo.getPortNumber());
if (isTDSS) {
tdsChannel.enableSSL(serverInfo.getParsedServerName(), serverInfo.getPortNumber(), clientCertificate,
clientKey, clientKeyPassword, isTDSS);
clientKeyPassword = "";
}

prelogin(serverInfo.getServerName(), serverInfo.getPortNumber());

// If prelogin negotiated SSL encryption then, enable it on the TDS channel.
if (TDS.ENCRYPT_NOT_SUP != negotiatedEncryptionLevel) {
// If not enabled already and prelogin negotiated SSL encryption then, enable it on the TDS channel.
if (!isTDSS && TDS.ENCRYPT_NOT_SUP != negotiatedEncryptionLevel) {
tdsChannel.enableSSL(serverInfo.getParsedServerName(), serverInfo.getPortNumber(), clientCertificate,
clientKey, clientKeyPassword);
clientKey, clientKeyPassword, false);
clientKeyPassword = "";
}

Expand Down Expand Up @@ -3251,7 +3286,7 @@ private void executeReconnect(LogonCommand logonCommand) throws SQLServerExcepti
/**
* Negotiates prelogin information with the server.
*/
void Prelogin(String serverName, int portNumber) throws SQLServerException {
void prelogin(String serverName, int portNumber) throws SQLServerException {
// Build a TDS Pre-Login packet to send to the server.
if ((!authenticationString.equalsIgnoreCase(SqlAuthentication.NotSpecified.toString()))
|| (null != accessTokenInByte)) {
Expand Down Expand Up @@ -3317,7 +3352,8 @@ void Prelogin(String serverName, int portNumber) throws SQLServerException {
// Build (Little Endian), 2 bytes
(byte) (SQLJdbcVersion.build & 0xff), (byte) ((SQLJdbcVersion.build & 0xff00) >> 8),

// - Encryption -
// Encryption
// turn encryption off for TDSS since it's already enabled
(null == clientCertificate) ? requestedEncryptionLevel
: (byte) (requestedEncryptionLevel | TDS.ENCRYPT_CLIENT_CERT),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -306,14 +306,21 @@ public boolean getLastUpdateCount() {
}

@Override
public void setEncrypt(boolean encrypt) {
setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.ENCRYPT.toString(), encrypt);
public void setEncrypt(String encryptOption) {
setStringProperty(connectionProps, SQLServerDriverStringProperty.ENCRYPT.toString(), encryptOption);
}

@Override
public boolean getEncrypt() {
return getBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.ENCRYPT.toString(),
SQLServerDriverBooleanProperty.ENCRYPT.getDefaultValue());
@Deprecated
public void setEncrypt(boolean encryptOption) {
setStringProperty(connectionProps, SQLServerDriverStringProperty.ENCRYPT.toString(),
Boolean.toString(encryptOption));
}

@Override
public String getEncrypt() {
return getStringProperty(connectionProps, SQLServerDriverStringProperty.ENCRYPT.toString(),
SQLServerDriverStringProperty.ENCRYPT.getDefaultValue());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1666,7 +1666,8 @@ public String getURL() throws SQLServerException {
if (!name.equals(SQLServerDriverBooleanProperty.INTEGRATED_SECURITY.toString())
&& !name.equals(SQLServerDriverStringProperty.USER.toString())
&& !name.equals(SQLServerDriverStringProperty.PASSWORD.toString())
&& !name.equals(SQLServerDriverStringProperty.KEY_STORE_SECRET.toString())) {
&& !name.equals(SQLServerDriverStringProperty.KEY_STORE_SECRET.toString())
&& !name.equals(SQLServerDriverStringProperty.TRUST_STORE_PASSWORD.toString())) {
String val = info[index].value;
// skip empty strings
if (0 != val.length()) {
Expand Down
Loading

0 comments on commit 887cf6f

Please sign in to comment.