Added multitier support.
This commit is contained in:
@@ -0,0 +1,180 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.security.kerberos.client;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.core.io.FileSystemResource;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.authority.AuthorityUtils;
|
||||||
|
import org.springframework.security.core.userdetails.User;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.security.kerberos.authentication.KerberosAuthenticationProvider;
|
||||||
|
import org.springframework.security.kerberos.authentication.KerberosMultiTier;
|
||||||
|
import org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider;
|
||||||
|
import org.springframework.security.kerberos.authentication.KerberosServiceRequestToken;
|
||||||
|
import org.springframework.security.kerberos.authentication.sun.SunJaasKerberosClient;
|
||||||
|
import org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator;
|
||||||
|
import org.springframework.security.kerberos.test.KerberosSecurityTestcase;
|
||||||
|
import org.springframework.security.kerberos.test.MiniKdc;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Bogdan Mustiata
|
||||||
|
*/
|
||||||
|
public class TestMultiTierAuthentication extends KerberosSecurityTestcase {
|
||||||
|
@Test
|
||||||
|
public void testServer() throws Exception {
|
||||||
|
MiniKdc kdc = getKdc();
|
||||||
|
File workDir = getWorkDir();
|
||||||
|
|
||||||
|
File webTierKeytabFile = new File(workDir, "webtier.keytab");
|
||||||
|
kdc.createKeyabFile(webTierKeytabFile, "HTTP/webtier@EXAMPLE.COM", "secret");
|
||||||
|
|
||||||
|
File serviceTierKeytabFile = new File(workDir, "servicetier.keytab");
|
||||||
|
kdc.createKeyabFile(serviceTierKeytabFile, "HTTP/servicetier@EXAMPLE.COM", "secret");
|
||||||
|
|
||||||
|
//
|
||||||
|
// User logs in as user1/secret
|
||||||
|
//
|
||||||
|
KerberosAuthenticationProvider kerberosAuthProvider =
|
||||||
|
createUserPassAuthenticator(/* debug: */ true);
|
||||||
|
|
||||||
|
Authentication authentication = kerberosAuthProvider
|
||||||
|
.authenticate(new UsernamePasswordAuthenticationToken("user1", "secret"));
|
||||||
|
|
||||||
|
assertEquals("user1@EXAMPLE.COM", authentication.getName());
|
||||||
|
|
||||||
|
//
|
||||||
|
// User creates a ticket for the HTTP/webtier@EXAMPLE.COM, using
|
||||||
|
// and then calls the service, using the tokenData
|
||||||
|
//
|
||||||
|
authentication = KerberosMultiTier.authenticateService(
|
||||||
|
authentication, "user1", 3600, "HTTP/webtier@EXAMPLE.COM");
|
||||||
|
|
||||||
|
byte[] tokenData = KerberosMultiTier
|
||||||
|
.getTokenForService(authentication, "HTTP/webtier@EXAMPLE.COM");
|
||||||
|
|
||||||
|
assertNotNull(tokenData);
|
||||||
|
assertTrue(tokenData.length != 0);
|
||||||
|
|
||||||
|
//
|
||||||
|
// The service HTTP/webtier@EXAMPLE.COM authenticates via tokens.
|
||||||
|
//
|
||||||
|
KerberosServiceAuthenticationProvider webTierAuthenticatorProvider =
|
||||||
|
createServiceAuthenticator(
|
||||||
|
true,
|
||||||
|
"HTTP/webtier@EXAMPLE.COM",
|
||||||
|
"EXAMPLE.COM",
|
||||||
|
webTierKeytabFile.getCanonicalPath()
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// The service HTTP/webtier@EXAMPLE.COM authenticates the user1@EXAMPLE.COM
|
||||||
|
// using the previously stored token, then authenticates itself further as
|
||||||
|
// user1@EXAMPLE.COM to the HTTP/servicetier@EXAMPLE.COM.
|
||||||
|
//
|
||||||
|
Authentication webTierAuthentication = webTierAuthenticatorProvider
|
||||||
|
.authenticate(new KerberosServiceRequestToken(tokenData));
|
||||||
|
|
||||||
|
assertEquals("user1@EXAMPLE.COM", webTierAuthentication.getName());
|
||||||
|
|
||||||
|
webTierAuthentication = KerberosMultiTier.authenticateService(
|
||||||
|
webTierAuthentication, "user1@EXAMPLE.COM", 3600, "HTTP/servicetier@EXAMPLE.COM");
|
||||||
|
|
||||||
|
byte[] workplaceTokenData = KerberosMultiTier.getTokenForService(
|
||||||
|
webTierAuthentication, "HTTP/servicetier@EXAMPLE.COM");
|
||||||
|
|
||||||
|
//
|
||||||
|
// The service HTTP/icr@EXAMPLE.COM authenticates via tokens.
|
||||||
|
//
|
||||||
|
webTierAuthenticatorProvider =
|
||||||
|
createServiceAuthenticator(
|
||||||
|
true,
|
||||||
|
"HTTP/servicetier@EXAMPLE.COM",
|
||||||
|
"EXAMPLE.COM",
|
||||||
|
serviceTierKeytabFile.getCanonicalPath()
|
||||||
|
);
|
||||||
|
|
||||||
|
//
|
||||||
|
// The service HTTP/servicetier@EXAMPLE.COM authenticates via the previously saved
|
||||||
|
// token, received from the HTTP/webtier@EXAMPLE.COM on behalf of user1@EXAMPLE.COM
|
||||||
|
//
|
||||||
|
Authentication serviceTierAuthentication = webTierAuthenticatorProvider
|
||||||
|
.authenticate(new KerberosServiceRequestToken(workplaceTokenData));
|
||||||
|
|
||||||
|
assertEquals("user1@EXAMPLE.COM", serviceTierAuthentication.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a username/password authenticator.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private KerberosAuthenticationProvider createUserPassAuthenticator(boolean debug) {
|
||||||
|
KerberosAuthenticationProvider kerberosAuthenticationProvider =
|
||||||
|
new KerberosAuthenticationProvider();
|
||||||
|
|
||||||
|
SunJaasKerberosClient sunJaasKerberosClient = new SunJaasKerberosClient();
|
||||||
|
|
||||||
|
sunJaasKerberosClient.setDebug(debug);
|
||||||
|
sunJaasKerberosClient.setMultiTier(true);
|
||||||
|
|
||||||
|
kerberosAuthenticationProvider.setKerberosClient(sunJaasKerberosClient);
|
||||||
|
kerberosAuthenticationProvider.setUserDetailsService(userDetailsService());
|
||||||
|
|
||||||
|
return kerberosAuthenticationProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
private KerberosServiceAuthenticationProvider createServiceAuthenticator(boolean debug,
|
||||||
|
String serviceName,
|
||||||
|
String realmName,
|
||||||
|
String keytabFileLocation) throws Exception {
|
||||||
|
KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider =
|
||||||
|
new KerberosServiceAuthenticationProvider();
|
||||||
|
|
||||||
|
SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();
|
||||||
|
ticketValidator.setDebug(debug);
|
||||||
|
ticketValidator.setServicePrincipal(serviceName);
|
||||||
|
ticketValidator.setRealmName(realmName);
|
||||||
|
ticketValidator.setKeyTabLocation(new FileSystemResource(keytabFileLocation));
|
||||||
|
ticketValidator.setMultiTier(true);
|
||||||
|
|
||||||
|
ticketValidator.afterPropertiesSet();
|
||||||
|
|
||||||
|
kerberosServiceAuthenticationProvider.setTicketValidator(ticketValidator);
|
||||||
|
kerberosServiceAuthenticationProvider.setUserDetailsService(userDetailsService());
|
||||||
|
|
||||||
|
return kerberosServiceAuthenticationProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserDetailsService userDetailsService() {
|
||||||
|
return new UserDetailsService() {
|
||||||
|
@Override
|
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
|
return new User(username, "notUsed", true, true, true, true,
|
||||||
|
AuthorityUtils.createAuthorityList("ROLE_USER"));
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
[libdefaults]
|
[libdefaults]
|
||||||
default_realm = {0}
|
default_realm = {0}
|
||||||
udp_preference_limit = 1
|
udp_preference_limit = 1
|
||||||
|
forwardable = true
|
||||||
|
|
||||||
[realms]
|
[realms]
|
||||||
{0} = '{'
|
{0} = '{'
|
||||||
|
|||||||
@@ -44,4 +44,43 @@ sn: Service
|
|||||||
uid: ldap
|
uid: ldap
|
||||||
userPassword: secret
|
userPassword: secret
|
||||||
krb5PrincipalName: ldap/${4}@${2}.${3}
|
krb5PrincipalName: ldap/${4}@${2}.${3}
|
||||||
krb5KeyVersionNumber: 0
|
krb5KeyVersionNumber: 0
|
||||||
|
|
||||||
|
dn: uid=user1,ou=users,dc=${0},dc=${1}
|
||||||
|
objectClass: top
|
||||||
|
objectClass: person
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: krb5principal
|
||||||
|
objectClass: krb5kdcentry
|
||||||
|
cn: user1
|
||||||
|
sn: Service
|
||||||
|
uid: user1
|
||||||
|
userPassword: secret
|
||||||
|
krb5PrincipalName: user1@${2}.${3}
|
||||||
|
krb5KeyVersionNumber: 0
|
||||||
|
|
||||||
|
dn: uid=webtier,ou=users,dc=${0},dc=${1}
|
||||||
|
objectClass: top
|
||||||
|
objectClass: person
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: krb5principal
|
||||||
|
objectClass: krb5kdcentry
|
||||||
|
cn: webtier
|
||||||
|
sn: Service
|
||||||
|
uid: webtier
|
||||||
|
userPassword: secret
|
||||||
|
krb5PrincipalName: HTTP/webtier@${2}.${3}
|
||||||
|
krb5KeyVersionNumber: 0
|
||||||
|
|
||||||
|
dn: uid=servicetier,ou=users,dc=${0},dc=${1}
|
||||||
|
objectClass: top
|
||||||
|
objectClass: person
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: krb5principal
|
||||||
|
objectClass: krb5kdcentry
|
||||||
|
cn: servicetier
|
||||||
|
sn: Service
|
||||||
|
uid: servicetier
|
||||||
|
userPassword: secret
|
||||||
|
krb5PrincipalName: HTTP/servicetier@${2}.${3}
|
||||||
|
krb5KeyVersionNumber: 0
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.security.kerberos.authentication;
|
||||||
|
|
||||||
|
import org.springframework.security.kerberos.authentication.sun.SunJaasKerberosClient;
|
||||||
|
|
||||||
|
import javax.security.auth.Subject;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Holds the Subject of the currently authenticated user, since this
|
||||||
|
* Jaas object also has the credentials, and permits creating new
|
||||||
|
* credentials against other Kerberos services.</p>
|
||||||
|
* @author Bogdan Mustiata
|
||||||
|
* @see SunJaasKerberosClient
|
||||||
|
* @see org.springframework.security.kerberos.authentication.KerberosAuthenticationProvider
|
||||||
|
*/
|
||||||
|
public class JaasSubjectHolder {
|
||||||
|
private Subject jaasSubject;
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
private Map<String, byte[]> savedTokens = new HashMap<String, byte[]>();
|
||||||
|
|
||||||
|
public JaasSubjectHolder(Subject jaasSubject) {
|
||||||
|
this.jaasSubject = jaasSubject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JaasSubjectHolder(Subject jaasSubject, String username) {
|
||||||
|
this.jaasSubject = jaasSubject;
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Subject getJaasSubject() {
|
||||||
|
return jaasSubject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addToken(String targetService, byte[] outToken) {
|
||||||
|
this.savedTokens.put(targetService, outToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getToken(String principalName) {
|
||||||
|
return savedTokens.get(principalName);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package org.springframework.security.kerberos.authentication;
|
||||||
|
|
||||||
|
public interface KerberosAuthentication {
|
||||||
|
|
||||||
|
JaasSubjectHolder getJaasSubjectHolder();
|
||||||
|
}
|
||||||
@@ -26,6 +26,7 @@ import org.springframework.security.core.userdetails.UserDetailsService;
|
|||||||
* {@link AuthenticationProvider} for kerberos.
|
* {@link AuthenticationProvider} for kerberos.
|
||||||
*
|
*
|
||||||
* @author Mike Wiesner
|
* @author Mike Wiesner
|
||||||
|
* @author Bogdan Mustiata
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
public class KerberosAuthenticationProvider implements AuthenticationProvider {
|
public class KerberosAuthenticationProvider implements AuthenticationProvider {
|
||||||
@@ -37,10 +38,10 @@ public class KerberosAuthenticationProvider implements AuthenticationProvider {
|
|||||||
@Override
|
@Override
|
||||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||||
UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication;
|
UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication;
|
||||||
String validatedUsername = kerberosClient.login(auth.getName(), auth.getCredentials().toString());
|
JaasSubjectHolder subjectHolder = kerberosClient.login(auth.getName(), auth.getCredentials().toString());
|
||||||
UserDetails userDetails = this.userDetailsService.loadUserByUsername(validatedUsername);
|
UserDetails userDetails = this.userDetailsService.loadUserByUsername(subjectHolder.getUsername());
|
||||||
UsernamePasswordAuthenticationToken output = new UsernamePasswordAuthenticationToken(userDetails,
|
KerberosUsernamePasswordAuthenticationToken output = new KerberosUsernamePasswordAuthenticationToken(
|
||||||
auth.getCredentials(), userDetails.getAuthorities());
|
userDetails, auth.getCredentials(), userDetails.getAuthorities(), subjectHolder);
|
||||||
output.setDetails(authentication.getDetails());
|
output.setDetails(authentication.getDetails());
|
||||||
return output;
|
return output;
|
||||||
|
|
||||||
|
|||||||
@@ -19,11 +19,12 @@ package org.springframework.security.kerberos.authentication;
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Mike Wiesner
|
* @author Mike Wiesner
|
||||||
|
* @author Bogdan Mustiata
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
public interface KerberosClient {
|
public interface KerberosClient {
|
||||||
|
|
||||||
public String login(String username, String password);
|
public JaasSubjectHolder login(String username, String password);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.security.kerberos.authentication;
|
||||||
|
|
||||||
|
import org.ietf.jgss.*;
|
||||||
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
|
||||||
|
import javax.security.auth.Subject;
|
||||||
|
import java.security.PrivilegedAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Allows creating tickets against other service principals storing the
|
||||||
|
* tickets in the KerberosAuthentication's JaasSubjectHolder.</p>
|
||||||
|
* @author Bogdan Mustiata
|
||||||
|
*/
|
||||||
|
public class KerberosMultiTier {
|
||||||
|
public static final String KERBEROS_OID_STRING = "1.2.840.113554.1.2.2";
|
||||||
|
|
||||||
|
public static final Oid KERBEROS_OID = createOid(KERBEROS_OID_STRING);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new ticket for the
|
||||||
|
* @param authentication
|
||||||
|
* @param username
|
||||||
|
* @param lifetimeInSeconds
|
||||||
|
* @param targetService
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Authentication authenticateService(Authentication authentication,
|
||||||
|
final String username,
|
||||||
|
final int lifetimeInSeconds,
|
||||||
|
final String targetService) {
|
||||||
|
|
||||||
|
KerberosAuthentication kerberosAuthentication = (KerberosAuthentication)authentication;
|
||||||
|
final JaasSubjectHolder jaasSubjectHolder = kerberosAuthentication.getJaasSubjectHolder();
|
||||||
|
Subject subject = jaasSubjectHolder.getJaasSubject();
|
||||||
|
|
||||||
|
Subject.doAs(subject, new PrivilegedAction<Object>() {
|
||||||
|
@Override
|
||||||
|
public Object run() {
|
||||||
|
runAuthentication(jaasSubjectHolder, username, lifetimeInSeconds, targetService);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return authentication;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] getTokenForService(Authentication authentication, String principalName) {
|
||||||
|
KerberosAuthentication kerberosAuthentication = (KerberosAuthentication)authentication;
|
||||||
|
final JaasSubjectHolder jaasSubjectHolder = kerberosAuthentication.getJaasSubjectHolder();
|
||||||
|
|
||||||
|
return jaasSubjectHolder.getToken(principalName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void runAuthentication(JaasSubjectHolder jaasContext,
|
||||||
|
String username,
|
||||||
|
int lifetimeInSeconds,
|
||||||
|
String targetService) {
|
||||||
|
try {
|
||||||
|
GSSManager manager = GSSManager.getInstance();
|
||||||
|
GSSName clientName = manager.createName(username, GSSName.NT_USER_NAME);
|
||||||
|
|
||||||
|
GSSCredential clientCredential = manager.createCredential(
|
||||||
|
clientName,
|
||||||
|
lifetimeInSeconds,
|
||||||
|
KERBEROS_OID,
|
||||||
|
GSSCredential.INITIATE_ONLY
|
||||||
|
);
|
||||||
|
|
||||||
|
GSSName serverName = manager.createName(targetService, GSSName.NT_USER_NAME);
|
||||||
|
|
||||||
|
GSSContext securityContext = manager.createContext(serverName,
|
||||||
|
KERBEROS_OID,
|
||||||
|
clientCredential,
|
||||||
|
GSSContext.DEFAULT_LIFETIME);
|
||||||
|
|
||||||
|
securityContext.requestCredDeleg(true);
|
||||||
|
securityContext.requestInteg(false);
|
||||||
|
securityContext.requestAnonymity(false);
|
||||||
|
securityContext.requestMutualAuth(false);
|
||||||
|
securityContext.requestReplayDet(false);
|
||||||
|
securityContext.requestSequenceDet(false);
|
||||||
|
|
||||||
|
boolean established = false;
|
||||||
|
|
||||||
|
byte[] outToken = new byte[0];
|
||||||
|
|
||||||
|
while (!established) {
|
||||||
|
byte[] inToken = new byte[0];
|
||||||
|
outToken = securityContext.initSecContext(inToken, 0, inToken.length);
|
||||||
|
|
||||||
|
established = securityContext.isEstablished();
|
||||||
|
}
|
||||||
|
|
||||||
|
jaasContext.addToken(targetService, outToken);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new BadCredentialsException("Kerberos authentication failed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Oid createOid(String oid)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return new Oid(oid);
|
||||||
|
} catch (GSSException e) {
|
||||||
|
throw new IllegalStateException("Unable to instantiate Oid: ", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,10 +41,13 @@ import org.springframework.security.crypto.codec.Base64;
|
|||||||
*
|
*
|
||||||
* @author Mike Wiesner
|
* @author Mike Wiesner
|
||||||
* @author Jeremy Stone
|
* @author Jeremy Stone
|
||||||
|
* @author Bogdan Mustiata
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
* @see KerberosServiceAuthenticationProvider
|
* @see KerberosServiceAuthenticationProvider
|
||||||
*/
|
*/
|
||||||
public class KerberosServiceRequestToken extends AbstractAuthenticationToken {
|
public class KerberosServiceRequestToken
|
||||||
|
extends AbstractAuthenticationToken
|
||||||
|
implements KerberosAuthentication {
|
||||||
|
|
||||||
private static final long serialVersionUID = 395488921064775014L;
|
private static final long serialVersionUID = 395488921064775014L;
|
||||||
|
|
||||||
@@ -54,6 +57,8 @@ public class KerberosServiceRequestToken extends AbstractAuthenticationToken {
|
|||||||
|
|
||||||
private final transient KerberosTicketValidation ticketValidation;
|
private final transient KerberosTicketValidation ticketValidation;
|
||||||
|
|
||||||
|
private JaasSubjectHolder jaasSubjectHolder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an authenticated token, normally used as an output of an
|
* Creates an authenticated token, normally used as an output of an
|
||||||
* authentication provider.
|
* authentication provider.
|
||||||
@@ -64,12 +69,17 @@ public class KerberosServiceRequestToken extends AbstractAuthenticationToken {
|
|||||||
* @param token the Kerberos/SPNEGO token
|
* @param token the Kerberos/SPNEGO token
|
||||||
* @see UserDetails
|
* @see UserDetails
|
||||||
*/
|
*/
|
||||||
public KerberosServiceRequestToken(Object principal, KerberosTicketValidation ticketValidation,
|
public KerberosServiceRequestToken(Object principal,
|
||||||
Collection<? extends GrantedAuthority> authorities, byte[] token) {
|
KerberosTicketValidation ticketValidation,
|
||||||
|
Collection<? extends GrantedAuthority> authorities,
|
||||||
|
byte[] token) {
|
||||||
super(authorities);
|
super(authorities);
|
||||||
this.token = token;
|
this.token = token;
|
||||||
this.principal = principal;
|
this.principal = principal;
|
||||||
this.ticketValidation = ticketValidation;
|
this.ticketValidation = ticketValidation;
|
||||||
|
this.jaasSubjectHolder = new JaasSubjectHolder(
|
||||||
|
ticketValidation.subject(),
|
||||||
|
ticketValidation.username());
|
||||||
super.setAuthenticated(true);
|
super.setAuthenticated(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,4 +235,8 @@ public class KerberosServiceRequestToken extends AbstractAuthenticationToken {
|
|||||||
return encrypt(data, 0, data.length);
|
return encrypt(data, 0, data.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JaasSubjectHolder getJaasSubjectHolder() {
|
||||||
|
return jaasSubjectHolder;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,13 +13,24 @@ import org.ietf.jgss.GSSContext;
|
|||||||
public class KerberosTicketValidation {
|
public class KerberosTicketValidation {
|
||||||
|
|
||||||
private final String username;
|
private final String username;
|
||||||
|
private final Subject subject;
|
||||||
private final byte[] responseToken;
|
private final byte[] responseToken;
|
||||||
private final GSSContext gssContext;
|
private final GSSContext gssContext;
|
||||||
private final String servicePrincipal;
|
|
||||||
|
|
||||||
public KerberosTicketValidation(String username, String servicePrincipal, byte[] responseToken, GSSContext gssContext) {
|
public KerberosTicketValidation(String username, String servicePrincipal, byte[] responseToken, GSSContext gssContext) {
|
||||||
|
final HashSet<KerberosPrincipal> princs = new HashSet<KerberosPrincipal>();
|
||||||
|
princs.add(new KerberosPrincipal(servicePrincipal));
|
||||||
|
|
||||||
this.username = username;
|
this.username = username;
|
||||||
this.servicePrincipal = servicePrincipal;
|
this.subject = new Subject(false, princs, new HashSet<Object>(), new HashSet<Object>());
|
||||||
|
this.responseToken = responseToken;
|
||||||
|
this.gssContext = gssContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public KerberosTicketValidation(String username, Subject subject, byte[] responseToken, GSSContext gssContext) {
|
||||||
|
this.username = username;
|
||||||
|
this.subject = subject;
|
||||||
this.responseToken = responseToken;
|
this.responseToken = responseToken;
|
||||||
this.gssContext = gssContext;
|
this.gssContext = gssContext;
|
||||||
}
|
}
|
||||||
@@ -37,9 +48,7 @@ public class KerberosTicketValidation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Subject subject() {
|
public Subject subject() {
|
||||||
final HashSet<KerberosPrincipal> princs = new HashSet<KerberosPrincipal>();
|
return this.subject;
|
||||||
princs.add(new KerberosPrincipal(servicePrincipal));
|
|
||||||
return new Subject(false, princs, new HashSet<Object>(), new HashSet<Object>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.security.kerberos.authentication;
|
||||||
|
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Holds the Username/Password as well as the JAAS Subject allowing
|
||||||
|
* multi-tier authentications using Kerberos.</p>
|
||||||
|
*
|
||||||
|
* <p>The JAAS Subject has in its private credentials the Kerberos tickets
|
||||||
|
* for generating new tickets against other service principals using
|
||||||
|
* <code>KerberosMultiTier.authenticateService()</code></p>
|
||||||
|
*
|
||||||
|
* @author Bogdan Mustiata
|
||||||
|
* @see KerberosAuthenticationProvider
|
||||||
|
* @see KerberosMultiTier
|
||||||
|
*/
|
||||||
|
public class KerberosUsernamePasswordAuthenticationToken
|
||||||
|
extends UsernamePasswordAuthenticationToken
|
||||||
|
implements KerberosAuthentication {
|
||||||
|
|
||||||
|
private final JaasSubjectHolder jaasSubjectHolder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates an authentication token that holds the username and password, and the Subject
|
||||||
|
* that the user will need to create new authentication tokens against other services.</p>
|
||||||
|
* @param principal
|
||||||
|
* @param credentials
|
||||||
|
* @param authorities
|
||||||
|
* @param subjectHolder
|
||||||
|
*/
|
||||||
|
public KerberosUsernamePasswordAuthenticationToken(Object principal,
|
||||||
|
Object credentials,
|
||||||
|
Collection<? extends GrantedAuthority> authorities,
|
||||||
|
JaasSubjectHolder subjectHolder) {
|
||||||
|
super(principal, credentials, authorities);
|
||||||
|
this.jaasSubjectHolder = subjectHolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JaasSubjectHolder getJaasSubjectHolder() {
|
||||||
|
return jaasSubjectHolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.security.kerberos.authentication.sun;
|
||||||
|
|
||||||
|
import javax.security.auth.Subject;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JAAS utility functions.
|
||||||
|
* @author Bogdan Mustiata
|
||||||
|
*/
|
||||||
|
public class JaasUtil {
|
||||||
|
/**
|
||||||
|
* Copy the principal and the credentials into a new Subject.
|
||||||
|
* @param subject
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Subject copySubject(Subject subject) {
|
||||||
|
Subject subjectCopy = new Subject(false,
|
||||||
|
new HashSet<Principal>(subject.getPrincipals()),
|
||||||
|
new HashSet<Object>(subject.getPublicCredentials()),
|
||||||
|
new HashSet<Object>(subject.getPrivateCredentials()));
|
||||||
|
|
||||||
|
return subjectCopy;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ package org.springframework.security.kerberos.authentication.sun;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import javax.security.auth.Subject;
|
||||||
import javax.security.auth.callback.Callback;
|
import javax.security.auth.callback.Callback;
|
||||||
import javax.security.auth.callback.CallbackHandler;
|
import javax.security.auth.callback.CallbackHandler;
|
||||||
import javax.security.auth.callback.NameCallback;
|
import javax.security.auth.callback.NameCallback;
|
||||||
@@ -32,6 +33,7 @@ import org.apache.commons.logging.Log;
|
|||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
import org.springframework.security.kerberos.authentication.KerberosClient;
|
import org.springframework.security.kerberos.authentication.KerberosClient;
|
||||||
|
import org.springframework.security.kerberos.authentication.JaasSubjectHolder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of {@link KerberosClient} which uses the SUN JAAS
|
* Implementation of {@link KerberosClient} which uses the SUN JAAS
|
||||||
@@ -40,39 +42,55 @@ import org.springframework.security.kerberos.authentication.KerberosClient;
|
|||||||
* is needed.
|
* is needed.
|
||||||
*
|
*
|
||||||
* @author Mike Wiesner
|
* @author Mike Wiesner
|
||||||
|
* @author Bogdan Mustiata
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
public class SunJaasKerberosClient implements KerberosClient {
|
public class SunJaasKerberosClient implements KerberosClient {
|
||||||
|
|
||||||
private boolean debug = false;
|
private boolean debug = false;
|
||||||
|
private boolean multiTier = false;
|
||||||
|
|
||||||
private static final Log LOG = LogFactory.getLog(SunJaasKerberosClient.class);
|
private static final Log LOG = LogFactory.getLog(SunJaasKerberosClient.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String login(String username, String password) {
|
public JaasSubjectHolder login(String username, String password) {
|
||||||
LOG.debug("Trying to authenticate " + username + " with Kerberos");
|
LOG.debug("Trying to authenticate " + username + " with Kerberos");
|
||||||
String validatedUsername;
|
JaasSubjectHolder result;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
LoginContext loginContext = new LoginContext("", null, new KerberosClientCallbackHandler(username, password),
|
LoginContext loginContext = new LoginContext("", null,
|
||||||
|
new KerberosClientCallbackHandler(username, password),
|
||||||
new LoginConfig(this.debug));
|
new LoginConfig(this.debug));
|
||||||
loginContext.login();
|
loginContext.login();
|
||||||
|
|
||||||
|
Subject jaasSubject = loginContext.getSubject();
|
||||||
|
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("Kerberos authenticated user: "+loginContext.getSubject());
|
LOG.debug("Kerberos authenticated user: "+ jaasSubject);
|
||||||
|
}
|
||||||
|
|
||||||
|
String validatedUsername = jaasSubject.getPrincipals().iterator().next().toString();
|
||||||
|
Subject subjectCopy = JaasUtil.copySubject(jaasSubject);
|
||||||
|
result = new JaasSubjectHolder(subjectCopy, validatedUsername);
|
||||||
|
|
||||||
|
if (!multiTier) {
|
||||||
|
loginContext.logout();
|
||||||
}
|
}
|
||||||
validatedUsername = loginContext.getSubject().getPrincipals().iterator().next().toString();
|
|
||||||
loginContext.logout();
|
|
||||||
} catch (LoginException e) {
|
} catch (LoginException e) {
|
||||||
throw new BadCredentialsException("Kerberos authentication failed", e);
|
throw new BadCredentialsException("Kerberos authentication failed", e);
|
||||||
}
|
}
|
||||||
return validatedUsername;
|
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDebug(boolean debug) {
|
public void setDebug(boolean debug) {
|
||||||
this.debug = debug;
|
this.debug = debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setMultiTier(boolean multiTier) {
|
||||||
|
this.multiTier = multiTier;
|
||||||
|
}
|
||||||
|
|
||||||
private static class LoginConfig extends Configuration {
|
private static class LoginConfig extends Configuration {
|
||||||
private boolean debug;
|
private boolean debug;
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import javax.security.auth.login.AppConfigurationEntry;
|
|||||||
import javax.security.auth.login.Configuration;
|
import javax.security.auth.login.Configuration;
|
||||||
import javax.security.auth.login.LoginContext;
|
import javax.security.auth.login.LoginContext;
|
||||||
|
|
||||||
|
import com.sun.security.jgss.GSSUtil;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.ietf.jgss.GSSContext;
|
import org.ietf.jgss.GSSContext;
|
||||||
@@ -39,6 +40,8 @@ import org.springframework.beans.factory.InitializingBean;
|
|||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
|
import org.springframework.security.kerberos.authentication.JaasSubjectHolder;
|
||||||
|
import org.springframework.security.kerberos.authentication.KerberosMultiTier;
|
||||||
import org.springframework.security.kerberos.authentication.KerberosTicketValidation;
|
import org.springframework.security.kerberos.authentication.KerberosTicketValidation;
|
||||||
import org.springframework.security.kerberos.authentication.KerberosTicketValidator;
|
import org.springframework.security.kerberos.authentication.KerberosTicketValidator;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
@@ -51,21 +54,33 @@ import org.springframework.util.Assert;
|
|||||||
*
|
*
|
||||||
* @author Mike Wiesner
|
* @author Mike Wiesner
|
||||||
* @author Jeremy Stone
|
* @author Jeremy Stone
|
||||||
|
* @author Bogdan Mustiata
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
public class SunJaasKerberosTicketValidator implements KerberosTicketValidator, InitializingBean {
|
public class SunJaasKerberosTicketValidator implements KerberosTicketValidator, InitializingBean {
|
||||||
|
|
||||||
private String servicePrincipal;
|
private String servicePrincipal;
|
||||||
|
private String realmName;
|
||||||
private Resource keyTabLocation;
|
private Resource keyTabLocation;
|
||||||
private Subject serviceSubject;
|
private Subject serviceSubject;
|
||||||
private boolean holdOnToGSSContext;
|
private boolean holdOnToGSSContext;
|
||||||
private boolean debug = false;
|
private boolean debug = false;
|
||||||
|
private boolean multiTier = false;
|
||||||
private static final Log LOG = LogFactory.getLog(SunJaasKerberosTicketValidator.class);
|
private static final Log LOG = LogFactory.getLog(SunJaasKerberosTicketValidator.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KerberosTicketValidation validateTicket(byte[] token) {
|
public KerberosTicketValidation validateTicket(byte[] token) {
|
||||||
try {
|
try {
|
||||||
return Subject.doAs(this.serviceSubject, new KerberosValidateAction(token));
|
if (!multiTier) {
|
||||||
|
return Subject.doAs(this.serviceSubject, new KerberosValidateAction(token));
|
||||||
|
}
|
||||||
|
|
||||||
|
Subject subjectCopy = JaasUtil.copySubject(serviceSubject);
|
||||||
|
JaasSubjectHolder subjectHolder = new JaasSubjectHolder(subjectCopy);
|
||||||
|
|
||||||
|
return Subject.doAs(subjectHolder.getJaasSubject(),
|
||||||
|
new KerberosMultitierValidateAction(token));
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (PrivilegedActionException e) {
|
catch (PrivilegedActionException e) {
|
||||||
throw new BadCredentialsException("Kerberos validation not successful", e);
|
throw new BadCredentialsException("Kerberos validation not successful", e);
|
||||||
@@ -86,7 +101,11 @@ public class SunJaasKerberosTicketValidator implements KerberosTicketValidator,
|
|||||||
{
|
{
|
||||||
keyTabLocationAsString = keyTabLocationAsString.substring(5);
|
keyTabLocationAsString = keyTabLocationAsString.substring(5);
|
||||||
}
|
}
|
||||||
LoginConfig loginConfig = new LoginConfig(keyTabLocationAsString, this.servicePrincipal,
|
LoginConfig loginConfig = new LoginConfig(
|
||||||
|
keyTabLocationAsString,
|
||||||
|
this.servicePrincipal,
|
||||||
|
this.realmName,
|
||||||
|
this.multiTier,
|
||||||
this.debug);
|
this.debug);
|
||||||
Set<Principal> princ = new HashSet<Principal>(1);
|
Set<Principal> princ = new HashSet<Principal>(1);
|
||||||
princ.add(new KerberosPrincipal(this.servicePrincipal));
|
princ.add(new KerberosPrincipal(this.servicePrincipal));
|
||||||
@@ -108,6 +127,23 @@ public class SunJaasKerberosTicketValidator implements KerberosTicketValidator,
|
|||||||
this.servicePrincipal = servicePrincipal;
|
this.servicePrincipal = servicePrincipal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The realm name of the application.
|
||||||
|
* For web apps this is <code>DOMAIN</code>
|
||||||
|
* @param realmName
|
||||||
|
*/
|
||||||
|
public void setRealmName(String realmName) {
|
||||||
|
this.realmName = realmName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param multiTier
|
||||||
|
*/
|
||||||
|
public void setMultiTier(boolean multiTier) {
|
||||||
|
this.multiTier = multiTier;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>The location of the keytab. You can use the normale Spring Resource
|
* <p>The location of the keytab. You can use the normale Spring Resource
|
||||||
* prefixes like <code>file:</code> or <code>classpath:</code>, but as the
|
* prefixes like <code>file:</code> or <code>classpath:</code>, but as the
|
||||||
@@ -146,6 +182,69 @@ public class SunJaasKerberosTicketValidator implements KerberosTicketValidator,
|
|||||||
this.holdOnToGSSContext = holdOnToGSSContext;
|
this.holdOnToGSSContext = holdOnToGSSContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is needed, because the validation must run with previously generated JAAS subject
|
||||||
|
* which belongs to the service principal and was loaded out of the keytab during startup.
|
||||||
|
*/
|
||||||
|
private class KerberosMultitierValidateAction implements PrivilegedExceptionAction<KerberosTicketValidation> {
|
||||||
|
byte[] kerberosTicket;
|
||||||
|
|
||||||
|
public KerberosMultitierValidateAction(byte[] kerberosTicket) {
|
||||||
|
this.kerberosTicket = kerberosTicket;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KerberosTicketValidation run() throws Exception {
|
||||||
|
byte[] responseToken = new byte[0];
|
||||||
|
GSSManager manager = GSSManager.getInstance();
|
||||||
|
|
||||||
|
GSSName serverName =
|
||||||
|
manager.createName(servicePrincipal,
|
||||||
|
GSSName.NT_USER_NAME);
|
||||||
|
|
||||||
|
GSSCredential serverCreds =
|
||||||
|
manager.createCredential(serverName,
|
||||||
|
GSSCredential.INDEFINITE_LIFETIME,
|
||||||
|
KerberosMultiTier.KERBEROS_OID,
|
||||||
|
GSSCredential.INITIATE_AND_ACCEPT);
|
||||||
|
|
||||||
|
GSSContext context = manager.createContext(serverCreds);
|
||||||
|
|
||||||
|
boolean first = true;
|
||||||
|
while (!context.isEstablished()) {
|
||||||
|
if (first) {
|
||||||
|
kerberosTicket = tweakJdkRegression(kerberosTicket);
|
||||||
|
}
|
||||||
|
responseToken = context.acceptSecContext(kerberosTicket, 0, kerberosTicket.length);
|
||||||
|
serverName = context.getSrcName();
|
||||||
|
if (serverName == null) {
|
||||||
|
throw new BadCredentialsException("GSSContext name of the context initiator is null");
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Subject subject = GSSUtil.createSubject(
|
||||||
|
context.getSrcName(),
|
||||||
|
context.getDelegCred());
|
||||||
|
|
||||||
|
KerberosTicketValidation result = new KerberosTicketValidation(
|
||||||
|
context.getSrcName().toString(),
|
||||||
|
subject,
|
||||||
|
responseToken,
|
||||||
|
context);
|
||||||
|
|
||||||
|
if (!holdOnToGSSContext) {
|
||||||
|
context.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is needed, because the validation must run with previously generated JAAS subject
|
* This class is needed, because the validation must run with previously generated JAAS subject
|
||||||
* which belongs to the service principal and was loaded out of the keytab during startup.
|
* which belongs to the service principal and was loaded out of the keytab during startup.
|
||||||
@@ -159,28 +258,33 @@ public class SunJaasKerberosTicketValidator implements KerberosTicketValidator,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KerberosTicketValidation run() throws Exception {
|
public KerberosTicketValidation run() throws Exception {
|
||||||
byte[] responseToken = new byte[0];
|
byte[] responseToken = new byte[0];
|
||||||
GSSName gssName = null;
|
GSSName gssName = null;
|
||||||
GSSContext context = GSSManager.getInstance().createContext((GSSCredential) null);
|
GSSContext context = GSSManager.getInstance().createContext((GSSCredential) null);
|
||||||
boolean first = true;
|
boolean first = true;
|
||||||
while (!context.isEstablished()) {
|
while (!context.isEstablished()) {
|
||||||
if (first) {
|
if (first) {
|
||||||
kerberosTicket = tweakJdkRegression(kerberosTicket);
|
kerberosTicket = tweakJdkRegression(kerberosTicket);
|
||||||
}
|
}
|
||||||
responseToken = context.acceptSecContext(kerberosTicket, 0, kerberosTicket.length);
|
responseToken = context.acceptSecContext(kerberosTicket, 0, kerberosTicket.length);
|
||||||
gssName = context.getSrcName();
|
gssName = context.getSrcName();
|
||||||
if (gssName == null) {
|
if (gssName == null) {
|
||||||
throw new BadCredentialsException("GSSContext name of the context initiator is null");
|
throw new BadCredentialsException("GSSContext name of the context initiator is null");
|
||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
if (!holdOnToGSSContext) {
|
if (!holdOnToGSSContext) {
|
||||||
context.dispose();
|
context.dispose();
|
||||||
}
|
}
|
||||||
return new KerberosTicketValidation(gssName.toString(), servicePrincipal, responseToken, context);
|
return new KerberosTicketValidation(gssName.toString(),
|
||||||
|
servicePrincipal,
|
||||||
|
responseToken,
|
||||||
|
context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normally you need a JAAS config file in order to use the JAAS Kerberos Login Module,
|
* Normally you need a JAAS config file in order to use the JAAS Kerberos Login Module,
|
||||||
* with this class it is not needed and you can have different configurations in one JVM.
|
* with this class it is not needed and you can have different configurations in one JVM.
|
||||||
@@ -188,11 +292,15 @@ public class SunJaasKerberosTicketValidator implements KerberosTicketValidator,
|
|||||||
private static class LoginConfig extends Configuration {
|
private static class LoginConfig extends Configuration {
|
||||||
private String keyTabLocation;
|
private String keyTabLocation;
|
||||||
private String servicePrincipalName;
|
private String servicePrincipalName;
|
||||||
|
private String realmName;
|
||||||
|
private boolean multiTier;
|
||||||
private boolean debug;
|
private boolean debug;
|
||||||
|
|
||||||
public LoginConfig(String keyTabLocation, String servicePrincipalName, boolean debug) {
|
public LoginConfig(String keyTabLocation, String servicePrincipalName, String realmName, boolean multiTier, boolean debug) {
|
||||||
this.keyTabLocation = keyTabLocation;
|
this.keyTabLocation = keyTabLocation;
|
||||||
this.servicePrincipalName = servicePrincipalName;
|
this.servicePrincipalName = servicePrincipalName;
|
||||||
|
this.realmName = realmName;
|
||||||
|
this.multiTier = multiTier;
|
||||||
this.debug = debug;
|
this.debug = debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +315,14 @@ public class SunJaasKerberosTicketValidator implements KerberosTicketValidator,
|
|||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
options.put("debug", "true");
|
options.put("debug", "true");
|
||||||
}
|
}
|
||||||
options.put("isInitiator", "false");
|
|
||||||
|
if (this.realmName != null) {
|
||||||
|
options.put("realm", realmName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!multiTier) {
|
||||||
|
options.put("isInitiator", "false");
|
||||||
|
}
|
||||||
|
|
||||||
return new AppConfigurationEntry[] { new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule",
|
return new AppConfigurationEntry[] { new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule",
|
||||||
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options), };
|
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options), };
|
||||||
|
|||||||
@@ -29,8 +29,6 @@ import org.springframework.security.core.authority.AuthorityUtils;
|
|||||||
import org.springframework.security.core.userdetails.User;
|
import org.springframework.security.core.userdetails.User;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.kerberos.authentication.KerberosAuthenticationProvider;
|
|
||||||
import org.springframework.security.kerberos.authentication.KerberosClient;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test class for {@link KerberosAuthenticationProvider}
|
* Test class for {@link KerberosAuthenticationProvider}
|
||||||
@@ -49,6 +47,7 @@ public class KerberosAuthenticationProviderTest {
|
|||||||
private static final UsernamePasswordAuthenticationToken INPUT_TOKEN = new UsernamePasswordAuthenticationToken(TEST_USER, TEST_PASSWORD);
|
private static final UsernamePasswordAuthenticationToken INPUT_TOKEN = new UsernamePasswordAuthenticationToken(TEST_USER, TEST_PASSWORD);
|
||||||
private static final List<GrantedAuthority> AUTHORITY_LIST = AuthorityUtils.createAuthorityList("ROLE_ADMIN");
|
private static final List<GrantedAuthority> AUTHORITY_LIST = AuthorityUtils.createAuthorityList("ROLE_ADMIN");
|
||||||
private static final UserDetails USER_DETAILS = new User(TEST_USER, "empty", true, true, true,true, AUTHORITY_LIST);
|
private static final UserDetails USER_DETAILS = new User(TEST_USER, "empty", true, true, true,true, AUTHORITY_LIST);
|
||||||
|
private static final JaasSubjectHolder JAAS_SUBJECT_HOLDER = new JaasSubjectHolder(null, TEST_USER);
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void before() {
|
public void before() {
|
||||||
@@ -63,7 +62,7 @@ public class KerberosAuthenticationProviderTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testLoginOk() throws Exception {
|
public void testLoginOk() throws Exception {
|
||||||
when(userDetailsService.loadUserByUsername(TEST_USER)).thenReturn(USER_DETAILS);
|
when(userDetailsService.loadUserByUsername(TEST_USER)).thenReturn(USER_DETAILS);
|
||||||
when(kerberosClient.login(TEST_USER, TEST_PASSWORD)).thenReturn(TEST_USER);
|
when(kerberosClient.login(TEST_USER, TEST_PASSWORD)).thenReturn(JAAS_SUBJECT_HOLDER);
|
||||||
|
|
||||||
Authentication authenticate = provider.authenticate(INPUT_TOKEN);
|
Authentication authenticate = provider.authenticate(INPUT_TOKEN);
|
||||||
|
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ import java.util.UUID;
|
|||||||
*
|
*
|
||||||
* @author Original Hadoop MiniKdc Authors
|
* @author Original Hadoop MiniKdc Authors
|
||||||
* @author Janne Valkealahti
|
* @author Janne Valkealahti
|
||||||
|
* @author Bogdan Mustiata
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class MiniKdc {
|
public class MiniKdc {
|
||||||
@@ -539,4 +540,28 @@ public class MiniKdc {
|
|||||||
keytab.setEntries(entries);
|
keytab.setEntries(entries);
|
||||||
keytab.write(keytabFile);
|
keytab.write(keytabFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates multiple principals in the KDC and adds them to a keytab file.
|
||||||
|
*
|
||||||
|
* @param keytabFile keytab file to add the created principal.
|
||||||
|
* @param principal The principal to store in the keytab file.
|
||||||
|
* @param password The password for the principal.
|
||||||
|
* @throws Exception thrown if the principals or the keytab file could not be created.
|
||||||
|
*/
|
||||||
|
public void createKeyabFile(File keytabFile, String principal, String password) throws Exception {
|
||||||
|
Keytab keytab = new Keytab();
|
||||||
|
List<KeytabEntry> entries = new ArrayList<KeytabEntry>();
|
||||||
|
|
||||||
|
KerberosTime timestamp = new KerberosTime();
|
||||||
|
for (Map.Entry<EncryptionType, EncryptionKey> entry : KerberosKeyFactory.getKerberosKeys(principal,
|
||||||
|
password).entrySet()) {
|
||||||
|
EncryptionKey ekey = entry.getValue();
|
||||||
|
byte keyVersion = (byte) ekey.getKeyVersion();
|
||||||
|
entries.add(new KeytabEntry(principal, 1L, timestamp, keyVersion, ekey));
|
||||||
|
}
|
||||||
|
|
||||||
|
keytab.setEntries(entries);
|
||||||
|
keytab.write(keytabFile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user