Skip to content

Commit 971fd93

Browse files
author
Suraiya Hameed
committed
constrained delegation
1 parent 369e5e0 commit 971fd93

File tree

4 files changed

+121
-40
lines changed

4 files changed

+121
-40
lines changed

src/main/java/com/microsoft/sqlserver/jdbc/KerbAuthentication.java

+54-28
Original file line numberDiff line numberDiff line change
@@ -127,37 +127,47 @@ private void intAuthInit() throws SQLServerException {
127127
// If we need to support NTLM as well, we can use null
128128
// Kerberos OID
129129
Oid kerberos = new Oid("1.2.840.113554.1.2.2");
130-
Subject currentSubject = null;
131-
try {
132-
AccessControlContext context = AccessController.getContext();
133-
currentSubject = Subject.getSubject(context);
134-
if (null == currentSubject) {
135-
lc = new LoginContext(CONFIGNAME);
136-
lc.login();
137-
// per documentation LoginContext will instantiate a new subject.
138-
currentSubject = lc.getSubject();
139-
}
140-
}
141-
catch (LoginException le) {
142-
con.terminate(SQLServerException.DRIVER_ERROR_NONE, SQLServerException.getErrString("R_integratedAuthenticationFailed"), le);
143-
}
144-
145130
// http://blogs.sun.com/harcey/entry/of_java_kerberos_and_access
146-
// We pass null to indicate that the system should interpret the SPN as it is.
131+
// We pass null to indicate that the system should interpret the SPN
132+
// as it is.
147133
GSSName remotePeerName = manager.createName(spn, null);
148-
if (authLogger.isLoggable(Level.FINER)) {
149-
authLogger.finer(toString() + " Getting client credentials");
150-
}
151-
peerCredentials = getClientCredential(currentSubject, manager, kerberos);
152-
if (authLogger.isLoggable(Level.FINER)) {
153-
authLogger.finer(toString() + " creating security context");
134+
135+
if (null != peerCredentials) {
136+
peerContext = manager.createContext(remotePeerName, kerberos, peerCredentials, GSSContext.DEFAULT_LIFETIME);
137+
peerContext.requestCredDeleg(false);
138+
peerContext.requestMutualAuth(true);
139+
peerContext.requestInteg(true);
154140
}
141+
else {
142+
Subject currentSubject = null;
143+
try {
144+
AccessControlContext context = AccessController.getContext();
145+
currentSubject = Subject.getSubject(context);
146+
if (null == currentSubject) {
147+
lc = new LoginContext(CONFIGNAME);
148+
lc.login();
149+
// per documentation LoginContext will instantiate a new subject.
150+
currentSubject = lc.getSubject();
151+
}
152+
}
153+
catch (LoginException le) {
154+
con.terminate(SQLServerException.DRIVER_ERROR_NONE, SQLServerException.getErrString("R_integratedAuthenticationFailed"), le);
155+
}
155156

156-
peerContext = manager.createContext(remotePeerName, kerberos, peerCredentials, GSSContext.DEFAULT_LIFETIME);
157-
// The following flags should be inline with our native implementation.
158-
peerContext.requestCredDeleg(true);
159-
peerContext.requestMutualAuth(true);
160-
peerContext.requestInteg(true);
157+
if (authLogger.isLoggable(Level.FINER)) {
158+
authLogger.finer(toString() + " Getting client credentials");
159+
}
160+
peerCredentials = getClientCredential(currentSubject, manager, kerberos);
161+
if (authLogger.isLoggable(Level.FINER)) {
162+
authLogger.finer(toString() + " creating security context");
163+
}
164+
165+
peerContext = manager.createContext(remotePeerName, kerberos, peerCredentials, GSSContext.DEFAULT_LIFETIME);
166+
// The following flags should be inline with our native implementation.
167+
peerContext.requestCredDeleg(true);
168+
peerContext.requestMutualAuth(true);
169+
peerContext.requestInteg(true);
170+
}
161171
}
162172

163173
catch (GSSException ge) {
@@ -182,7 +192,7 @@ public GSSCredential run() throws GSSException {
182192
}
183193
};
184194
// TO support java 5, 6 we have to do this
185-
// The signature for Java 5 returns an object 6 returns GSSCredential, immediate casting throws
195+
// The signature for Java 5 returns an object 6 returns GSSCredential, immediate casting throws
186196
// warning in Java 6.
187197
Object credential = Subject.doAs(subject, action);
188198
return (GSSCredential) credential;
@@ -262,6 +272,22 @@ private String makeSpn(String server,
262272
}
263273
}
264274

