// Copyright (C) 2002 Microsoft Corporation
// All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS"
// WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
// OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY
// AND/OR FITNESS FOR A PARTICULAR PURPOSE.
//
// Date    - 10/08/2002
// Author  - Sanj Surati

/////////////////////////////////////////////////////////////
//
// SPNEGOPARSE.C
//
// SPNEGO Token Handler Source File
//
// Contains implementation of SPNEGO Token parsing functions.
//
/////////////////////////////////////////////////////////////

#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include "spnego.h"
#include "derparse.h"
#include "spnegoparse.h"

//
// Defined in DERPARSE.C
//

extern MECH_OID g_stcMechOIDList[];

/**********************************************************************/
/**                                                                  **/
/**                                                                  **/
/**                                                                  **/
/**                                                                  **/
/**                 Local SPNEGO Helper definitions                  **/
/**                                                                  **/
/**                                                                  **/
/**                                                                  **/
/**                                                                  **/
/**********************************************************************/


/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    CalculateMinSpnegoInitTokenSize
//
// Parameters:
//    [in]  nMechTokenLength        -  Length of the MechToken Element
//    [in]  nMechListMICLength      -  Length of the MechListMIC Element
//    [in]  mechOID                 -  OID for MechList
//    [in]  nReqFlagsAvailable      -  Is ContextFlags element available
//    [out] pnTokenSize             -  Filled out with total size of token
//    [out] pnInternalTokenLength   -  Filled out with length minus length
//                                     for initial token.
//
// Returns:
//    int   Success - SPNEGO_E_SUCCESS
//          Failure - SPNEGO API Error code
//
// Comments :
//    Calculates the required length for a SPNEGO NegTokenInit token based
//    on the supplied variable length values and which elements are present.
//    Note that because the lengths can be represented by an arbitrary
//    number of bytes in DER encodings, we actually calculate the lengths
//    backwards, so we always know how many bytes we will potentially be
//    writing out.
//
////////////////////////////////////////////////////////////////////////////

int CalculateMinSpnegoInitTokenSize(long nMechTokenLength,
	long nMechListMICLength, SPNEGO_MECH_OID mechOid,
	int nReqFlagsAvailable, long* pnTokenSize,
	long* pnInternalTokenLength)
{
	int   nReturn = SPNEGO_E_INVALID_LENGTH;

	// Start at 0.
	long  nTotalLength = 0;
	long  nTempLength = 0L;

	// We will calculate this by walking the token backwards

	// Start with MIC Element
	if (nMechListMICLength > 0L)
	{
		nTempLength = ASNDerCalcElementLength(nMechListMICLength, NULL);

		// Check for rollover error
		if (nTempLength < nMechListMICLength)
		{
			goto xEndTokenInitLength;
		}

		nTotalLength += nTempLength;
	}

	// Next is the MechToken
	if (nMechTokenLength > 0L)
	{
		nTempLength += ASNDerCalcElementLength(nMechTokenLength, NULL);

		// Check for rollover error
		if (nTempLength < nTotalLength)
		{
			goto xEndTokenInitLength;
		}

		nTotalLength = nTempLength;
	}

	// Next is the ReqFlags
	if (nReqFlagsAvailable)
	{
		nTempLength += ASNDerCalcElementLength(SPNEGO_NEGINIT_MAXLEN_REQFLAGS, NULL);

		// Check for rollover error
		if (nTempLength < nTotalLength)
		{
			goto xEndTokenInitLength;
		}

		nTotalLength = nTempLength;
	}

	// Next is the MechList - This is REQUIRED
	nTempLength += ASNDerCalcMechListLength(mechOid, NULL);

	// Check for rollover error
	if (nTempLength < nTotalLength)
	{
		goto xEndTokenInitLength;
	}

	nTotalLength = nTempLength;

	// Following four fields are the basic header tokens

	// Sequence Token
	nTempLength += ASNDerCalcTokenLength(nTotalLength, 0L);

	// Check for rollover error
	if (nTempLength < nTotalLength)
	{
		goto xEndTokenInitLength;
	}

	nTotalLength = nTempLength;

	// Neg Token Identifier Token
	nTempLength += ASNDerCalcTokenLength(nTotalLength, 0L);

	// Check for rollover error
	if (nTempLength < nTotalLength)
	{
		goto xEndTokenInitLength;
	}

	nTotalLength = nTempLength;

	// SPNEGO OID Token
	nTempLength += g_stcMechOIDList[spnego_mech_oid_Spnego].iLen;

	// Check for rollover error
	if (nTempLength < nTotalLength)
	{
		goto xEndTokenInitLength;
	}

	nTotalLength = nTempLength;

	// App Constructed Token
	nTempLength += ASNDerCalcTokenLength(nTotalLength, 0L);

	// Check for rollover error
	if (nTempLength < nTotalLength)
	{
		goto xEndTokenInitLength;
	}

	// The internal length doesn't include the number of bytes
	// for the initial token
	*pnInternalTokenLength = nTotalLength;
	nTotalLength = nTempLength;

	// We're done
	*pnTokenSize = nTotalLength;
	nReturn = SPNEGO_E_SUCCESS;

xEndTokenInitLength:

	return nReturn;

}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    CreateSpnegoInitToken
//
// Parameters:
//    [in]  MechType                -  OID in MechList
//    [in]  ucContextFlags          -  ContextFlags value
//    [in]  pbMechToken             -  Mech Token Binary Data
//    [in]  ulMechTokenLen          -  Length of Mech Token
//    [in]  pbMechListMIC           -  MechListMIC Binary Data
//    [in]  ulMechListMICn          -  Length of MechListMIC
//    [out] pbTokenData             -  Buffer to write token into.
//    [in]  nTokenLength            -  Length of pbTokenData buffer
//    [in]  nInternalTokenLength    -  Length of full token without leading
//                                     token bytes.
//
// Returns:
//    int   Success - SPNEGO_E_SUCCESS
//          Failure - SPNEGO API Error code
//
// Comments :
//    Uses DER to fill out pbTokenData with a SPNEGO NegTokenInit Token
//    Note that because the lengths can be represented by an arbitrary
//    number of bytes in DER encodings, we actually calculate the lengths
//    backwards, so we always know how many bytes we will potentially be
//    writing out.
//
////////////////////////////////////////////////////////////////////////////

