Browse Source

OPEN - issue SEC-865: Re-Challenge NTLM Clients after Authentication Failure

http://jira.springframework.org/browse/SEC-865. Changed NTLM filter to re-challenge if retryOnAuthFailure is set and the Smb logon call fails. Updated JCIFS version in pom.
2.0.x
Luke Taylor 18 years ago
parent
commit
827d0e1ebf
  1. 88
      ntlm/pom.xml
  2. 797
      ntlm/src/main/java/org/springframework/security/ui/ntlm/NtlmProcessingFilter.java

88
ntlm/pom.xml

@ -1,36 +1,36 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.springframework.security</groupId> <groupId>org.springframework.security</groupId>
<artifactId>spring-security-parent</artifactId> <artifactId>spring-security-parent</artifactId>
<version>2.0.4-SNAPSHOT</version> <version>2.0.4-SNAPSHOT</version>
</parent> </parent>
<packaging>jar</packaging> <packaging>jar</packaging>
<artifactId>spring-security-ntlm</artifactId> <artifactId>spring-security-ntlm</artifactId>
<name>Spring Security - NTLM support</name> <name>Spring Security - NTLM support</name>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.springframework.security</groupId> <groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId> <artifactId>spring-security-core</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<!-- SMT NTLM--> <!-- SMT NTLM-->
<dependency> <dependency>
<groupId>org.samba.jcifs</groupId> <groupId>org.samba.jcifs</groupId>
<artifactId>jcifs</artifactId> <artifactId>jcifs</artifactId>
<version>1.2.15</version> <version>1.2.19</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>javax.servlet</groupId> <groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId> <artifactId>jsp-api</artifactId>
<version>2.0</version> <version>2.0</version>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>javax.servlet</groupId> <groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId> <artifactId>servlet-api</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.ldap</groupId> <groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap</artifactId> <artifactId>spring-ldap</artifactId>
@ -38,17 +38,17 @@
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>
<resources> <resources>
<resource> <resource>
<directory>${basedir}/src/main/resources</directory> <directory>${basedir}/src/main/resources</directory>
<targetPath>/</targetPath> <targetPath>/</targetPath>
<includes> <includes>
<include>**/*</include> <include>**/*</include>
</includes> </includes>
<filtering>false</filtering> <filtering>false</filtering>
</resource> </resource>
</resources> </resources>
</build> </build>
</project> </project>

797
ntlm/src/main/java/org/springframework/security/ui/ntlm/NtlmProcessingFilter.java

