mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
Redesign of the CIFS authentication code to support NTLMv1/NTLMv2, SPNEGO and NTLMSSP
authentication methods via the session setup. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2760 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -20,8 +20,9 @@ import java.security.NoSuchAlgorithmException;
|
||||
import net.sf.acegisecurity.Authentication;
|
||||
|
||||
import org.alfresco.filesys.server.SrvSession;
|
||||
import org.alfresco.filesys.server.auth.CifsAuthenticator;
|
||||
import org.alfresco.filesys.server.auth.ClientInfo;
|
||||
import org.alfresco.filesys.server.auth.SrvAuthenticator;
|
||||
import org.alfresco.filesys.server.auth.NTLanManAuthContext;
|
||||
import org.alfresco.filesys.smb.server.SMBSrvSession;
|
||||
import org.alfresco.filesys.util.DataPacker;
|
||||
import org.alfresco.repo.security.authentication.NTLMMode;
|
||||
@@ -38,7 +39,7 @@ import org.alfresco.repo.security.authentication.ntlm.NTLMPassthruToken;
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class AlfrescoAuthenticator extends SrvAuthenticator
|
||||
public class AlfrescoAuthenticator extends CifsAuthenticator
|
||||
{
|
||||
/**
|
||||
* Default Constructor
|
||||
@@ -47,8 +48,6 @@ public class AlfrescoAuthenticator extends SrvAuthenticator
|
||||
*/
|
||||
public AlfrescoAuthenticator()
|
||||
{
|
||||
setAccessMode(SrvAuthenticator.USER_MODE);
|
||||
setEncryptedPasswords(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,7 +85,7 @@ public class AlfrescoAuthenticator extends SrvAuthenticator
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug("Null CIFS logon allowed");
|
||||
|
||||
return SrvAuthenticator.AUTH_ALLOW;
|
||||
return CifsAuthenticator.AUTH_ALLOW;
|
||||
}
|
||||
|
||||
// Check if the client is already authenticated, and it is not a null logon
|
||||
@@ -196,12 +195,13 @@ public class AlfrescoAuthenticator extends SrvAuthenticator
|
||||
|
||||
// Check if the client is already authenticated, and it is not a null logon
|
||||
|
||||
if ( sess.hasClientInformation() && sess.getClientInformation().getAuthenticationToken() != null &&
|
||||
if ( sess.hasAuthenticationContext() && sess.hasAuthenticationToken() &&
|
||||
sess.getClientInformation().getLogonType() != ClientInfo.LogonNull)
|
||||
{
|
||||
// Return the previous challenge, user is already authenticated
|
||||
|
||||
key = sess.getChallengeKey();
|
||||
|
||||
NTLanManAuthContext authCtx = (NTLanManAuthContext) sess.getAuthenticationContext();
|
||||
key = authCtx.getChallenge();
|
||||
|
||||
// DEBUG
|
||||
|
||||
@@ -260,7 +260,7 @@ public class AlfrescoAuthenticator extends SrvAuthenticator
|
||||
// Check if the client has supplied an NTLM hashed password, if not then do not allow access
|
||||
|
||||
if ( client.getPassword() == null)
|
||||
return SrvAuthenticator.AUTH_BADPASSWORD;
|
||||
return CifsAuthenticator.AUTH_BADPASSWORD;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -270,21 +270,30 @@ public class AlfrescoAuthenticator extends SrvAuthenticator
|
||||
byte[] md4byts = m_md4Encoder.decodeHash(md4hash);
|
||||
System.arraycopy(md4byts, 0, p21, 0, 16);
|
||||
|
||||
// Get the challenge that was sent to the client
|
||||
|
||||
NTLanManAuthContext authCtx = null;
|
||||
|
||||
if ( sess.hasAuthenticationContext() && sess.getAuthenticationContext() instanceof NTLanManAuthContext)
|
||||
authCtx = (NTLanManAuthContext) sess.getAuthenticationContext();
|
||||
else
|
||||
return CifsAuthenticator.AUTH_DISALLOW;
|
||||
|
||||
// Generate the local hash of the password using the same challenge
|
||||
|
||||
byte[] localHash = getEncryptor().doNTLM1Encryption(p21, sess.getChallengeKey());
|
||||
byte[] localHash = getEncryptor().doNTLM1Encryption(p21, authCtx.getChallenge());
|
||||
|
||||
// Validate the password
|
||||
|
||||
byte[] clientHash = client.getPassword();
|
||||
|
||||
if ( clientHash == null || clientHash.length != localHash.length)
|
||||
return SrvAuthenticator.AUTH_BADPASSWORD;
|
||||
return CifsAuthenticator.AUTH_BADPASSWORD;
|
||||
|
||||
for ( int i = 0; i < clientHash.length; i++)
|
||||
{
|
||||
if ( clientHash[i] != localHash[i])
|
||||
return SrvAuthenticator.AUTH_BADPASSWORD;
|
||||
return CifsAuthenticator.AUTH_BADPASSWORD;
|
||||
}
|
||||
|
||||
// Set the current user to be authenticated, save the authentication token
|
||||
@@ -297,7 +306,7 @@ public class AlfrescoAuthenticator extends SrvAuthenticator
|
||||
|
||||
// Passwords match, grant access
|
||||
|
||||
return SrvAuthenticator.AUTH_ALLOW;
|
||||
return CifsAuthenticator.AUTH_ALLOW;
|
||||
}
|
||||
catch (NoSuchAlgorithmException ex)
|
||||
{
|
||||
@@ -305,7 +314,7 @@ public class AlfrescoAuthenticator extends SrvAuthenticator
|
||||
|
||||
// Error during password check, do not allow access
|
||||
|
||||
return SrvAuthenticator.AUTH_DISALLOW;
|
||||
return CifsAuthenticator.AUTH_DISALLOW;
|
||||
}
|
||||
|
||||
// Check if this is an SMB/CIFS null session logon.
|
||||
@@ -313,11 +322,11 @@ public class AlfrescoAuthenticator extends SrvAuthenticator
|
||||
// The null session will only be allowed to connect to the IPC$ named pipe share.
|
||||
|
||||
if (client.isNullSession() && sess instanceof SMBSrvSession)
|
||||
return SrvAuthenticator.AUTH_ALLOW;
|
||||
return CifsAuthenticator.AUTH_ALLOW;
|
||||
|
||||
// User does not exist, check if guest access is allowed
|
||||
|
||||
return allowGuest() ? SrvAuthenticator.AUTH_GUEST : SrvAuthenticator.AUTH_DISALLOW;
|
||||
return allowGuest() ? CifsAuthenticator.AUTH_GUEST : CifsAuthenticator.AUTH_DISALLOW;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -335,11 +344,11 @@ public class AlfrescoAuthenticator extends SrvAuthenticator
|
||||
NTLMPassthruToken authToken = (NTLMPassthruToken) sess.getAuthenticationToken();
|
||||
|
||||
if ( authToken == null)
|
||||
return SrvAuthenticator.AUTH_DISALLOW;
|
||||
return CifsAuthenticator.AUTH_DISALLOW;
|
||||
|
||||
// Get the appropriate hashed password for the algorithm
|
||||
|
||||
int authSts = SrvAuthenticator.AUTH_DISALLOW;
|
||||
int authSts = CifsAuthenticator.AUTH_DISALLOW;
|
||||
byte[] hashedPassword = null;
|
||||
|
||||
if ( alg == NTLM1)
|
||||
@@ -350,7 +359,7 @@ public class AlfrescoAuthenticator extends SrvAuthenticator
|
||||
{
|
||||
// Invalid/unsupported algorithm specified
|
||||
|
||||
return SrvAuthenticator.AUTH_DISALLOW;
|
||||
return CifsAuthenticator.AUTH_DISALLOW;
|
||||
}
|
||||
|
||||
// Set the username and hashed password in the authentication token
|
||||
@@ -379,7 +388,7 @@ public class AlfrescoAuthenticator extends SrvAuthenticator
|
||||
|
||||
// Allow the user access as a guest
|
||||
|
||||
authSts = SrvAuthenticator.AUTH_GUEST;
|
||||
authSts = CifsAuthenticator.AUTH_GUEST;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -387,7 +396,7 @@ public class AlfrescoAuthenticator extends SrvAuthenticator
|
||||
|
||||
// Allow the user full access to the server
|
||||
|
||||
authSts = SrvAuthenticator.AUTH_ALLOW;
|
||||
authSts = CifsAuthenticator.AUTH_ALLOW;
|
||||
}
|
||||
|
||||
// Set the current user to be authenticated, save the authentication token
|
||||
|
@@ -42,14 +42,15 @@ public class NTLM
|
||||
public static final int FlagNegotiateSeal = 0x00000020;
|
||||
public static final int FlagDatagramStyle = 0x00000040;
|
||||
public static final int FlagLanManKey = 0x00000080;
|
||||
public static final int FlagNegotiateNetware = 0x00000100;
|
||||
public static final int FlagNegotiateNTLM = 0x00000200;
|
||||
public static final int FlagDomainSupplied = 0x00001000;
|
||||
public static final int FlagWorkstationSupplied = 0x00002000;
|
||||
public static final int FlagLocalCall = 0x00004000;
|
||||
public static final int FlagAlwaysSign = 0x00008000;
|
||||
public static final int FlagTypeDomain = 0x00010000;
|
||||
public static final int FlagTypeServer = 0x00020000;
|
||||
public static final int FlagTypeShare = 0x00040000;
|
||||
public static final int FlagChallengeInit = 0x00010000;
|
||||
public static final int FlagChallengeAccept = 0x00020000;
|
||||
public static final int FlagChallengeNonNT = 0x00040000;
|
||||
public static final int FlagNTLM2Key = 0x00080000;
|
||||
public static final int FlagTargetInfo = 0x00800000;
|
||||
public static final int Flag128Bit = 0x20000000;
|
||||
|
@@ -204,7 +204,7 @@ public abstract class NTLMMessage
|
||||
*/
|
||||
protected final int getByteOffset(int offset)
|
||||
{
|
||||
return getIntValue(m_offset + offset + 4);
|
||||
return getIntValue(offset + 4);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -217,11 +217,11 @@ public abstract class NTLMMessage
|
||||
{
|
||||
// Get the byte block length
|
||||
|
||||
int bLen = getShortValue(m_offset + offset);
|
||||
int bLen = getShortValue(offset);
|
||||
if ( bLen == 0)
|
||||
return null;
|
||||
|
||||
int bOff = getIntValue(m_offset + offset + 4);
|
||||
int bOff = getIntValue(offset + 4);
|
||||
return getRawBytes(bOff, bLen);
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2006 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.filesys.server.auth.ntlm;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import org.alfresco.filesys.smb.NTTime;
|
||||
import org.alfresco.filesys.util.DataPacker;
|
||||
import org.alfresco.filesys.util.HexDump;
|
||||
|
||||
/**
|
||||
* NTLMv2 Blob Class
|
||||
*
|
||||
* <P>Contains methods to pack/unpack and calculate the hash of an NTLMv2 blob.
|
||||
*
|
||||
* @author gkspencer
|
||||
*/
|
||||
public class NTLMv2Blob
|
||||
{
|
||||
// Constants
|
||||
|
||||
public static final int HMAC_LEN = 16;
|
||||
public static final int CHALLENGE_LEN = 8;
|
||||
|
||||
// Offsets
|
||||
|
||||
public static final int OFFSET_HMAC = 0;
|
||||
public static final int OFFSET_HEADER = 16;
|
||||
public static final int OFFSET_RESERVED = 20;
|
||||
public static final int OFFSET_TIMESTAMP = 24;
|
||||
public static final int OFFSET_CHALLENGE = 32;
|
||||
public static final int OFFSET_UNKNOWN = 36;
|
||||
public static final int OFFSET_TARGETINFO = 40;
|
||||
|
||||
// NTLMv2 blob
|
||||
|
||||
private byte[] m_blob;
|
||||
private int m_offset;
|
||||
private int m_len;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param buf byte[]
|
||||
*/
|
||||
public NTLMv2Blob(byte[] buf)
|
||||
{
|
||||
m_blob = buf;
|
||||
m_offset = 0;
|
||||
m_len = m_blob.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param buf byte[]
|
||||
* @param offset int
|
||||
* @param len int
|
||||
*/
|
||||
public NTLMv2Blob(byte[] buf, int offset, int len)
|
||||
{
|
||||
m_blob = buf;
|
||||
m_offset = offset;
|
||||
m_len = len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the buffer
|
||||
*
|
||||
* @return byte[]
|
||||
*/
|
||||
public final byte[] getBuffer()
|
||||
{
|
||||
return m_blob;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the offset
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getOffset()
|
||||
{
|
||||
return m_offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the blob length
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getLength()
|
||||
{
|
||||
return m_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the HMAC from the buffer
|
||||
*
|
||||
* @return byte[]
|
||||
*/
|
||||
public final byte[] getHMAC()
|
||||
{
|
||||
byte[] hmac = new byte[HMAC_LEN];
|
||||
System.arraycopy(m_blob, m_offset, hmac, 0, HMAC_LEN);
|
||||
|
||||
return hmac;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the timestamp from the buffer, in NT 64bit time format
|
||||
*
|
||||
* @return long
|
||||
*/
|
||||
public final long getTimeStamp()
|
||||
{
|
||||
return DataPacker.getIntelLong(m_blob, m_offset + OFFSET_TIMESTAMP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the client challenge
|
||||
*
|
||||
* @return byte[]
|
||||
*/
|
||||
public final byte[] getClientChallenge()
|
||||
{
|
||||
byte[] challenge = new byte[CHALLENGE_LEN];
|
||||
System.arraycopy( m_blob, m_offset + OFFSET_CHALLENGE, challenge, 0, CHALLENGE_LEN);
|
||||
|
||||
return challenge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the HMAC of the blob using the specified NTLMv2 hash and challenge
|
||||
*
|
||||
* @param challenge byte[]
|
||||
* @param v2hash byte[]
|
||||
* @return byte[]
|
||||
* @exception Exception
|
||||
*/
|
||||
public final byte[] calculateHMAC( byte[] challenge, byte[] v2hash)
|
||||
throws Exception
|
||||
{
|
||||
// Create a copy of the NTLMv2 blob with room for the challenge
|
||||
|
||||
byte[] blob = new byte[(m_len - HMAC_LEN) + CHALLENGE_LEN];
|
||||
System.arraycopy( challenge, 0, blob, 0, CHALLENGE_LEN);
|
||||
System.arraycopy( m_blob, m_offset + OFFSET_HEADER, blob, CHALLENGE_LEN, m_len - HMAC_LEN);
|
||||
|
||||
// Generate the HMAC of the blob using the v2 hash as the key
|
||||
|
||||
Mac hmacMd5 = Mac.getInstance( "HMACMD5");
|
||||
SecretKeySpec blobKey = new SecretKeySpec( v2hash, 0, v2hash.length, "MD5");
|
||||
|
||||
hmacMd5.init( blobKey);
|
||||
return hmacMd5.doFinal( blob);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the NTLMv2 blob details
|
||||
*/
|
||||
public final void Dump()
|
||||
{
|
||||
System.out.println("NTLMv2 blob :");
|
||||
System.out.println(" HMAC : " + HexDump.hexString( getHMAC()));
|
||||
System.out.println(" Header : 0x" + Integer.toHexString(DataPacker.getIntelInt( m_blob, m_offset + OFFSET_HEADER)));
|
||||
System.out.println(" Timestamp : " + new Date(NTTime.toJavaDate( getTimeStamp())));
|
||||
System.out.println(" Challenge : " + HexDump.hexString( getClientChallenge()));
|
||||
}
|
||||
}
|
@@ -92,6 +92,16 @@ public class Type3NTLMMessage extends NTLMMessage
|
||||
return getIntValue(OffsetFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the length of the LM hash
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getLMHashLength()
|
||||
{
|
||||
return getShortValue(OffsetLMResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the LM password hash
|
||||
*
|
||||
@@ -102,6 +112,16 @@ public class Type3NTLMMessage extends NTLMMessage
|
||||
return getByteValue(OffsetLMResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the length of the NTLM hash
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getNTLMHashLength()
|
||||
{
|
||||
return getShortValue(OffsetNTLMResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the NTLM password hash
|
||||
*
|
||||
|
Reference in New Issue
Block a user