int CreateSpnegoInitToken(SPNEGO_MECH_OID MechType,
	unsigned char ucContextFlags, unsigned char* pbMechToken,
	unsigned long ulMechTokenLen, unsigned char* pbMechListMIC,
	unsigned long ulMechListMICLen, unsigned char* pbTokenData,
	long nTokenLength, long nInternalTokenLength)
{
	int   nReturn = SPNEGO_E_INVALID_LENGTH;

	// Start at 0.
	long  nTempLength = 0L;
	long  nTotalBytesWritten = 0L;
	long  nInternalLength = 0L;

	unsigned char* pbWriteTokenData = pbTokenData + nTokenLength;

	// Temporary buffer to hold the REQ Flags as BIT String Data
	unsigned char  abTempReqFlags[SPNEGO_NEGINIT_MAXLEN_REQFLAGS];


	// We will write the token out backwards to properly handle the cases
	// where the length bytes become adjustable

	// Start with MIC Element
	if (ulMechListMICLen > 0L)
	{
		nTempLength = ASNDerCalcElementLength(ulMechListMICLen, &nInternalLength);

		// Decrease the pbWriteTokenData, now we know the length and
		// write it out.

		pbWriteTokenData -= nTempLength;
		nTempLength = ASNDerWriteElement(pbWriteTokenData, SPNEGO_NEGINIT_ELEMENT_MECHLISTMIC,
			OCTETSTRING, pbMechListMIC, ulMechListMICLen);

		// Adjust Values and sanity check
		nTotalBytesWritten += nTempLength;
		nInternalTokenLength -= nTempLength;

		if (nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0)
		{
			goto xEndWriteNegTokenInit;
		}

	}  // IF MechListMIC is present

	// Next is the MechToken
	if (ulMechTokenLen > 0L)
	{
		nTempLength = ASNDerCalcElementLength(ulMechTokenLen, &nInternalLength);

		// Decrease the pbWriteTokenData, now we know the length and
		// write it out.
		pbWriteTokenData -= nTempLength;
		nTempLength = ASNDerWriteElement(pbWriteTokenData, SPNEGO_NEGINIT_ELEMENT_MECHTOKEN,
			OCTETSTRING, pbMechToken, ulMechTokenLen);
		// Adjust Values and sanity check
		nTotalBytesWritten += nTempLength;
		nInternalTokenLength -= nTempLength;

		if (nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0)
		{
			goto xEndWriteNegTokenInit;
		}

	}  // IF MechToken Length is present

	// Next is the ReqFlags
	if (ucContextFlags > 0L)
	{

		nTempLength = ASNDerCalcElementLength(SPNEGO_NEGINIT_MAXLEN_REQFLAGS, &nInternalLength);

		// We need a byte that indicates how many bits difference between the number
		// of bits used in final octet (we only have one) and the max (8)

		abTempReqFlags[0] = SPNEGO_NEGINIT_REQFLAGS_BITDIFF;
		abTempReqFlags[1] = ucContextFlags;

		// Decrease the pbWriteTokenData, now we know the length and
		// write it out.
		pbWriteTokenData -= nTempLength;
		nTempLength = ASNDerWriteElement(pbWriteTokenData, SPNEGO_NEGINIT_ELEMENT_REQFLAGS,
			BITSTRING, abTempReqFlags, SPNEGO_NEGINIT_MAXLEN_REQFLAGS);

		// Adjust Values and sanity check
		nTotalBytesWritten += nTempLength;
		nInternalTokenLength -= nTempLength;

		if (nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0)
		{
			goto xEndWriteNegTokenInit;
		}

	}  // IF ContextFlags

	// Next is the MechList - This is REQUIRED
	nTempLength = ASNDerCalcMechListLength(MechType, &nInternalLength);

	// Decrease the pbWriteTokenData, now we know the length and
	// write it out.
	pbWriteTokenData -= nTempLength;
	nTempLength = ASNDerWriteMechList(pbWriteTokenData, MechType);

	// Adjust Values and sanity check
	nTotalBytesWritten += nTempLength;
	nInternalTokenLength -= nTempLength;

	if (nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0)
	{
		goto xEndWriteNegTokenInit;
	}

	// The next tokens we're writing out reflect the total number of bytes
	// we have actually written out.

	// Sequence Token
	nTempLength = ASNDerCalcTokenLength(nTotalBytesWritten, 0L);

	// Decrease the pbWriteTokenData, now we know the length and
	// write it out.
	pbWriteTokenData -= nTempLength;
	nTempLength = ASNDerWriteToken(pbWriteTokenData, SPNEGO_CONSTRUCTED_SEQUENCE,
		NULL, nTotalBytesWritten);

	// Adjust Values and sanity check
	nTotalBytesWritten += nTempLength;
	nInternalTokenLength -= nTempLength;

	if (nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0)
	{
		goto xEndWriteNegTokenInit;
	}

	// Neg Init Token Identifier Token
	nTempLength = ASNDerCalcTokenLength(nTotalBytesWritten, 0L);

	// Decrease the pbWriteTokenData, now we know the length and
	// write it out.
	pbWriteTokenData -= nTempLength;
	nTempLength = ASNDerWriteToken(pbWriteTokenData, SPNEGO_NEGINIT_TOKEN_IDENTIFIER,
		NULL, nTotalBytesWritten);

	// Adjust Values and sanity check
	nTotalBytesWritten += nTempLength;
	nInternalTokenLength -= nTempLength;

	if (nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0)
	{
		goto xEndWriteNegTokenInit;
	}

	// SPNEGO OID Token
	nTempLength = g_stcMechOIDList[spnego_mech_oid_Spnego].iLen;

	// Decrease the pbWriteTokenData, now we know the length and
	// write it out.
	pbWriteTokenData -= nTempLength;
	nTempLength = ASNDerWriteOID(pbWriteTokenData, spnego_mech_oid_Spnego);

	// Adjust Values and sanity check
	nTotalBytesWritten += nTempLength;
	nInternalTokenLength -= nTempLength;

	if (nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0)
	{
		goto xEndWriteNegTokenInit;
	}

	// App Constructed Token
	nTempLength = ASNDerCalcTokenLength(nTotalBytesWritten, 0L);

	// Decrease the pbWriteTokenData, now we know the length and
	// write it out.
	pbWriteTokenData -= nTempLength;
	nTempLength = ASNDerWriteToken(pbWriteTokenData, SPNEGO_NEGINIT_APP_CONSTRUCT,
		NULL, nTotalBytesWritten);

	// Adjust Values and sanity check
	nTotalBytesWritten += nTempLength;

	// Don't adjust the internal token length here, it doesn't account
	// the initial bytes written out (we really don't need to keep
	// a running count here, but for debugging, it helps to be able
	// to see the total number of bytes written out as well as the
	// number of bytes left to write).

	if (nTotalBytesWritten == nTokenLength && nInternalTokenLength == 0 &&
		pbWriteTokenData == pbTokenData)
	{
		nReturn = SPNEGO_E_SUCCESS;
	}

xEndWriteNegTokenInit:

	return nReturn;

}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    CalculateMinSpnegoTargTokenSize
//
// Parameters:
//    [in]  MechType                -  Supported MechType
//    [in]  spnegoNegResult         -  Neg Result
//    [in]  nMechTokenLength        -  Length of the MechToken Element
//    [in]  nMechListMICLength      -  Length of the MechListMIC Element
//    [out] pnTokenSize             -  Filled out with total size of token
//    [out] pnInternalTokenLength   -  Filled out with length minus length
//                                     for initial token.
//
// Returns:
//    int   Success - SPNEGO_E_SUCCESS
//          Failure - SPNEGO API Error code
//
// Comments :
//    Calculates the required length for a SPNEGO NegTokenTarg token based
//    on the supplied variable length values and which elements are present.
//    Note that because the lengths can be represented by an arbitrary
//    number of bytes in DER encodings, we actually calculate the lengths
//    backwards, so we always know how many bytes we will potentially be
//    writing out.
//
////////////////////////////////////////////////////////////////////////////

