diff --git a/spring-security-kerberos-client/src/test/java/org/springframework/security/kerberos/client/TestMultiTierAuthentication.java b/spring-security-kerberos-client/src/test/java/org/springframework/security/kerberos/client/TestMultiTierAuthentication.java new file mode 100644 index 0000000..4d5a2c3 --- /dev/null +++ b/spring-security-kerberos-client/src/test/java/org/springframework/security/kerberos/client/TestMultiTierAuthentication.java @@ -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")); + + } + }; + } + +} diff --git a/spring-security-kerberos-client/src/test/resources/minikdc-krb5.conf b/spring-security-kerberos-client/src/test/resources/minikdc-krb5.conf index d118dd1..ed6c5d9 100644 --- a/spring-security-kerberos-client/src/test/resources/minikdc-krb5.conf +++ b/spring-security-kerberos-client/src/test/resources/minikdc-krb5.conf @@ -18,6 +18,7 @@ [libdefaults] default_realm = {0} udp_preference_limit = 1 + forwardable = true [realms] {0} = '{' diff --git a/spring-security-kerberos-client/src/test/resources/minikdc.ldiff b/spring-security-kerberos-client/src/test/resources/minikdc.ldiff index 603ccb5..e7cc011 100644 --- a/spring-security-kerberos-client/src/test/resources/minikdc.ldiff +++ b/spring-security-kerberos-client/src/test/resources/minikdc.ldiff @@ -44,4 +44,43 @@ sn: Service uid: ldap userPassword: secret krb5PrincipalName: ldap/${4}@${2}.${3} -krb5KeyVersionNumber: 0 \ No newline at end of file +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 diff --git a/spring-security-kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/JaasSubjectHolder.java b/spring-security-kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/JaasSubjectHolder.java new file mode 100644 index 0000000..7991ca9 --- /dev/null +++ b/spring-security-kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/JaasSubjectHolder.java @@ -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; + +/** + *
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.
+ * @author Bogdan Mustiata + * @see SunJaasKerberosClient + * @see org.springframework.security.kerberos.authentication.KerberosAuthenticationProvider + */ +public class JaasSubjectHolder { + private Subject jaasSubject; + private String username; + + private MapAllows creating tickets against other service principals storing the + * tickets in the KerberosAuthentication's JaasSubjectHolder.
+ * @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