2

Moin!

My attempts to authenticate a user via SSO with Spring Security 5 and Kerberos fail due to an exception from deep in the Kerberos code. I will first show the stack trace and the code causing it and then give additional information about my environment which might help to eliminate some possibilities.

Stack trace

WARN 3932 --- [apr-8080-exec-1] w.a.SpnegoAuthenticationProcessingFilter : Negotiate Header was invalid: Negotiate YIILSwYGKwYBBQUCoIILPzCCCzugMDAuBgkqhkiC9xIBAgIGCSqGSIb3EgECAgYKKwYBBAGCN[and so on]

org.springframework.security.authentication.BadCredentialsException: Kerberos validation not successful
at org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator.validateTicket(SunJaasKerberosTicketValidator.java:71) ~[spring-security-kerberos-core-1.0.1.RELEASE.jar:1.0.1.RELEASE]
at org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider.authenticate(KerberosServiceAuthenticationProvider.java:64) ~[spring-security-kerberos-core-1.0.1.RELEASE.jar:1.0.1.RELEASE]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174) ~[spring-security-core-5.1.1.RELEASE.jar:5.1.1.RELEASE]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:199) ~[spring-security-core-5.1.1.RELEASE.jar:5.1.1.RELEASE]
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:512) ~[spring-security-config-5.1.1.RELEASE.jar:5.1.1.RELEASE]
...
Caused by: java.security.PrivilegedActionException: null
at java.security.AccessController.doPrivileged(Native Method) ~[na:1.8.0_162]
at javax.security.auth.Subject.doAs(Subject.java:422) ~[na:1.8.0_162]
at org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator.validateTicket(SunJaasKerberosTicketValidator.java:68) ~[spring-security-kerberos-core-1.0.1.RELEASE.jar:1.0.1.RELEASE]
...
Caused by: org.ietf.jgss.GSSException: No valid credentials provided (Mechanism level: Failed to find any Kerberos credentails)
at sun.security.jgss.krb5.Krb5AcceptCredential.getInstance(Krb5AcceptCredential.java:87) ~[na:1.8.0_162]
at sun.security.jgss.krb5.Krb5MechFactory.getCredentialElement(Krb5MechFactory.java:127) ~[na:1.8.0_162]
at sun.security.jgss.krb5.Krb5MechFactory.getMechanismContext(Krb5MechFactory.java:198) ~[na:1.8.0_162]
  1. So there is a BadCredentialsException while my SunJaasKerberosTicketValidator is validating the SSO Ticket. This is just rethrowing a PrivilegedActionException coming from

    public KerberosTicketValidation validateTicket(byte[] token) {
    try {
        return Subject.doAs(this.serviceSubject, new KerberosValidateAction(token));
    }
    catch (PrivilegedActionException e) {
        throw new BadCredentialsException("Kerberos validation not successful", e);
    }
    

    }

  2. The PrivilegedActionException is hard to track as it comes from a native method java.security.AccessController.doPrivileged. I don't know the implementation. What I find interesting is that the PrivilegedActionException prints out as

    Caused by: java.security.PrivilegedActionException: null
    

    The PrivilegedActionException.toString method is

    public String toString() {
        String s = getClass().getName();
        return (exception != null) ? (s + ": " + exception.toString()) : s;
    }
    

    So the exception (the cause exception) is not null but it prints out as null...

  3. However The stack trace tells us that the root of the problem is a GSSException coming from the class Krb5AcceptCredential.

    if (creds == null)
        throw new GSSException(GSSException.NO_CRED, -1,"Failed to find any Kerberos credentails");
    

    And creds == null is because Krb5Util.getServiceCreds (see implementation) returns null without causing an exception.

This is how far I got until now. Now some additional information.

Creating the Ticket Validator in my WebSecurityConfig

    SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator(); 
    ticketValidator.setServicePrincipal("HTTP/host@REALM");

    FileSystemResource fs = new FileSystemResource("PATH_TO_KEYTAB");
    ticketValidator.setKeyTabLocation(fs);
    LOGGER.info(fs.exists()); // prints 'true'

Creating the KerberosServiceAuthenticationProvider

This is the configuration of the object which will throw BadCredentialsException.

    KerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();
    provider.setTicketValidator(sunJaasKerberosTicketValidator());
    provider.setUserDetailsService(myUserDetailService);
    provider.supports(KerberosServiceRequestToken.class);

I know SSO works

I have the luxury of being able to proof that the SSO infrastructor of my company works. The same server is running another app (Spring Security 4 with Kerberos) where a user can be successfully authenticated via SSO. So there is most likely something wrong with my setup.

I use Chrome by the way, but I also tested it with IE.

If you need additional information from my WebSecurityConfig or something else I will provide it. May the force be with you :-)

Other questions

This is what I found so far but these examples are slightly different.

Ruik
  • 1,222
  • 1
  • 20
  • 36
  • Still don't have a clue how to solve this issue? – Klapsa2503 Jun 15 '21 at 14:30
  • @Klapsa2503 - if you have the same issue, it's very helpful to provide the HTTP Request and Response details (including headers etc - curl -v equivalent). These issues usually crop up because the client isn't sending the Kerb ticket in the first place on the request. Sometimes it falls back to NTLM or just flat out doesn't add the ticket. Either way it's impossible to know without the request context – stringy05 Jun 17 '21 at 02:20

1 Answers1

2

It's been 2 years since I had that problem, but I remember, that I solved it... and forgot to post the solution here (my bad). Since the question got some new attention I'll try my best to remember.

I dug out the code and I think I rembered the problem. In the WebSecurityConfig there are some methods annotated as @Bean - and one was missing. I think is was the SunJaasKerberosTicketValidator. It is used in this class to configure the KerberosServiceAuthenticationProvider but Spring Security seems to use that Bean internally too - and fails if the bean is missing in the Spring Context.

Here is a (shortened) version of my code from back then. Check all methods annotated with @Bean if you have them too.

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder());
        auth.authenticationProvider(kerberosServiceAuthenticationProvider());  
    }

    @Override
    public void configure(WebSecurity web) {
        // ...
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //...
    }

    @Bean
    public SpnegoEntryPoint spnegoEntryPoint() {
        //...
    }

    private KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() {
        KerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();
        provider.setTicketValidator(ticketValidator());
        provider.setUserDetailsService(myUserDetailsService);
        return provider;
    }

    @Bean
    public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter() {
        SpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter();
        AuthenticationFailureHandler failureHandler =
            new SimpleUrlAuthenticationFailureHandler(AUTHENTIFICATION_FAILED_URL);
        filter.setFailureHandler(failureHandler);
        try {
            filter.setAuthenticationManager(authenticationManagerBean());
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
        return filter;
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public SunJaasKerberosTicketValidator ticketValidator() {
        SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();
        ticketValidator.setDebug(true);
        ticketValidator.setServicePrincipal(kerberosConfigMgmt.securityKerberosServicePrincipal());

        FileSystemResource fs = new FileSystemResource(kerberosConfigMgmt.securityKerberosKeyTapFileAbsolutePath());
        ticketValidator.setKeyTabLocation(fs);

        return ticketValidator;
    }

    @Bean(name = "authenticationSuccessHandler")
    public AuthenticationSuccessHandler authenticationSuccessHandler() {
        return new SimpleUrlAuthenticationSuccessHandler(STARTSEITE_URL);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}
Ruik
  • 1,222
  • 1
  • 20
  • 36