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
82 changes: 54 additions & 28 deletions src/main/java/com/microsoft/sqlserver/jdbc/KerbAuthentication.java
Original file line number Diff line number Diff line change
Expand Up @@ -127,37 +127,47 @@ private void intAuthInit() throws SQLServerException {
// If we need to support NTLM as well, we can use null
// Kerberos OID
Oid kerberos = new Oid("1.2.840.113554.1.2.2");
Subject currentSubject = null;
try {
AccessControlContext context = AccessController.getContext();
currentSubject = Subject.getSubject(context);
if (null == currentSubject) {
lc = new LoginContext(CONFIGNAME);
lc.login();
// per documentation LoginContext will instantiate a new subject.
currentSubject = lc.getSubject();
}
}
catch (LoginException le) {
con.terminate(SQLServerException.DRIVER_ERROR_NONE, SQLServerException.getErrString("R_integratedAuthenticationFailed"), le);
}

// http://blogs.sun.com/harcey/entry/of_java_kerberos_and_access
// We pass null to indicate that the system should interpret the SPN as it is.
// We pass null to indicate that the system should interpret the SPN
// as it is.
GSSName remotePeerName = manager.createName(spn, null);
if (authLogger.isLoggable(Level.FINER)) {
authLogger.finer(toString() + " Getting client credentials");
}
peerCredentials = getClientCredential(currentSubject, manager, kerberos);
if (authLogger.isLoggable(Level.FINER)) {
authLogger.finer(toString() + " creating security context");

if (null != peerCredentials) {
peerContext = manager.createContext(remotePeerName, kerberos, peerCredentials, GSSContext.DEFAULT_LIFETIME);
peerContext.requestCredDeleg(false);
peerContext.requestMutualAuth(true);
peerContext.requestInteg(true);
}
else {
Subject currentSubject = null;
try {
AccessControlContext context = AccessController.getContext();
currentSubject = Subject.getSubject(context);
if (null == currentSubject) {
lc = new LoginContext(CONFIGNAME);
lc.login();
// per documentation LoginContext will instantiate a new subject.
currentSubject = lc.getSubject();
}
}
catch (LoginException le) {
con.terminate(SQLServerException.DRIVER_ERROR_NONE, SQLServerException.getErrString("R_integratedAuthenticationFailed"), le);
}

peerContext = manager.createContext(remotePeerName, kerberos, peerCredentials, GSSContext.DEFAULT_LIFETIME);
// The following flags should be inline with our native implementation.
peerContext.requestCredDeleg(true);
peerContext.requestMutualAuth(true);
peerContext.requestInteg(true);
if (authLogger.isLoggable(Level.FINER)) {
authLogger.finer(toString() + " Getting client credentials");
}
peerCredentials = getClientCredential(currentSubject, manager, kerberos);
if (authLogger.isLoggable(Level.FINER)) {
authLogger.finer(toString() + " creating security context");
}

peerContext = manager.createContext(remotePeerName, kerberos, peerCredentials, GSSContext.DEFAULT_LIFETIME);
// The following flags should be inline with our native implementation.
peerContext.requestCredDeleg(true);
peerContext.requestMutualAuth(true);
peerContext.requestInteg(true);
}
}

catch (GSSException ge) {
Expand All @@ -182,7 +192,7 @@ public GSSCredential run() throws GSSException {
}
};
// TO support java 5, 6 we have to do this
// The signature for Java 5 returns an object 6 returns GSSCredential, immediate casting throws
// The signature for Java 5 returns an object 6 returns GSSCredential, immediate casting throws
// warning in Java 6.
Object credential = Subject.doAs(subject, action);
return (GSSCredential) credential;
Expand Down Expand Up @@ -262,6 +272,22 @@ private String makeSpn(String server,
}
}

/**
*
* @param con
* @param address
* @param port
* @param ImpersonatedUserCred
* @throws SQLServerException
*/
KerbAuthentication(SQLServerConnection con,
String address,
int port,
GSSCredential ImpersonatedUserCred) throws SQLServerException {
this(con, address, port);
peerCredentials = ImpersonatedUserCred;
}