275+
/**
276+
*
277+
* @param con
278+
* @param address
279+
* @param port
280+
* @param ImpersonatedUserCred
281+
* @throws SQLServerException
282+
*/
283+
KerbAuthentication(SQLServerConnection con,
284+
String address,
285+
int port,
286+
GSSCredential ImpersonatedUserCred) throws SQLServerException {
287+
this(con, address, port);
288+
peerCredentials = ImpersonatedUserCred;
289+
}
290+
265291
byte[] GenerateClientContext(byte[] pin,
266292
boolean[] done) throws SQLServerException {
267293
if (null == peerContext) {

src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java

+27-2
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@
5252
import javax.sql.XAConnection;
5353
import javax.xml.bind.DatatypeConverter;
5454

55+
import org.ietf.jgss.GSSCredential;
56+
import org.ietf.jgss.GSSException;
57+
5558
/**
5659
* SQLServerConnection implements a JDBC connection to SQL Server. SQLServerConnections support JDBC connection pooling and may be either physical
5760
* JDBC connections or logical JDBC connections.
@@ -505,6 +508,7 @@ static synchronized List<String> getColumnEncryptionTrustedMasterKeyPaths(String
505508
Properties activeConnectionProperties; // the active set of connection properties
506509
private boolean integratedSecurity = SQLServerDriverBooleanProperty.INTEGRATED_SECURITY.getDefaultValue();
507510
private AuthenticationScheme intAuthScheme = AuthenticationScheme.nativeAuthentication;
511+
private GSSCredential ImpersonatedUserCred ;
508512
// This is the current connect place holder this should point one of the primary or failover place holder
509513
ServerPortPlaceHolder currentConnectPlaceHolder = null;
510514

@@ -1192,6 +1196,12 @@ Connection connectInternal(Properties propsIn,
11921196
}
11931197
}
11941198

1199+
if(intAuthScheme == AuthenticationScheme.javaKerberos){
1200+
sPropKey = SQLServerDriverObjectProperty.GSS_CREDENTIAL.toString();
1201+
if(activeConnectionProperties.containsKey(sPropKey))
1202+
ImpersonatedUserCred = (GSSCredential) activeConnectionProperties.get(sPropKey);
1203+
}
1204+
11951205
sPropKey = SQLServerDriverStringProperty.AUTHENTICATION.toString();
11961206
sPropValue = activeConnectionProperties.getProperty(sPropKey);
11971207
if (sPropValue == null) {
@@ -2988,8 +2998,13 @@ final boolean doExecute() throws SQLServerException {
29882998
SSPIAuthentication authentication = null;
29892999
if (integratedSecurity && AuthenticationScheme.nativeAuthentication == intAuthScheme)
29903000
authentication = new AuthenticationJNI(this, currentConnectPlaceHolder.getServerName(), currentConnectPlaceHolder.getPortNumber());
2991-
if (integratedSecurity && AuthenticationScheme.javaKerberos == intAuthScheme)
2992-
authentication = new KerbAuthentication(this, currentConnectPlaceHolder.getServerName(), currentConnectPlaceHolder.getPortNumber());
3001+
if (integratedSecurity && AuthenticationScheme.javaKerberos == intAuthScheme) {
3002+
if (null != ImpersonatedUserCred)
3003+
authentication = new KerbAuthentication(this, currentConnectPlaceHolder.getServerName(), currentConnectPlaceHolder.getPortNumber(),
3004+
ImpersonatedUserCred);
3005+
else
3006+
authentication = new KerbAuthentication(this, currentConnectPlaceHolder.getServerName(), currentConnectPlaceHolder.getPortNumber());
3007+
}
29933008

29943009
// If the workflow being used is Active Directory Password or Active Directory Integrated and server's prelogin response
29953010
// for FEDAUTHREQUIRED option indicates Federated Authentication is required, we have to insert FedAuth Feature Extension
@@ -3028,6 +3043,16 @@ final boolean doExecute() throws SQLServerException {
30283043
if (null != authentication)
30293044
authentication.ReleaseClientContext();
30303045
authentication = null;
3046+
3047+
if (null != ImpersonatedUserCred) {
3048+
try {
3049+
ImpersonatedUserCred.dispose();
3050+
}
3051+
catch (GSSException e) {
3052+
if (connectionlogger.isLoggable(Level.FINER))
3053+
connectionlogger.finer(toString() + " Release of the credentials failed GSSException: " + e);
3054+
}
3055+
}
30313056
}
30323057
}
30333058
}

src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java

+39-10
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import java.util.logging.Level;
2121
import java.util.logging.Logger;
2222

23+
import org.ietf.jgss.GSSCredential;
24+
2325
/**
2426
* SQLServerDriver implements the java.sql.Driver for SQLServerConnect.
2527
*
@@ -192,6 +194,30 @@ else if (value.equalsIgnoreCase(ApplicationIntent.READ_WRITE.toString())) {
192194
}
193195
}
194196

197+
enum SQLServerDriverObjectProperty {
198+
GSS_CREDENTIAL("gsscredential", null);
199+
private String name;
200+
private Object defaultValue;
201+
202+
private SQLServerDriverObjectProperty(String name,
203+
Object defaultValue) {
204+
this.name = name;
205+
this.defaultValue = defaultValue;
206+
}
207+
208+
/**
209+
* returning string due to structure of DRIVER_PROPERTIES_PROPERTY_ONLY
210+
* @return
211+
*/
212+
public String getDefaultValue() {
213+
return null;
214+
}
215+
216+
public String toString() {
217+
return name;
218+
}
219+
}
220+
195221
enum SQLServerDriverStringProperty
196222
{
197223
APPLICATION_INTENT ("applicationIntent", ApplicationIntent.READ_WRITE.toString()),
@@ -217,7 +243,8 @@ enum SQLServerDriverStringProperty
217243
KEY_STORE_AUTHENTICATION ("keyStoreAuthentication", ""),
218244
KEY_STORE_SECRET ("keyStoreSecret", ""),
219245
KEY_STORE_LOCATION ("keyStoreLocation", ""),
220-
FIPS_PROVIDER ("fipsProvider", "");
246+
FIPS_PROVIDER ("fipsProvider", ""),
247+
;
221248

222249
private String name;
223250
private String defaultValue;
@@ -351,10 +378,11 @@ public final class SQLServerDriver implements java.sql.Driver {
351378
// Properties that can only be set by using Properties.
352379
// Cannot set in connection string
353380
private static final SQLServerDriverPropertyInfo[] DRIVER_PROPERTIES_PROPERTY_ONLY = {
354-
// default required available choices
355-
// property name value property (if appropriate)
356-
new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.ACCESS_TOKEN.toString(),
357-
SQLServerDriverStringProperty.ACCESS_TOKEN.getDefaultValue(), false, null),};
381+
// default required available choices
382+
// property name value property (if appropriate)
383+
new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.ACCESS_TOKEN.toString(), SQLServerDriverStringProperty.ACCESS_TOKEN.getDefaultValue(), false, null),
384+
new SQLServerDriverPropertyInfo(SQLServerDriverObjectProperty.GSS_CREDENTIAL.toString(), SQLServerDriverObjectProperty.GSS_CREDENTIAL.getDefaultValue(), false, null),
385+
};
358386