int CalculateMinSpnegoTargTokenSize(SPNEGO_MECH_OID MechType,
	SPNEGO_NEGRESULT spnegoNegResult, long nMechTokenLen,
	long nMechListMICLen, long* pnTokenSize,
	long* pnInternalTokenLength)
{
	int   nReturn = SPNEGO_E_INVALID_LENGTH;

	// Start at 0.
	long  nTotalLength = 0;
	long  nTempLength = 0L;

	// We will calculate this by walking the token backwards

	// Start with MIC Element
	if (nMechListMICLen > 0L)
	{
		nTempLength = ASNDerCalcElementLength(nMechListMICLen, NULL);

		// Check for rollover error
		if (nTempLength < nMechListMICLen)
		{
			goto xEndTokenTargLength;
		}

		nTotalLength += nTempLength;
	}

	// Next is the MechToken
	if (nMechTokenLen > 0L)
	{
		nTempLength += ASNDerCalcElementLength(nMechTokenLen, NULL);

		// Check for rollover error
		if (nTempLength < nTotalLength)
		{
			goto xEndTokenTargLength;
		}

		nTotalLength = nTempLength;
	}

	// Supported MechType
	if (spnego_mech_oid_NotUsed != MechType)
	{
		// Supported MechOID element - we use the token function since
		// we already know the size of the OID token and value
		nTempLength += ASNDerCalcElementLength(g_stcMechOIDList[MechType].iActualDataLen,
			NULL);

		// Check for rollover error
		if (nTempLength < nTotalLength)
		{
			goto xEndTokenTargLength;
		}

		nTotalLength = nTempLength;

	}  // IF MechType is available

	// NegResult Element
	if (spnego_negresult_NotUsed != spnegoNegResult)
	{
		nTempLength += ASNDerCalcElementLength(SPNEGO_NEGTARG_MAXLEN_NEGRESULT, NULL);

		// Check for rollover error
		if (nTempLength < nTotalLength)
		{
			goto xEndTokenTargLength;
		}

		nTotalLength = nTempLength;

	}  // IF negResult is available

	// Following two fields are the basic header tokens

	// Sequence Token
	nTempLength += ASNDerCalcTokenLength(nTotalLength, 0L);

	// Check for rollover error
	if (nTempLength < nTotalLength)
	{
		goto xEndTokenTargLength;
	}

	nTotalLength = nTempLength;

	// Neg Token Identifier Token
	nTempLength += ASNDerCalcTokenLength(nTotalLength, 0L);

	// Check for rollover error
	if (nTempLength < nTotalLength)
	{
		goto xEndTokenTargLength;
	}

	// The internal length doesn't include the number of bytes
	// for the initial token
	*pnInternalTokenLength = nTotalLength;
	nTotalLength = nTempLength;

	// We're done
	*pnTokenSize = nTotalLength;
	nReturn = SPNEGO_E_SUCCESS;

xEndTokenTargLength:

	return nReturn;

}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    CreateSpnegoTargToken
//
// Parameters:
//    [in]  MechType                -  Supported MechType
//    [in]  eNegResult              -  NegResult value
//    [in]  pbMechToken             -  Mech Token Binary Data
//    [in]  ulMechTokenLen          -  Length of Mech Token
//    [in]  pbMechListMIC           -  MechListMIC Binary Data
//    [in]  ulMechListMICn          -  Length of MechListMIC
//    [out] pbTokenData             -  Buffer to write token into.
//    [in]  nTokenLength            -  Length of pbTokenData buffer
//    [in]  nInternalTokenLength    -  Length of full token without leading
//                                     token bytes.
//
// Returns:
//    int   Success - SPNEGO_E_SUCCESS
//          Failure - SPNEGO API Error code
//
// Comments :
//    Uses DER to fill out pbTokenData with a SPNEGO NegTokenTarg Token
//    Note that because the lengths can be represented by an arbitrary
//    number of bytes in DER encodings, we actually calculate the lengths
//    backwards, so we always know how many bytes we will potentially be
//    writing out.
//
////////////////////////////////////////////////////////////////////////////

int CreateSpnegoTargToken(SPNEGO_MECH_OID MechType,
	SPNEGO_NEGRESULT eNegResult, unsigned char* pbMechToken,
	unsigned long ulMechTokenLen, unsigned char* pbMechListMIC,
	unsigned long ulMechListMICLen, unsigned char* pbTokenData,
	long nTokenLength, long nInternalTokenLength)
{
	int   nReturn = SPNEGO_E_INVALID_LENGTH;

	// Start at 0.
	long  nTempLength = 0L;
	long  nTotalBytesWritten = 0L;
	long  nInternalLength = 0L;

	unsigned char  ucTemp = 0;

	// We will write the token out backwards to properly handle the cases
	// where the length bytes become adjustable, so the write location
	// is initialized to point *just* past the end of the buffer.

	unsigned char* pbWriteTokenData = pbTokenData + nTokenLength;


	// Start with MIC Element
	if (ulMechListMICLen > 0L)
	{
		nTempLength = ASNDerCalcElementLength(ulMechListMICLen, &nInternalLength);

		// Decrease the pbWriteTokenData, now we know the length and
		// write it out.

		pbWriteTokenData -= nTempLength;
		nTempLength = ASNDerWriteElement(pbWriteTokenData, SPNEGO_NEGTARG_ELEMENT_MECHLISTMIC,
			OCTETSTRING, pbMechListMIC, ulMechListMICLen);

		// Adjust Values and sanity check
		nTotalBytesWritten += nTempLength;
		nInternalTokenLength -= nTempLength;

		if (nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0)
		{
			goto xEndWriteNegTokenTarg;
		}

	}  // IF MechListMIC is present

	// Next is the MechToken
	if (ulMechTokenLen > 0L)
	{
		nTempLength = ASNDerCalcElementLength(ulMechTokenLen, &nInternalLength);

		// Decrease the pbWriteTokenData, now we know the length and
		// write it out.
		pbWriteTokenData -= nTempLength;
		nTempLength = ASNDerWriteElement(pbWriteTokenData, SPNEGO_NEGTARG_ELEMENT_RESPONSETOKEN,
			OCTETSTRING, pbMechToken, ulMechTokenLen);
		// Adjust Values and sanity check
		nTotalBytesWritten += nTempLength;
		nInternalTokenLength -= nTempLength;

		if (nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0)
		{
			goto xEndWriteNegTokenTarg;
		}

	}  // IF MechToken Length is present

	// Supported Mech Type
	if (spnego_mech_oid_NotUsed != MechType)
	{

		nTempLength = ASNDerCalcElementLength(g_stcMechOIDList[MechType].iActualDataLen,
			&nInternalLength);

		// Decrease the pbWriteTokenData, now we know the length and
		// write it out.
		pbWriteTokenData -= nTempLength;
		nTempLength = ASNDerWriteToken(pbWriteTokenData, SPNEGO_NEGTARG_ELEMENT_SUPPORTEDMECH,
			g_stcMechOIDList[MechType].ucOid,
			g_stcMechOIDList[MechType].iLen);

		// Adjust Values and sanity check
		nTotalBytesWritten += nTempLength;
		nInternalTokenLength -= nTempLength;

		if (nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0)
		{
			goto xEndWriteNegTokenTarg;
		}

	}  // IF MechType is present

	// Neg Result
	// NegResult Element
	if (spnego_negresult_NotUsed != eNegResult)
	{
		ucTemp = (unsigned char)eNegResult;

		nTempLength = ASNDerCalcElementLength(SPNEGO_NEGTARG_MAXLEN_NEGRESULT, &nInternalLength);

		// Decrease the pbWriteTokenData, now we know the length and
		// write it out.
		pbWriteTokenData -= nTempLength;
		nTempLength = ASNDerWriteElement(pbWriteTokenData, SPNEGO_NEGTARG_ELEMENT_NEGRESULT,
			ENUMERATED, &ucTemp, SPNEGO_NEGTARG_MAXLEN_NEGRESULT);

		// Adjust Values and sanity check
		nTotalBytesWritten += nTempLength;
		nInternalTokenLength -= nTempLength;

		if (nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0)
		{
			goto xEndWriteNegTokenTarg;
		}

	}  // If eNegResult is available

	// The next tokens we're writing out reflect the total number of bytes
	// we have actually written out.

	// Sequence Token
	nTempLength = ASNDerCalcTokenLength(nTotalBytesWritten, 0L);

	// Decrease the pbWriteTokenData, now we know the length and
	// write it out.
	pbWriteTokenData -= nTempLength;
	nTempLength = ASNDerWriteToken(pbWriteTokenData, SPNEGO_CONSTRUCTED_SEQUENCE,
		NULL, nTotalBytesWritten);

	// Adjust Values and sanity check
	nTotalBytesWritten += nTempLength;
	nInternalTokenLength -= nTempLength;

	if (nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0)
	{
		goto xEndWriteNegTokenTarg;
	}

	// Neg Targ Token Identifier Token
	nTempLength = ASNDerCalcTokenLength(nTotalBytesWritten, 0L);

	// Decrease the pbWriteTokenData, now we know the length and
	// write it out.
	pbWriteTokenData -= nTempLength;
	nTempLength = ASNDerWriteToken(pbWriteTokenData, SPNEGO_NEGTARG_TOKEN_IDENTIFIER,
		NULL, nTotalBytesWritten);

	// Adjust Values and sanity check
	nTotalBytesWritten += nTempLength;

	// Don't adjust the internal token length here, it doesn't account
	// the initial bytes written out (we really don't need to keep
	// a running count here, but for debugging, it helps to be able
	// to see the total number of bytes written out as well as the
	// number of bytes left to write).

	if (nTotalBytesWritten == nTokenLength && nInternalTokenLength == 0 &&
		pbWriteTokenData == pbTokenData)
	{
		nReturn = SPNEGO_E_SUCCESS;
	}


xEndWriteNegTokenTarg:

	return nReturn;


}