byte[] GenerateClientContext(byte[] pin,
boolean[] done) throws SQLServerException {
if (null == peerContext) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@
import javax.sql.XAConnection;
import javax.xml.bind.DatatypeConverter;

import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;

/**
* SQLServerConnection implements a JDBC connection to SQL Server. SQLServerConnections support JDBC connection pooling and may be either physical
* JDBC connections or logical JDBC connections.
Expand Down Expand Up @@ -505,6 +508,7 @@ static synchronized List<String> getColumnEncryptionTrustedMasterKeyPaths(String
Properties activeConnectionProperties; // the active set of connection properties
private boolean integratedSecurity = SQLServerDriverBooleanProperty.INTEGRATED_SECURITY.getDefaultValue();
private AuthenticationScheme intAuthScheme = AuthenticationScheme.nativeAuthentication;
private GSSCredential ImpersonatedUserCred ;
// This is the current connect place holder this should point one of the primary or failover place holder
ServerPortPlaceHolder currentConnectPlaceHolder = null;

Expand Down Expand Up @@ -1192,6 +1196,12 @@ Connection connectInternal(Properties propsIn,
}
}

if(intAuthScheme == AuthenticationScheme.javaKerberos){
sPropKey = SQLServerDriverObjectProperty.GSS_CREDENTIAL.toString();
if(activeConnectionProperties.containsKey(sPropKey))
ImpersonatedUserCred = (GSSCredential) activeConnectionProperties.get(sPropKey);
}

sPropKey = SQLServerDriverStringProperty.AUTHENTICATION.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (sPropValue == null) {
Expand Down Expand Up @@ -2988,8 +2998,13 @@ final boolean doExecute() throws SQLServerException {
SSPIAuthentication authentication = null;
if (integratedSecurity && AuthenticationScheme.nativeAuthentication == intAuthScheme)
authentication = new AuthenticationJNI(this, currentConnectPlaceHolder.getServerName(), currentConnectPlaceHolder.getPortNumber());
if (integratedSecurity && AuthenticationScheme.javaKerberos == intAuthScheme)
authentication = new KerbAuthentication(this, currentConnectPlaceHolder.getServerName(), currentConnectPlaceHolder.getPortNumber());
if (integratedSecurity && AuthenticationScheme.javaKerberos == intAuthScheme) {
if (null != ImpersonatedUserCred)
authentication = new KerbAuthentication(this, currentConnectPlaceHolder.getServerName(), currentConnectPlaceHolder.getPortNumber(),
ImpersonatedUserCred);
else
authentication = new KerbAuthentication(this, currentConnectPlaceHolder.getServerName(), currentConnectPlaceHolder.getPortNumber());
}

// If the workflow being used is Active Directory Password or Active Directory Integrated and server's prelogin response
// for FEDAUTHREQUIRED option indicates Federated Authentication is required, we have to insert FedAuth Feature Extension
Expand Down Expand Up @@ -3028,6 +3043,16 @@ final boolean doExecute() throws SQLServerException {
if (null != authentication)
authentication.ReleaseClientContext();
authentication = null;

if (null != ImpersonatedUserCred) {
try {
ImpersonatedUserCred.dispose();
}
catch (GSSException e) {
if (connectionlogger.isLoggable(Level.FINER))
connectionlogger.finer(toString() + " Release of the credentials failed GSSException: " + e);
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import javax.naming.StringRefAddr;
import javax.sql.DataSource;

import org.ietf.jgss.GSSCredential;

/**
* This datasource lists properties specific for the SQLServerConnection class.
*/
Expand Down Expand Up @@ -175,6 +177,25 @@ public String getAuthentication() {
SQLServerDriverStringProperty.AUTHENTICATION.getDefaultValue());
}

/**
* sets GSSCredential
*
* @param userCredential
*/
public void setGSSCredentials(GSSCredential userCredential){
setObjectProperty(connectionProps,SQLServerDriverObjectProperty.GSS_CREDENTIAL.toString(), userCredential);
}

/**
* Retrieves the GSSCredential
*
* @return GSSCredential
*/
public GSSCredential getGSSCredentials(){
return (GSSCredential) getObjectProperty(connectionProps, SQLServerDriverObjectProperty.GSS_CREDENTIAL.toString(),
SQLServerDriverObjectProperty.GSS_CREDENTIAL.getDefaultValue());
}

/**
* Sets the access token.
*
Expand Down Expand Up @@ -772,6 +793,30 @@ private boolean getBooleanProperty(Properties props,
return value.booleanValue();
}

private void setObjectProperty(Properties props,
String propKey,
Object propValue) {
if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) {
loggerExternal.entering(getClassNameLogging(), "set" + propKey);
}
if (null != propValue) {
props.put(propKey, propValue);
}
loggerExternal.exiting(getClassNameLogging(), "set" + propKey);
}

private Object getObjectProperty(Properties props,
String propKey,
Object defaultValue) {
if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
loggerExternal.entering(getClassNameLogging(), "get" + propKey);
Object propValue = props.get(propKey);
if (null == propValue)
propValue = defaultValue;
loggerExternal.exiting(getClassNameLogging(), "get" + propKey);
return propValue;
}

// Returns a SQLServerConnection given username, password, and pooledConnection.
// Note that the DataSource properties set to connectionProps are used when creating
// the connection.
Expand Down
49 changes: 39 additions & 10 deletions src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import java.util.logging.Level;
import java.util.logging.Logger;

import org.ietf.jgss.GSSCredential;

/**
* SQLServerDriver implements the java.sql.Driver for SQLServerConnect.
*
Expand Down Expand Up @@ -192,6 +194,30 @@ else if (value.equalsIgnoreCase(ApplicationIntent.READ_WRITE.toString())) {
}
}

enum SQLServerDriverObjectProperty {
GSS_CREDENTIAL("gsscredential", null);
private String name;
private Object defaultValue;

private SQLServerDriverObjectProperty(String name,
Object defaultValue) {
this.name = name;
this.defaultValue = defaultValue;
}

/**
* returning string due to structure of DRIVER_PROPERTIES_PROPERTY_ONLY
* @return
*/
public String getDefaultValue() {
return null;
}

