mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
Merged V3.0 to HEAD
11469: MT - fix ETHREEOH-227 11470: Added check for request method type so that GET requests are redirected to the web client and PROPFIND/OPTIONS requests are routed to the WebDAV servlet. Fix Vista WebDAV client as it walks the path when connecting using the WebDAV mini redirector. ETHREEOH-554. 11471: Merged V2.2 to V3.0 11317: Fix ETWOTWO-698 and ETWOTWO-743. 11472: Fix for ETHREEOH-505 11473: Added LMv2 support, fallback from NTLMv2 to NTLMv1 and NTLMv2 can be switched off by the implementation. 11474: Removed the Principal configuration parameter, no longer needed as it is picked up after the server side Kerberos logon. 11476: Missing URL encoding for site manager username in Site Profile dashlet. 11477: Fix to padding on User Welcome dashlet now it is scrollable. 11479: Fixed: ETHREEOH-150 Possible to add empty post or empty comment to a post at blog page 11480: Added calculation of the LMv2 HMAC. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@12445 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -90,19 +90,21 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
|
||||
protected static final String AUTH_NTLM = "NTLM";
|
||||
|
||||
// NTLM flags mask for use with an authentication component that supports MD4 hashed password
|
||||
private static final int NTLM_FLAGS_MD4 = NTLM.Flag56Bit +
|
||||
NTLM.Flag128Bit +
|
||||
NTLM.FlagLanManKey +
|
||||
NTLM.FlagNegotiateNTLM +
|
||||
NTLM.FlagNTLM2Key +
|
||||
NTLM.FlagNegotiateUnicode;
|
||||
// Enable NTLMv1 and NTLMv2
|
||||
private static final int NTLM_FLAGS_NTLM2 = NTLM.Flag56Bit +
|
||||
NTLM.Flag128Bit +
|
||||
NTLM.FlagLanManKey +
|
||||
NTLM.FlagNegotiateNTLM +
|
||||
NTLM.FlagNTLM2Key +
|
||||
NTLM.FlagNegotiateUnicode;
|
||||
|
||||
// NTLM flags mask for use with an authentication component that uses passthru auth
|
||||
private static final int NTLM_FLAGS_PASSTHRU = NTLM.Flag56Bit +
|
||||
NTLM.FlagLanManKey +
|
||||
NTLM.FlagNegotiateNTLM +
|
||||
NTLM.FlagNegotiateOEM +
|
||||
NTLM.FlagNegotiateUnicode;
|
||||
// Enable NTLMv1 only
|
||||
private static final int NTLM_FLAGS_NTLM1 = NTLM.Flag56Bit +
|
||||
NTLM.FlagLanManKey +
|
||||
NTLM.FlagNegotiateNTLM +
|
||||
NTLM.FlagNegotiateOEM +
|
||||
NTLM.FlagNegotiateUnicode;
|
||||
|
||||
// NTLM flags to send to the client with the allowed logon types
|
||||
private int m_ntlmFlags;
|
||||
@@ -141,6 +143,8 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
|
||||
// Allow guest access, map unknown users to the guest account
|
||||
private boolean m_mapUnknownUserToGuest = false;
|
||||
|
||||
// Disable NTLMv2 support
|
||||
private boolean m_disableNTLMv2 = false;
|
||||
|
||||
/**
|
||||
* Initialize the filter
|
||||
@@ -251,15 +255,17 @@ 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)
|
||||
if (m_authComponent.getNTLMMode() == NTLMMode.MD4_PROVIDER && m_disableNTLMv2 == false)
|
||||
{
|
||||
// Allow the client to use an NTLMv2 logon
|
||||
m_ntlmFlags = NTLM_FLAGS_MD4;
|
||||
|
||||
m_ntlmFlags = NTLM_FLAGS_NTLM2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only allows NTLMv1 type logons as passthru authentication is being used
|
||||
m_ntlmFlags = NTLM_FLAGS_PASSTHRU;
|
||||
|
||||
m_ntlmFlags = NTLM_FLAGS_NTLM1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,6 +331,7 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
|
||||
if (m_authComponent.getNTLMMode() == NTLMMode.MD4_PROVIDER)
|
||||
{
|
||||
// Generate a random 8 byte challenge
|
||||
|
||||
challenge = new byte[8];
|
||||
DataPacker.putIntelLong(m_random.nextLong(), challenge, 0);
|
||||
}
|
||||
@@ -677,6 +684,20 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
|
||||
*/
|
||||
protected boolean validateLocalHashedPassword(Type3NTLMMessage type3Msg, NTLMLogonDetails ntlmDetails, boolean authenticated, String md4hash)
|
||||
{
|
||||
// Make sure we have hte cached NTLM details, including the type2 message with the server challenge
|
||||
|
||||
if ( ntlmDetails == null || ntlmDetails.getType2Message() == null)
|
||||
{
|
||||
// DEBUG
|
||||
|
||||
if ( getLogger().isDebugEnabled())
|
||||
getLogger().debug("No cached Type2, ntlmDetails=" + ntlmDetails);
|
||||
|
||||
// Not authenticated
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Determine if the client sent us NTLMv1 or NTLMv2
|
||||
if (type3Msg.hasFlag(NTLM.FlagNTLM2Key))
|
||||
{
|
||||
@@ -688,6 +709,20 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
|
||||
|
||||
if (getLogger().isDebugEnabled())
|
||||
getLogger().debug((authenticated ? "Logged on" : "Logon failed") + " using NTLMSSP/NTLMv2");
|
||||
|
||||
// If the NTlmv2 autentication failed then check if the client has sent an NTLMv1 hash
|
||||
|
||||
if ( authenticated == false && type3Msg.hasFlag(NTLM.Flag56Bit) && type3Msg.getLMHashLength() == 24)
|
||||
{
|
||||
// Check the LM hash field
|
||||
|
||||
authenticated = checkNTLMv1(md4hash, ntlmDetails.getChallengeKey(), type3Msg, true);
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (getLogger().isDebugEnabled())
|
||||
getLogger().debug((authenticated ? "Logged on" : "Logon failed") + " using NTLMSSP/NTLMv1 (via fallback)");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -701,7 +736,7 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
|
||||
else
|
||||
{
|
||||
// Looks like an NTLMv1 blob
|
||||
authenticated = checkNTLMv1(md4hash, ntlmDetails.getChallengeKey(), type3Msg);
|
||||
authenticated = checkNTLMv1(md4hash, ntlmDetails.getChallengeKey(), type3Msg, false);
|
||||
|
||||
if (getLogger().isDebugEnabled())
|
||||
getLogger().debug((authenticated ? "Logged on" : "Logon failed") + " using NTLMSSP/NTLMv1");
|
||||
@@ -715,9 +750,10 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
|
||||
* @param String md4hash
|
||||
* @param byte[] challenge
|
||||
* @param Type3NTLMMessage type3Msg
|
||||
* @param checkLMHash boolean
|
||||
* @return boolean
|
||||
*/
|
||||
protected final boolean checkNTLMv1(String md4hash, byte[] challenge, Type3NTLMMessage type3Msg)
|
||||
protected final boolean checkNTLMv1(String md4hash, byte[] challenge, Type3NTLMMessage type3Msg, boolean checkLMHash)
|
||||
{
|
||||
// Generate the local encrypted password using the challenge that was sent to the client
|
||||
byte[] p21 = new byte[21];
|
||||
@@ -736,7 +772,7 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
|
||||
}
|
||||
|
||||
// Validate the password
|
||||
byte[] clientHash = type3Msg.getNTLMHash();
|
||||
byte[] clientHash = checkLMHash ? type3Msg.getLMHash() : type3Msg.getNTLMHash();
|
||||
|
||||
if (clientHash != null && localHash != null && clientHash.length == localHash.length)
|
||||
{
|
||||
@@ -768,6 +804,9 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
|
||||
*/
|
||||
protected final boolean checkNTLMv2(String md4hash, byte[] challenge, Type3NTLMMessage type3Msg)
|
||||
{
|
||||
boolean ntlmv2OK = false;
|
||||
boolean lmv2OK = false;
|
||||
|
||||
try
|
||||
{
|
||||
// Generate the v2 hash using the challenge that was sent to the client
|
||||
@@ -792,9 +831,51 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
|
||||
if (i == clientHmac.length)
|
||||
{
|
||||
// HMAC matches the client, user authenticated
|
||||
return true;
|
||||
|
||||
ntlmv2OK = true;
|
||||
}
|
||||
}
|
||||
|
||||
// NTLMv2 check failed, try the LMv2 value
|
||||
|
||||
if ( ntlmv2OK == false)
|
||||
{
|
||||
byte[] lmv2 = type3Msg.getLMHash();
|
||||
byte[] clChallenge = v2blob.getClientChallenge();
|
||||
|
||||
if ( lmv2 != null && lmv2.length == 24 && clChallenge != null && clChallenge.length == 8)
|
||||
{
|
||||
// Check that the LM hash contains the client challenge as the last 8 bytes
|
||||
|
||||
int i = 0;
|
||||
|
||||
while ( i < clChallenge.length && lmv2[ i + 16] == clChallenge[ i])
|
||||
i++;
|
||||
|
||||
if ( i == clChallenge.length)
|
||||
{
|
||||
// Calculate the LMv2 value
|
||||
|
||||
byte[] lmv2Hmac = v2blob.calculateLMv2HMAC(v2hash, challenge, clChallenge);
|
||||
|
||||
// Check if the LMv2 HMAC matches
|
||||
|
||||
i = 0;
|
||||
|
||||
while (i < lmv2Hmac.length && lmv2[i] == lmv2Hmac[i])
|
||||
i++;
|
||||
|
||||
if (i == lmv2Hmac.length)
|
||||
{
|
||||
// LMv2 HMAC matches the client, user authenticated
|
||||
|
||||
//return true;
|
||||
lmv2OK = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -802,7 +883,10 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
|
||||
getLogger().debug(ex);
|
||||
}
|
||||
|
||||
// NTLMv2 check failed
|
||||
// Check if either of the NTLMv2 checks passed
|
||||
|
||||
if ( ntlmv2OK || lmv2OK)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -993,5 +1077,18 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the logger
|
||||
*
|
||||
* @return Log
|
||||
*/
|
||||
protected abstract Log getLogger();
|
||||
|
||||
/**
|
||||
* Disable NTLMv2 support, must be called from the implementation constructor
|
||||
*/
|
||||
protected final void disableNTLMv2()
|
||||
{
|
||||
m_disableNTLMv2 = true;
|
||||
}
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@ 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;
|
||||
@@ -254,12 +255,12 @@ public class KerberosAuthenticationFilter implements Filter, CallbackHandler
|
||||
else
|
||||
throw new ServletException("Kerberos realm not specified");
|
||||
|
||||
// Get the CIFS service account password
|
||||
// Get the HTTP service account password
|
||||
|
||||
String srvPassword = args.getInitParameter("Password");
|
||||
if ( srvPassword != null && srvPassword.length() > 0)
|
||||
{
|
||||
// Set the CIFS service account password
|
||||
// Set the HTTP service account password
|
||||
|
||||
m_password = srvPassword;
|
||||
}
|
||||
@@ -295,46 +296,11 @@ public class KerberosAuthenticationFilter implements Filter, CallbackHandler
|
||||
throw new ServletException( "Failed to get local host name");
|
||||
}
|
||||
|
||||
// Get the server principal name
|
||||
|
||||
String principal = args.getInitParameter( "Principal");
|
||||
|
||||
if ( principal != null) {
|
||||
|
||||
// Use the supplied principal name to build the account name
|
||||
|
||||
StringBuffer httpAccount = new StringBuffer();
|
||||
|
||||
httpAccount.append( principal);
|
||||
httpAccount.append("@");
|
||||
httpAccount.append(m_krbRealm);
|
||||
|
||||
m_accountName = httpAccount.toString();
|
||||
}
|
||||
else {
|
||||
|
||||
// Build the HTTP service account name
|
||||
|
||||
StringBuilder httpAccount = new StringBuilder();
|
||||
|
||||
httpAccount.append("HTTP/");
|
||||
httpAccount.append( localName);
|
||||
httpAccount.append("@");
|
||||
httpAccount.append(m_krbRealm);
|
||||
|
||||
m_accountName = httpAccount.toString();
|
||||
}
|
||||
|
||||
// Create a login context for the CIFS server service
|
||||
// Create a login context for the HTTP server service
|
||||
|
||||
try
|
||||
{
|
||||
// DEBUG
|
||||
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug( "HTTP Kerberos login using account " + m_accountName);
|
||||
|
||||
// Login the CIFS server service
|
||||
// Login the HTTP server service
|
||||
|
||||
m_loginContext = new LoginContext( m_loginEntryName, this);
|
||||
m_loginContext.login();
|
||||
@@ -354,6 +320,18 @@ public class KerberosAuthenticationFilter implements Filter, CallbackHandler
|
||||
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<Oid> mechTypes = new Vector<Oid>();
|
||||
|
Reference in New Issue
Block a user