/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    AllocEmptySpnegoToken
//
// Parameters:
//    [in]  ucCopyData        -  Flag to copy data or pointer.
//    [in]  ulFlags           -  Flags for SPNEGO_TOKEN data member.
//    [in]  pbTokenData       -  Binary token data.
//    [in]  ulTokenSize       -  Size of pbTokenData.
//
// Returns:
//    SPNEGO_TOKEN*  Success - Pointer to initialized SPNEGO_TOKEN struct
//                   Failure - NULL
//
// Comments :
//    Allocates a SPNEGO_TOKEN data structure and initializes it.  Based on
//    the value of ucCopyData, if non-zero, we copy the data into a buffer
//    we allocate in this function, otherwise, we copy the data pointer
//    direcly.
//
////////////////////////////////////////////////////////////////////////////

SPNEGO_TOKEN* AllocEmptySpnegoToken(unsigned char ucCopyData, unsigned long ulFlags,
	unsigned char* pbTokenData, unsigned long ulTokenSize)
{
	SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*)calloc(1, sizeof(SPNEGO_TOKEN));

	if (NULL != pSpnegoToken)
	{
		// Set the token size
		pSpnegoToken->nStructSize = SPNEGO_TOKEN_SIZE;

		// Initialize the element array
		InitSpnegoTokenElementArray(pSpnegoToken);

		// Assign the flags value
		pSpnegoToken->ulFlags = ulFlags;

		//
		// IF ucCopyData is TRUE, we will allocate a buffer and copy data into it.
		// Otherwise, we will just copy the pointer and the length.  This is so we
		// can cut out additional allocations for performance reasons
		//

		if (SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA == ucCopyData)
		{
			// Alloc the internal buffer.  Cleanup on failure.
			pSpnegoToken->pbBinaryData = (unsigned char*)calloc(ulTokenSize, sizeof(unsigned char));

			if (NULL != pSpnegoToken->pbBinaryData)
			{
				// We must ALWAYS free this buffer
				pSpnegoToken->ulFlags |= SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA;

				// Copy the data locally
				memcpy(pSpnegoToken->pbBinaryData, pbTokenData, ulTokenSize);
				pSpnegoToken->ulBinaryDataLen = ulTokenSize;
			}
			else
			{
				free(pSpnegoToken);
				pSpnegoToken = NULL;
			}

		}  // IF ucCopyData
		else
		{
			// Copy the pointer and the length directly - ulFlags will control whether or not
			// we are allowed to free the value

			pSpnegoToken->pbBinaryData = pbTokenData;
			pSpnegoToken->ulBinaryDataLen = ulTokenSize;
		}

	}

	return pSpnegoToken;
}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    FreeSpnegoToken
//
// Parameters:
//    [in]  pSpnegoToken      -  Points to SPNEGO_TOKEN to free.
//
// Returns:
//    void
//
// Comments :
//    If non-NULL, interprets pSpnegoToken, freeing any internal allocations
//    and finally the actual structure.
//
////////////////////////////////////////////////////////////////////////////

void FreeSpnegoToken(SPNEGO_TOKEN* pSpnegoToken)
{
	if (NULL != pSpnegoToken)
	{

		// Cleanup internal allocation per the flags
		if (pSpnegoToken->ulFlags & SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA &&
			NULL != pSpnegoToken->pbBinaryData)
		{
			free(pSpnegoToken->pbBinaryData);
			pSpnegoToken->pbBinaryData = NULL;
		}

		free(pSpnegoToken);
	}
}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    InitSpnegoTokenElementArray
//
// Parameters:
//    [in]  pSpnegoToken      -  Points to SPNEGO_TOKEN structure.
//
// Returns:
//    void
//
// Comments :
//    Initializes the element array data member of a SPNEGO_TOKEN data
//    structure.
//
////////////////////////////////////////////////////////////////////////////

void InitSpnegoTokenElementArray(SPNEGO_TOKEN* pSpnegoToken)
{
	int   nCtr;

	// Set the number of elemnts
	pSpnegoToken->nNumElements = MAX_NUM_TOKEN_ELEMENTS;

	//
	// Initially, all elements are unavailable
	//

	for (nCtr = 0; nCtr < MAX_NUM_TOKEN_ELEMENTS; nCtr++)
	{
		// Set the element size as well
		pSpnegoToken->aElementArray[nCtr].nStructSize = SPNEGO_ELEMENT_SIZE;
		pSpnegoToken->aElementArray[nCtr].iElementPresent = SPNEGO_TOKEN_ELEMENT_UNAVAILABLE;
	}

}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    InitSpnegoTokenType
//
// Parameters:
//    [in]  pSpnegoToken            -  Points to SPNEGO_TOKEN structure.
//    [out] pnTokenLength           -  Filled out with total token length
//    [out] pnRemainingTokenLength  -  Filled out with remaining length
//                                     after header is parsed
//    [out] ppbFirstElement         -  Filled out with pointer to first
//                                     element after header info.
//
// Returns:
//    int   Success - SPNEGO_E_SUCCESS
//          Failure - SPNEGO API Error code
//
// Comments :
//    Walks the underlying binary data for a SPNEGO_TOKEN data structure
//    and determines the type of the underlying token based on token header
//    information.
//
////////////////////////////////////////////////////////////////////////////