359387
private static final String driverPropertiesSynonyms[][] = {
360388
{"database", SQLServerDriverStringProperty.DATABASE_NAME.toString()},
@@ -421,6 +449,9 @@ static Properties fixupProperties(Properties props) throws SQLServerException {
421449
// replace with the driver approved name
422450
fixedup.setProperty(newname, val);
423451
}
452+
else if(newname.equalsIgnoreCase("gsscredential") && (props.get(name) instanceof GSSCredential)){
453+
fixedup.put(newname, props.get(name));
454+
}
424455
else {
425456
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidpropertyValue"));
426457
Object[] msgArgs = {name};
@@ -441,9 +472,7 @@ static Properties mergeURLAndSuppliedProperties(Properties urlProps,
441472
return urlProps;
442473
if (suppliedProperties.isEmpty())
443474
return urlProps;
444-
445475
Properties suppliedPropertiesFixed = fixupProperties(suppliedProperties);
446-
447476
// Merge URL properties and supplied properties.
448477
for (int i = 0; i < DRIVER_PROPERTIES.length; i++) {
449478
String sProp = DRIVER_PROPERTIES[i].getName();
@@ -457,10 +486,10 @@ static Properties mergeURLAndSuppliedProperties(Properties urlProps,
457486
// Merge URL properties with property-only properties
458487
for (int i = 0; i < DRIVER_PROPERTIES_PROPERTY_ONLY.length; i++) {
459488
String sProp = DRIVER_PROPERTIES_PROPERTY_ONLY[i].getName();
460-
String sPropVal = suppliedPropertiesFixed.getProperty(sProp); // supplied properties have precedence
461-
if (null != sPropVal) {
489+
Object oPropVal = suppliedPropertiesFixed.get(sProp); // supplied properties have precedence
490+
if (null != oPropVal) {
462491
// overwrite the property in urlprops if already exists. supp prop has more precedence
463-
urlProps.put(sProp, sPropVal);
492+
urlProps.put(sProp, oPropVal);
464493
}
465494
}
466495

src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java

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

0 commit comments

Comments
 (0)