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

@@ -0,0 +1,407 @@
/*
* 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.spnego;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.DERApplicationSpecific;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.DERGeneralString;
import org.bouncycastle.asn1.DERObject;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.DERTags;
import org.bouncycastle.asn1.DERUnknownTag;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.Oid;
/**
* NegTokenInit Class
*
* <p>Contains the details of an SPNEGO NegTokenInit blob for use with CIFS.
*
* @author gkspencer
*/
public class NegTokenInit
{
// Mechtypes list
private Oid[] m_mechTypes;
// Context flags
private int m_contextFlags = -1;
// Mechtoken
private byte[] m_mechToken;
// MectListMIC principal
private String m_mecListMICPrincipal;
/**
* Class constructor for decoding
*/
public NegTokenInit()
{
}
/**
* Class constructor for encoding
*
* @param mechTypes Oid[]
* @param mechPrinciple String
*/
public NegTokenInit( Oid[] mechTypes, String mechPrinciple)
{
m_mechTypes = mechTypes;
m_mecListMICPrincipal = mechPrinciple;
}
/**
* Class constructor for encoding
*
* @param mechTypes Vector<Oid>
* @param mechPrinciple String
*/
public NegTokenInit( Vector<Oid> mechTypes, String mechPrinciple)
{
// Create the mechTypes array
m_mechTypes = new Oid[ mechTypes.size()];
for ( int i = 0; i < mechTypes.size(); i++)
m_mechTypes[i] = mechTypes.get(i);
m_mecListMICPrincipal = mechPrinciple;
}
/**
* Return the mechTypes OID list
*
* @return Oid[]
*/
public final Oid[] getOids()
{
return m_mechTypes;
}
/**
* Return the context flags
*
* @return int
*/
public final int getContextFlags()
{
return m_contextFlags;
}
/**
* Return the mechToken
*
* @return byte[]
*/
public final byte[] getMechtoken()
{
return m_mechToken;
}
/**
* Return the mechListMIC principal
*
* @return String
*/
public final String getPrincipal()
{
return m_mecListMICPrincipal;
}
/**
* Check if the OID list contains the specified OID
*
* @param oid Oid
* @return boolean
*/
public final boolean hasOid( Oid oid)
{
boolean foundOid = false;
if ( m_mechTypes != null)
{
foundOid = oid.containedIn( m_mechTypes);
}
return foundOid;
}
/**
* Return the count of OIDs
*
* @return int
*/
public final int numberOfOids()
{
return m_mechTypes != null ? m_mechTypes.length : 0;
}
/**
* Return the specified OID
*
* @param idx int
* @return OID
*/
public final Oid getOidAt(int idx)
{
if ( m_mechTypes != null && idx >= 0 && idx < m_mechTypes.length)
return m_mechTypes[idx];
return null;
}
/**
* Decode an SPNEGO NegTokenInit blob
*
* @param buf byte[]
* @param off int
* @param len int
* @exception IOException
*/
public void decode(byte[] buf, int off, int len) throws IOException
{
// Create a stream around the security blob
ByteArrayInputStream bytStream = new ByteArrayInputStream( buf, off, len);
ASN1InputStream asnStream = new ASN1InputStream( bytStream);
// Read the top level object from the security blob
DERObject derObj = asnStream.readObject();
if ( derObj instanceof DERApplicationSpecific == false)
throw new IOException("Bad blob format (AppSpec)");
// Access the application specific contents
DERApplicationSpecific derApp = (DERApplicationSpecific) derObj;
ByteArrayInputStream appStream = new ByteArrayInputStream( derApp.getContents());
ASN1InputStream asnAppStream = new ASN1InputStream( appStream);
// First object should be an OID, make sure it is the SPNEGO OID
derObj = asnAppStream.readObject();
if ( derObj instanceof DERObjectIdentifier == false)
throw new IOException("Bad blob format (SPNEGO OID)");
DERObjectIdentifier derOid = (DERObjectIdentifier) derObj;
if ( derOid.getId().equals( OID.ID_SPNEGO) == false)
throw new IOException("Not an SPNEGO blob");
// Next object should be a tagged object with a sequence
derObj = asnAppStream.readObject();
if ( derObj instanceof DERTaggedObject == false)
throw new IOException("Bad blob format, tagged object missing");
DERTaggedObject derTagSeq = (DERTaggedObject) derObj;
if ( derTagSeq.getTagNo() != 0 || derTagSeq.getObject() instanceof DERSequence == false)
throw new IOException("Bad blob format, sequence missing");
// Enumerate the main NegTokenInit sequence
DERSequence negTokInitSeq = (DERSequence) derTagSeq.getObject();
Enumeration seqEnum = negTokInitSeq.getObjects();
while ( seqEnum.hasMoreElements())
{
// Read an object from the sequence
derObj = (DERObject) seqEnum.nextElement();
if ( derObj instanceof DERTaggedObject)
{
// Tag 0 should be a sequence of object identifiers
DERTaggedObject derTag = (DERTaggedObject) derObj;
if ( derTag.getTagNo() == 0 && derTag.getObject() instanceof DERSequence)
{
DERSequence derSeq = (DERSequence) derTag.getObject();
Enumeration typesEnum = derSeq.getObjects();
// Allocate the OID list
m_mechTypes = new Oid[derSeq.size()];
int idx = 0;
while( typesEnum.hasMoreElements())
{
derObj = (DERObject) typesEnum.nextElement();
if ( derObj instanceof DERObjectIdentifier)
{
derOid = (DERObjectIdentifier) derObj;
try
{
m_mechTypes[idx++] = new Oid( derOid.getId());
}
catch (GSSException ex)
{
throw new IOException("Bad mechType OID");
}
}
}
}
else if ( derTag.getTagNo() == 1 && derTag.getObject() instanceof DERBitString)
{
// Context flags
}
else if ( derTag.getTagNo() == 2 && derTag.getObject() instanceof DEROctetString)
{
// Unpack the mechToken
DEROctetString derStr = (DEROctetString) derTag.getObject();
m_mechToken = derStr.getOctets();
}
else if ( derTag.getTagNo() == 3 &&derTag.getObject() instanceof DEROctetString)
{
// mechListMIC
}
else if ( derTag.getTagNo() == 3 && derTag.getObject() instanceof DERSequence)
{
// mechListMIC (Microsoft)
DERSequence derSeq = (DERSequence) derTag.getObject();
Enumeration subEnum = derSeq.getObjects();
while( subEnum.hasMoreElements())
{
derObj = (DERObject) subEnum.nextElement();
System.out.println("mechListMIC Seq: " + derObj);
}
}
else
throw new IOException("Bad format, unexpected type");
}
else
throw new IOException("Bad format, untagged type");
}
}
/**
* Encode an SPNEGO NegTokenInit blob
*
* @return byte[]
* @exception IOException
*/
public byte[] encode() throws IOException
{
ByteArrayOutputStream tokStream = new ByteArrayOutputStream();
// Create an SPNEGO NegTokenInit token
DEROutputStream derOut = new DEROutputStream( tokStream);
derOut.writeObject( new DERObjectIdentifier( OID.ID_SPNEGO));
ASN1EncodableVector asnList = new ASN1EncodableVector();
// Build the mechTypes sequence
ASN1EncodableVector mechTypesList = new ASN1EncodableVector();
for ( Oid mechType : m_mechTypes)
{
mechTypesList.add( new DERObjectIdentifier( mechType.toString()));
}
asnList.add( new DERTaggedObject( true, 0, new DERSequence( mechTypesList)));
// Build the mechListMIC
//
// Note: This field is not as specified
if ( m_mecListMICPrincipal != null)
{
ASN1EncodableVector micList = new ASN1EncodableVector();
micList.add( new DERTaggedObject( true, 0, new DERGeneralString( m_mecListMICPrincipal)));
asnList.add( new DERTaggedObject( true, 3, new DERSequence( micList)));
}
// Generate the SPNEGO NegTokenInit blob
derOut.writeObject( new DERTaggedObject( true, 0, new DERSequence( asnList)));
DERObject token = new DERUnknownTag( DERTags.CONSTRUCTED | DERTags.APPLICATION, tokStream.toByteArray());
tokStream = new ByteArrayOutputStream();
derOut = new DEROutputStream( tokStream);
derOut.writeObject( token);
return tokStream.toByteArray();
}
/**
* Return the NegTokenInit object as a string
*
* @return String
*/
public String toString()
{
StringBuilder str = new StringBuilder();
str.append("[NegTokenInit ");
if ( m_mechTypes != null)
{
str.append("mechTypes=");
for ( Oid oid : m_mechTypes)
{
str.append(oid.toString());
str.append(",");
}
}
if ( m_contextFlags != -1)
{
str.append(" context=0x");
str.append(Integer.toHexString(m_contextFlags));
}
if ( m_mechToken != null)
{
str.append(" token=");
str.append(m_mechToken.length);
str.append(" bytes");
}
if ( m_mecListMICPrincipal != null)
{
str.append(" principal=");
str.append(m_mecListMICPrincipal);
}
str.append("]");
return str.toString();
}
}