int InitSpnegoTokenType(SPNEGO_TOKEN* pSpnegoToken, long* pnTokenLength,
	long* pnRemainingTokenLength, unsigned char** ppbFirstElement)
{
	int   nReturn = SPNEGO_E_INVALID_TOKEN;
	long  nActualTokenLength = 0L;
	long  nBoundaryLength = pSpnegoToken->ulBinaryDataLen;
	unsigned char* pbTokenData = pSpnegoToken->pbBinaryData;

	//
	// First byte MUST be either an APP_CONSTRUCT or the NEGTARG_TOKEN_TARG
	//

	if (SPNEGO_NEGINIT_APP_CONSTRUCT == *pbTokenData)
	{
		// Validate the above token - this will tell us the actual length of the token
		// per the encoding (minus the actual token bytes)
		if ((nReturn = ASNDerCheckToken(pbTokenData, SPNEGO_NEGINIT_APP_CONSTRUCT, 0L, nBoundaryLength,
			pnTokenLength, &nActualTokenLength))
			== SPNEGO_E_SUCCESS)
		{
			// Initialize the remaining token length value.  This will be used
			// to tell the caller how much token there is left once we've parsed
			// the header (they could calculate it from the other values, but this
			// is a bit friendlier)
			*pnRemainingTokenLength = *pnTokenLength;

			// Make adjustments to next token
			pbTokenData += nActualTokenLength;
			nBoundaryLength -= nActualTokenLength;

			// The next token should be an OID
			if ((nReturn = ASNDerCheckOID(pbTokenData, spnego_mech_oid_Spnego, nBoundaryLength,
				&nActualTokenLength)) == SPNEGO_E_SUCCESS)
			{
				// Make adjustments to next token
				pbTokenData += nActualTokenLength;
				nBoundaryLength -= nActualTokenLength;
				*pnRemainingTokenLength -= nActualTokenLength;

				// The next token should specify the NegTokenInit
				if ((nReturn = ASNDerCheckToken(pbTokenData, SPNEGO_NEGINIT_TOKEN_IDENTIFIER,
					*pnRemainingTokenLength, nBoundaryLength, pnTokenLength,
					&nActualTokenLength))
					== SPNEGO_E_SUCCESS)
				{
					// Make adjustments to next token
					pbTokenData += nActualTokenLength;
					nBoundaryLength -= nActualTokenLength;
					*pnRemainingTokenLength -= nActualTokenLength;

					// The next token should specify the start of a sequence
					if ((nReturn = ASNDerCheckToken(pbTokenData, SPNEGO_CONSTRUCTED_SEQUENCE,
						*pnRemainingTokenLength, nBoundaryLength, pnTokenLength,
						&nActualTokenLength))
						== SPNEGO_E_SUCCESS)
					{
						// NegTokenInit header is now checked out!

						// Make adjustments to next token
						*pnRemainingTokenLength -= nActualTokenLength;

						// Store pointer to first element
						*ppbFirstElement = pbTokenData + nActualTokenLength;
						pSpnegoToken->ucTokenType = SPNEGO_TOKEN_INIT;
					}  // IF Check Sequence Token

				}  // IF Check NegTokenInit token


			}  // IF Check for SPNEGO OID


		}  // IF check app construct token

	}
	else if (SPNEGO_NEGTARG_TOKEN_IDENTIFIER == *pbTokenData)
	{

		// The next token should specify the NegTokenInit
		if ((nReturn = ASNDerCheckToken(pbTokenData, SPNEGO_NEGTARG_TOKEN_IDENTIFIER,
			*pnRemainingTokenLength, nBoundaryLength, pnTokenLength,
			&nActualTokenLength))
			== SPNEGO_E_SUCCESS)
		{
			// Initialize the remaining token length value.  This will be used
			// to tell the caller how much token there is left once we've parsed
			// the header (they could calculate it from the other values, but this
			// is a bit friendlier)
			*pnRemainingTokenLength = *pnTokenLength;

			// Make adjustments to next token
			pbTokenData += nActualTokenLength;
			nBoundaryLength -= nActualTokenLength;

			// The next token should specify the start of a sequence
			if ((nReturn = ASNDerCheckToken(pbTokenData, SPNEGO_CONSTRUCTED_SEQUENCE,
				*pnRemainingTokenLength, nBoundaryLength, pnTokenLength,
				&nActualTokenLength))
				== SPNEGO_E_SUCCESS)
			{
				// NegTokenInit header is now checked out!

				// Make adjustments to next token
				*pnRemainingTokenLength -= nActualTokenLength;

				// Store pointer to first element
				*ppbFirstElement = pbTokenData + nActualTokenLength;
				pSpnegoToken->ucTokenType = SPNEGO_TOKEN_TARG;
			}  // IF Check Sequence Token

		}  // IF Check NegTokenInit token

	}  // ELSE IF it's a NegTokenTarg

	return nReturn;
}


/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    GetSpnegoInitTokenMechList
//
// Parameters:
//    [in]  pbTokenData             -  Points to binary MechList element
//                                     in NegTokenInit.
//    [in]  nMechListLength         -  Length of the MechList
//    [out] pSpnegoElement          -  Filled out with MechList Element
//                                     data.
//
// Returns:
//    int   Success - SPNEGO_E_SUCCESS
//          Failure - SPNEGO API Error code
//
// Comments :
//    Checks that pbTokenData is pointing at something that at least
//    *looks* like a MechList and then fills out the supplied
//    SPNEGO_ELEMENT structure.
//
////////////////////////////////////////////////////////////////////////////

int GetSpnegoInitTokenMechList(unsigned char* pbTokenData, int nMechListLength,
	SPNEGO_ELEMENT* pSpnegoElement)
{
	int   nReturn = SPNEGO_E_INVALID_TOKEN;
	long  nLength = 0L;
	long  nActualTokenLength = 0L;

	// Actual MechList is prepended by a Constructed Sequence Token
	if ((nReturn = ASNDerCheckToken(pbTokenData, SPNEGO_CONSTRUCTED_SEQUENCE,
		nMechListLength, nMechListLength,
		&nLength, &nActualTokenLength))
		== SPNEGO_E_SUCCESS)
	{
		// Adjust for this token
		nMechListLength -= nActualTokenLength;
		pbTokenData += nActualTokenLength;

		// Perform simple validation of the actual MechList (i.e. ensure that
		// the OIDs in the MechList are reasonable).

		if ((nReturn = ValidateMechList(pbTokenData, nLength)) == SPNEGO_E_SUCCESS)
		{
			// Initialize the element now
			pSpnegoElement->eElementType = spnego_init_mechtypes;
			pSpnegoElement->iElementPresent = SPNEGO_TOKEN_ELEMENT_AVAILABLE;
			pSpnegoElement->type = SPNEGO_MECHLIST_TYPE;
			pSpnegoElement->nDatalength = nLength;
			pSpnegoElement->pbData = pbTokenData;
		}

	}  // IF Check Token

	return nReturn;
}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    InitSpnegoTokenElementFromBasicType
//
// Parameters:
//    [in]  pbTokenData             -  Points to binary element data in
//                                     a SPNEGO token.
//    [in]  nElementLength          -  Length of the element
//    [in]  ucExpectedType          -  Expected DER type.
//    [in]  spnegoElementType       -  Which element is this?
//    [out] pSpnegoElement          -  Filled out with element data.
//
// Returns:
//    int   Success - SPNEGO_E_SUCCESS
//          Failure - SPNEGO API Error code
//
// Comments :
//    Checks that pbTokenData is pointing at the specified DER type.  If so,
//    then we verify that lengths are proper and then fill out the 
//    SPNEGO_ELEMENT data structure.
//
////////////////////////////////////////////////////////////////////////////

