SEC-607: Changed NtlmUsernamePasswordAuthenticationToken to make authenticated=true the default state when an instance is created. NtlmAwareLdapAuthenticator now rejects tokens with authenticated=false (e.g. if the token has been passed remotely).

This commit is contained in:
Luke Taylor 2007-11-24 20:13:29 +00:00
parent 4f3a1739aa
commit 292320bd33
4 changed files with 81 additions and 23 deletions

View File

@ -20,9 +20,10 @@ import org.springframework.security.GrantedAuthority;
/** /**
* An {@link org.springframework.security.Authentication} implementation that is designed for simple presentation of a * An {@link org.springframework.security.Authentication} implementation that is designed for simple presentation of a
* username and password.<p>The <code>principal</code> and <code>credentials</code> should be set with an * username and password.
* <code>Object</code> that provides the respective property via its <code>Object.toString()</code> method. The * <p>The <code>principal</code> and <code>credentials</code> should be set with an <code>Object</code> that provides
* simplest such <code>Object</code> to use is <code>String</code>.</p> * the respective property via its <code>Object.toString()</code> method. The simplest such <code>Object</code> to use
* is <code>String</code>.</p>
* *
* @author Ben Alex * @author Ben Alex
* @version $Id$ * @version $Id$
@ -36,13 +37,11 @@ public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationT
//~ Constructors =================================================================================================== //~ Constructors ===================================================================================================
/** /**
* This constructor can be safely used by any code that wishes to create a * This constructor can be safely used by any code that wishes to create a
* <code>UsernamePasswordAuthenticationToken</code>, as the {@link * <code>UsernamePasswordAuthenticationToken</code>, as the {@link
* #isAuthenticated()} will return <code>false</code>. * #isAuthenticated()} will return <code>false</code>.
* *
* @param principal DOCUMENT ME!
* @param credentials DOCUMENT ME!
*/ */
public UsernamePasswordAuthenticationToken(Object principal, Object credentials) { public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
super(null); super(null);
@ -51,12 +50,10 @@ public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationT
setAuthenticated(false); setAuthenticated(false);
} }
/** /**
* This constructor should only be used by * This constructor should only be used by <code>AuthenticationManager</code> or <code>AuthenticationProvider</code>
* <code>AuthenticationManager</code> or * implementations that are satisfied with producing a trusted (ie {@link #isAuthenticated()} = <code>true</code>)
* <code>AuthenticationProvider</code> implementations that are satisfied * authentication token.
* with producing a trusted (ie {@link #isAuthenticated()} =
* <code>true</code>) authentication token.
* *
* @param principal * @param principal
* @param credentials * @param credentials
@ -79,8 +76,7 @@ public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationT
return this.principal; return this.principal;
} }
public void setAuthenticated(boolean isAuthenticated) public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
throws IllegalArgumentException {
if (isAuthenticated) { if (isAuthenticated) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Cannot set this token to trusted - use constructor containing GrantedAuthority[]s instead"); "Cannot set this token to trusted - use constructor containing GrantedAuthority[]s instead");

View File

@ -18,11 +18,12 @@ package org.springframework.security.ui.ntlm;
import jcifs.smb.NtlmPasswordAuthentication; import jcifs.smb.NtlmPasswordAuthentication;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken; import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.util.AuthorityUtils;
/** /**
* An NTLM-specific {@link UsernamePasswordAuthenticationToken} that allows * An NTLM-specific {@link UsernamePasswordAuthenticationToken} that allows any provider to bypass the problem of an
* any provider to bypass the problem of an empty password since NTLM does * empty password since NTLM does not retrieve the user's password from the PDC.
* not retrieve the user's password from the PDC.
* *
* @author Sylvain Mougenot * @author Sylvain Mougenot
*/ */
@ -30,6 +31,13 @@ public class NtlmUsernamePasswordAuthenticationToken extends UsernamePasswordAut
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/**
* Dummy authority array which is passed to the constructor of the parent class,
* ensuring that the "authenticated" property is set to "true" by default. See SEC-609.
*/
private static final GrantedAuthority[] NTLM_AUTHENTICATED =
AuthorityUtils.stringArrayToAuthorityArray(new String[] {"NTLM_AUTHENTICATED"});
/** /**
* Spring Security often checks password ; but we do not have one. This is the replacement password * Spring Security often checks password ; but we do not have one. This is the replacement password
*/ */
@ -44,6 +52,6 @@ public class NtlmUsernamePasswordAuthenticationToken extends UsernamePasswordAut
* otherwise use the username and domain name. * otherwise use the username and domain name.
*/ */
public NtlmUsernamePasswordAuthenticationToken(final NtlmPasswordAuthentication ntlmAuth, final boolean stripDomain) { public NtlmUsernamePasswordAuthenticationToken(final NtlmPasswordAuthentication ntlmAuth, final boolean stripDomain) {
super((stripDomain) ? ntlmAuth.getUsername() : ntlmAuth.getName(), DEFAULT_PASSWORD); super((stripDomain) ? ntlmAuth.getUsername() : ntlmAuth.getName(), DEFAULT_PASSWORD, NTLM_AUTHENTICATED);
} }
} }

View File

@ -69,6 +69,10 @@ public class NtlmAwareLdapAuthenticator extends BindAuthenticator {
return super.authenticate(authentication); return super.authenticate(authentication);
} }
if (!authentication.isAuthenticated()) {
throw new BadCredentialsException("Unauthenticated NTLM authentication token found");
}
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("authenticate(NtlmUsernamePasswordAuthenticationToken) - start"); //$NON-NLS-1$ logger.debug("authenticate(NtlmUsernamePasswordAuthenticationToken) - start"); //$NON-NLS-1$
} }

View File

@ -0,0 +1,50 @@
package org.springframework.security.ui.ntlm.ldap.authenticator;
import org.springframework.security.BadCredentialsException;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ui.ntlm.NtlmUsernamePasswordAuthenticationToken;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import jcifs.smb.NtlmPasswordAuthentication;
import org.junit.Test;
/**
* @author Luke Taylor
* @version $Id$
*/
public class NtlmAwareLdapAuthenticatorTests {
/**
* See SEC-609.
*/
@Test(expected = BadCredentialsException.class)
public void unauthenticatedTokenIsRejected() {
NtlmAwareLdapAuthenticator authenticator = new NtlmAwareLdapAuthenticator(
new DefaultSpringSecurityContextSource("ldap://blah"));
NtlmUsernamePasswordAuthenticationToken token = new NtlmUsernamePasswordAuthenticationToken(
new NtlmPasswordAuthentication("blah"), false);
token.setAuthenticated(false);
authenticator.authenticate(token);
}
@Test
public void authenticatedTokenIsAccepted() {
NtlmAwareLdapAuthenticator authenticator = new NtlmAwareLdapAuthenticator(new DefaultSpringSecurityContextSource("ldap://blah")) {
// mimic loading of user
protected DirContextOperations loadUser(String aUserDn, String aUserName) {
return new DirContextAdapter();
}
};
authenticator.setUserDnPatterns(new String[] {"somepattern"});
NtlmUsernamePasswordAuthenticationToken token = new NtlmUsernamePasswordAuthenticationToken(
new NtlmPasswordAuthentication("blah"), false);
authenticator.authenticate(token);
}
}