View File

@@ -0,0 +1,262 @@
/*
* 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.spnego;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.DEREnumerated;
import org.bouncycastle.asn1.DERObject;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERTaggedObject;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.Oid;
/**
* NegTokenTarg Class
*
* <p>Contains the details of an SPNEGO NegTokenTarg blob for use with CIFS.
*
* @author gkspencer
*/
public class NegTokenTarg
{
// Result code
private int m_result;
// Supported mechanism
private Oid m_supportedMech;
// Response token
private byte[] m_responseToken;
/**
* Class constructor for decoding
*/
public NegTokenTarg()
{
}
/**
* Class constructor
*
* @param result int
* @param mech Oid
* @param response byte[]
*/
public NegTokenTarg(int result, Oid mech, byte[] response)
{
m_result = result;
m_supportedMech = mech;
m_responseToken = response;
}
/**
* Return the result
*
* @return int
*/
public final int getResult()
{
return m_result;
}
/**
* Return the supported mech type Oid
*
* @return Oid
*/
public final Oid getSupportedMech()
{
return m_supportedMech;
}
/**
* Determine if there is a valid response token
*
* @return boolean
*/
public final boolean hasResponseToken()
{
return m_responseToken != null ? true : false;
}
/**
* Return the response token
*
* @return byte[]
*/
public final byte[] getResponseToken()
{
return m_responseToken;
}
/**
* Decode an SPNEGO NegTokenTarg blob
*
* @param buf byte[]
* @param off int
* @param len int
* @exception IOException
*/
public void decode(byte[] buf, int off, int len) throws IOException
{
// Create a stream around the security blob
ByteArrayInputStream bytStream = new ByteArrayInputStream( buf, off, len);
ASN1InputStream asnStream = new ASN1InputStream( bytStream);
// Read the top level object from the security blob
DERObject derObj = asnStream.readObject();
if ( derObj instanceof DERTaggedObject == false)
throw new IOException("Bad blob format (Tagged)");
// Access the sequence
DERTaggedObject derTag = (DERTaggedObject) derObj;
if ( derTag.getObject() instanceof DERSequence == false)
throw new IOException("Bad blob format (Seq)");
DERSequence derSeq = (DERSequence) derTag.getObject();
Enumeration seqEnum = derSeq.getObjects();
while ( seqEnum.hasMoreElements())
{
// Read an object from the sequence
derObj = (DERObject) seqEnum.nextElement();
if ( derObj instanceof DERTaggedObject)
{
// Tag 0 should be a status
derTag = (DERTaggedObject) derObj;
if ( derTag.getTagNo() == 0 && derTag.getObject() instanceof DEREnumerated)
{
// Result code
DEREnumerated derEnum = (DEREnumerated) derTag.getObject();
m_result = derEnum.getValue().intValue();
}
else if ( derTag.getTagNo() == 1 && derTag.getObject() instanceof DERObjectIdentifier)
{
// Mech type
DERObjectIdentifier derOid = (DERObjectIdentifier) derTag.getObject();
try
{
m_supportedMech = new Oid(derOid.getId());
}
catch (GSSException ex)
{
}
}
else if ( derTag.getTagNo() == 2 && derTag.getObject() instanceof DEROctetString)
{
// Unpack the response token
DEROctetString derStr = (DEROctetString) derTag.getObject();
m_responseToken = derStr.getOctets();
}
else if ( derTag.getTagNo() == 3 &&derTag.getObject() instanceof DEROctetString)
{
// mechListMIC
}
else
throw new IOException("Bad format, unexpected type");
}
else
throw new IOException("Bad format, untagged type");
}
}
/**
* Encode an SPNEGO NegTokenTarg blob
*
* @return byte[]
* @exception IOException
*/
public byte[] encode() throws IOException
{
ByteArrayOutputStream tokStream = new ByteArrayOutputStream();
// Create an SPNEGO NegTokenTarg token
DEROutputStream derOut = new DEROutputStream( tokStream);
ASN1EncodableVector asnList = new ASN1EncodableVector();
// Pack the result code
asnList.add( new DERTaggedObject( true, 0, new DEREnumerated(m_result)));
// Pack the supportedMech field
if ( m_supportedMech != null)
asnList.add( new DERTaggedObject( true, 1, new DERObjectIdentifier( m_supportedMech.toString())));
// Pack the response token
if ( m_responseToken != null)
asnList.add( new DERTaggedObject( true, 2, new DEROctetString(m_responseToken)));
// Generate the SPNEGO NegTokenTarg blob
derOut.writeObject( new DERTaggedObject( true, SPNEGO.NegTokenTarg, new DERSequence( asnList)));
return tokStream.toByteArray();
}
/**
* Return the NegtokenTarg object as a string
*
* @return String
*/
public String toString()
{
StringBuilder str = new StringBuilder();
str.append("[NegtokenTarg result=");
str.append( SPNEGO.asResultString( getResult()));
str.append(" oid=");
str.append( getSupportedMech());
str.append(" response=");
if ( hasResponseToken())
{
str.append(getResponseToken().length);
str.append(" bytes");
}
else
str.append("null");
str.append("]");
return str.toString();
}
}