@ -82,221 +82,222 @@ import java.util.Properties;
* @version $Id$ * @version $Id$
*/ */
public class NtlmProcessingFilter extends SpringSecurityFilter implements InitializingBean { public class NtlmProcessingFilter extends SpringSecurityFilter implements InitializingBean {
//~ Static fields/initializers ===================================================================================== //~ Static fields/initializers =====================================================================================
private static Log logger = LogFactory.getLog(NtlmProcessingFilter.class); private static Log logger = LogFactory.getLog(NtlmProcessingFilter.class);
private static final String STATE_ATTR = "SpringSecurityNtlm"; private static final String STATE_ATTR = "SpringSecurityNtlm";
private static final String CHALLENGE_ATTR = "NtlmChal"; private static final String CHALLENGE_ATTR = "NtlmChal";
private static final Integer BEGIN = new Integer(0); private static final Integer BEGIN = new Integer(0);
private static final Integer NEGOTIATE = new Integer(1); private static final Integer NEGOTIATE = new Integer(1);
private static final Integer COMPLETE = new Integer(2); private static final Integer COMPLETE = new Integer(2);
private static final Integer DELAYED = new Integer(3); private static final Integer DELAYED = new Integer(3);
//~ Instance fields ================================================================================================ //~ Instance fields ================================================================================================
/** Should the filter load balance among multiple domain controllers, default <code>false</code> */ /** Should the filter load balance among multiple domain controllers, default <code>false</code> */
private boolean loadBalance; private boolean loadBalance;
/** Should the domain name be stripped from the username, default <code>true</code> */ /** Should the domain name be stripped from the username, default <code>true</code> */
private boolean stripDomain = true; private boolean stripDomain = true;
/** Should the filter initiate NTLM negotiations, default <code>true</code> */ /** Should the filter initiate NTLM negotiations, default <code>true</code> */
private boolean forceIdentification = true; private boolean forceIdentification = true;
/** Should the filter retry NTLM on authorization failure, default <code>false</code> */ /** Should the filter retry NTLM on authorization failure, default <code>false</code> */
private boolean retryOnAuthFailure; private boolean retryOnAuthFailure;
private String soTimeout; private String soTimeout;
private String cachePolicy; private String cachePolicy;
private String defaultDomain; private String defaultDomain;
private String domainController; private String domainController;
private AuthenticationManager authenticationManager; private AuthenticationManager authenticationManager;
private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource(); private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();
//~ Methods ======================================================================================================== //~ Methods ========================================================================================================
/** /**
* Ensures an <code>AuthenticationManager</code> and authentication failure * Ensures an <code>AuthenticationManager</code> and authentication failure
* URL have been provided in the bean configuration file. * URL have been provided in the bean configuration file.
*/ */
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
Assert.notNull(this.authenticationManager, "An AuthenticationManager is required"); Assert.notNull(this.authenticationManager, "An AuthenticationManager is required");
// Default to 5 minutes if not already specified // Default to 5 minutes if not already specified
Config.setProperty("jcifs.smb.client.soTimeout", soTimeout == null ? "300000" : soTimeout); Config.setProperty("jcifs.smb.client.soTimeout", soTimeout == null ? "300000" : soTimeout);
// Default to 20 minutes if not already specified // Default to 20 minutes if not already specified
Config.setProperty("jcifs.netbios.cachePolicy", cachePolicy == null ? "1200" : cachePolicy); Config.setProperty("jcifs.netbios.cachePolicy", cachePolicy == null ? "1200" : cachePolicy);
if (domainController == null) { if (domainController == null) {
domainController = defaultDomain; domainController = defaultDomain;
} }
} }
/** /**
* Sets the <code>AuthenticationManager</code> to use. * Sets the <code>AuthenticationManager</code> to use.
* *
* @param authenticationManager the <code>AuthenticationManager</code> to use. * @param authenticationManager the <code>AuthenticationManager</code> to use.
*/ */
public void setAuthenticationManager(AuthenticationManager authenticationManager) { public void setAuthenticationManager(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager; this.authenticationManager = authenticationManager;
} }
/** /**
* The NT domain against which clients should be authenticated. If the SMB * The NT domain against which clients should be authenticated. If the SMB
* client username and password are also set, then preauthentication will * client username and password are also set, then preauthentication will
* be used which is necessary to initialize the SMB signing digest. SMB * be used which is necessary to initialize the SMB signing digest. SMB
* signatures are required by default on Windows 2003 domain controllers. * signatures are required by default on Windows 2003 domain controllers.
* *
* @param defaultDomain The name of the default domain. * @param defaultDomain The name of the default domain.
*/ */
public void setDefaultDomain(String defaultDomain) { public void setDefaultDomain(String defaultDomain) {
this.defaultDomain = defaultDomain; this.defaultDomain = defaultDomain;
Config.setProperty("jcifs.smb.client.domain", defaultDomain); Config.setProperty("jcifs.smb.client.domain", defaultDomain);
} }
/** /**
* Sets the SMB client username. * Sets the SMB client username.
* *
* @param smbClientUsername The SMB client username. * @param smbClientUsername The SMB client username.
*/ */
public void setSmbClientUsername(String smbClientUsername) { public void setSmbClientUsername(String smbClientUsername) {
Config.setProperty("jcifs.smb.client.username", smbClientUsername); Config.setProperty("jcifs.smb.client.username", smbClientUsername);
} }
/** /**
* Sets the SMB client password. * Sets the SMB client password.
* *
* @param smbClientPassword The SMB client password. * @param smbClientPassword The SMB client password.
*/ */
public void setSmbClientPassword(String smbClientPassword) { public void setSmbClientPassword(String smbClientPassword) {
Config.setProperty("jcifs.smb.client.password", smbClientPassword); Config.setProperty("jcifs.smb.client.password", smbClientPassword);
} }
/** /**
* Sets the SMB client SSN limit. When set to <code>1</code>, every * Sets the SMB client SSN limit. When set to <code>1</code>, every
* authentication is forced to use a separate transport. This effectively * authentication is forced to use a separate transport. This effectively
* ignores SMB signing requirements, however at the expense of reducing * ignores SMB signing requirements, however at the expense of reducing
* scalability. Preauthentication with a domain, username, and password is * scalability. Preauthentication with a domain, username, and password is
* the preferred method for working with servers that require signatures. * the preferred method for working with servers that require signatures.
* *
* @param smbClientSSNLimit The SMB client SSN limit. * @param smbClientSSNLimit The SMB client SSN limit.
*/ */
public void setSmbClientSSNLimit(String smbClientSSNLimit) { public void setSmbClientSSNLimit(String smbClientSSNLimit) {
Config.setProperty("jcifs.smb.client.ssnLimit", smbClientSSNLimit); Config.setProperty("jcifs.smb.client.ssnLimit", smbClientSSNLimit);
} }
/** /**
* Configures JCIFS to use a WINS server. It is preferred to use a WINS * Configures JCIFS to use a WINS server. It is preferred to use a WINS
* server over a specific domain controller. Set this property instead of * server over a specific domain controller. Set this property instead of
* <code>domainController</code> if there is a WINS server available. * <code>domainController</code> if there is a WINS server available.
* *
* @param netbiosWINS The WINS server JCIFS will use. * @param netbiosWINS The WINS server JCIFS will use.
*/ */
public void setNetbiosWINS(String netbiosWINS) { public void setNetbiosWINS(String netbiosWINS) {
Config.setProperty("jcifs.netbios.wins", netbiosWINS); Config.setProperty("jcifs.netbios.wins", netbiosWINS);
} }
/** /**
* The IP address of any SMB server that should be used to authenticate * The IP address of any SMB server that should be used to authenticate
* HTTP clients. * HTTP clients.
* *
* @param domainController The IP address of the domain controller. * @param domainController The IP address of the domain controller.
*/ */
public void setDomainController(String domainController) { public void setDomainController(String domainController) {
this.domainController = domainController; this.domainController = domainController;
} }
/** /**
* If the default domain is specified and the domain controller is not * If the default domain is specified and the domain controller is not
* specified, then query for domain controllers by name. When load * specified, then query for domain controllers by name. When load
* balance is <code>true</code>, rotate through the list of domain * balance is <code>true</code>, rotate through the list of domain
* controllers when authenticating users. * controllers when authenticating users.
* *
* @param loadBalance The load balance flag value. * @param loadBalance The load balance flag value.
*/ */
public void setLoadBalance(boolean loadBalance) { public void setLoadBalance(boolean loadBalance) {
this.loadBalance = loadBalance; this.loadBalance = loadBalance;
} }
/** /**
* Configures <code>NtlmProcessingFilter</code> to strip the Windows * Configures <code>NtlmProcessingFilter</code> to strip the Windows
* domain name from the username when set to <code>true</code>, which * domain name from the username when set to <code>true</code>, which
* is the default value. * is the default value.
* *
* @param stripDomain The strip domain flag value. * @param stripDomain The strip domain flag value.
*/ */
public void setStripDomain(boolean stripDomain) { public void setStripDomain(boolean stripDomain) {
this.stripDomain = stripDomain; this.stripDomain = stripDomain;
} }
/** /**
* Sets the <code>jcifs.smb.client.soTimeout</code> property to the * Sets the <code>jcifs.smb.client.soTimeout</code> property to the
* timeout value specified in milliseconds. Defaults to 5 minutes * timeout value specified in milliseconds. Defaults to 5 minutes
* if not specified. * if not specified.
* *
* @param timeout The milliseconds timeout value. * @param timeout The milliseconds timeout value.
*/ */
public void setSoTimeout(String timeout) { public void setSoTimeout(String timeout) {
this.soTimeout = timeout; this.soTimeout = timeout;
} }
/** /**
* Sets the <code>jcifs.netbios.cachePolicy</code> property to the * Sets the <code>jcifs.netbios.cachePolicy</code> property to the
* number of seconds a NetBIOS address is cached by JCIFS. Defaults to * number of seconds a NetBIOS address is cached by JCIFS. Defaults to
* 20 minutes if not specified. * 20 minutes if not specified.
* *
* @param numSeconds The number of seconds a NetBIOS address is cached. * @param numSeconds The number of seconds a NetBIOS address is cached.
*/ */
public void setCachePolicy(String numSeconds) { public void setCachePolicy(String numSeconds) {
this.cachePolicy = numSeconds; this.cachePolicy = numSeconds;
} }
/** /**
* Loads properties starting with "jcifs" into the JCIFS configuration. * Loads properties starting with "jcifs" into the JCIFS configuration.
* Any other properties are ignored. * Any other properties are ignored.
* *
* @param props The JCIFS properties to set. * @param props The JCIFS properties to set.
*/ */
public void setJcifsProperties(Properties props) { public void setJcifsProperties(Properties props) {
String name; String name;
for (Enumeration e=props.keys(); e.hasMoreElements();) { for (Enumeration e=props.keys(); e.hasMoreElements();) {
name = (String) e.nextElement(); name = (String) e.nextElement();
if (name.startsWith("jcifs.")) { if (name.startsWith("jcifs.")) {
Config.setProperty(name, props.getProperty(name)); Config.setProperty(name, props.getProperty(name));
} }
} }
} }
/** /**
* Returns <code>true</code> if NTLM authentication is forced. * Returns <code>true</code> if NTLM authentication is forced.
* *
* @return <code>true</code> if NTLM authentication is forced. * @return <code>true</code> if NTLM authentication is forced.
*/ */
public boolean isForceIdentification() { public boolean isForceIdentification() {
return this.forceIdentification; return this.forceIdentification;
} }
/** /**
* Sets a flag denoting whether NTLM authentication should be forced. * Sets a flag denoting whether NTLM authentication should be forced.
* *
* @param forceIdentification the force identification flag value to set. * @param forceIdentification the force identification flag value to set.
*/ */
public void setForceIdentification(boolean forceIdentification) { public void setForceIdentification(boolean forceIdentification) {
this.forceIdentification = forceIdentification; this.forceIdentification = forceIdentification;
} }
/** /**
* Sets a flag denoting whether NTLM should retry whenever authentication * Sets a flag denoting whether NTLM should retry whenever authentication
* fails. Retry will only occur on an {@link AuthenticationCredentialsNotFoundException} * fails. Retry will occur if the credentials are rejected by the domain controller or if an
* or {@link InsufficientAuthenticationException}. * an {@link AuthenticationCredentialsNotFoundException}
* * or {@link InsufficientAuthenticationException} is thrown.
* @param retryOnFailure the retry on failure flag value to set. *
*/ * @param retryOnFailure the retry on failure flag value to set.
public void setRetryOnAuthFailure(boolean retryOnFailure) { */
this.retryOnAuthFailure = retryOnFailure; public void setRetryOnAuthFailure(boolean retryOnFailure) {
} this.retryOnAuthFailure = retryOnFailure;
}
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) { public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
Assert.notNull(authenticationDetailsSource, "authenticationDetailsSource cannot be null"); Assert.notNull(authenticationDetailsSource, "authenticationDetailsSource cannot be null");
@ -305,207 +306,213 @@ public class NtlmProcessingFilter extends SpringSecurityFilter implements Initia
protected void doFilterHttp(final HttpServletRequest request, protected void doFilterHttp(final HttpServletRequest request,
final HttpServletResponse response, final FilterChain chain) throws IOException, ServletException { final HttpServletResponse response, final FilterChain chain) throws IOException, ServletException {
final HttpSession session = request.getSession(); final HttpSession session = request.getSession();
Integer ntlmState = (Integer) session.getAttribute(STATE_ATTR); Integer ntlmState = (Integer) session.getAttribute(STATE_ATTR);
// Start NTLM negotiations the first time through the filter // Start NTLM negotiations the first time through the filter
if (ntlmState == null) { if (ntlmState == null) {
if (forceIdentification) { if (forceIdentification) {
logger.debug("Starting NTLM handshake"); logger.debug("Starting NTLM handshake");
session.setAttribute(STATE_ATTR, BEGIN); session.setAttribute(STATE_ATTR, BEGIN);
throw new NtlmBeginHandshakeException(); throw new NtlmBeginHandshakeException();
} else { } else {
logger.debug("NTLM handshake not yet started"); logger.debug("NTLM handshake not yet started");
session.setAttribute(STATE_ATTR, DELAYED); session.setAttribute(STATE_ATTR, DELAYED);
} }
} }
// IE will send a Type 1 message to reauthenticate the user during an HTTP POST // IE will send a Type 1 message to reauthenticate the user during an HTTP POST
if (ntlmState == COMPLETE && this.reAuthOnIEPost(request)) if (ntlmState == COMPLETE && this.reAuthOnIEPost(request))
ntlmState = BEGIN; ntlmState = BEGIN;
final String authMessage = request.getHeader("Authorization"); final String authMessage = request.getHeader("Authorization");
if (ntlmState != COMPLETE && authMessage != null && authMessage.startsWith("NTLM ")) { if (ntlmState != COMPLETE && authMessage != null && authMessage.startsWith("NTLM ")) {
final UniAddress dcAddress = this.getDCAddress(session); final UniAddress dcAddress = this.getDCAddress(session);
if (ntlmState == BEGIN) { if (ntlmState == BEGIN) {
logger.debug("Processing NTLM Type 1 Message"); logger.debug("Processing NTLM Type 1 Message");
session.setAttribute(STATE_ATTR, NEGOTIATE); session.setAttribute(STATE_ATTR, NEGOTIATE);
this.processType1Message(authMessage, session, dcAddress); this.processType1Message(authMessage, session, dcAddress);
} else { } else {
logger.debug("Processing NTLM Type 3 Message"); logger.debug("Processing NTLM Type 3 Message");
final NtlmPasswordAuthentication auth = this.processType3Message(authMessage, session, dcAddress); final NtlmPasswordAuthentication auth = this.processType3Message(authMessage, session, dcAddress);
logger.debug("NTLM negotiation complete"); logger.debug("NTLM negotiation complete");
this.logon(session, dcAddress, auth); this.logon(session, dcAddress, auth);
session.setAttribute(STATE_ATTR, COMPLETE); session.setAttribute(STATE_ATTR, COMPLETE);
// Do not reauthenticate the user in Spring Security during an IE POST // Do not reauthenticate the user in Spring Security during an IE POST
final Authentication myCurrentAuth = SecurityContextHolder.getContext().getAuthentication(); final Authentication myCurrentAuth = SecurityContextHolder.getContext().getAuthentication();
if (myCurrentAuth == null || myCurrentAuth instanceof AnonymousAuthenticationToken) { if (myCurrentAuth == null || myCurrentAuth instanceof AnonymousAuthenticationToken) {
logger.debug("Authenticating user credentials"); logger.debug("Authenticating user credentials");
this.authenticate(request, response, session, auth); this.authenticate(request, response, session, auth);
} }
} }
} }
chain.doFilter(request, response); chain.doFilter(request, response);
} }
/** /**
* Returns <code>true</code> if reauthentication is needed on an IE POST. * Returns <code>true</code> if reauthentication is needed on an IE POST.
*/ */
private boolean reAuthOnIEPost(final HttpServletRequest request) { private boolean reAuthOnIEPost(final HttpServletRequest request) {
String ua = request.getHeader("User-Agent"); String ua = request.getHeader("User-Agent");
return (request.getMethod().equalsIgnoreCase("POST") && ua != null && ua.indexOf("MSIE") != -1); return (request.getMethod().equalsIgnoreCase("POST") && ua != null && ua.indexOf("MSIE") != -1);
} }
/** /**
* Creates and returns a Type 2 message from the provided Type 1 message. * Creates and returns a Type 2 message from the provided Type 1 message.
* *
* @param message the Type 1 message to process. * @param message the Type 1 message to process.
* @param session the <code>HTTPSession</code> object. * @param session the <code>HTTPSession</code> object.
* @param dcAddress the domain controller address. * @param dcAddress the domain controller address.
* @throws IOException * @throws IOException
*/ */
private void processType1Message(final String message, final HttpSession session, final UniAddress dcAddress) throws IOException { private void processType1Message(final String message, final HttpSession session, final UniAddress dcAddress) throws IOException {
final Type2Message type2msg = new Type2Message( final Type2Message type2msg = new Type2Message(
new Type1Message(Base64.decode(message.substring(5))), new Type1Message(Base64.decode(message.substring(5))),
this.getChallenge(session, dcAddress), this.getChallenge(session, dcAddress),
null); null);
throw new NtlmType2MessageException(Base64.encode(type2msg.toByteArray())); throw new NtlmType2MessageException(Base64.encode(type2msg.toByteArray()));
} }
/** /**
* Builds and returns an <code>NtlmPasswordAuthentication</code> object * Builds and returns an <code>NtlmPasswordAuthentication</code> object
* from the provided Type 3 message. * from the provided Type 3 message.
* *
* @param message the Type 3 message to process. * @param message the Type 3 message to process.
* @param session the <code>HTTPSession</code> object. * @param session the <code>HTTPSession</code> object.
* @param dcAddress the domain controller address. * @param dcAddress the domain controller address.
* @return an <code>NtlmPasswordAuthentication</code> object. * @return an <code>NtlmPasswordAuthentication</code> object.
* @throws IOException * @throws IOException
*/ */
private NtlmPasswordAuthentication processType3Message(final String message, final HttpSession session, final UniAddress dcAddress) throws IOException { private NtlmPasswordAuthentication processType3Message(final String message, final HttpSession session, final UniAddress dcAddress) throws IOException {
final Type3Message type3msg = new Type3Message(Base64.decode(message.substring(5))); final Type3Message type3msg = new Type3Message(Base64.decode(message.substring(5)));
final byte[] lmResponse = (type3msg.getLMResponse() != null) ? type3msg.getLMResponse() : new byte[0]; final byte[] lmResponse = (type3msg.getLMResponse() != null) ? type3msg.getLMResponse() : new byte[0];
final byte[] ntResponse = (type3msg.getNTResponse() != null) ? type3msg.getNTResponse() : new byte[0]; final byte[] ntResponse = (type3msg.getNTResponse() != null) ? type3msg.getNTResponse() : new byte[0];
return new NtlmPasswordAuthentication( return new NtlmPasswordAuthentication(
type3msg.getDomain(), type3msg.getDomain(),
type3msg.getUser(), type3msg.getUser(),
this.getChallenge(session, dcAddress), this.getChallenge(session, dcAddress),
lmResponse, lmResponse,
ntResponse); ntResponse);
} }
/** /**
* Checks the user credentials against the domain controller. * Checks the user credentials against the domain controller.
* *
* @param session the <code>HTTPSession</code> object. * @param session the <code>HTTPSession</code> object.
* @param dcAddress the domain controller address. * @param dcAddress the domain controller address.
* @param auth the <code>NtlmPasswordAuthentication</code> object. * @param auth the <code>NtlmPasswordAuthentication</code> object.
* @throws IOException * @throws IOException
*/ */
private void logon(final HttpSession session, final UniAddress dcAddress, final NtlmPasswordAuthentication auth) throws IOException { private void logon(final HttpSession session, final UniAddress dcAddress, final NtlmPasswordAuthentication auth) throws IOException {
try { try {
SmbSession.logon(dcAddress, auth); SmbSession.logon(dcAddress, auth);
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug(auth + " successfully authenticated against " + dcAddress); logger.debug(auth + " successfully authenticated against " + dcAddress);
} }
} catch(SmbAuthException e) { } catch(SmbAuthException e) {
logger.error("Credentials " + auth + " were not accepted by the domain controller " + dcAddress); logger.error("Credentials " + auth + " were not accepted by the domain controller " + dcAddress);
throw new BadCredentialsException("Bad NTLM credentials");
} finally { if (retryOnAuthFailure) {
if (loadBalance) logger.debug("Restarting NTLM authentication handshake");
session.removeAttribute(CHALLENGE_ATTR); session.setAttribute(STATE_ATTR, BEGIN);
} throw new NtlmBeginHandshakeException();
} }
/** throw new BadCredentialsException("Bad NTLM credentials");
* Authenticates the user credentials acquired from NTLM against the Spring } finally {
* Security <code>AuthenticationManager</code>. session.removeAttribute(CHALLENGE_ATTR);
* }
* @param request the <code>HttpServletRequest</code> object. }
* @param response the <code>HttpServletResponse</code> object.
* @param session the <code>HttpSession</code> object. /**
* @param auth the <code>NtlmPasswordAuthentication</code> object. * Authenticates the user credentials acquired from NTLM against the Spring
* @throws IOException * Security <code>AuthenticationManager</code>.
*/ *
private void authenticate(final HttpServletRequest request, final HttpServletResponse response, final HttpSession session, final NtlmPasswordAuthentication auth) throws IOException { * @param request the <code>HttpServletRequest</code> object.
final Authentication authResult; * @param response the <code>HttpServletResponse</code> object.
final UsernamePasswordAuthenticationToken authRequest; * @param session the <code>HttpSession</code> object.
final Authentication backupAuth; * @param auth the <code>NtlmPasswordAuthentication</code> object.
* @throws IOException
authRequest = new NtlmUsernamePasswordAuthenticationToken(auth, stripDomain); */
authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); private void authenticate(final HttpServletRequest request, final HttpServletResponse response, final HttpSession session, final NtlmPasswordAuthentication auth) throws IOException {
final Authentication authResult;
// Place the last username attempted into HttpSession for views final UsernamePasswordAuthenticationToken authRequest;
session.setAttribute(AuthenticationProcessingFilter.SPRING_SECURITY_LAST_USERNAME_KEY, authRequest.getName()); final Authentication backupAuth;
// Backup the current authentication in case of an AuthenticationException authRequest = new NtlmUsernamePasswordAuthenticationToken(auth, stripDomain);
backupAuth = SecurityContextHolder.getContext().getAuthentication(); authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
try { // Place the last username attempted into HttpSession for views
// Authenitcate the user with the authentication manager session.setAttribute(AuthenticationProcessingFilter.SPRING_SECURITY_LAST_USERNAME_KEY, authRequest.getName());
authResult = authenticationManager.authenticate(authRequest);
} catch (AuthenticationException failed) { // Backup the current authentication in case of an AuthenticationException
if (logger.isInfoEnabled()) { backupAuth = SecurityContextHolder.getContext().getAuthentication();
logger.info("Authentication request for user: " + authRequest.getName() + " failed: " + failed.toString());
} try {
// Authenitcate the user with the authentication manager
// Reset the backup Authentication object and rethrow the AuthenticationException authResult = authenticationManager.authenticate(authRequest);
SecurityContextHolder.getContext().setAuthentication(backupAuth); } catch (AuthenticationException failed) {
if (logger.isInfoEnabled()) {
if (retryOnAuthFailure && (failed instanceof AuthenticationCredentialsNotFoundException || failed instanceof InsufficientAuthenticationException)) { logger.info("Authentication request for user: " + authRequest.getName() + " failed: " + failed.toString());
logger.debug("Restart NTLM authentication handshake due to AuthenticationException"); }
session.setAttribute(STATE_ATTR, BEGIN);
throw new NtlmBeginHandshakeException(); // Reset the backup Authentication object and rethrow the AuthenticationException
} SecurityContextHolder.getContext().setAuthentication(backupAuth);
throw failed; if (retryOnAuthFailure && (failed instanceof AuthenticationCredentialsNotFoundException || failed instanceof InsufficientAuthenticationException)) {
} logger.debug("Restart NTLM authentication handshake due to AuthenticationException");
session.setAttribute(STATE_ATTR, BEGIN);
// Set the Authentication object with the valid authentication result throw new NtlmBeginHandshakeException();
SecurityContextHolder.getContext().setAuthentication(authResult); }
}
throw failed;
/** }
* Returns the domain controller address based on the <code>loadBalance</code>
* setting. // Set the Authentication object with the valid authentication result
* SecurityContextHolder.getContext().setAuthentication(authResult);
* @param session the <code>HttpSession</code> object. }
* @return the domain controller address.
* @throws UnknownHostException /**
* @throws SmbException * Returns the domain controller address based on the <code>loadBalance</code>
*/ * setting.
private UniAddress getDCAddress(final HttpSession session) throws UnknownHostException, SmbException { *
if (loadBalance) { * @param session the <code>HttpSession</code> object.
NtlmChallenge chal = (NtlmChallenge) session.getAttribute(CHALLENGE_ATTR); * @return the domain controller address.
if (chal == null) { * @throws UnknownHostException
chal = SmbSession.getChallengeForDomain(); * @throws SmbException
session.setAttribute(CHALLENGE_ATTR, chal); */
} private UniAddress getDCAddress(final HttpSession session) throws UnknownHostException, SmbException {
return chal.dc; if (loadBalance) {
} NtlmChallenge chal = (NtlmChallenge) session.getAttribute(CHALLENGE_ATTR);
if (chal == null) {
return UniAddress.getByName(domainController, true); chal = SmbSession.getChallengeForDomain();
} session.setAttribute(CHALLENGE_ATTR, chal);
}
/** return chal.dc;
* Returns the domain controller challenge based on the <code>loadBalance</code> }
* setting.
* return UniAddress.getByName(domainController, true);
* @param session the <code>HttpSession</code> object. }
* @param dcAddress the domain controller address.
* @return the domain controller challenge. /**
* @throws UnknownHostException * Returns the domain controller challenge based on the <code>loadBalance</code>
* @throws SmbException * setting.
*/ *
private byte[] getChallenge(final HttpSession session, final UniAddress dcAddress) throws UnknownHostException, SmbException { * @param session the <code>HttpSession</code> object.
if (loadBalance) { * @param dcAddress the domain controller address.
return ((NtlmChallenge) session.getAttribute(CHALLENGE_ATTR)).challenge; * @return the domain controller challenge.
* @throws UnknownHostException
* @throws SmbException
*/
private byte[] getChallenge(final HttpSession session, final UniAddress dcAddress) throws UnknownHostException, SmbException {
if (loadBalance) {
return ((NtlmChallenge) session.getAttribute(CHALLENGE_ATTR)).challenge;
} }
return SmbSession.getChallenge(dcAddress); return SmbSession.getChallenge(dcAddress);
} }
public int getOrder() { public int getOrder() {
return FilterChainOrder.NTLM_FILTER; return FilterChainOrder.NTLM_FILTER;

Loading…
Cancel
Save