public String toString() {
return name;
}
}

enum SQLServerDriverStringProperty
{
APPLICATION_INTENT ("applicationIntent", ApplicationIntent.READ_WRITE.toString()),
Expand All @@ -217,7 +243,8 @@ enum SQLServerDriverStringProperty
KEY_STORE_AUTHENTICATION ("keyStoreAuthentication", ""),
KEY_STORE_SECRET ("keyStoreSecret", ""),
KEY_STORE_LOCATION ("keyStoreLocation", ""),
FIPS_PROVIDER ("fipsProvider", "");
FIPS_PROVIDER ("fipsProvider", ""),
;

private String name;
private String defaultValue;
Expand Down Expand Up @@ -351,10 +378,11 @@ public final class SQLServerDriver implements java.sql.Driver {
// Properties that can only be set by using Properties.
// Cannot set in connection string
private static final SQLServerDriverPropertyInfo[] DRIVER_PROPERTIES_PROPERTY_ONLY = {
// default required available choices
// property name value property (if appropriate)
new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.ACCESS_TOKEN.toString(),
SQLServerDriverStringProperty.ACCESS_TOKEN.getDefaultValue(), false, null),};
// default required available choices
// property name value property (if appropriate)
new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.ACCESS_TOKEN.toString(), SQLServerDriverStringProperty.ACCESS_TOKEN.getDefaultValue(), false, null),
new SQLServerDriverPropertyInfo(SQLServerDriverObjectProperty.GSS_CREDENTIAL.toString(), SQLServerDriverObjectProperty.GSS_CREDENTIAL.getDefaultValue(), false, null),
};

private static final String driverPropertiesSynonyms[][] = {
{"database", SQLServerDriverStringProperty.DATABASE_NAME.toString()},
Expand Down Expand Up @@ -421,6 +449,9 @@ static Properties fixupProperties(Properties props) throws SQLServerException {
// replace with the driver approved name
fixedup.setProperty(newname, val);
}
else if(newname.equalsIgnoreCase("gsscredential") && (props.get(name) instanceof GSSCredential)){
fixedup.put(newname, props.get(name));
}
else {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidpropertyValue"));
Object[] msgArgs = {name};
Expand All @@ -441,9 +472,7 @@ static Properties mergeURLAndSuppliedProperties(Properties urlProps,
return urlProps;
if (suppliedProperties.isEmpty())
return urlProps;

Properties suppliedPropertiesFixed = fixupProperties(suppliedProperties);

// Merge URL properties and supplied properties.
for (int i = 0; i < DRIVER_PROPERTIES.length; i++) {
String sProp = DRIVER_PROPERTIES[i].getName();
Expand All @@ -457,10 +486,10 @@ static Properties mergeURLAndSuppliedProperties(Properties urlProps,
// Merge URL properties with property-only properties
for (int i = 0; i < DRIVER_PROPERTIES_PROPERTY_ONLY.length; i++) {
String sProp = DRIVER_PROPERTIES_PROPERTY_ONLY[i].getName();
String sPropVal = suppliedPropertiesFixed.getProperty(sProp); // supplied properties have precedence
if (null != sPropVal) {
Object oPropVal = suppliedPropertiesFixed.get(sProp); // supplied properties have precedence
if (null != oPropVal) {
// overwrite the property in urlprops if already exists. supp prop has more precedence
urlProps.put(sProp, sPropVal);
urlProps.put(sProp, oPropVal);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ protected Object[][] getContents() {
{"R_TransparentNetworkIPResolutionPropertyDescription", "Determines whether to use the Transparent Network IP Resolution feature."},
{"R_queryTimeoutPropertyDescription", "The number of seconds to wait before the database reports a query time-out."},
{"R_socketTimeoutPropertyDescription", "The number of milliseconds to wait before the java.net.SocketTimeoutException is raised."},
{"R_gsscredentialPropertyDescription", "Impersonated GSS Credential to access SQL Server."},
{"R_noParserSupport", "An error occurred while instantiating the required parser. Error: \"{0}\""},
{"R_writeOnlyXML", "Cannot read from this SQLXML instance. This instance is for writing data only."},
{"R_dataHasBeenReadXML", "Cannot read from this SQLXML instance. The data has already been read."},
Expand Down