View File

@@ -0,0 +1,74 @@
/*
* 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.spnego;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.Oid;
/**
* OID Class
*
* <p>Contains Oids used by SPNEGO
*
* @author gkspencer
*/
public class OID
{
// IDs
public static final String ID_SPNEGO = "1.3.6.1.5.5.2";
// Kerberos providers
public static final String ID_KERBEROS5 = "1.2.840.113554.1.2.2";
public static final String ID_MSKERBEROS5 = "1.2.840.48018.1.2.2";
// Microsoft NTLM security support provider
public static final String ID_NTLMSSP = "1.3.6.1.4.1.311.2.2.10";
// OIDs
public static Oid SPNEGO;
public static Oid KERBEROS5;
public static Oid MSKERBEROS5;
public static Oid NTLMSSP;
/**
* Static initializer
*/
static {
// Create the OIDs
try
{
SPNEGO = new Oid(ID_SPNEGO);
KERBEROS5 = new Oid(ID_KERBEROS5);
MSKERBEROS5 = new Oid( ID_MSKERBEROS5);
NTLMSSP = new Oid(ID_NTLMSSP);
}
catch ( GSSException ex)
{
}
}
}

View File

@@ -0,0 +1,132 @@
/*
* 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.spnego;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.DERApplicationSpecific;
import org.bouncycastle.asn1.DERObject;
import org.bouncycastle.asn1.DERTaggedObject;
/**
* SPNEGO Class
*
* <p>Contains SPNEGO constants
*
* @author gkspencer
*/
public class SPNEGO
{
// Message types
public static final int NegTokenInit = 0;
public static final int NegTokenTarg = 1;
// NegTokenInit context flags
public static final int ContextDelete = 0;
public static final int ContextMutual = 1;
public static final int ContextReplay = 2;
public static final int ContextSequence = 3;
public static final int ContextAnon = 4;
public static final int ContextConf = 5;
public static final int ContextInteg = 6;
// NegTokenTarg result codes
public static final int AcceptCompleted = 0;
public static final int AcceptIncomplete = 1;
public static final int Reject = 2;
/**
* Return a result code as a string
*
* @param res int
* @return String
*/
public static String asResultString(int res)
{
String resStr = null;
switch ( res)
{
case AcceptCompleted:
resStr = "AcceptCompleted";
break;
case AcceptIncomplete:
resStr = "AcceptIncomplete";
break;
case Reject:
resStr = "Reject";
break;
default:
resStr = "" + res;
break;
}
return resStr;
}
/**
* Determine the SPNEGO token type
*
* @param buf byte[]
* @param off int
* @param len int
* @return int
* @exception IOException
*/
public static int checkTokenType( byte[] buf, int off, int len)
throws IOException
{
// Create a stream around the security blob
ByteArrayInputStream bytStream = new ByteArrayInputStream( buf, off, len);
ASN1InputStream asnStream = new ASN1InputStream( bytStream);
// Read the top level object from the security blob
DERObject derObj = asnStream.readObject();
int tokType = -1;
if ( derObj instanceof DERApplicationSpecific)
{
// Looks like a NegTokenInit token
tokType = NegTokenInit;
}
else if ( derObj instanceof DERTaggedObject)
{
// Check the tag number
DERTaggedObject derTag = (DERTaggedObject) derObj;
if ( derTag.getTagNo() == 1)
tokType = NegTokenTarg;
}
// Close the streams
asnStream.close();
bytStream.close();
// Return the token type
return tokType;
}
}