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,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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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>();
|
||||||
|
Reference in New Issue
Block a user