diff --git a/src/main/java/org/springframework/security/extensions/kerberos/KerberosServiceAuthenticationProvider.java b/src/main/java/org/springframework/security/extensions/kerberos/KerberosServiceAuthenticationProvider.java new file mode 100644 index 0000000..a22e5e1 --- /dev/null +++ b/src/main/java/org/springframework/security/extensions/kerberos/KerberosServiceAuthenticationProvider.java @@ -0,0 +1,38 @@ +package org.springframework.security.extensions.kerberos; + +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; + +public class KerberosServiceAuthenticationProvider implements + AuthenticationProvider { + + private KerberosTicketValidator ticketValidator; + private UserDetailsService userDetailsService; + + public void setUserDetailsService(UserDetailsService userDetailsService) { + this.userDetailsService = userDetailsService; + } + + public void setTicketValidator(KerberosTicketValidator ticketValidator) { + this.ticketValidator = ticketValidator; + } + + @Override + public Authentication authenticate(Authentication authentication) + throws AuthenticationException { + KerberosServiceRequestToken auth = (KerberosServiceRequestToken) authentication; + byte[] token = auth.getToken(); + String username = this.ticketValidator.validateTicket(token); + UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); + return new KerberosServiceRequestToken(userDetails, userDetails.getAuthorities(), token); + } + + @Override + public boolean supports(Class auth) { + return KerberosServiceRequestToken.class.isAssignableFrom(auth); + } + +} diff --git a/src/main/java/org/springframework/security/extensions/kerberos/KerberosServiceRequestToken.java b/src/main/java/org/springframework/security/extensions/kerberos/KerberosServiceRequestToken.java index 49a6f1e..7bbd4c1 100644 --- a/src/main/java/org/springframework/security/extensions/kerberos/KerberosServiceRequestToken.java +++ b/src/main/java/org/springframework/security/extensions/kerberos/KerberosServiceRequestToken.java @@ -16,7 +16,10 @@ package org.springframework.security.extensions.kerberos; +import java.util.List; + import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; /** * Holds the Kerberos/SPNEGO token for requesting a kerberized service Will @@ -30,9 +33,15 @@ import org.springframework.security.authentication.AbstractAuthenticationToken; public class KerberosServiceRequestToken extends AbstractAuthenticationToken { + public KerberosServiceRequestToken(Object principal, List authorities, byte[] token) { + super(authorities); + this.token = token; + this.principal = principal; + } + private static final long serialVersionUID = 395488921064775014L; private final byte[] token; - private final Object principal = null; + private final Object principal; /** * Creates an unauthenticated instance which should then be authenticated by @@ -44,6 +53,7 @@ public class KerberosServiceRequestToken extends AbstractAuthenticationToken { public KerberosServiceRequestToken(byte[] token) { super(null); this.token = token; + this.principal = null; } @Override diff --git a/src/main/java/org/springframework/security/extensions/kerberos/KerberosTicketValidator.java b/src/main/java/org/springframework/security/extensions/kerberos/KerberosTicketValidator.java new file mode 100644 index 0000000..e22ba97 --- /dev/null +++ b/src/main/java/org/springframework/security/extensions/kerberos/KerberosTicketValidator.java @@ -0,0 +1,7 @@ +package org.springframework.security.extensions.kerberos; + +public interface KerberosTicketValidator { + + public abstract String validateTicket(byte[] token); + +} \ No newline at end of file diff --git a/src/main/java/org/springframework/security/extensions/kerberos/SunJaasKerberosTicketValidator.java b/src/main/java/org/springframework/security/extensions/kerberos/SunJaasKerberosTicketValidator.java new file mode 100644 index 0000000..f6a6495 --- /dev/null +++ b/src/main/java/org/springframework/security/extensions/kerberos/SunJaasKerberosTicketValidator.java @@ -0,0 +1,112 @@ +package org.springframework.security.extensions.kerberos; + +import java.security.Principal; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +import javax.security.auth.Subject; +import javax.security.auth.kerberos.KerberosPrincipal; +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.Configuration; +import javax.security.auth.login.LoginContext; + +import org.ietf.jgss.GSSContext; +import org.ietf.jgss.GSSCredential; +import org.ietf.jgss.GSSManager; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.core.io.Resource; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.util.Assert; + +public class SunJaasKerberosTicketValidator implements KerberosTicketValidator, InitializingBean { + + private String servicePrincipal; + private Resource keyTabLocation; + private Subject serviceSubject; + + public String validateTicket(byte[] token) { + String username = null; + try { + username = Subject.doAs(this.serviceSubject, new KerberosValidateAction(token)); + } catch (PrivilegedActionException e) { + throw new BadCredentialsException("Kerberos validation not succesfull", e); + } + return username; + } + + + public void setServicePrincipal(String servicePrincipal) { + this.servicePrincipal = servicePrincipal; + } + + public void setKeyTabLocation(Resource keyTabLocation) { + this.keyTabLocation = keyTabLocation; + } + + public void setServiceSubject(Subject serviceSubject) { + this.serviceSubject = serviceSubject; + } + + @Override + public void afterPropertiesSet() throws Exception { + Assert.notNull(this.servicePrincipal, "ServicePrincipal muss gesetzt werden"); + Assert.notNull(this.keyTabLocation, "KeyTab muss gesetzt werden"); + LoginConfig loginConfig = new LoginConfig(this.keyTabLocation.getURL().toExternalForm(), servicePrincipal); + Set princ = new HashSet(1); + princ.add(new KerberosPrincipal(this.servicePrincipal)); + Subject sub = new Subject(false, princ, new HashSet(), new HashSet()); + LoginContext lc = new LoginContext("", sub, null, loginConfig); + lc.login(); + this.serviceSubject = lc.getSubject(); + } + + private static class KerberosValidateAction implements PrivilegedExceptionAction { + byte[] kerberosTicket; + + public KerberosValidateAction(byte[] kerberosTicket) { + this.kerberosTicket = kerberosTicket; + } + + @Override + public String run() throws Exception { + GSSContext context = GSSManager.getInstance().createContext((GSSCredential) null); + context.acceptSecContext(kerberosTicket, 0, kerberosTicket.length); + String user = context.getSrcName().toString(); + context.dispose(); + return user; + } + + } + + private static class LoginConfig extends Configuration { + private String keyTabLocation; + private String servicePrincipalName; + + public LoginConfig(String keyTabLocation, String servicePrincipalName) { + this.keyTabLocation = keyTabLocation; + this.servicePrincipalName = servicePrincipalName; + } + + @Override + public AppConfigurationEntry[] getAppConfigurationEntry(String name) { + HashMap options = new HashMap(); + options.put("useKeyTab", "true"); + options.put("keyTab", this.keyTabLocation); + options.put("principal", this.servicePrincipalName); + options.put("storeKey", "true"); + options.put("doNotPrompt", "true"); + options.put("debug", "true"); + options.put("isInitiator", "false"); + + return new AppConfigurationEntry[] { new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule", + AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options), }; + } + + } + + + +}