Skip to content

Commit

Permalink
Add optional realm connection string property for Kerberos authentica…
Browse files Browse the repository at this point in the history
…tion (#1581)
  • Loading branch information
VeryVerySpicy committed Jun 4, 2021
1 parent c559bb5 commit fb15da6
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 15 deletions.
Expand Up @@ -505,6 +505,21 @@ public interface ISQLServerDataSource extends javax.sql.CommonDataSource {
*/
String getAuthentication();

/**
* Sets the realm for Kerberos authentication.
*
* @param realm
* A String that contains the realm
*/
void setRealm(String realm);

/**
* Returns the realm for Kerberos authentication.
*
* @return A String that contains the realm
*/
String getRealm();

/**
* Sets the server spn.
*
Expand Down
Expand Up @@ -581,6 +581,22 @@ public String getServerName() {
return getStringProperty(connectionProps, SQLServerDriverStringProperty.SERVER_NAME.toString(), null);
}

/**
* Sets the realm for Kerberos authentication.
*
* @param realm
* realm
*/
@Override
public void setRealm(String realm) {
setStringProperty(connectionProps, SQLServerDriverStringProperty.REALM.toString(), realm);
}

@Override
public String getRealm() {
return getStringProperty(connectionProps, SQLServerDriverStringProperty.REALM.toString(), null);
}

/**
* Sets the Service Principal Name (SPN) of the target SQL Server.
* https://msdn.microsoft.com/en-us/library/cc280459.aspx
Expand Down
Expand Up @@ -349,6 +349,7 @@ enum SQLServerDriverStringProperty {
DOMAIN("domain", ""),
SERVER_NAME("serverName", ""),
SERVER_SPN("serverSpn", ""),
REALM("realm", ""),
SOCKET_FACTORY_CLASS("socketFactoryClass", ""),
SOCKET_FACTORY_CONSTRUCTOR_ARG("socketFactoryConstructorArg", ""),
TRUST_STORE_TYPE("trustStoreType", "JKS"),
Expand Down Expand Up @@ -569,6 +570,8 @@ public final class SQLServerDriver implements java.sql.Driver {
SQLServerDriverStringProperty.SERVER_NAME.getDefaultValue(), false, null),
new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.SERVER_SPN.toString(),
SQLServerDriverStringProperty.SERVER_SPN.getDefaultValue(), false, null),
new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.REALM.toString(),
SQLServerDriverStringProperty.REALM.getDefaultValue(), false, null),
new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.SOCKET_FACTORY_CLASS.toString(),
SQLServerDriverStringProperty.SOCKET_FACTORY_CLASS.getDefaultValue(), false, null),
new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.SOCKET_FACTORY_CONSTRUCTOR_ARG.toString(),
Expand Down
Expand Up @@ -187,6 +187,7 @@ protected Object[][] getContents() {
{"R_domainPropertyDescription", "The Windows domain to authenticate in using NTLM."},
{"R_serverNamePropertyDescription", "The computer running SQL Server."},
{"R_portNumberPropertyDescription", "The TCP port where an instance of SQL Server is listening."},
{"R_realmPropertyDescription", "The realm for Kerberos authentication."},
{"R_serverSpnPropertyDescription", "SQL Server SPN."},
{"R_columnEncryptionSettingPropertyDescription", "The column encryption setting."},
{"R_enclaveAttestationUrlPropertyDescription", "The enclave attestation URL."},
Expand Down
41 changes: 26 additions & 15 deletions src/main/java/com/microsoft/sqlserver/jdbc/SSPIAuthentication.java
Expand Up @@ -9,6 +9,8 @@
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand All @@ -31,6 +33,8 @@ abstract class SSPIAuthentication {
*/
private static final Pattern SPN_PATTERN = Pattern.compile("MSSQLSvc/(.*):([^:@]+)(@.+)?",
Pattern.CASE_INSENSITIVE);

private static final Logger logger = Logger.getLogger("com.microsoft.sqlserver.jdbc.SSPIAuthentication");

/**
* Make SPN name
Expand Down Expand Up @@ -124,34 +128,41 @@ private String findRealmFromHostname(RealmValidator realmValidator, String hostn
* flag to indicate of hostname canonicalization is allowed
* @return SPN enriched with realm
*/
String enrichSpnWithRealm(String spn, boolean allowHostnameCanonicalization) {
String enrichSpnWithRealm(SQLServerConnection con, String spn, boolean allowHostnameCanonicalization) {
if (spn == null) {
return spn;
}
Matcher m = SPN_PATTERN.matcher(spn);
if (!m.matches()) {
return spn;
}
if (m.group(3) != null) {
String realm = con.activeConnectionProperties.getProperty(SQLServerDriverStringProperty.REALM.toString());
if (m.group(3) != null && (null == realm || realm.trim().isEmpty())) {
// Realm is already present, no need to enrich, the job has already been done
return spn;
}
String dnsName = m.group(1);
String portOrInstance = m.group(2);
RealmValidator realmValidator = getRealmValidator();
String realm = findRealmFromHostname(realmValidator, dnsName);
if (realm == null && allowHostnameCanonicalization) {
// We failed, try with canonical host name to find a better match
try {
String canonicalHostName = InetAddress.getByName(dnsName).getCanonicalHostName();
realm = findRealmFromHostname(realmValidator, canonicalHostName);
// match means hostname is correct (for instance if server name was an IP) so override dnsName as well
dnsName = canonicalHostName;
} catch (UnknownHostException e) {
// ignored, cannot canonicalize
// If realm is not specified in the connection, try to derive it.
if (null == realm || realm.trim().isEmpty()) {
RealmValidator realmValidator = getRealmValidator();
realm = findRealmFromHostname(realmValidator, dnsName);
if (null == realm && allowHostnameCanonicalization) {
// We failed, try with canonical host name to find a better match
try {
String canonicalHostName = InetAddress.getByName(dnsName).getCanonicalHostName();
realm = findRealmFromHostname(realmValidator, canonicalHostName);
// match means hostname is correct (for instance if server name was an IP) so override dnsName as well
dnsName = canonicalHostName;
} catch (UnknownHostException e) {
// ignored, cannot canonicalize
if (logger.isLoggable(Level.FINER)) {
logger.finer("Could not canonicalize host name. " + e.toString());
}
}
}
}
if (realm == null) {
if (null == realm) {
return spn;
} else {
StringBuilder sb = new StringBuilder("MSSQLSvc/");
Expand Down Expand Up @@ -188,6 +199,6 @@ String getSpn(SQLServerConnection con) {
spn = makeSpn(con, con.currentConnectPlaceHolder.getServerName(),
con.currentConnectPlaceHolder.getPortNumber());
}
return enrichSpnWithRealm(spn, null == userSuppliedServerSpn);
return enrichSpnWithRealm(con, spn, null == userSuppliedServerSpn);
}
}
Expand Up @@ -189,6 +189,9 @@ public void testDataSource() {
ds.setServerName(stringPropValue);
assertEquals(stringPropValue, ds.getServerName(), TestResource.getResource("R_valuesAreDifferent"));

ds.setRealm(stringPropValue);
assertEquals(stringPropValue, ds.getRealm(), TestResource.getResource("R_valuesAreDifferent"));

ds.setServerSpn(stringPropValue);
assertEquals(stringPropValue, ds.getServerSpn(), TestResource.getResource("R_valuesAreDifferent"));

Expand Down

0 comments on commit fb15da6

Please sign in to comment.