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,7 +90,8 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
protected static final String AUTH_NTLM = "NTLM"; protected static final String AUTH_NTLM = "NTLM";
// NTLM flags mask for use with an authentication component that supports MD4 hashed password // NTLM flags mask for use with an authentication component that supports MD4 hashed password
private static final int NTLM_FLAGS_MD4 = NTLM.Flag56Bit + // Enable NTLMv1 and NTLMv2
private static final int NTLM_FLAGS_NTLM2 = NTLM.Flag56Bit +
NTLM.Flag128Bit + NTLM.Flag128Bit +
NTLM.FlagLanManKey + NTLM.FlagLanManKey +
NTLM.FlagNegotiateNTLM + NTLM.FlagNegotiateNTLM +
@@ -98,7 +99,8 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
NTLM.FlagNegotiateUnicode; NTLM.FlagNegotiateUnicode;
// NTLM flags mask for use with an authentication component that uses passthru auth // NTLM flags mask for use with an authentication component that uses passthru auth
private static final int NTLM_FLAGS_PASSTHRU = NTLM.Flag56Bit + // Enable NTLMv1 only
private static final int NTLM_FLAGS_NTLM1 = NTLM.Flag56Bit +
NTLM.FlagLanManKey + NTLM.FlagLanManKey +
NTLM.FlagNegotiateNTLM + NTLM.FlagNegotiateNTLM +
NTLM.FlagNegotiateOEM + NTLM.FlagNegotiateOEM +
@@ -141,6 +143,8 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
// Allow guest access, map unknown users to the guest account // Allow guest access, map unknown users to the guest account
private boolean m_mapUnknownUserToGuest = false; private boolean m_mapUnknownUserToGuest = false;
// Disable NTLMv2 support
private boolean m_disableNTLMv2 = false;
/** /**
* Initialize the filter * 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, // Set the NTLM flags depending on the authentication component supporting MD4 passwords,
// or is using passthru auth // 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 // Allow the client to use an NTLMv2 logon
m_ntlmFlags = NTLM_FLAGS_MD4;
m_ntlmFlags = NTLM_FLAGS_NTLM2;
} }
else else
{ {
// Only allows NTLMv1 type logons as passthru authentication is being used // 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) if (m_authComponent.getNTLMMode() == NTLMMode.MD4_PROVIDER)
{ {
// Generate a random 8 byte challenge // Generate a random 8 byte challenge
challenge = new byte[8]; challenge = new byte[8];
DataPacker.putIntelLong(m_random.nextLong(), challenge, 0); 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) 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 // Determine if the client sent us NTLMv1 or NTLMv2
if (type3Msg.hasFlag(NTLM.FlagNTLM2Key)) if (type3Msg.hasFlag(NTLM.FlagNTLM2Key))
{ {
@@ -688,6 +709,20 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
if (getLogger().isDebugEnabled()) if (getLogger().isDebugEnabled())
getLogger().debug((authenticated ? "Logged on" : "Logon failed") + " using NTLMSSP/NTLMv2"); 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 else
{ {
@@ -701,7 +736,7 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
else else
{ {
// Looks like an NTLMv1 blob // Looks like an NTLMv1 blob
authenticated = checkNTLMv1(md4hash, ntlmDetails.getChallengeKey(), type3Msg); authenticated = checkNTLMv1(md4hash, ntlmDetails.getChallengeKey(), type3Msg, false);
if (getLogger().isDebugEnabled()) if (getLogger().isDebugEnabled())
getLogger().debug((authenticated ? "Logged on" : "Logon failed") + " using NTLMSSP/NTLMv1"); getLogger().debug((authenticated ? "Logged on" : "Logon failed") + " using NTLMSSP/NTLMv1");
@@ -715,9 +750,10 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
* @param String md4hash * @param String md4hash
* @param byte[] challenge * @param byte[] challenge
* @param Type3NTLMMessage type3Msg * @param Type3NTLMMessage type3Msg
* @param checkLMHash boolean
* @return 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 // Generate the local encrypted password using the challenge that was sent to the client
byte[] p21 = new byte[21]; byte[] p21 = new byte[21];
@@ -736,7 +772,7 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
} }
// Validate the password // Validate the password
byte[] clientHash = type3Msg.getNTLMHash(); byte[] clientHash = checkLMHash ? type3Msg.getLMHash() : type3Msg.getNTLMHash();
if (clientHash != null && localHash != null && clientHash.length == localHash.length) 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) protected final boolean checkNTLMv2(String md4hash, byte[] challenge, Type3NTLMMessage type3Msg)
{ {
boolean ntlmv2OK = false;
boolean lmv2OK = false;
try try
{ {
// Generate the v2 hash using the challenge that was sent to the client // Generate the v2 hash using the challenge that was sent to the client
@@ -792,7 +831,49 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
if (i == clientHmac.length) if (i == clientHmac.length)
{ {
// HMAC matches the client, user authenticated // 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;
}
}
} }
} }
} }
@@ -802,7 +883,10 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
getLogger().debug(ex); getLogger().debug(ex);
} }
// NTLMv2 check failed // Check if either of the NTLMv2 checks passed
if ( ntlmv2OK || lmv2OK)
return true;
return false; return false;
} }
@@ -993,5 +1077,18 @@ public abstract class BaseNTLMAuthenticationFilter implements Filter
return null; return null;
} }
/**
* Return the logger
*
* @return Log
*/
protected abstract Log getLogger(); 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.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.security.Principal;
import java.util.Vector; import java.util.Vector;
import javax.security.auth.Subject; import javax.security.auth.Subject;
@@ -254,12 +255,12 @@ public class KerberosAuthenticationFilter implements Filter, CallbackHandler
else else
throw new ServletException("Kerberos realm not specified"); throw new ServletException("Kerberos realm not specified");
// Get the CIFS service account password // Get the HTTP service account password
String srvPassword = args.getInitParameter("Password"); String srvPassword = args.getInitParameter("Password");
if ( srvPassword != null && srvPassword.length() > 0) if ( srvPassword != null && srvPassword.length() > 0)
{ {
// Set the CIFS service account password // Set the HTTP service account password
m_password = srvPassword; m_password = srvPassword;
} }
@@ -295,46 +296,11 @@ public class KerberosAuthenticationFilter implements Filter, CallbackHandler
throw new ServletException( "Failed to get local host name"); throw new ServletException( "Failed to get local host name");
} }
// Get the server principal name // Create a login context for the HTTP server service
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
try try
{ {
// DEBUG // Login the HTTP server service
if ( logger.isDebugEnabled())
logger.debug( "HTTP Kerberos login using account " + m_accountName);
// Login the CIFS server service
m_loginContext = new LoginContext( m_loginEntryName, this); m_loginContext = new LoginContext( m_loginEntryName, this);
m_loginContext.login(); m_loginContext.login();
@@ -354,6 +320,18 @@ public class KerberosAuthenticationFilter implements Filter, CallbackHandler
throw new ServletException("Failed to login HTTP server service"); 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 // Create the Oid list for the SPNEGO NegTokenInit, include NTLMSSP for fallback
Vector<Oid> mechTypes = new Vector<Oid>(); Vector<Oid> mechTypes = new Vector<Oid>();