int InitSpnegoTokenElementFromBasicType(unsigned char* pbTokenData, int nElementLength,
	unsigned char ucExpectedType,
	SPNEGO_ELEMENT_TYPE spnegoElementType,
	SPNEGO_ELEMENT* pSpnegoElement)
{
	int   nReturn = SPNEGO_E_UNEXPECTED_TYPE;
	long  nLength = 0L;
	long  nActualTokenLength = 0L;

	// The type BYTE must match our token data or something is badly wrong
	if (*pbTokenData == ucExpectedType)
	{

		// Check that we are pointing at the specified type
		if ((nReturn = ASNDerCheckToken(pbTokenData, ucExpectedType,
			nElementLength, nElementLength,
			&nLength, &nActualTokenLength))
			== SPNEGO_E_SUCCESS)
		{
			// Adjust for this token
			nElementLength -= nActualTokenLength;
			pbTokenData += nActualTokenLength;

			// Initialize the element now
			pSpnegoElement->eElementType = spnegoElementType;
			pSpnegoElement->iElementPresent = SPNEGO_TOKEN_ELEMENT_AVAILABLE;
			pSpnegoElement->type = ucExpectedType;
			pSpnegoElement->nDatalength = nLength;
			pSpnegoElement->pbData = pbTokenData;
		}

	}  // IF type makes sense

	return nReturn;
}


/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    InitSpnegoTokenElementFromOID
//
// Parameters:
//    [in]  pbTokenData             -  Points to binary element data in
//                                     a SPNEGO token.
//    [in]  nElementLength          -  Length of the element
//    [in]  spnegoElementType       -  Which element is this?
//    [out] pSpnegoElement          -  Filled out with element data.
//
// Returns:
//    int   Success - SPNEGO_E_SUCCESS
//          Failure - SPNEGO API Error code
//
// Comments :
//    Initializes a SpnegoElement from an OID - normally, this would have
//    used the Basic Type function above, but since we do binary compares
//    on the OIDs against the DER information as well as the OID, we need
//    to account for that.
//
////////////////////////////////////////////////////////////////////////////

int InitSpnegoTokenElementFromOID(unsigned char* pbTokenData, int nElementLength,
	SPNEGO_ELEMENT_TYPE spnegoElementType,
	SPNEGO_ELEMENT* pSpnegoElement)
{
	int   nReturn = SPNEGO_E_UNEXPECTED_TYPE;
	long  nLength = 0L;
	long  nActualTokenLength = 0L;

	// The type BYTE must match our token data or something is badly wrong
	if (*pbTokenData == OID)
	{

		// Check that we are pointing at an OID type
		if ((nReturn = ASNDerCheckToken(pbTokenData, OID,
			nElementLength, nElementLength,
			&nLength, &nActualTokenLength))
			== SPNEGO_E_SUCCESS)
		{
			// Don't adjust any values for this function

			// Initialize the element now
			pSpnegoElement->eElementType = spnegoElementType;
			pSpnegoElement->iElementPresent = SPNEGO_TOKEN_ELEMENT_AVAILABLE;
			pSpnegoElement->type = OID;
			pSpnegoElement->nDatalength = nElementLength;
			pSpnegoElement->pbData = pbTokenData;
		}

	}  // IF type makes sense

	return nReturn;
}


/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    InitSpnegoTokenElements
//
// Parameters:
//    [in]  pSpnegoToken            -  Points to SPNEGO_TOKEN struct
//    [in]  pbTokenData             -  Points to initial binary element
//                                     data in a SPNEGO token.
//    [in]  nRemainingTokenLength   -  Length remaining past header
//
// Returns:
//    int   Success - SPNEGO_E_SUCCESS
//          Failure - SPNEGO API Error code
//
// Comments :
//    Interprets the data at pbTokenData based on the TokenType in
//    pSpnegoToken.  Since some elements are optional (technically all are
//    but the token becomes quite useless if this is so), we check if
//    an element exists before filling out the element in the array.
//
////////////////////////////////////////////////////////////////////////////

int InitSpnegoTokenElements(SPNEGO_TOKEN* pSpnegoToken, unsigned char* pbTokenData,
	long nRemainingTokenLength)
{
	//
	// The following arrays contain the token identifiers for the elements
	// comprising the actual token.  All values are optional, and there are
	// no defaults.
	//

	static unsigned char abNegTokenInitElements[] =
	{ SPNEGO_NEGINIT_ELEMENT_MECHTYPES, SPNEGO_NEGINIT_ELEMENT_REQFLAGS,
	   SPNEGO_NEGINIT_ELEMENT_MECHTOKEN, SPNEGO_NEGINIT_ELEMENT_MECHLISTMIC };

	static unsigned char abNegTokenTargElements[] =
	{ SPNEGO_NEGTARG_ELEMENT_NEGRESULT, SPNEGO_NEGTARG_ELEMENT_SUPPORTEDMECH,
	   SPNEGO_NEGTARG_ELEMENT_RESPONSETOKEN, SPNEGO_NEGTARG_ELEMENT_MECHLISTMIC };

	int   nReturn = SPNEGO_E_SUCCESS;
	int   nCtr = 0L;
	long  nElementLength = 0L;
	long  nActualTokenLength = 0L;
	unsigned char* pbElements = NULL;
	unsigned char* ptok;
	long  tlen, elen, len;

	// Point to the correct array
	switch (pSpnegoToken->ucTokenType)
	{
	case SPNEGO_TOKEN_INIT:
	{
		pbElements = abNegTokenInitElements;
	}
	break;

	case SPNEGO_TOKEN_TARG:
	{
		pbElements = abNegTokenTargElements;
	}
	break;

	}  // SWITCH tokentype

	//
	// Enumerate the element arrays and look for the tokens at our current location
	//

	for (nCtr = 0L;
		SPNEGO_E_SUCCESS == nReturn &&
		nCtr < MAX_NUM_TOKEN_ELEMENTS &&
		nRemainingTokenLength > 0L;
		nCtr++)
	{

		// Check if the token exists
		if ((nReturn = ASNDerCheckToken(pbTokenData, pbElements[nCtr],
			0L, nRemainingTokenLength,
			&nElementLength, &nActualTokenLength))
			== SPNEGO_E_SUCCESS)
		{

			// Token data should skip over the sequence token and then
			// call the appropriate function to initialize the element
			pbTokenData += nActualTokenLength;

			// Lengths in the elements should NOT go beyond the element
			// length

			// Different tokens mean different elements
			if (SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType)
			{

				// Handle each element as appropriate
				switch (pbElements[nCtr])
				{

				case SPNEGO_NEGINIT_ELEMENT_MECHTYPES:
				{
					//
					// This is a Mech List that specifies which OIDs the
					// originator of the Init Token supports.
					//

					nReturn = GetSpnegoInitTokenMechList(pbTokenData, nElementLength,
						&pSpnegoToken->aElementArray[nCtr]);

				}
				break;

				case SPNEGO_NEGINIT_ELEMENT_REQFLAGS:
				{
					//
					// This is a BITSTRING which specifies the flags that the receiver
					// pass to the gss_accept_sec_context() function.
					//

					nReturn = InitSpnegoTokenElementFromBasicType(pbTokenData, nElementLength,
						BITSTRING, spnego_init_reqFlags,
						&pSpnegoToken->aElementArray[nCtr]);
				}
				break;

				case SPNEGO_NEGINIT_ELEMENT_MECHTOKEN:
				{
					//
					// This is an OCTETSTRING which contains a GSSAPI token corresponding
					// to the first OID in the MechList.
					//

					nReturn = InitSpnegoTokenElementFromBasicType(pbTokenData, nElementLength,
						OCTETSTRING, spnego_init_mechToken,
						&pSpnegoToken->aElementArray[nCtr]);
				}
				break;

				case SPNEGO_NEGINIT_ELEMENT_MECHLISTMIC:
				{
					//
					// This is an OCTETSTRING which contains a message integrity BLOB.
					//

					nReturn = InitSpnegoTokenElementFromBasicType(pbTokenData, nElementLength,
						OCTETSTRING, spnego_init_mechListMIC,
						&pSpnegoToken->aElementArray[nCtr]);
					/*
					 * don't believe everything you read in RFCs (and MS
					 * sample code)...  win2k is sending not an octet string,
					 * but a "general string", wrapped in a sequence.
					 */
					if (nReturn != SPNEGO_E_UNEXPECTED_TYPE)
						break;
					ptok = pbTokenData;
					elen = nElementLength;
					if ((nReturn = ASNDerCheckToken(ptok, SPNEGO_CONSTRUCTED_SEQUENCE, elen, elen, &len, &tlen)) != SPNEGO_E_SUCCESS)
						break;
					elen -= tlen;
					ptok += tlen;

					if ((nReturn = ASNDerCheckToken(ptok, SEQ_ELM(0), elen, elen, &len, &tlen)) != SPNEGO_E_SUCCESS)
						break;
					elen -= tlen;
					ptok += tlen;
					nReturn = InitSpnegoTokenElementFromBasicType(ptok, elen, GENERALSTR, spnego_init_mechListMIC, &pSpnegoToken->aElementArray[nCtr]);
				}
				break;

				}  // SWITCH Element
			}
			else
			{

				switch (pbElements[nCtr])
				{

				case SPNEGO_NEGTARG_ELEMENT_NEGRESULT:
				{
					//
					// This is an ENUMERATION which specifies result of the last GSS
					// token negotiation call.
					//

					nReturn = InitSpnegoTokenElementFromBasicType(pbTokenData, nElementLength,
						ENUMERATED, spnego_targ_negResult,
						&pSpnegoToken->aElementArray[nCtr]);
				}
				break;

				case SPNEGO_NEGTARG_ELEMENT_SUPPORTEDMECH:
				{
					//
					// This is an OID which specifies a supported mechanism.
					//

					nReturn = InitSpnegoTokenElementFromOID(pbTokenData, nElementLength,
						spnego_targ_mechListMIC,
						&pSpnegoToken->aElementArray[nCtr]);
				}
				break;

				case SPNEGO_NEGTARG_ELEMENT_RESPONSETOKEN:
				{
					//
					// This is an OCTETSTRING which specifies results of the last GSS
					// token negotiation call.
					//

					nReturn = InitSpnegoTokenElementFromBasicType(pbTokenData, nElementLength,
						OCTETSTRING, spnego_targ_responseToken,
						&pSpnegoToken->aElementArray[nCtr]);
				}
				break;

				case SPNEGO_NEGTARG_ELEMENT_MECHLISTMIC:
				{
					//
					// This is an OCTETSTRING which specifies a message integrity BLOB.
					//

					nReturn = InitSpnegoTokenElementFromBasicType(pbTokenData, nElementLength,
						OCTETSTRING, spnego_targ_mechListMIC,
						&pSpnegoToken->aElementArray[nCtr]);
				}
				break;

				}  // SWITCH Element

			}  // ELSE !NegTokenInit

			// Account for the entire token and following data
			nRemainingTokenLength -= (nActualTokenLength + nElementLength);

			// Token data should skip past the element length now
			pbTokenData += nElementLength;

		}  // IF Token found
		else if (SPNEGO_E_TOKEN_NOT_FOUND == nReturn)
		{
			// For now, this is a benign error (remember, all elements are optional, so
			// if we don't find one, it's okay).

			nReturn = SPNEGO_E_SUCCESS;
		}

	}  // FOR enum elements

	//
	// We should always run down to 0 remaining bytes in the token.  If not, we've got
	// a bad token.
	//

	if (SPNEGO_E_SUCCESS == nReturn && nRemainingTokenLength != 0L)
	{
		nReturn = SPNEGO_E_INVALID_TOKEN;
	}

	return nReturn;
}


/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    FindMechOIDInMechList
//
// Parameters:
//    [in]  pSpnegoElement          -  SPNEGO_ELEMENT for MechList
//    [in]  MechOID                 -  OID we're looking for.
//    [out] piMechTypeIndex         -  Index in the list where OID was
//                                     found
//
// Returns:
//    int   Success - SPNEGO_E_SUCCESS
//          Failure - SPNEGO API Error code
//
// Comments :
//    Walks the MechList for MechOID.  When it is found, the index in the
//    list is written to piMechTypeIndex.
//
////////////////////////////////////////////////////////////////////////////

int FindMechOIDInMechList(SPNEGO_ELEMENT* pSpnegoElement, SPNEGO_MECH_OID MechOID,
	int* piMechTypeIndex)
{
	int   nReturn = SPNEGO_E_NOT_FOUND;
	int   nCtr = 0;
	long  nLength = 0L;
	long  nBoundaryLength = pSpnegoElement->nDatalength;
	unsigned char* pbMechListData = pSpnegoElement->pbData;

	while (SPNEGO_E_SUCCESS != nReturn && nBoundaryLength > 0L)
	{

		// Use the helper function to check the OID
		if ((nReturn = ASNDerCheckOID(pbMechListData, MechOID, nBoundaryLength, &nLength))
			== SPNEGO_E_SUCCESS)
		{
			*piMechTypeIndex = nCtr;
		}

		// Adjust for the current OID
		pbMechListData += nLength;
		nBoundaryLength -= nLength;
		nCtr++;

	}  // WHILE enuming OIDs

	return nReturn;

}


/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    ValidateMechList
//
// Parameters:
//    [in]  pbMechListData          -  Pointer to binary MechList data
//    [in]  nBoundaryLength         -  Length we must not exceed
//
// Returns:
//    int   Success - SPNEGO_E_SUCCESS
//          Failure - SPNEGO API Error code
//
// Comments :
//    Checks the data at pbMechListData to see if it looks like a MechList.
//    As part of this, we walk the list and ensure that none of the OIDs
//    have a length that takes us outside of nBoundaryLength.
//
////////////////////////////////////////////////////////////////////////////

int ValidateMechList(unsigned char* pbMechListData, long nBoundaryLength)
{
	int   nReturn = SPNEGO_E_SUCCESS;
	long  nLength = 0L;
	long  nTokenLength = 0L;

	while (SPNEGO_E_SUCCESS == nReturn && nBoundaryLength > 0L)
	{
		// Verify that we have something that at least *looks* like an OID - in other
		// words it has an OID identifier and specifies a length that doesn't go beyond
		// the size of the list.
		nReturn = ASNDerCheckToken(pbMechListData, OID, 0L, nBoundaryLength,
			&nLength, &nTokenLength);

		// Adjust for the current OID
		pbMechListData += (nLength + nTokenLength);
		nBoundaryLength -= (nLength + nTokenLength);

	}  // WHILE enuming OIDs

	return nReturn;

}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    IsValidMechOid
//
// Parameters:
//    [in]  mechOid  -  mechOID id enumeration
//
// Returns:
//    int   Success - 1
//          Failure - 0
//
// Comments :
//    Checks for a valid mechOid value.
//
////////////////////////////////////////////////////////////////////////////

