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:
Gary Spencer
2006-05-04 15:29:26 +00:00
parent fe5257a3a2
commit d021b46d07
29 changed files with 4719 additions and 2124 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -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
*