Selaa lähdekoodia

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).

Luke Taylor 18 vuotta sitten
vanhempi
commit
292320bd33

+ 10 - 14
core/src/main/java/org/springframework/security/providers/UsernamePasswordAuthenticationToken.java

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

+ 17 - 9
ntlm/src/main/java/org/springframework/security/ui/ntlm/NtlmUsernamePasswordAuthenticationToken.java

@@ -18,32 +18,40 @@ package org.springframework.security.ui.ntlm;
 import jcifs.smb.NtlmPasswordAuthentication;
 
 import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.util.AuthorityUtils;
 
 /**
- * An NTLM-specific {@link UsernamePasswordAuthenticationToken} that allows
- * any provider to bypass the problem of an empty password since NTLM does
- * not retrieve the user's password from the PDC.
- * 
+ * An NTLM-specific {@link UsernamePasswordAuthenticationToken} that allows any provider to bypass the problem of an
+ * empty password since NTLM does not retrieve the user's password from the PDC.
+ *
  * @author Sylvain Mougenot
  */
 public class NtlmUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken {
 
 	private static final long serialVersionUID = 1L;
-	
-	/**
-	 * Spring Security often checks password ; but we do not have one. This is the replacement password 
+
+    /**
+     * 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
 	 */
 	public static final String DEFAULT_PASSWORD = "";
 
 	/**
 	 * Create an NTLM {@link UsernamePasswordAuthenticationToken} using the
 	 * JCIFS {@link NtlmPasswordAuthentication} object.
-	 * 
+	 *
 	 * @param ntlmAuth		The {@link NtlmPasswordAuthentication} object.
 	 * @param stripDomain	Uses just the username if <code>true</code>,
 	 * 						otherwise use the username and domain name.
 	 */
 	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);
 	}
 }

+ 4 - 0
ntlm/src/main/java/org/springframework/security/ui/ntlm/ldap/authenticator/NtlmAwareLdapAuthenticator.java

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

+ 50 - 0
ntlm/src/main/test/org/springframework/security/ui/ntlm/ldap/authenticator/NtlmAwareLdapAuthenticatorTests.java

@@ -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);
+    }
+
+
+}