diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/touch.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/touch.get.desc.xml
index 42a2e8263f..004b066fe2 100644
--- a/config/alfresco/templates/webscripts/org/alfresco/repository/touch.get.desc.xml
+++ b/config/alfresco/templates/webscripts/org/alfresco/repository/touch.get.desc.xml
@@ -3,6 +3,6 @@
SSO authentication touch point - return a simple 200 OK status
/touch
argument
- none
+ guest
none
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/webdav/auth/BaseKerberosAuthenticationFilter.java b/source/java/org/alfresco/repo/webdav/auth/BaseKerberosAuthenticationFilter.java
new file mode 100644
index 0000000000..d99a389be7
--- /dev/null
+++ b/source/java/org/alfresco/repo/webdav/auth/BaseKerberosAuthenticationFilter.java
@@ -0,0 +1,612 @@
+/*
+ * Copyright (C) 2006-2008 Alfresco Software Limited.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ * As a special exception to the terms and conditions of version 2.0 of
+ * the GPL, you may redistribute this Program in connection with Free/Libre
+ * and Open Source Software ("FLOSS") applications as described in Alfresco's
+ * FLOSS exception. You should have recieved a copy of the text describing
+ * the FLOSS exception, and it is also available here:
+ * http://www.alfresco.com/legal/licensing"
+ */
+
+package org.alfresco.repo.webdav.auth;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.security.Principal;
+import java.util.Vector;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import javax.security.sasl.RealmCallback;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.alfresco.jlan.server.auth.kerberos.KerberosDetails;
+import org.alfresco.jlan.server.auth.kerberos.SessionSetupPrivilegedAction;
+import org.alfresco.jlan.server.auth.spnego.NegTokenInit;
+import org.alfresco.jlan.server.auth.spnego.NegTokenTarg;
+import org.alfresco.jlan.server.auth.spnego.OID;
+import org.alfresco.jlan.server.auth.spnego.SPNEGO;
+import org.alfresco.repo.SessionUser;
+import org.alfresco.repo.security.authentication.AuthenticationException;
+import org.apache.commons.codec.binary.Base64;
+import org.ietf.jgss.Oid;
+
+/**
+ * Base class with common code and initialisation for Kerberos authentication filters.
+ *
+ * @author gkspencer
+ */
+public abstract class BaseKerberosAuthenticationFilter extends BaseSSOAuthenticationFilter implements CallbackHandler {
+
+ // Constants
+ //
+ // Default login configuration entry name
+
+ private static final String LoginConfigEntry = "AlfrescoHTTP";
+
+ // Kerberos settings
+ //
+ // Account name and password for server ticket
+ //
+ // The account name must be built from the HTTP server name, in the format :-
+ //
+ // HTTP/@
+
+ private String m_accountName;
+ private String m_password;
+
+ // Kerberos realm and KDC address
+
+ private String m_krbRealm;
+ private String m_krbKDC;
+
+ // Login configuration entry name
+
+ private String m_loginEntryName = LoginConfigEntry;
+
+ // Server login context
+
+ private LoginContext m_loginContext;
+
+ // SPNEGO NegTokenInit blob, sent to the client in the SMB negotiate response
+
+ private byte[] m_negTokenInit;
+
+ /**
+ * Initialize the filter
+ *
+ * @param args FilterConfig
+ * @exception ServletException
+ */
+ public void init(FilterConfig args) throws ServletException
+ {
+ // Call the base SSO filter initialization
+
+ super.init( args);
+
+ // Check if Kerberos is enabled, get the Kerberos KDC address
+
+ String kdcAddress = args.getInitParameter("KDC");
+
+ if (kdcAddress != null && kdcAddress.length() > 0)
+ {
+ // Set the Kerberos KDC address
+
+ m_krbKDC = kdcAddress;
+
+ // Get the Kerberos realm
+
+ String krbRealm = args.getInitParameter("Realm");
+ if ( krbRealm != null && krbRealm.length() > 0)
+ {
+ // Set the Kerberos realm
+
+ m_krbRealm = krbRealm;
+ }
+ else
+ throw new ServletException("Kerberos realm not specified");
+
+ // Get the HTTP service account password
+
+ String srvPassword = args.getInitParameter("Password");
+ if ( srvPassword != null && srvPassword.length() > 0)
+ {
+ // Set the HTTP service account password
+
+ m_password = srvPassword;
+ }
+ else
+ throw new ServletException("HTTP service account password not specified");
+
+ // Get the login configuration entry name
+
+ String loginEntry = args.getInitParameter("LoginEntry");
+
+ if ( loginEntry != null)
+ {
+ if ( loginEntry.length() > 0)
+ {
+ // Set the login configuration entry name to use
+
+ m_loginEntryName = loginEntry;
+ }
+ else
+ throw new ServletException("Invalid login entry specified");
+ }
+
+ // Get the local host name
+
+ String localName = null;
+
+ try
+ {
+ localName = InetAddress.getLocalHost().getCanonicalHostName();
+ }
+ catch ( UnknownHostException ex)
+ {
+ throw new ServletException( "Failed to get local host name");
+ }
+
+ // Create a login context for the HTTP server service
+
+ try
+ {
+ // Login the HTTP server service
+
+ m_loginContext = new LoginContext( m_loginEntryName, this);
+ m_loginContext.login();
+
+ // DEBUG
+
+ if ( getLogger().isDebugEnabled())
+ getLogger().debug( "HTTP Kerberos login successful");
+ }
+ catch ( LoginException ex)
+ {
+ // Debug
+
+ if ( getLogger().isErrorEnabled())
+ getLogger().error("HTTP Kerberos web filter error", ex);
+
+ throw new ServletException("Failed to login HTTP server service");
+ }
+
+ // Get the HTTP service account name from the subject
+
+ Subject subj = m_loginContext.getSubject();
+ Principal princ = subj.getPrincipals().iterator().next();
+
+ m_accountName = princ.getName();
+
+ // DEBUG
+
+ if ( getLogger().isDebugEnabled())
+ getLogger().debug("Logged on using principal " + m_accountName);
+
+ // Create the Oid list for the SPNEGO NegTokenInit, include NTLMSSP for fallback
+
+ Vector mechTypes = new Vector();
+
+ mechTypes.add(OID.KERBEROS5);
+ mechTypes.add(OID.MSKERBEROS5);
+
+ // Build the SPNEGO NegTokenInit blob
+
+ try
+ {
+ // Build the mechListMIC principle
+ //
+ // Note: This field is not as specified
+
+ String mecListMIC = null;
+
+ StringBuilder mic = new StringBuilder();
+ mic.append( localName);
+ mic.append("$@");
+ mic.append( m_krbRealm);
+
+ mecListMIC = mic.toString();
+
+ // Build the SPNEGO NegTokenInit that contains the authentication types that the HTTP server accepts
+
+ NegTokenInit negTokenInit = new NegTokenInit(mechTypes, mecListMIC);
+
+ // Encode the NegTokenInit blob
+
+ m_negTokenInit = negTokenInit.encode();
+ }
+ catch (IOException ex)
+ {
+ // Debug
+
+ if ( getLogger().isErrorEnabled())
+ getLogger().error("Error creating SPNEGO NegTokenInit blob", ex);
+
+ throw new ServletException("Failed to create SPNEGO NegTokenInit blob");
+ }
+ }
+ }
+
+ /**
+ * Run the filter
+ *
+ * @param sreq ServletRequest
+ * @param sresp ServletResponse
+ * @param chain FilterChain
+ * @exception IOException
+ * @exception ServletException
+ */
+ public void doFilter(ServletRequest sreq, ServletResponse sresp, FilterChain chain) throws IOException,
+ ServletException
+ {
+ // Get the HTTP request/response/session
+
+ HttpServletRequest req = (HttpServletRequest) sreq;
+ HttpServletResponse resp = (HttpServletResponse) sresp;
+
+ HttpSession httpSess = req.getSession(true);
+
+ // If a filter up the chain has marked the request as not requiring auth then respect it
+
+ if (req.getAttribute( NO_AUTH_REQUIRED) != null)
+ {
+ if ( getLogger().isDebugEnabled())
+ getLogger().debug("Authentication not required (filter), chaining ...");
+
+ // Chain to the next filter
+ chain.doFilter(sreq, sresp);
+ return;
+ }
+
+ // Check if there is an authorization header with an SPNEGO security blob
+
+ String authHdr = req.getHeader("Authorization");
+ boolean reqAuth = false;
+
+ if ( authHdr != null)
+ {
+ // Check for a Kerberos/SPNEGO authorization header
+
+ if ( authHdr.startsWith( "Negotiate"))
+ reqAuth = true;
+ else if ( authHdr.startsWith( "NTLM"))
+ {
+ if ( getLogger().isDebugEnabled())
+ getLogger().debug("Received NTLM logon from client");
+
+ // Restart the authentication
+
+ restartLoginChallenge(resp, httpSess);
+
+ chain.doFilter(sreq, sresp);
+ return;
+ }
+ }
+
+ // Check if the user is already authenticated
+
+ SessionUser user = getSessionUser( httpSess);
+
+ if ( user != null && reqAuth == false)
+ {
+ try
+ {
+ // Debug
+
+ if ( getLogger().isDebugEnabled())
+ getLogger().debug("User " + user.getUserName() + " validate ticket");
+
+ // Validate the user ticket
+
+ m_authService.validate( user.getTicket());
+ reqAuth = false;
+
+ // Filter validate hook
+ onValidate( req, httpSess);
+ }
+ catch (AuthenticationException ex)
+ {
+ if ( getLogger().isErrorEnabled())
+ getLogger().error("Failed to validate user " + user.getUserName(), ex);
+
+ reqAuth = true;
+ }
+ }
+
+ // If the user has been validated and we do not require re-authentication then continue to
+ // the next filter
+
+ if ( reqAuth == false && user != null)
+ {
+ // Debug
+
+ if ( getLogger().isDebugEnabled())
+ getLogger().debug("Authentication not required (user), chaining ...");
+
+ // Chain to the next filter
+
+ chain.doFilter(sreq, sresp);
+ return;
+ }
+
+ // Check the authorization header
+
+ if ( authHdr == null) {
+
+ // If ticket based logons are allowed, check for a ticket parameter
+
+ if ( allowsTicketLogons())
+ {
+ // Check if a ticket parameter has been specified in the reuqest
+
+ if ( checkForTicketParameter( req, httpSess))
+ {
+ // Chain to the next filter
+
+ chain.doFilter(sreq, sresp);
+ return;
+ }
+ }
+
+ // Debug
+
+ if ( getLogger().isDebugEnabled())
+ getLogger().debug("New Kerberos auth request from " + req.getRemoteHost() + " (" +
+ req.getRemoteAddr() + ":" + req.getRemotePort() + ")");
+
+ // Send back a request for SPNEGO authentication
+
+ restartLoginChallenge( resp, httpSess);
+ }
+ else
+ {
+ // Decode the received SPNEGO blob and validate
+
+ final byte[] spnegoByts = Base64.decodeBase64( authHdr.substring(10).getBytes());
+
+ // Check if the client sent an NTLMSSP blob
+
+ if ( isNTLMSSPBlob( spnegoByts, 0))
+ {
+ if ( getLogger().isDebugEnabled())
+ getLogger().debug( "Client sent an NTLMSSP security blob");
+
+ // Restart the authentication
+
+ restartLoginChallenge(resp, httpSess);
+ return;
+ }
+
+ // Check the received SPNEGO token type
+
+ int tokType = -1;
+
+ try
+ {
+ tokType = SPNEGO.checkTokenType( spnegoByts, 0, spnegoByts.length);
+ }
+ catch ( IOException ex)
+ {
+ }
+
+ // Check for a NegTokenInit blob
+
+ if ( tokType == SPNEGO.NegTokenInit)
+ {
+ // Parse the SPNEGO security blob to get the Kerberos ticket
+
+ NegTokenInit negToken = new NegTokenInit();
+
+ try
+ {
+ // Decode the security blob
+
+ negToken.decode( spnegoByts, 0, spnegoByts.length);
+
+ // Determine the authentication mechanism the client is using and logon
+
+ String oidStr = null;
+ if ( negToken.numberOfOids() > 0)
+ oidStr = negToken.getOidAt( 0).toString();
+
+ if ( oidStr != null && (oidStr.equals( OID.ID_MSKERBEROS5) || oidStr.equals(OID.ID_KERBEROS5)))
+ {
+ // Kerberos logon
+
+ if ( doKerberosLogon( negToken, req, resp, httpSess) != null)
+ {
+ // Allow the user to access the requested page
+
+ chain.doFilter( req, resp);
+ }
+ else
+ {
+ // Send back a request for SPNEGO authentication
+
+ restartLoginChallenge( resp, httpSess);
+ }
+ }
+ }
+ catch ( IOException ex)
+ {
+ // Log the error
+
+ if ( getLogger().isDebugEnabled())
+ getLogger().debug(ex);
+ }
+ }
+ else
+ {
+ // Unknown SPNEGO token type
+
+ if ( getLogger().isDebugEnabled())
+ getLogger().debug( "Unknown SPNEGO token type");
+
+ // Send back a request for SPNEGO authentication
+
+ restartLoginChallenge( resp, httpSess);
+ }
+ }
+ }
+
+ /**
+ * JAAS callback handler
+ *
+ * @param callbacks Callback[]
+ * @exception IOException
+ * @exception UnsupportedCallbackException
+ */
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException
+ {
+ // Process the callback list
+
+ for (int i = 0; i < callbacks.length; i++)
+ {
+ // Request for user name
+
+ if (callbacks[i] instanceof NameCallback)
+ {
+ NameCallback cb = (NameCallback) callbacks[i];
+ cb.setName(m_accountName);
+ }
+
+ // Request for password
+ else if (callbacks[i] instanceof PasswordCallback)
+ {
+ PasswordCallback cb = (PasswordCallback) callbacks[i];
+ cb.setPassword(m_password.toCharArray());
+ }
+
+ // Request for realm
+
+ else if (callbacks[i] instanceof RealmCallback)
+ {
+ RealmCallback cb = (RealmCallback) callbacks[i];
+ cb.setText(m_krbRealm);
+ }
+ else
+ {
+ throw new UnsupportedCallbackException(callbacks[i]);
+ }
+ }
+ }
+
+ /**
+ * Perform a Kerberos login and return an SPNEGO response
+ *
+ * @param negToken NegTokenInit
+ * @param req HttpServletRequest
+ * @param resp HttpServletResponse
+ * @param httpSess HttpSession
+ * @return NegTokenTarg
+ */
+ private final NegTokenTarg doKerberosLogon( NegTokenInit negToken, HttpServletRequest req, HttpServletResponse resp, HttpSession httpSess)
+ {
+ // Authenticate the user
+
+ KerberosDetails krbDetails = null;
+ NegTokenTarg negTokenTarg = null;
+
+ try
+ {
+ // Run the session setup as a privileged action
+
+ SessionSetupPrivilegedAction sessSetupAction = new SessionSetupPrivilegedAction( m_accountName, negToken.getMechtoken());
+ Object result = Subject.doAs( m_loginContext.getSubject(), sessSetupAction);
+
+ if ( result != null)
+ {
+ // Access the Kerberos response
+
+ krbDetails = (KerberosDetails) result;
+
+ // Create the NegTokenTarg response blob
+
+ negTokenTarg = new NegTokenTarg( SPNEGO.AcceptCompleted, OID.KERBEROS5, krbDetails.getResponseToken());
+
+ // Check if the user has been authenticated, if so then setup the user environment
+
+ if ( negTokenTarg != null)
+ {
+
+ // Create the user authentication context
+
+ SessionUser user = createUserEnvironment( httpSess, krbDetails.getUserName());
+
+ // Store the user
+
+ httpSess.setAttribute(AUTHENTICATION_USER, user);
+
+ // Debug
+
+ if ( getLogger().isDebugEnabled())
+ getLogger().debug("User " + user.getUserName() + " logged on via Kerberos");
+ }
+ }
+ else
+ {
+ // Debug
+
+ if ( getLogger().isDebugEnabled())
+ getLogger().debug( "No SPNEGO response, Kerberos logon failed");
+ }
+ }
+ catch (Exception ex)
+ {
+ // Log the error
+
+ if ( getLogger().isDebugEnabled())
+ getLogger().debug("Kerberos logon error", ex);
+ }
+
+ // Return the response SPNEGO blob
+
+ return negTokenTarg;
+ }
+
+ /**
+ * Restart the Kerberos logon process
+ *
+ * @param resp HttpServletResponse
+ * @param httpSess HttpSession
+ * @throws IOException
+ */
+ protected void restartLoginChallenge(HttpServletResponse resp, HttpSession session) throws IOException
+ {
+ // Force the logon to start again
+
+ resp.setHeader("WWW-Authenticate", "Negotiate");
+ resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+
+ resp.flushBuffer();
+ }
+}
diff --git a/source/java/org/alfresco/repo/webdav/auth/BaseNTLMAuthenticationFilter.java b/source/java/org/alfresco/repo/webdav/auth/BaseNTLMAuthenticationFilter.java
index d4963c192a..93b42534c9 100644
--- a/source/java/org/alfresco/repo/webdav/auth/BaseNTLMAuthenticationFilter.java
+++ b/source/java/org/alfresco/repo/webdav/auth/BaseNTLMAuthenticationFilter.java
@@ -25,19 +25,18 @@
package org.alfresco.repo.webdav.auth;
import java.io.IOException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Random;
-import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
-import javax.servlet.ServletContext;
import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@@ -46,40 +45,29 @@ import javax.transaction.UserTransaction;
import net.sf.acegisecurity.BadCredentialsException;
-import org.alfresco.filesys.ServerConfigurationBean;
import org.alfresco.jlan.server.auth.PasswordEncryptor;
import org.alfresco.jlan.server.auth.ntlm.NTLM;
import org.alfresco.jlan.server.auth.ntlm.NTLMLogonDetails;
+import org.alfresco.jlan.server.auth.ntlm.NTLMMessage;
import org.alfresco.jlan.server.auth.ntlm.NTLMv2Blob;
import org.alfresco.jlan.server.auth.ntlm.TargetInfo;
import org.alfresco.jlan.server.auth.ntlm.Type1NTLMMessage;
import org.alfresco.jlan.server.auth.ntlm.Type2NTLMMessage;
import org.alfresco.jlan.server.auth.ntlm.Type3NTLMMessage;
-import org.alfresco.jlan.server.auth.passthru.DomainMapping;
-import org.alfresco.jlan.server.config.SecurityConfigSection;
import org.alfresco.jlan.util.DataPacker;
-import org.alfresco.jlan.util.IPAddress;
import org.alfresco.repo.SessionUser;
-import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.repo.security.authentication.MD4PasswordEncoder;
import org.alfresco.repo.security.authentication.MD4PasswordEncoderImpl;
import org.alfresco.repo.security.authentication.NTLMMode;
import org.alfresco.repo.security.authentication.ntlm.NTLMPassthruToken;
-import org.alfresco.service.ServiceRegistry;
-import org.alfresco.service.cmr.repository.NodeService;
-import org.alfresco.service.cmr.security.AuthenticationService;
-import org.alfresco.service.cmr.security.PersonService;
-import org.alfresco.service.transaction.TransactionService;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
-import org.springframework.web.context.WebApplicationContext;
-import org.springframework.web.context.support.WebApplicationContextUtils;
/**
* Base class with common code and initialisation for NTLM authentication filters.
*/
-public abstract class BaseNTLMAuthenticationFilter implements Filter
+public abstract class BaseNTLMAuthenticationFilter extends BaseSSOAuthenticationFilter
{
// NTLM authentication session object names
public static final String NTLM_AUTH_SESSION = "_alfNTLMAuthSess";
@@ -109,22 +97,6 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
// NTLM flags to send to the client with the allowed logon types
private int m_ntlmFlags;
- // Servlet context, required to get authentication service
- protected ServletContext m_context;
-
- // File server configuration
- private ServerConfigurationBean m_srvConfig;
-
- // Security configuration section, for domain mappings
- private SecurityConfigSection m_secConfig;
-
- // Various services required by NTLM authenticator
- protected AuthenticationService m_authService;
- protected AuthenticationComponent m_authComponent;
- protected PersonService m_personService;
- protected NodeService m_nodeService;
- protected TransactionService m_transactionService;
-
// Password encryptor
private PasswordEncryptor m_encryptor = new PasswordEncryptor();
@@ -134,9 +106,6 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
// MD4 hash decoder
private MD4PasswordEncoder m_md4Encoder = new MD4PasswordEncoderImpl();
- // Local server name, from either the file servers config or DNS host name
- protected String m_srvName;
-
// Allow guest access
private boolean m_allowGuest = false;
@@ -155,88 +124,20 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
*/
public void init(FilterConfig args) throws ServletException
{
- // Save the servlet context, needed to get hold of the authentication service
- m_context = args.getServletContext();
-
- // Setup the authentication context
- WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(m_context);
-
- ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
- m_nodeService = serviceRegistry.getNodeService();
- m_transactionService = serviceRegistry.getTransactionService();
- m_authService = serviceRegistry.getAuthenticationService();
-
- m_authComponent = (AuthenticationComponent) ctx.getBean("AuthenticationComponent");
- m_personService = (PersonService) ctx.getBean("personService");
-
- m_srvConfig = (ServerConfigurationBean) ctx.getBean(ServerConfigurationBean.SERVER_CONFIGURATION);
-
- // Check that the authentication component supports the required mode
+ // Call the base SSO filter initialization
+
+ super.init( args);
+
+ // Check that the authentication component supports the required mode
+
if (m_authComponent.getNTLMMode() != NTLMMode.MD4_PROVIDER &&
m_authComponent.getNTLMMode() != NTLMMode.PASS_THROUGH)
{
throw new ServletException("Required authentication mode not available");
}
- // Get the local server name, try the file server config first
- if (m_srvConfig != null)
- {
- m_srvName = m_srvConfig.getServerName();
-
- if (m_srvName != null)
- {
- try
- {
- InetAddress resolved = InetAddress.getByName(m_srvName);
- if (resolved == null)
- {
- // failed to resolve the configured name
- m_srvName = m_srvConfig.getLocalServerName(true);
- }
- }
- catch (UnknownHostException ex)
- {
- if (getLogger().isErrorEnabled())
- getLogger().error("NTLM filter, error getting resolving host name", ex);
- }
- }
- else
- {
- m_srvName = m_srvConfig.getLocalServerName(true);
- }
-
- // Find the security configuration section
- m_secConfig = (SecurityConfigSection)m_srvConfig.getConfigSection(SecurityConfigSection.SectionName);
- }
- else
- {
- // Get the host name
- try
- {
- // Get the local host name
- m_srvName = InetAddress.getLocalHost().getHostName();
-
- // Strip any domain name
- int pos = m_srvName.indexOf(".");
- if (pos != -1)
- {
- m_srvName = m_srvName.substring(0, pos - 1);
- }
- }
- catch (UnknownHostException ex)
- {
- if (getLogger().isErrorEnabled())
- getLogger().error("NTLM filter, error getting local host name", ex);
- }
- }
-
- // Check if the server name is valid
- if (m_srvName == null || m_srvName.length() == 0)
- {
- throw new ServletException("Failed to get local server name");
- }
-
// Check if guest access is to be allowed
+
String guestAccess = args.getInitParameter("AllowGuest");
if (guestAccess != null)
{
@@ -247,6 +148,7 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
}
// Check if unknown users should be mapped to guest access
+
String mapUnknownToGuest = args.getInitParameter("MapUnknownUserToGuest");
if (mapUnknownToGuest != null)
{
@@ -258,6 +160,7 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
// Set the NTLM flags depending on the authentication component supporting MD4 passwords,
// or is using passthru auth
+
if (m_authComponent.getNTLMMode() == NTLMMode.MD4_PROVIDER && m_disableNTLMv2 == false)
{
// Allow the client to use an NTLMv2 logon
@@ -272,6 +175,179 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
}
}
+ /**
+ * Run the filter
+ *
+ * @param sreq ServletRequest
+ * @param sresp ServletResponse
+ * @param chain FilterChain
+ * @exception IOException
+ * @exception ServletException
+ */
+ public void doFilter(ServletRequest sreq, ServletResponse sresp, FilterChain chain) throws IOException,
+ ServletException
+ {
+ // Get the HTTP request/response/session
+ HttpServletRequest req = (HttpServletRequest) sreq;
+ HttpServletResponse resp = (HttpServletResponse) sresp;
+ HttpSession httpSess = req.getSession(true);
+
+ // If a filter up the chain has marked the request as not requiring auth then respect it
+
+ if (req.getAttribute( NO_AUTH_REQUIRED) != null)
+ {
+ if ( getLogger().isDebugEnabled())
+ getLogger().debug("Authentication not required (filter), chaining ...");
+
+ // Chain to the next filter
+ chain.doFilter(sreq, sresp);
+ return;
+ }
+
+ // Check if there is an authorization header with an NTLM security blob
+ String authHdr = req.getHeader(AUTHORIZATION);
+ boolean reqAuth = false;
+
+ // Check if an NTLM authorization header was received
+
+ if ( authHdr != null)
+ {
+ // Check for an NTLM authorization header
+
+ if ( authHdr.startsWith(AUTH_NTLM))
+ reqAuth = true;
+ else if ( authHdr.startsWith( "Negotiate"))
+ {
+ if ( getLogger().isDebugEnabled())
+ getLogger().debug("Received 'Negotiate' from client, may be SPNEGO/Kerberos logon");
+
+ // Restart the authentication
+
+ restartLoginChallenge(resp, httpSess);
+ return;
+ }
+ }
+
+ // Check if the user is already authenticated
+ SessionUser user = getSessionUser( httpSess);
+
+ if (user != null && reqAuth == false)
+ {
+ try
+ {
+ if (getLogger().isDebugEnabled())
+ getLogger().debug("User " + user.getUserName() + " validate ticket");
+
+ // Validate the user ticket
+ m_authService.validate(user.getTicket());
+ reqAuth = false;
+
+ // Filter validate hook
+ onValidate( req, httpSess);
+ }
+ catch (AuthenticationException ex)
+ {
+ if (getLogger().isErrorEnabled())
+ getLogger().error("Failed to validate user " + user.getUserName(), ex);
+
+ reqAuth = true;
+ }
+ }
+
+ // If the user has been validated and we do not require re-authentication then continue to
+ // the next filter
+ if (reqAuth == false && user != null)
+ {
+ if (getLogger().isDebugEnabled())
+ getLogger().debug("Authentication not required (user), chaining ...");
+
+ // Chain to the next filter
+ chain.doFilter(sreq, sresp);
+ return;
+ }
+
+ // Check if the login page is being accessed, do not intercept the login page
+ if (hasLoginPage() && req.getRequestURI().endsWith(getLoginPage()) == true)
+ {
+ if (getLogger().isDebugEnabled())
+ getLogger().debug("Login page requested, chaining ...");
+
+ // Chain to the next filter
+ chain.doFilter( sreq, sresp);
+ return;
+ }
+
+ // Check if the browser is Opera, if so then display the login page as Opera does not
+ // support NTLM and displays an error page if a request to use NTLM is sent to it
+ String userAgent = req.getHeader("user-agent");
+ if (userAgent != null && userAgent.indexOf("Opera ") != -1)
+ {
+ if (getLogger().isDebugEnabled())
+ getLogger().debug("Opera detected, redirecting to login page");
+
+ // If there is no login page configured (WebDAV) then just keep requesting the user details from the client
+
+ if ( hasLoginPage())
+ redirectToLoginPage(req, resp);
+ else
+ restartLoginChallenge(resp, httpSess);
+ return;
+ }
+
+ // Check the authorization header
+ if (authHdr == null)
+ {
+ // Check for a ticket based logon, if enabled
+
+ if ( allowsTicketLogons())
+ {
+ // Check if the request includes an authentication ticket
+
+ if ( checkForTicketParameter(req, httpSess)) {
+
+ // Authentication was bypassed using a ticket parameter
+
+ chain.doFilter(sreq, sresp);
+ return;
+ }
+ }
+
+ // DEBUG
+
+ if (getLogger().isDebugEnabled())
+ getLogger().debug("New NTLM auth request from " + req.getRemoteHost() + " (" +
+ req.getRemoteAddr() + ":" + req.getRemotePort() + ")");
+
+ // Send back a request for NTLM authentication
+ restartLoginChallenge(resp, httpSess);
+ }
+ else
+ {
+ // Decode the received NTLM blob and validate
+ final byte[] ntlmByts = Base64.decodeBase64(authHdr.substring(5).getBytes());
+ int ntlmTyp = NTLMMessage.isNTLMType(ntlmByts);
+ if (ntlmTyp == NTLM.Type1)
+ {
+ // Process the type 1 NTLM message
+ Type1NTLMMessage type1Msg = new Type1NTLMMessage(ntlmByts);
+ processType1(type1Msg, req, resp, httpSess);
+ }
+ else if (ntlmTyp == NTLM.Type3)
+ {
+ // Process the type 3 NTLM message
+ Type3NTLMMessage type3Msg = new Type3NTLMMessage(ntlmByts);
+ processType3(type3Msg, req, resp, httpSess, chain);
+ }
+ else
+ {
+ if (getLogger().isDebugEnabled())
+ getLogger().debug("NTLM blob not handled, redirecting to login page.");
+
+ redirectToLoginPage(req, resp);
+ }
+ }
+ }
+
/**
* Delete the servlet filter
*/
@@ -293,8 +369,8 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
{
Log logger = getLogger();
- if (logger.isDebugEnabled())
- logger.debug("Received type1 " + type1Msg);
+ if (getLogger().isDebugEnabled())
+ getLogger().debug("Received type1 " + type1Msg);
// Get the existing NTLM details
NTLMLogonDetails ntlmDetails = null;
@@ -314,8 +390,8 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
byte[] type2Bytes = cachedType2.getBytes();
String ntlmBlob = "NTLM " + new String(Base64.encodeBase64(type2Bytes));
- if (logger.isDebugEnabled())
- logger.debug("Sending cached NTLM type2 to client - " + cachedType2);
+ if (getLogger().isDebugEnabled())
+ getLogger().debug("Sending cached NTLM type2 to client - " + cachedType2);
// Send back a request for NTLM authentication
res.setHeader(WWW_AUTHENTICATE, ntlmBlob);
@@ -347,8 +423,8 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
domain = mapClientAddressToDomain(req.getRemoteAddr());
}
- if (logger.isDebugEnabled())
- logger.debug("Client domain " + domain);
+ if (getLogger().isDebugEnabled())
+ getLogger().debug("Client domain " + domain);
// Create an authentication token for the new logon
authToken = new NTLMPassthruToken(domain);
@@ -380,8 +456,8 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
session.setAttribute(NTLM_AUTH_DETAILS, ntlmDetails);
- if (logger.isDebugEnabled())
- logger.debug("Sending NTLM type2 to client - " + type2Msg);
+ if (getLogger().isDebugEnabled())
+ getLogger().debug("Sending NTLM type2 to client - " + type2Msg);
// Send back a request for NTLM authentication
byte[] type2Bytes = type2Msg.getBytes();
@@ -439,15 +515,7 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
if (ntlmPwd != null)
{
- if (ntlmPwd.length == cachedPwd.length)
- {
- authenticated = true;
- for (int i = 0; i < ntlmPwd.length; i++)
- {
- if (ntlmPwd[i] != cachedPwd[i])
- authenticated = false;
- }
- }
+ authenticated = Arrays.equals( cachedPwd, ntlmPwd);
}
if (logger.isDebugEnabled())
@@ -487,8 +555,8 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
// Indicate that the user has been authenticated
authenticated = true;
- if (logger.isDebugEnabled())
- logger.debug("Guest logon");
+ if (getLogger().isDebugEnabled())
+ getLogger().debug("Guest logon");
}
else
{
@@ -635,46 +703,6 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
}
}
- /**
- * Callback to get the specific impl of the Session User for a filter
- *
- * @return User from the session
- */
- protected abstract SessionUser getSessionUser(HttpSession session);
-
- /**
- * Callback to create the User environment as appropriate for a filter impl
- *
- * @param session
- * @param userName
- *
- * @return SessionUser impl
- *
- * @throws IOException
- * @throws ServletException
- */
- protected abstract SessionUser createUserEnvironment(HttpSession session, String userName)
- throws IOException, ServletException;
-
- /**
- * Callback executed on successful ticket validation during Type3 Message processing
- */
- protected abstract void onValidate(HttpServletRequest req, HttpSession session);
-
- /**
- * Callback executed on failed authentication of a user ticket during Type3 Message processing
- */
- protected abstract void onValidateFailed(HttpServletRequest req, HttpServletResponse res, HttpSession session)
- throws IOException;
-
- /**
- * Callback executed on completion of NTLM login
- *
- * @return true to continue filter chaining, false otherwise
- */
- protected abstract boolean onLoginComplete(HttpServletRequest req, HttpServletResponse res)
- throws IOException;
-
/**
* Validate the MD4 hash against local password
*
@@ -1030,6 +1058,8 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
}
/**
+ * Restart the NTLM logon process
+ *
* @param resp
* @param httpSess
* @throws IOException
@@ -1046,50 +1076,6 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
res.flushBuffer();
}
- /**
- * Map a client IP address to a domain
- *
- * @param clientIP String
- * @return String
- */
- protected final String mapClientAddressToDomain(String clientIP)
- {
- // Check if there are any domain mappings
- if (m_secConfig != null && m_secConfig.hasDomainMappings() == false)
- {
- return null;
- }
-
- if (m_secConfig != null)
- {
- // convert the client IP address to an integer value
- int clientAddr = IPAddress.parseNumericAddress(clientIP);
- for (DomainMapping domainMap : m_secConfig.getDomainMappings())
- {
- if (domainMap.isMemberOfDomain(clientAddr))
- {
- if (getLogger().isDebugEnabled())
- getLogger().debug("Mapped client IP " + clientIP + " to domain " + domainMap.getDomain());
-
- return domainMap.getDomain();
- }
- }
- }
-
- if (getLogger().isDebugEnabled())
- getLogger().debug("Failed to map client IP " + clientIP + " to a domain");
-
- // No domain mapping for the client address
- return null;
- }
-
- /**
- * Return the logger
- *
- * @return Log
- */
- protected abstract Log getLogger();
-
/**
* Disable NTLMv2 support, must be called from the implementation constructor
*/
diff --git a/source/java/org/alfresco/repo/webdav/auth/BaseSSOAuthenticationFilter.java b/source/java/org/alfresco/repo/webdav/auth/BaseSSOAuthenticationFilter.java
new file mode 100644
index 0000000000..d27cd0be03
--- /dev/null
+++ b/source/java/org/alfresco/repo/webdav/auth/BaseSSOAuthenticationFilter.java
@@ -0,0 +1,580 @@
+/*
+ * Copyright (C) 2005-2007 Alfresco Software Limited.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ * As a special exception to the terms and conditions of version 2.0 of
+ * the GPL, you may redistribute this Program in connection with Free/Libre
+ * and Open Source Software ("FLOSS") applications as described in Alfresco's
+ * FLOSS exception. You should have recieved a copy of the text describing
+ * the FLOSS exception, and it is also available here:
+ * http://www.alfresco.com/legal/licensing
+ */
+package org.alfresco.repo.webdav.auth;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.transaction.UserTransaction;
+
+import org.alfresco.filesys.ServerConfigurationBean;
+import org.alfresco.jlan.server.auth.ntlm.NTLM;
+import org.alfresco.jlan.server.auth.passthru.DomainMapping;
+import org.alfresco.jlan.server.config.SecurityConfigSection;
+import org.alfresco.jlan.util.IPAddress;
+import org.alfresco.model.ContentModel;
+import org.alfresco.repo.SessionUser;
+import org.alfresco.repo.security.authentication.AuthenticationComponent;
+import org.alfresco.repo.security.authentication.AuthenticationException;
+import org.alfresco.service.ServiceRegistry;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.cmr.security.AuthenticationService;
+import org.alfresco.service.cmr.security.PersonService;
+import org.alfresco.service.transaction.TransactionService;
+import org.apache.commons.logging.Log;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.context.support.WebApplicationContextUtils;
+
+/**
+ * Base class with common code and initialisation for single signon authentication filters.
+ *
+ * @author gkspencer
+ * @author kroast
+ */
+public abstract class BaseSSOAuthenticationFilter implements Filter
+{
+ // Constants
+ //
+ // Session value names
+ //
+ // Note: These values are copied from the AuthenticationHelper and LoginBean classes to avoid project dependencies
+
+ protected static final String AUTHENTICATION_USER = "_alfAuthTicket";
+ protected static final String LOGIN_EXTERNAL_AUTH = "_alfExternalAuth";
+
+ // Request level marker to indicate that authentication should not be processed
+ //
+ // Note: copied from the AbstractAuthenticationFilter to avoid project dependencies
+
+ protected static final String NO_AUTH_REQUIRED = "alfNoAuthRequired";
+
+ // Allow an authenitcation ticket to be passed as part of a request to bypass authentication
+
+ private static final String ARG_TICKET = "ticket";
+
+ // Servlet context, required to get authentication service
+
+ protected ServletContext m_context;
+
+ // File server configuration
+
+ private ServerConfigurationBean m_srvConfig;
+
+ // Security configuration section, for domain mappings
+
+ private SecurityConfigSection m_secConfig;
+
+ // Various services required by NTLM authenticator
+
+ protected AuthenticationService m_authService;
+ protected AuthenticationComponent m_authComponent;
+ protected PersonService m_personService;
+ protected NodeService m_nodeService;
+ protected TransactionService m_transactionService;
+
+ // Local server name, from either the file servers config or DNS host name
+
+ protected String m_srvName;
+
+ // Login page relative address, if null then login will loop until a valid login is received
+
+ private String m_loginPage;
+
+ // Indicate whether ticket based logons are supported
+
+ private boolean m_ticketLogons;
+
+ /**
+ * Initialize the filter
+ *
+ * @param args FilterConfig
+ *
+ * @exception ServletException
+ */
+ public void init(FilterConfig args) throws ServletException
+ {
+ // Save the servlet context, needed to get hold of the authentication service
+
+ m_context = args.getServletContext();
+
+ // Setup the authentication context
+
+ WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(m_context);
+
+ ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
+ m_nodeService = serviceRegistry.getNodeService();
+ m_transactionService = serviceRegistry.getTransactionService();
+ m_authService = serviceRegistry.getAuthenticationService();
+
+ m_authComponent = (AuthenticationComponent) ctx.getBean("AuthenticationComponent");
+ m_personService = (PersonService) ctx.getBean("personService");
+
+ m_srvConfig = (ServerConfigurationBean) ctx.getBean(ServerConfigurationBean.SERVER_CONFIGURATION);
+
+ // Get the local server name, try the file server config first
+
+ if (m_srvConfig != null)
+ {
+ m_srvName = m_srvConfig.getServerName();
+
+ if (m_srvName != null)
+ {
+ try
+ {
+ InetAddress resolved = InetAddress.getByName(m_srvName);
+ if (resolved == null)
+ {
+ // Failed to resolve the configured name
+
+ m_srvName = m_srvConfig.getLocalServerName(true);
+ }
+ }
+ catch (UnknownHostException ex)
+ {
+ if (getLogger().isErrorEnabled())
+ getLogger().error("NTLM filter, error resolving CIFS host name", ex);
+ }
+ }
+
+ // If we still do not have a name use the DNS name of the server, with the domain part removed
+
+ if ( m_srvName == null)
+ {
+ m_srvName = m_srvConfig.getLocalServerName(true);
+ }
+
+ // Find the security configuration section
+
+ m_secConfig = (SecurityConfigSection)m_srvConfig.getConfigSection(SecurityConfigSection.SectionName);
+ }
+ else
+ {
+ // Get the host name
+
+ try
+ {
+ // Get the local host name
+
+ m_srvName = InetAddress.getLocalHost().getHostName();
+
+ // Strip any domain name
+
+ int pos = m_srvName.indexOf(".");
+ if (pos != -1)
+ {
+ m_srvName = m_srvName.substring(0, pos - 1);
+ }
+ }
+ catch (UnknownHostException ex)
+ {
+ if (getLogger().isErrorEnabled())
+ getLogger().error("NTLM filter, error getting local host name", ex);
+ }
+ }
+
+ // Check if the server name is valid
+
+ if (m_srvName == null || m_srvName.length() == 0)
+ {
+ throw new ServletException("Failed to get local server name");
+ }
+ }
+
+ /**
+ * Delete the servlet filter
+ */
+ public void destroy()
+ {
+ }
+
+ /**
+ * Create the user object that will be stored in the session
+ *
+ * @param userName String
+ * @param ticket String
+ * @param personNode NodeRef
+ * @param homeSpace String
+ * @return SessionUser
+ */
+ protected abstract SessionUser createUserObject( String userName, String ticket, NodeRef personNode, String homeSpace);
+
+ /**
+ * Callback to get the specific impl of the Session User for a filter
+ *
+ * @return User from the session
+ */
+ protected SessionUser getSessionUser(HttpSession session)
+ {
+ return (SessionUser)session.getAttribute( AUTHENTICATION_USER);
+ }
+
+ /**
+ * Callback to create the User environment as appropriate for a filter impl
+ *
+ * @param session HttpSession
+ * @param userName String
+ * @return SessionUser
+ * @throws IOException
+ * @throws ServletException
+ */
+ protected SessionUser createUserEnvironment(HttpSession session, String userName)
+ throws IOException, ServletException
+ {
+ SessionUser user = null;
+
+ UserTransaction tx = m_transactionService.getUserTransaction();
+
+ try
+ {
+ tx.begin();
+
+ // Setup User object and Home space ID etc.
+
+ NodeRef personNodeRef = m_personService.getPerson(userName);
+
+ // Use the system user context to do the user lookup
+
+ m_authComponent.setCurrentUser(m_authComponent.getSystemUserName());
+
+ // User name should match the uid in the person entry found
+
+ m_authComponent.setSystemUserAsCurrentUser();
+ userName = (String) m_nodeService.getProperty(personNodeRef, ContentModel.PROP_USERNAME);
+
+ m_authComponent.setCurrentUser(userName);
+ String currentTicket = m_authService.getCurrentTicket();
+
+ NodeRef homeSpaceRef = (NodeRef) m_nodeService.getProperty(personNodeRef, ContentModel.PROP_HOMEFOLDER);
+
+ // Create the user object to be stored in the session
+
+ user = createUserObject( userName, currentTicket, personNodeRef, homeSpaceRef.getId());
+
+ tx.commit();
+ }
+ catch (Throwable ex)
+ {
+ try
+ {
+ tx.rollback();
+ }
+ catch (Exception err)
+ {
+ getLogger().error("Failed to rollback transaction", err);
+ }
+ if (ex instanceof RuntimeException)
+ {
+ throw (RuntimeException)ex;
+ }
+ else if (ex instanceof IOException)
+ {
+ throw (IOException)ex;
+ }
+ else if (ex instanceof ServletException)
+ {
+ throw (ServletException)ex;
+ }
+ else
+ {
+ throw new RuntimeException("Authentication setup failed", ex);
+ }
+ }
+
+ // Store the user on the session
+
+ session.setAttribute( AUTHENTICATION_USER, user);
+ session.setAttribute( LOGIN_EXTERNAL_AUTH, Boolean.TRUE);
+
+ return user;
+ }
+
+ /**
+ * Callback executed on successful ticket validation during Type3 Message processing
+ *
+ * @param req HttpServletReqeust
+ * @param session HttpSession
+ */
+ protected void onValidate(HttpServletRequest req, HttpSession session)
+ {
+ }
+
+ /**
+ * Callback executed on failed authentication of a user ticket during Type3 Message processing
+ *
+ * @param req HttpServletRequest
+ * @param res HttpServletResponse
+ * @param session HttpSession
+ */
+ protected void onValidateFailed(HttpServletRequest req, HttpServletResponse res, HttpSession session)
+ throws IOException
+ {
+ }
+
+ /**
+ * Callback executed on completion of NTLM login
+ *
+ * @param req HttpServletRequest
+ * @param res HttpServletResponse
+ * @return true to continue filter chaining, false otherwise
+ */
+ protected boolean onLoginComplete(HttpServletRequest req, HttpServletResponse res)
+ throws IOException
+ {
+ return true;
+ }
+
+ /**
+ * Map a client IP address to a domain
+ *
+ * @param clientIP String
+ * @return String
+ */
+ protected final String mapClientAddressToDomain(String clientIP)
+ {
+ // Check if there are any domain mappings
+
+ if (m_secConfig != null && m_secConfig.hasDomainMappings() == false)
+ {
+ return null;
+ }
+
+ if (m_secConfig != null)
+ {
+ // Convert the client IP address to an integer value
+
+ int clientAddr = IPAddress.parseNumericAddress(clientIP);
+ for (DomainMapping domainMap : m_secConfig.getDomainMappings())
+ {
+ if (domainMap.isMemberOfDomain(clientAddr))
+ {
+ if (getLogger().isDebugEnabled())
+ getLogger().debug("Mapped client IP " + clientIP + " to domain " + domainMap.getDomain());
+
+ return domainMap.getDomain();
+ }
+ }
+ }
+
+ if (getLogger().isDebugEnabled())
+ getLogger().debug("Failed to map client IP " + clientIP + " to a domain");
+
+ // No domain mapping for the client address
+
+ return null;
+ }
+
+ /**
+ * Check if the request has specified a ticket parameter to bypass the standard authentication
+ *
+ * @param req HttpServletRequest
+ * @param sess HttpSession
+ * @return boolean
+ */
+ protected boolean checkForTicketParameter( HttpServletRequest req, HttpSession sess)
+ {
+ // Check if the request includes an authentication ticket
+
+ boolean ticketValid = false;
+ String ticket = req.getParameter(ARG_TICKET);
+
+ if (ticket != null && ticket.length() != 0)
+ {
+ if (getLogger().isDebugEnabled())
+ getLogger().debug("Logon via ticket from " + req.getRemoteHost() + " (" +
+ req.getRemoteAddr() + ":" + req.getRemotePort() + ")" + " ticket=" + ticket);
+
+ UserTransaction tx = null;
+ try
+ {
+ // Validate the ticket
+
+ m_authService.validate(ticket);
+
+ SessionUser user = getSessionUser( sess);
+
+ if ( user == null)
+ {
+ // Start a transaction
+
+ tx = m_transactionService.getUserTransaction();
+ tx.begin();
+
+ // Need to create the User instance if not already available
+
+ String currentUsername = m_authService.getCurrentUserName();
+
+ NodeRef personRef = m_personService.getPerson(currentUsername);
+ user = createUserObject( currentUsername, m_authService.getCurrentTicket(), personRef, null);
+
+ tx.commit();
+ tx = null;
+
+ // Store the User object in the Session - the authentication servlet will then proceed
+
+ req.getSession().setAttribute(AUTHENTICATION_USER, user);
+ }
+
+ // Indicate the ticket parameter was specified, and valid
+
+ ticketValid = true;
+ }
+ catch (AuthenticationException authErr)
+ {
+ if (getLogger().isDebugEnabled())
+ getLogger().debug("Failed to authenticate user ticket: " + authErr.getMessage(), authErr);
+ }
+ catch (Throwable e)
+ {
+ if (getLogger().isDebugEnabled())
+ getLogger().debug("Error during ticket validation and user creation: " + e.getMessage(), e);
+ }
+ finally
+ {
+ try
+ {
+ if (tx != null)
+ {
+ tx.rollback();
+ }
+ }
+ catch (Exception tex)
+ {
+ }
+ }
+ }
+
+ // Return the ticket parameter status
+
+ return ticketValid;
+ }
+
+ /**
+ * Redirect to the login page
+ *
+ * @param req HttpServletRequest
+ * @param req HttpServletResponse
+ * @exception IOException
+ */
+ protected void redirectToLoginPage(HttpServletRequest req, HttpServletResponse res)
+ throws IOException
+ {
+ if ( hasLoginPage())
+ res.sendRedirect(req.getContextPath() + "/faces" + getLoginPage());
+ }
+
+ /**
+ * Return the logger
+ *
+ * @return Log
+ */
+ protected abstract Log getLogger();
+
+ /**
+ * Determine if the login page is available
+ *
+ * @return boolean
+ */
+ protected final boolean hasLoginPage()
+ {
+ return m_loginPage != null ? true : false;
+ }
+
+ /**
+ * Return the login page address
+ *
+ * @return String
+ */
+ protected final String getLoginPage()
+ {
+ return m_loginPage;
+ }
+
+ /**
+ * Set the login page address
+ *
+ * @param loginPage String
+ */
+ protected final void setLoginPage( String loginPage)
+ {
+ m_loginPage = loginPage;
+ }
+
+ /**
+ * Check if ticket based logons are allowed
+ *
+ * @return boolean
+ */
+ protected final boolean allowsTicketLogons()
+ {
+ return m_ticketLogons;
+ }
+
+ /**
+ * Set the ticket based logons allowed flag
+ *
+ * @param ticketsAllowed boolean
+ */
+ protected final void setTicketLogons( boolean ticketsAllowed)
+ {
+ m_ticketLogons = ticketsAllowed;
+ }
+
+ /**
+ * Check if a security blob starts with the NTLMSSP signature
+ *
+ * @param byts byte[]
+ * @param offset int
+ * @return boolean
+ */
+ protected final boolean isNTLMSSPBlob( byte[] byts, int offset)
+ {
+ // Check if the blob has the NTLMSSP signature
+
+ boolean isNTLMSSP = false;
+
+ if (( byts.length - offset) >= NTLM.Signature.length) {
+
+ // Check for the NTLMSSP signature
+
+ int idx = 0;
+ while ( idx < NTLM.Signature.length && byts[offset + idx] == NTLM.Signature[ idx])
+ idx++;
+
+ if ( idx == NTLM.Signature.length)
+ isNTLMSSP = true;
+ }
+
+ return isNTLMSSP;
+ }
+}
diff --git a/source/java/org/alfresco/repo/webdav/auth/KerberosAuthenticationFilter.java b/source/java/org/alfresco/repo/webdav/auth/KerberosAuthenticationFilter.java
index 096be4df90..60646fdcea 100644
--- a/source/java/org/alfresco/repo/webdav/auth/KerberosAuthenticationFilter.java
+++ b/source/java/org/alfresco/repo/webdav/auth/KerberosAuthenticationFilter.java
@@ -25,135 +25,29 @@
package org.alfresco.repo.webdav.auth;
import java.io.IOException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.security.Principal;
-import java.util.Vector;
-import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-import javax.security.sasl.RealmCallback;
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
-import javax.servlet.ServletContext;
import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
-import javax.transaction.UserTransaction;
-import org.alfresco.filesys.ServerConfigurationBean;
-import org.alfresco.jlan.server.auth.kerberos.KerberosDetails;
-import org.alfresco.jlan.server.auth.kerberos.SessionSetupPrivilegedAction;
-import org.alfresco.jlan.server.auth.spnego.NegTokenInit;
-import org.alfresco.jlan.server.auth.spnego.NegTokenTarg;
-import org.alfresco.jlan.server.auth.spnego.OID;
-import org.alfresco.jlan.server.auth.spnego.SPNEGO;
-import org.alfresco.model.ContentModel;
-import org.alfresco.repo.security.authentication.AuthenticationComponent;
-import org.alfresco.repo.security.authentication.AuthenticationException;
-import org.alfresco.repo.security.authentication.AuthenticationUtil;
-import org.alfresco.repo.security.authentication.NTLMMode;
-import org.alfresco.service.ServiceRegistry;
-import org.alfresco.service.cmr.repository.InvalidNodeRefException;
+import org.alfresco.repo.SessionUser;
import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.cmr.repository.NodeService;
-import org.alfresco.service.cmr.security.AuthenticationService;
-import org.alfresco.service.cmr.security.PersonService;
-import org.alfresco.service.transaction.TransactionService;
-import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.ietf.jgss.Oid;
-import org.springframework.web.context.WebApplicationContext;
-import org.springframework.web.context.support.WebApplicationContextUtils;
/**
* WebDAV Kerberos Authentication Filter Class
*
* @author GKSpencer
*/
-public class KerberosAuthenticationFilter implements Filter, CallbackHandler
+public class KerberosAuthenticationFilter extends BaseKerberosAuthenticationFilter
{
- // Constants
- //
- // Default login configuration entry name
-
- private static final String LoginConfigEntry = "AlfrescoHTTP";
-
- // Authenticated user session object name
-
- public final static String AUTHENTICATION_USER = "_alfDAVAuthTicket";
-
- // Allow an authenitcation ticket to be passed as part of a request to bypass authentication
-
- private static final String ARG_TICKET = "ticket";
-
// Debug logging
private static Log logger = LogFactory.getLog(KerberosAuthenticationFilter.class);
-
- // Servlet context, required to get authentication service
-
- private ServletContext m_context;
-
- // File server configuration
-
- private ServerConfigurationBean m_srvConfig;
-
- // Various services required by the Kerberos authenticator
-
- private AuthenticationService m_authService;
- private AuthenticationComponent m_authComponent;
- private PersonService m_personService;
- private NodeService m_nodeService;
- private TransactionService m_transactionService;
-
- // Login page address
-
- private String m_loginPage;
- // Local server name, from either the file servers config or DNS host name
-
- private String m_srvName;
-
- // Kerberos settings
- //
- // Account name and password for server ticket
- //
- // The account name must be built from the HTTP server name, in the format :-
- //
- // HTTP/@
-
- private String m_accountName;
- private String m_password;
-
- // Kerberos realm and KDC address
-
- private String m_krbRealm;
- private String m_krbKDC;
-
- // Login configuration entry name
-
- private String m_loginEntryName = LoginConfigEntry;
-
- // Server login context
-
- private LoginContext m_loginContext;
-
- // SPNEGO NegTokenInit blob, sent to the client in the SMB negotiate response
-
- private byte[] m_negTokenInit;
-
/**
* Initialize the filter
*
@@ -162,698 +56,43 @@ public class KerberosAuthenticationFilter implements Filter, CallbackHandler
*/
public void init(FilterConfig args) throws ServletException
{
- // Save the servlet context, needed to get hold of the authentication service
-
- m_context = args.getServletContext();
-
- // Setup the authentication context
-
- WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(m_context);
-
- ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
- m_nodeService = serviceRegistry.getNodeService();
- m_transactionService = serviceRegistry.getTransactionService();
-
- m_authService = (AuthenticationService) ctx.getBean("AuthenticationService");
- m_authComponent = (AuthenticationComponent) ctx.getBean("AuthenticationComponent");
- m_personService = (PersonService) ctx.getBean("personService");
-
- m_srvConfig = (ServerConfigurationBean) ctx.getBean(ServerConfigurationBean.SERVER_CONFIGURATION);
-
- // Check that the authentication component supports the required mode
-
- if ( m_authComponent.getNTLMMode() != NTLMMode.MD4_PROVIDER &&
- m_authComponent.getNTLMMode() != NTLMMode.PASS_THROUGH)
- {
- throw new ServletException("Required authentication mode not available");
- }
-
- // Get the local server name, try the file server config first
-
- if ( m_srvConfig != null)
- {
- m_srvName = m_srvConfig.getServerName();
-
- if ( m_srvName == null)
- {
- // CIFS server may not be running so the local server name has not been set, generate
- // a server name
-
- m_srvName = m_srvConfig.getLocalServerName(true) + "_A";
- }
- }
- else
- {
- // Get the host name
-
- try
- {
- // Get the local host name
-
- m_srvName = InetAddress.getLocalHost().getHostName();
-
- // Strip any domain name
-
- int pos = m_srvName.indexOf(".");
- if ( pos != -1)
- m_srvName = m_srvName.substring(0, pos - 1);
- }
- catch (UnknownHostException ex)
- {
- // Log the error
-
- if ( logger.isErrorEnabled())
- logger.error("Kerberos filter, error getting local host name", ex);
- }
-
- }
-
- // Check if the server name is valid
-
- if ( m_srvName == null || m_srvName.length() == 0)
- throw new ServletException("Failed to get local server name");
-
- // Check if Kerberos is enabled, get the Kerberos KDC address
-
- String kdcAddress = args.getInitParameter("KDC");
-
- if (kdcAddress != null && kdcAddress.length() > 0)
- {
- // Set the Kerberos KDC address
-
- m_krbKDC = kdcAddress;
-
- // Get the Kerberos realm
-
- String krbRealm = args.getInitParameter("Realm");
- if ( krbRealm != null && krbRealm.length() > 0)
- {
- // Set the Kerberos realm
-
- m_krbRealm = krbRealm;
- }
- else
- throw new ServletException("Kerberos realm not specified");
-
- // Get the HTTP service account password
-
- String srvPassword = args.getInitParameter("Password");
- if ( srvPassword != null && srvPassword.length() > 0)
- {
- // Set the HTTP service account password
-
- m_password = srvPassword;
- }
- else
- throw new ServletException("HTTP service account password not specified");
-
- // Get the login configuration entry name
-
- String loginEntry = args.getInitParameter("LoginEntry");
-
- if ( loginEntry != null)
- {
- if ( loginEntry.length() > 0)
- {
- // Set the login configuration entry name to use
-
- m_loginEntryName = loginEntry;
- }
- else
- throw new ServletException("Invalid login entry specified");
- }
-
- // Get the local host name
-
- String localName = null;
-
- try
- {
- localName = InetAddress.getLocalHost().getCanonicalHostName();
- }
- catch ( UnknownHostException ex)
- {
- throw new ServletException( "Failed to get local host name");
- }
-
- // Create a login context for the HTTP server service
-
- try
- {
- // Login the HTTP server service
-
- m_loginContext = new LoginContext( m_loginEntryName, this);
- m_loginContext.login();
-
- // DEBUG
-
- if ( logger.isDebugEnabled())
- logger.debug( "HTTP Kerberos login successful");
- }
- catch ( LoginException ex)
- {
- // Debug
-
- if ( logger.isErrorEnabled())
- logger.error("HTTP Kerberos web filter error", ex);
-
- throw new ServletException("Failed to login HTTP server service");
- }
-
- // Get the HTTP service account name from the subject
-
- Subject subj = m_loginContext.getSubject();
- Principal princ = subj.getPrincipals().iterator().next();
-
- m_accountName = princ.getName();
-
- // DEBUG
-
- if ( logger.isDebugEnabled())
- logger.debug("Logged on using principal " + m_accountName);
-
- // Create the Oid list for the SPNEGO NegTokenInit, include NTLMSSP for fallback
-
- Vector mechTypes = new Vector();
-
- mechTypes.add(OID.KERBEROS5);
- mechTypes.add(OID.MSKERBEROS5);
-
- // Build the SPNEGO NegTokenInit blob
-
- try
- {
- // Build the mechListMIC principle
- //
- // Note: This field is not as specified
-
- String mecListMIC = null;
-
- StringBuilder mic = new StringBuilder();
- mic.append( localName);
- mic.append("$@");
- mic.append( m_krbRealm);
-
- mecListMIC = mic.toString();
-
- // Build the SPNEGO NegTokenInit that contains the authentication types that the HTTP server accepts
-
- NegTokenInit negTokenInit = new NegTokenInit(mechTypes, mecListMIC);
-
- // Encode the NegTokenInit blob
-
- m_negTokenInit = negTokenInit.encode();
- }
- catch (IOException ex)
- {
- // Debug
-
- if ( logger.isErrorEnabled())
- logger.error("Error creating SPNEGO NegTokenInit blob", ex);
-
- throw new ServletException("Failed to create SPNEGO NegTokenInit blob");
- }
- }
+ // Call the base Kerberos filter initialization
+
+ super.init( args);
+
+ // Enable ticket based logons
+
+ setTicketLogons( true);
}
- /**
- * Run the filter
- *
- * @param sreq ServletRequest
- * @param sresp ServletResponse
- * @param chain FilterChain
- * @exception IOException
- * @exception ServletException
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.webdav.auth.BaseSSOAuthenticationFilter#createUserObject(java.lang.String, java.lang.String, org.alfresco.service.cmr.repository.NodeRef, java.lang.String)
+ */
+ @Override
+ protected SessionUser createUserObject(String userName, String ticket, NodeRef personNode, String homeSpace) {
+
+ // Create a WebDAV user object
+
+ return new WebDAVUser( userName, ticket, personNode);
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.webdav.auth.BaseNTLMAuthenticationFilter#onValidateFailed(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.servlet.http.HttpSession)
*/
- public void doFilter(ServletRequest sreq, ServletResponse sresp, FilterChain chain) throws IOException,
- ServletException
- {
- // Get the HTTP request/response/session
-
- HttpServletRequest req = (HttpServletRequest) sreq;
- HttpServletResponse resp = (HttpServletResponse) sresp;
-
- HttpSession httpSess = req.getSession(true);
-
- // Check if there is an authorization header with an SPNEGO security blob
-
- String authHdr = req.getHeader("Authorization");
- boolean reqAuth = false;
-
- if ( authHdr != null && authHdr.startsWith("Negotiate"))
- reqAuth = true;
-
- // Check if the user is already authenticated
-
- WebDAVUser user = (WebDAVUser) httpSess.getAttribute( AUTHENTICATION_USER);
-
- if ( user != null && reqAuth == false)
- {
- try
- {
- // Debug
-
- if ( logger.isDebugEnabled())
- logger.debug("User " + user.getUserName() + " validate ticket");
-
- // Validate the user ticket
-
- m_authService.validate( user.getTicket());
- reqAuth = false;
- }
- catch (AuthenticationException ex)
- {
- if ( logger.isErrorEnabled())
- logger.error("Failed to validate user " + user.getUserName(), ex);
-
- reqAuth = true;
- }
- }
-
- // If the user has been validated and we do not require re-authentication then continue to
- // the next filter
-
- if ( reqAuth == false && user != null)
- {
- // Debug
-
- if ( logger.isDebugEnabled())
- logger.debug("Authentication not required, chaining ...");
-
- // Chain to the next filter
-
- chain.doFilter(sreq, sresp);
- return;
- }
-
- // Check the authorization header
-
- if ( authHdr == null) {
-
- // Check if the request includes an authentication ticket
-
- String ticket = req.getParameter( ARG_TICKET);
-
- if ( ticket != null && ticket.length() > 0)
- {
- // Debug
-
- if ( logger.isDebugEnabled())
- logger.debug("Logon via ticket from " + req.getRemoteHost() + " (" +
- req.getRemoteAddr() + ":" + req.getRemotePort() + ")" + " ticket=" + ticket);
-
- UserTransaction tx = null;
- try
- {
- // Validate the ticket
-
- m_authService.validate(ticket);
-
- // Need to create the User instance if not already available
-
- String currentUsername = m_authService.getCurrentUserName();
-
- // Start a transaction
-
- tx = m_transactionService.getUserTransaction();
- tx.begin();
-
- NodeRef personRef = m_personService.getPerson(currentUsername);
- user = new WebDAVUser( currentUsername, m_authService.getCurrentTicket(), personRef);
- NodeRef homeRef = (NodeRef) m_nodeService.getProperty(personRef, ContentModel.PROP_HOMEFOLDER);
-
- // Check that the home space node exists - else Login cannot proceed
-
- if (m_nodeService.exists(homeRef) == false)
- {
- throw new InvalidNodeRefException(homeRef);
- }
- user.setHomeNode(homeRef);
-
- tx.commit();
- tx = null;
-
- // Store the User object in the Session - the authentication servlet will then proceed
-
- req.getSession().setAttribute( AUTHENTICATION_USER, user);
-
- // Chain to the next filter
-
- chain.doFilter(sreq, sresp);
- return;
- }
- catch (AuthenticationException authErr)
- {
- // Clear the user object to signal authentication failure
-
- user = null;
- }
- catch (Throwable e)
- {
- // Clear the user object to signal authentication failure
-
- user = null;
- }
- finally
- {
- try
- {
- if (tx != null)
- {
- tx.rollback();
- }
- }
- catch (Exception tex)
- {
- }
- }
- }
-
- // Debug
-
- if ( logger.isDebugEnabled())
- logger.debug("New Kerberos auth request from " + req.getRemoteHost() + " (" +
- req.getRemoteAddr() + ":" + req.getRemotePort() + ")");
-
- // Send back a request for SPNEGO authentication
-
- resp.setHeader("WWW-Authenticate", "Negotiate");
- resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
-
- resp.flushBuffer();
- }
- else
- {
- // Decode the received SPNEGO blob and validate
-
- final byte[] spnegoByts = Base64.decodeBase64( authHdr.substring(10).getBytes());
-
- // Check the received SPNEGO token type
-
- int tokType = -1;
-
- try
- {
- tokType = SPNEGO.checkTokenType( spnegoByts, 0, spnegoByts.length);
- }
- catch ( IOException ex)
- {
- }
-
- // Check for a NegTokenInit blob
-
- if ( tokType == SPNEGO.NegTokenInit)
- {
- // Parse the SPNEGO security blob to get the Kerberos ticket
-
- NegTokenInit negToken = new NegTokenInit();
-
- try
- {
- // Decode the security blob
-
- negToken.decode( spnegoByts, 0, spnegoByts.length);
-
- // Determine the authentication mechanism the client is using and logon
-
- String oidStr = null;
- if ( negToken.numberOfOids() > 0)
- oidStr = negToken.getOidAt( 0).toString();
-
- if ( oidStr != null && (oidStr.equals( OID.ID_MSKERBEROS5) || oidStr.equals(OID.ID_KERBEROS5)))
- {
- // Kerberos logon
-
- if ( doKerberosLogon( negToken, req, resp, httpSess) != null)
- {
- // Allow the user to access the requested page
-
- chain.doFilter( req, resp);
- }
- else
- {
- // Send back a request for SPNEGO authentication
-
- resp.setHeader("WWW-Authenticate", "Negotiate");
- resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
-
- resp.flushBuffer();
- }
- }
- }
- catch ( IOException ex)
- {
- // Log the error
-
- if ( logger.isDebugEnabled())
- logger.debug(ex);
- }
- }
- else
- {
- // Unknown SPNEGO token type
-
- if ( logger.isDebugEnabled())
- logger.debug( "Unknown SPNEGO token type");
-
- // Send back a request for SPNEGO authentication
-
- resp.setHeader("WWW-Authenticate", "Negotiate");
- resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
-
- resp.flushBuffer();
- }
- }
- }
-
- /**
- * Delete the servlet filter
- */
- public void destroy()
+ @Override
+ protected void onValidateFailed(HttpServletRequest req, HttpServletResponse res, HttpSession session)
+ throws IOException
{
+ // Restart the login challenge process if validation fails
+
+ restartLoginChallenge(res, session);
}
- /**
- * JAAS callback handler
- *
- * @param callbacks Callback[]
- * @exception IOException
- * @exception UnsupportedCallbackException
- */
- public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException
- {
- // Process the callback list
-
- for (int i = 0; i < callbacks.length; i++)
- {
- // Request for user name
-
- if (callbacks[i] instanceof NameCallback)
- {
- NameCallback cb = (NameCallback) callbacks[i];
- cb.setName(m_accountName);
- }
-
- // Request for password
- else if (callbacks[i] instanceof PasswordCallback)
- {
- PasswordCallback cb = (PasswordCallback) callbacks[i];
- cb.setPassword(m_password.toCharArray());
- }
-
- // Request for realm
-
- else if (callbacks[i] instanceof RealmCallback)
- {
- RealmCallback cb = (RealmCallback) callbacks[i];
- cb.setText(m_krbRealm);
- }
- else
- {
- throw new UnsupportedCallbackException(callbacks[i]);
- }
- }
- }
-
- /**
- * Perform a Kerberos login and return an SPNEGO response
- *
- * @param negToken NegTokenInit
- * @param req HttpServletRequest
- * @param resp HttpServletResponse
- * @param httpSess HttpSession
- * @return NegTokenTarg
- */
- private final NegTokenTarg doKerberosLogon( NegTokenInit negToken, HttpServletRequest req, HttpServletResponse resp, HttpSession httpSess)
- {
- // Authenticate the user
-
- KerberosDetails krbDetails = null;
- NegTokenTarg negTokenTarg = null;
-
- UserTransaction tx = null;
-
- try
- {
- // Run the session setup as a privileged action
-
- SessionSetupPrivilegedAction sessSetupAction = new SessionSetupPrivilegedAction( m_accountName, negToken.getMechtoken());
- Object result = Subject.doAs( m_loginContext.getSubject(), sessSetupAction);
-
- if ( result != null)
- {
- // Access the Kerberos response
-
- krbDetails = (KerberosDetails) result;
-
- // Create the NegTokenTarg response blob
-
- negTokenTarg = new NegTokenTarg( SPNEGO.AcceptCompleted, OID.KERBEROS5, krbDetails.getResponseToken());
-
- // Check if the user has been authenticated, if so then setup the user environment
-
- if ( negTokenTarg != null)
- {
- // Create a read transaction
-
- tx = m_transactionService.getUserTransaction();
-
- NodeRef homeSpaceRef = null;
- WebDAVUser user = null;
- String userName = null;
-
- try
- {
- // Start the transaction
-
- tx.begin();
-
- // Setup User object and Home space ID etc.
-
- NodeRef personNodeRef = m_personService.getPerson( krbDetails.getUserName());
-
- // Use the system user to do the user name lookup
-
- m_authComponent.setSystemUserAsCurrentUser();
-
- // User name should match the uid in the person entry found
-
- userName = (String) m_nodeService.getProperty(personNodeRef, ContentModel.PROP_USERNAME);
- AuthenticationUtil.setCurrentUser( userName);
- String currentTicket = m_authService.getCurrentTicket();
- user = new WebDAVUser(userName, currentTicket, personNodeRef);
-
- homeSpaceRef = (NodeRef) m_nodeService.getProperty( personNodeRef, ContentModel.PROP_HOMEFOLDER);
- user.setHomeNode( homeSpaceRef);
-
- // Commit
-
- tx.commit();
- }
- catch (Throwable ex)
- {
- try
- {
- tx.rollback();
- }
- catch (Exception ex2)
- {
- logger.error("Failed to rollback transaction", ex2);
- }
- if(ex instanceof RuntimeException)
- {
- throw (RuntimeException)ex;
- }
- else if(ex instanceof IOException)
- {
- throw (IOException)ex;
- }
- else if(ex instanceof ServletException)
- {
- throw (ServletException)ex;
- }
- else
- {
- throw new RuntimeException("Authentication setup failed", ex);
- }
- }
-
- // Store the user
-
- httpSess.setAttribute(AUTHENTICATION_USER, user);
-
- // Debug
-
- if ( logger.isDebugEnabled())
- logger.debug("User " + userName + " logged on via Kerberos");
-
- }
- }
- else
- {
- // Debug
-
- if ( logger.isDebugEnabled())
- logger.debug( "No SPNEGO response, Kerberos logon failed");
- }
- }
- catch (Exception ex)
- {
- // Log the error
-
- if ( logger.isDebugEnabled())
- logger.debug("Kerberos logon error", ex);
- }
-
- // Return the response SPNEGO blob
-
- return negTokenTarg;
- }
-
- /**
- * Map the case insensitive logon name to the internal person object user name
- *
- * @param userName String
- * @return String
- */
- protected final String mapUserNameToPerson(String userName)
- {
- // Get the home folder for the user
-
- UserTransaction tx = m_transactionService.getUserTransaction();
- String personName = null;
-
- try
- {
- tx.begin();
- personName = m_personService.getUserIdentifier( userName);
- tx.commit();
- }
- catch (Throwable ex)
- {
- try
- {
- tx.rollback();
- }
- catch (Throwable ex2)
- {
- logger.error("Failed to rollback transaction", ex2);
- }
-
- // Re-throw the exception
-
- if (ex instanceof RuntimeException)
- {
- throw (RuntimeException) ex;
- }
- else
- {
- throw new RuntimeException("Error during execution of transaction.", ex);
- }
- }
-
- // Return the person name
-
- return personName;
- }
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.webdav.auth.BaseSSOAuthenticationFilter#getLogger()
+ */
+ @Override
+ protected Log getLogger() {
+ return logger;
+ }
}
diff --git a/source/java/org/alfresco/repo/webdav/auth/NTLMAuthenticationFilter.java b/source/java/org/alfresco/repo/webdav/auth/NTLMAuthenticationFilter.java
index 29cb4284cd..4cfd73751f 100644
--- a/source/java/org/alfresco/repo/webdav/auth/NTLMAuthenticationFilter.java
+++ b/source/java/org/alfresco/repo/webdav/auth/NTLMAuthenticationFilter.java
@@ -26,24 +26,14 @@ package org.alfresco.repo.webdav.auth;
import java.io.IOException;
-import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
-import javax.transaction.UserTransaction;
-import org.alfresco.jlan.server.auth.ntlm.NTLM;
-import org.alfresco.jlan.server.auth.ntlm.NTLMMessage;
-import org.alfresco.jlan.server.auth.ntlm.Type1NTLMMessage;
-import org.alfresco.jlan.server.auth.ntlm.Type3NTLMMessage;
-import org.alfresco.model.ContentModel;
import org.alfresco.repo.SessionUser;
-import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.service.cmr.repository.NodeRef;
-import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -54,190 +44,35 @@ import org.apache.commons.logging.LogFactory;
*/
public class NTLMAuthenticationFilter extends BaseNTLMAuthenticationFilter
{
- // Authenticated user session object name
- public final static String AUTHENTICATION_USER = "_alfDAVAuthTicket";
-
- // Allow an authenitcation ticket to be passed as part of a request to bypass authentication
- private static final String ARG_TICKET = "ticket";
-
// Debug logging
private static Log logger = LogFactory.getLog(NTLMAuthenticationFilter.class);
-
/**
- * Run the filter
+ * Initialize the filter
*
- * @param sreq ServletRequest
- * @param sresp ServletResponse
- * @param chain FilterChain
- * @exception IOException
+ * @param args FilterConfig
* @exception ServletException
*/
- public void doFilter(ServletRequest sreq, ServletResponse sresp, FilterChain chain) throws IOException,
- ServletException
+ public void init(FilterConfig args) throws ServletException
{
- // Get the HTTP request/response/session
- HttpServletRequest req = (HttpServletRequest) sreq;
- HttpServletResponse resp = (HttpServletResponse) sresp;
- HttpSession httpSess = req.getSession(true);
+ super.init(args);
+
+ // Enable ticket based logons
- // Check if there is an authorization header with an NTLM security blob
- String authHdr = req.getHeader(AUTHORIZATION);
- boolean reqAuth = (authHdr != null && authHdr.startsWith(AUTH_NTLM));
-
- // Check if the user is already authenticated
- WebDAVUser user = (WebDAVUser) httpSess.getAttribute(AUTHENTICATION_USER);
- if (user != null && reqAuth == false)
- {
- try
- {
- if (logger.isDebugEnabled())
- logger.debug("User " + user.getUserName() + " validate ticket");
-
- // Validate the user ticket
- m_authService.validate( user.getTicket());
- reqAuth = false;
- }
- catch (AuthenticationException ex)
- {
- if (logger.isErrorEnabled())
- logger.error("Failed to validate user " + user.getUserName(), ex);
-
- reqAuth = true;
- }
- }
-
- // If the user has been validated and we do not require re-authentication then continue to
- // the next filter
- if (reqAuth == false && user != null)
- {
- if (logger.isDebugEnabled())
- logger.debug("Authentication not required, chaining ...");
-
- // Chain to the next filter
- chain.doFilter(sreq, sresp);
- return;
- }
-
- // Check the authorization header
- if (authHdr == null)
- {
- // Check if the request includes an authentication ticket
- String ticket = req.getParameter(ARG_TICKET);
- if (ticket != null && ticket.length() != 0)
- {
- if (logger.isDebugEnabled())
- logger.debug("Logon via ticket from " + req.getRemoteHost() + " (" +
- req.getRemoteAddr() + ":" + req.getRemotePort() + ")" + " ticket=" + ticket);
-
- UserTransaction tx = null;
- try
- {
- // Validate the ticket
- m_authService.validate(ticket);
-
- if (user == null)
- {
- // Start a transaction
- tx = m_transactionService.getUserTransaction();
- tx.begin();
-
- // Need to create the User instance if not already available
- String currentUsername = m_authService.getCurrentUserName();
-
- NodeRef personRef = m_personService.getPerson(currentUsername);
- user = new WebDAVUser(currentUsername, m_authService.getCurrentTicket(), personRef);
- NodeRef homeRef = (NodeRef)m_nodeService.getProperty(personRef, ContentModel.PROP_HOMEFOLDER);
- user.setHomeNode(homeRef);
-
- tx.commit();
- tx = null;
-
- // Store the User object in the Session - the authentication servlet will then proceed
- req.getSession().setAttribute(AUTHENTICATION_USER, user);
- }
-
- // Chain to the next filter
- chain.doFilter(sreq, sresp);
- return;
- }
- catch (AuthenticationException authErr)
- {
- if (logger.isDebugEnabled())
- logger.debug("Failed to authenticate user ticket: " + authErr.getMessage(), authErr);
- }
- catch (Throwable e)
- {
- if (logger.isDebugEnabled())
- logger.debug("Error during ticket validation and user creation: " + e.getMessage(), e);
- }
- finally
- {
- try
- {
- if (tx != null)
- {
- tx.rollback();
- }
- }
- catch (Exception tex)
- {
- }
- }
- }
-
- if (logger.isDebugEnabled())
- logger.debug("New NTLM auth request from " + req.getRemoteHost() + " (" +
- req.getRemoteAddr() + ":" + req.getRemotePort() + ")");
-
- // Send back a request for NTLM authentication
- restartLoginChallenge(resp, httpSess);
- }
- else
- {
- // Decode the received NTLM blob and validate
- final byte[] ntlmByts = Base64.decodeBase64(authHdr.substring(5).getBytes());
- int ntlmTyp = NTLMMessage.isNTLMType(ntlmByts);
- if (ntlmTyp == NTLM.Type1)
- {
- // Process the type 1 NTLM message
- Type1NTLMMessage type1Msg = new Type1NTLMMessage(ntlmByts);
- processType1(type1Msg, req, resp, httpSess);
- }
- else if (ntlmTyp == NTLM.Type3)
- {
- // Process the type 3 NTLM message
- Type3NTLMMessage type3Msg = new Type3NTLMMessage(ntlmByts);
- processType3(type3Msg, req, resp, httpSess, chain);
- }
- else
- {
- if (logger.isDebugEnabled())
- logger.debug("NTLM blob not handled, restarting login challenge.");
-
- restartLoginChallenge(resp, httpSess);
- }
- }
- }
-
- /* (non-Javadoc)
- * @see org.alfresco.repo.webdav.auth.BaseNTLMAuthenticationFilter#getSessionUser(javax.servlet.http.HttpSession)
- */
- @Override
- protected SessionUser getSessionUser(HttpSession session)
- {
- return (SessionUser)session.getAttribute(AUTHENTICATION_USER);
+ setTicketLogons( true);
}
- /* (non-Javadoc)
- * @see org.alfresco.repo.webdav.auth.BaseNTLMAuthenticationFilter#onValidate(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpSession)
- */
- @Override
- protected void onValidate(HttpServletRequest req, HttpSession session)
- {
- // nothing to do for webdav filter
- }
-
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.webdav.auth.BaseSSOAuthenticationFilter#createUserObject(java.lang.String, java.lang.String, org.alfresco.service.cmr.repository.NodeRef, java.lang.String)
+ */
+ @Override
+ protected SessionUser createUserObject(String userName, String ticket, NodeRef personNode, String homeSpace) {
+
+ // Create a WebDAV user object
+
+ return new WebDAVUser( userName, ticket, personNode);
+ }
+
/* (non-Javadoc)
* @see org.alfresco.repo.webdav.auth.BaseNTLMAuthenticationFilter#onValidateFailed(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.servlet.http.HttpSession)
*/
@@ -245,88 +80,11 @@ public class NTLMAuthenticationFilter extends BaseNTLMAuthenticationFilter
protected void onValidateFailed(HttpServletRequest req, HttpServletResponse res, HttpSession session)
throws IOException
{
- // restart the login challenge process if validation fails
+ // Restart the login challenge process if validation fails
+
restartLoginChallenge(res, session);
}
- /* (non-Javadoc)
- * @see org.alfresco.repo.webdav.auth.BaseNTLMAuthenticationFilter#createUserEnvironment(javax.servlet.http.HttpSession, java.lang.String)
- */
- @Override
- protected SessionUser createUserEnvironment(HttpSession session, String userName)
- throws IOException, ServletException
- {
- SessionUser user = null;
-
- UserTransaction tx = m_transactionService.getUserTransaction();
-
- try
- {
- tx.begin();
-
- // Setup User object and Home space ID etc.
- NodeRef personNodeRef = m_personService.getPerson(userName);
-
- // Use the system user context to do the user lookup
- m_authComponent.setCurrentUser(m_authComponent.getSystemUserName());
-
- // User name should match the uid in the person entry found
- m_authComponent.setSystemUserAsCurrentUser();
- userName = (String) m_nodeService.getProperty(personNodeRef, ContentModel.PROP_USERNAME);
-
- m_authComponent.setCurrentUser(userName);
- String currentTicket = m_authService.getCurrentTicket();
- user = new WebDAVUser(userName, currentTicket, personNodeRef);
-
- NodeRef homeSpaceRef = (NodeRef) m_nodeService.getProperty(personNodeRef, ContentModel.PROP_HOMEFOLDER);
- ((WebDAVUser)user).setHomeNode(homeSpaceRef);
-
- tx.commit();
- }
- catch (Throwable ex)
- {
- try
- {
- tx.rollback();
- }
- catch (Exception ex2)
- {
- logger.error("Failed to rollback transaction", ex2);
- }
- if (ex instanceof RuntimeException)
- {
- throw (RuntimeException)ex;
- }
- else if (ex instanceof IOException)
- {
- throw (IOException)ex;
- }
- else if (ex instanceof ServletException)
- {
- throw (ServletException)ex;
- }
- else
- {
- throw new RuntimeException("Authentication setup failed", ex);
- }
- }
-
- // Store the user on the session
- session.setAttribute(AUTHENTICATION_USER, user);
-
- return user;
- }
-
- /* (non-Javadoc)
- * @see org.alfresco.repo.webdav.auth.BaseNTLMAuthenticationFilter#onLoginComplete(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
- */
- @Override
- protected boolean onLoginComplete(HttpServletRequest req, HttpServletResponse res) throws IOException
- {
- // no futher processing to do, allow to complete
- return true;
- }
-
/* (non-Javadoc)
* @see org.alfresco.repo.webdav.auth.BaseNTLMAuthenticationFilter#getLogger()
*/