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:
Kevin Roast
2008-12-17 12:50:08 +00:00
parent cd3024c83b
commit 466aba8908
2 changed files with 133 additions and 58 deletions

View File

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

View File

@@ -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>();