int IsValidMechOid(SPNEGO_MECH_OID mechOid)
{
	return (mechOid >= spnego_mech_oid_Kerberos_V5_Legacy &&
		mechOid <= spnego_mech_oid_NTLMSSP);
}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    IsValidContextFlags
//
// Parameters:
//    [in]  ucContextFlags -  ContextFlags value
//
// Returns:
//    int   Success - 1
//          Failure - 0
//
// Comments :
//    Checks for a valid ContextFlags value.
//
////////////////////////////////////////////////////////////////////////////

int IsValidContextFlags(unsigned char ucContextFlags)
{
	// Mask out our valid bits.  If there is anything leftover, this
	// is not a valid value for Context Flags
	return ((ucContextFlags & ~SPNEGO_NEGINIT_CONTEXT_MASK) == 0);
}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    IsValidNegResult
//
// Parameters:
//    [in]  negResult   -  NegResult value
//
// Returns:
//    int   Success - 1
//          Failure - 0
//
// Comments :
//    Checks for a valid NegResult value.
//
////////////////////////////////////////////////////////////////////////////

int IsValidNegResult(SPNEGO_NEGRESULT negResult)
{
	return (negResult >= spnego_negresult_success &&
		negResult <= spnego_negresult_rejected);
}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    IsValidSpnegoToken
//
// Parameters:
//    [in]  pSpnegoToken   -  Points to SPNEGO_TOKEN data structure
//
// Returns:
//    int   Success - 1
//          Failure - 0
//
// Comments :
//    Performs simple heuristic on location pointed to by pSpnegoToken.
//
////////////////////////////////////////////////////////////////////////////

int IsValidSpnegoToken(SPNEGO_TOKEN* pSpnegoToken)
{
	int   nReturn = 0;

	// Parameter should be non-NULL
	if (NULL != pSpnegoToken)
	{
		// Length should be at least the size defined in the header
		if (pSpnegoToken->nStructSize >= SPNEGO_TOKEN_SIZE)
		{
			// Number of elements should be >= our maximum - if it's greater, that's
			// okay, since we'll only be accessing the elements up to MAX_NUM_TOKEN_ELEMENTS
			if (pSpnegoToken->nNumElements >= MAX_NUM_TOKEN_ELEMENTS)
			{
				// Check for proper token type
				if (SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType ||
					SPNEGO_TOKEN_TARG == pSpnegoToken->ucTokenType)
				{
					nReturn = 1;
				}
			}

		}  // IF struct size makes sense

	}  // IF non-NULL spnego Token

	return nReturn;
}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    IsValidSpnegoElement
//
// Parameters:
//    [in]  pSpnegoToken   -  Points to SPNEGO_TOKEN data structure
//    [in]  spnegoElement  -  spnegoElement Type from enumeration
//
// Returns:
//    int   Success - 1
//          Failure - 0
//
// Comments :
//    Checks that spnegoElement has a valid value and is appropriate for
//    the SPNEGO token encapsulated by pSpnegoToken.
//
////////////////////////////////////////////////////////////////////////////

int IsValidSpnegoElement(SPNEGO_TOKEN* pSpnegoToken, SPNEGO_ELEMENT_TYPE spnegoElement)
{
	int   nReturn = 0;

	// Check boundaries
	if (spnegoElement > spnego_element_min &&
		spnegoElement < spnego_element_max)
	{

		// Check for appropriateness to token type
		if (SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType)
		{
			nReturn = (spnegoElement >= spnego_init_mechtypes &&
				spnegoElement <= spnego_init_mechListMIC);
		}
		else
		{
			nReturn = (spnegoElement >= spnego_targ_negResult &&
				spnegoElement <= spnego_targ_mechListMIC);
		}

	}  // IF boundary conditions are met

	return nReturn;
}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    CalculateElementArrayIndex
//
// Parameters:
//    [in]  pSpnegoToken   -  Points to SPNEGO_TOKEN data structure
//    [in]  spnegoElement  -  spnegoElement Type from enumeration
//
// Returns:
//    int   index in the SPNEGO_TOKEN element array that the element can
//          can be found
//
// Comments :
//    Based on the Token Type, calculates the index in the element array
//    at which the specified element can be found.
//
////////////////////////////////////////////////////////////////////////////

int CalculateElementArrayIndex(SPNEGO_TOKEN* pSpnegoToken, SPNEGO_ELEMENT_TYPE spnegoElement)
{
	int   nReturn = 0;

	// Offset is difference between value and initial element identifier
	// (these differ based on ucTokenType)

	if (SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType)
	{
		nReturn = spnegoElement - spnego_init_mechtypes;
	}
	else
	{
		nReturn = spnegoElement - spnego_targ_negResult;
	}

	return nReturn;
}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    InitTokenFromBinary
//
// Parameters:
//    [in]  ucCopyData     -  Flag indicating if data should be copied
//    [in]  ulFlags        -  Flags value for structure
//    [in]  pnTokenData    -  Binary Token Data
//    [in]  ulLength       -  Length of the data
//    [out] ppSpnegoToken  -  Pointer to call allocated SPNEGO Token
//                            data structure
//
// Returns:
//    int   Success - SPNEGO_E_SUCCESS
//          Failure - SPNEGO API Error code
//
// Comments :
//    Allocates a SPNEGO_TOKEN data structure and fills it out as
//    appropriate based in the flags passed into the function.
//
////////////////////////////////////////////////////////////////////////////


// Initializes SPNEGO_TOKEN structure from DER encoded binary data
int InitTokenFromBinary(unsigned char ucCopyData, unsigned long ulFlags,
	unsigned char* pbTokenData, unsigned long ulLength,
	SPNEGO_TOKEN** ppSpnegoToken)
{
	int            nReturn = SPNEGO_E_INVALID_PARAMETER;
	SPNEGO_TOKEN* pSpnegoToken = NULL;
	unsigned char* pbFirstElement = NULL;
	long           nTokenLength = 0L;
	long           nRemainingTokenLength = 0L;

	// Basic Parameter Validation

	if (NULL != pbTokenData &&
		NULL != ppSpnegoToken &&
		0L != ulLength)
	{

		//
		// Allocate the empty token, then initialize the data structure.
		//

		pSpnegoToken = AllocEmptySpnegoToken(ucCopyData, ulFlags, pbTokenData, ulLength);

		if (NULL != pSpnegoToken)
		{

			// Copy the binary data locally


			// Initialize the token type
			if ((nReturn = InitSpnegoTokenType(pSpnegoToken, &nTokenLength,
				&nRemainingTokenLength, &pbFirstElement))
				== SPNEGO_E_SUCCESS)
			{

				// Initialize the element array
				if ((nReturn = InitSpnegoTokenElements(pSpnegoToken, pbFirstElement,
					nRemainingTokenLength))
					== SPNEGO_E_SUCCESS)
				{
					*ppSpnegoToken = pSpnegoToken;
				}

			}  // IF Init Token Type

			// Cleanup on error condition
			if (SPNEGO_E_SUCCESS != nReturn)
			{
				spnegoFreeData(pSpnegoToken);
			}

		}
		else
		{
			nReturn = SPNEGO_E_OUT_OF_MEMORY;
		}

	}  // IF Valid parameters


	return nReturn;
}
