Skip to content

Instantly share code, notes, and snippets.

@krlm
Last active February 7, 2018 21:25
Show Gist options
  • Save krlm/410b480c0740e963ff68a3130048b769 to your computer and use it in GitHub Desktop.
Save krlm/410b480c0740e963ff68a3130048b769 to your computer and use it in GitHub Desktop.
mssql-jdbc connection pooling and kerberos constrained delegation
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.Oid;
import com.sun.security.jgss.ExtendedGSSCredential;
/**
*
* Sample of constrained delegation connection.
*
* An intermediate service is necessary to impersonate the client. This service needs to be configured with the
* options:
* "Trust this user for delegation to specified services only"
* "Use any authentication protocol"
*
*/
public class ConstrainedSample {
// Connection properties
private static final String DRIVER_CLASS_NAME ="com.microsoft.sqlserver.jdbc.SQLServerDriver";
private static final String CONNECTION_URI = "jdbc:sqlserver://sql-krb.ppadfs.com;databaseName=master;integratedSecurity=true;authenticationScheme=JavaKerberos";
private static final String TARGET_USER_NAME = "bsmith@PPADFS.COM";
// Impersonation service properties
private static final String SERVICE_PRINCIPAL = "HTTP/kerberos-poc.ppadfs.com@PPADFS.COM";
private static final String KEYTAB_ROUTE = "/etc/spring-service.keytab";
private static final Properties driverProperties;
private static Oid krb5Oid;
private static Subject serviceSubject;
static {
driverProperties = new Properties();
driverProperties.setProperty("integratedSecurity", "true");
driverProperties.setProperty("authenticationScheme", "JavaKerberos");
try {
krb5Oid = new Oid("1.2.840.113554.1.2.2");
} catch (GSSException e) {
System.out.println("Error creating Oid: " + e);
System.exit(-1);
}
}
public static void main(String... args) throws Exception {
Class.forName(DRIVER_CLASS_NAME).getConstructor().newInstance();
System.out.println("Service subject: " + doInitialLogin());
// Get impersonated user credentials thanks S4U2self mechanism
GSSCredential impersonatedUserCreds = impersonate();
System.out.println("Credentials for " + TARGET_USER_NAME + ": " + impersonatedUserCreds);
// Create a connection for target service thanks S4U2proxy mechanism
for (int i = 0; i < 3; i++) {
Connection con = createConnection(impersonatedUserCreds);
System.out.println("Connection succesfully: " + con);
ResultSet result = con.createStatement().executeQuery("SELECT SYSTEM_USER");
while(result.next()) {
System.out.println("User on DB: " + result.getString(1)); // .getString("SYSTEM_USER"));
}
}
}
/**
*
* Authenticate the intermediate server that is going to impersonate the client
*
* @return a subject for the intermediate server with the keytab credentials
* @throws PrivilegedActionException in case of failure
*/
private static Subject doInitialLogin() throws PrivilegedActionException {
serviceSubject = new Subject();
LoginModule krb5Module;
try {
krb5Module = (LoginModule) Class.forName("com.sun.security.auth.module.Krb5LoginModule").getConstructor()
.newInstance();
} catch (Exception e) {
System.out.print("Error loading Krb5LoginModule module: " + e);
throw new PrivilegedActionException(e);
}
System.setProperty("sun.security.krb5.debug", String.valueOf(true));
Map<String, String> options = new HashMap<>();
options.put("useKeyTab", "true");
options.put("storeKey", "true");
options.put("doNotPrompt", "true");
options.put("keyTab", KEYTAB_ROUTE);
options.put("principal", SERVICE_PRINCIPAL);
options.put("debug", "true");
options.put("isInitiator", "true");
Map<String, String> sharedState = new HashMap<>(0);
krb5Module.initialize(serviceSubject, null, sharedState, options);
try {
krb5Module.login();
krb5Module.commit();
} catch (LoginException e) {
System.out.print("Error authenticating with Kerberos: " + e);
try {
krb5Module.abort();
} catch (LoginException e1) {
System.out.print("Error aborting Kerberos authentication: " + e1);
throw new PrivilegedActionException(e);
}
throw new PrivilegedActionException(e);
}
return serviceSubject;
}
/**
* Generate the impersonated user credentials thanks to the S4U2self mechanism
*
* @return the client impersonated GSSCredential
* @throws PrivilegedActionException in case of failure
*/
private static GSSCredential impersonate() throws PrivilegedActionException {
return Subject.doAs(serviceSubject, (PrivilegedExceptionAction<GSSCredential>) () -> {
GSSManager manager = GSSManager.getInstance();
GSSCredential self = manager.createCredential(null, GSSCredential.DEFAULT_LIFETIME, krb5Oid,
GSSCredential.INITIATE_ONLY);
GSSName user = manager.createName(TARGET_USER_NAME, GSSName.NT_USER_NAME);
return ((ExtendedGSSCredential) self).impersonate(user);
});
}
/**
* Obtains a connection using an impersonated credential
*
* @param impersonatedUserCredential impersonated user credentials
* @return a connection to the SQL Server opened using the given impersonated credential
* @throws PrivilegedActionException in case of failure
*/
private static Connection createConnection(final GSSCredential impersonatedUserCredential)
throws PrivilegedActionException {
return Subject.doAs(new Subject(), (PrivilegedExceptionAction<Connection>) () -> {
driverProperties.put("gsscredential", impersonatedUserCredential);
return DriverManager.getConnection(CONNECTION_URI, driverProperties);
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment