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

/////////////////////////////////////////////////////////////
//
// SPNEGO.C
//
// SPNEGO Token Handler Source File
//
// Contains implementation of SPNEGO Token Handling API
// as defined in SPNEGO.H.
//
/////////////////////////////////////////////////////////////

#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[];


/**********************************************************************/
/**                                                                  **/
/**                                                                  **/
/**                                                                  **/
/**                                                                  **/
/**               SPNEGO Token Handler API implementation            **/
/**                                                                  **/
/**                                                                  **/
/**                                                                  **/
/**                                                                  **/
/**********************************************************************/


/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    spnegoInitFromBinary
//
// Parameters:
//    [in]  pbTokenData       -  Binary Token Data
//    [in]  ulLength          -  Length of binary Token Data
//    [out] phSpnegoToken     -  SPNEGO_TOKEN_HANDLE pointer
//
// Returns:
//    int   Success - SPNEGO_E_SUCCESS
//          Failure - SPNEGO API Error code
//
// Comments :
//    Initializes a SPNEGO_TOKEN_HANDLE from the supplied
//    binary data.  Data is copied locally.  Returned data structure
//    must be freed by calling spnegoFreeData().
//
////////////////////////////////////////////////////////////////////////////

int spnegoInitFromBinary(unsigned char* pbTokenData, unsigned long ulLength, SPNEGO_TOKEN_HANDLE* phSpnegoToken)
{
	int            nReturn = SPNEGO_E_INVALID_PARAMETER;
	SPNEGO_TOKEN** ppSpnegoToken = (SPNEGO_TOKEN * *)phSpnegoToken;

	// Pass off to a handler function that allows tighter control over how the token structure
	// is handled.  In this case, we want the token data copied and we want the associated buffer
	// freed.
	nReturn = InitTokenFromBinary(SPNEGO_TOKEN_INTERNAL_COPYDATA,
		SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA, pbTokenData,
		ulLength, ppSpnegoToken);

	return nReturn;
}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    spnegoCreateNegTokenInit
//
// Parameters:
//    [in]  MechType          -  MechType to specify in MechTypeList element
//    [in]  ucContextFlags    -  Context Flags element value
//    [in]  pbMechToken       -  Pointer to binary MechToken Data
//    [in]  ulMechTokenLen    -  Length of MechToken Data
//    [in]  pbMechListMIC     -  Pointer to binary MechListMIC Data
//    [in]  ulMechListMICLen  -  Length of MechListMIC Data
//    [out] phSpnegoToken     -  SPNEGO_TOKEN_HANDLE pointer
//
// Returns:
//    int   Success - SPNEGO_E_SUCCESS
//          Failure - SPNEGO API Error code
//
// Comments :
//    Initializes a SPNEGO_TOKEN_HANDLE for a NegTokenInit type
//    from the supplied parameters.  ucContextFlags may be 0 or must be
//    a valid flag combination.  MechToken data can be NULL - if not, it
//    must correspond to the MechType.  MechListMIC can also be NULL.
//    Returned data structure must be freed by calling spnegoFreeData().
//
////////////////////////////////////////////////////////////////////////////

int spnegoCreateNegTokenInit(SPNEGO_MECH_OID MechType,
	unsigned char ucContextFlags, unsigned char* pbMechToken,
	unsigned long ulMechTokenLen, unsigned char* pbMechListMIC,
	unsigned long ulMechListMICLen, SPNEGO_TOKEN_HANDLE* phSpnegoToken)
{
	int   nReturn = SPNEGO_E_INVALID_PARAMETER;
	long  nTokenLength = 0L;
	long  nInternalTokenLength = 0L;
	unsigned char* pbTokenData = NULL;
	SPNEGO_TOKEN** ppSpnegoToken = (SPNEGO_TOKEN * *)phSpnegoToken;

	if (NULL != ppSpnegoToken &&
		IsValidMechOid(MechType) &&
		IsValidContextFlags(ucContextFlags))
	{
		// Get the actual token size

		if ((nReturn = CalculateMinSpnegoInitTokenSize(ulMechTokenLen, ulMechListMICLen,
			MechType, (ucContextFlags != 0L),
			&nTokenLength, &nInternalTokenLength))
			== SPNEGO_E_SUCCESS)
		{
			// Allocate a buffer to hold the data.
			pbTokenData = calloc(1, nTokenLength);

			if (NULL != pbTokenData)
			{

				// Now write the token
				if ((nReturn = CreateSpnegoInitToken(MechType,
					ucContextFlags, pbMechToken,
					ulMechTokenLen, pbMechListMIC,
					ulMechListMICLen, pbTokenData,
					nTokenLength, nInternalTokenLength))
					== SPNEGO_E_SUCCESS)
				{

					// This will copy our allocated pointer, and ensure that the sructure cleans
					// up the data later
					nReturn = InitTokenFromBinary(SPNEGO_TOKEN_INTERNAL_COPYPTR,
						SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA,
						pbTokenData, nTokenLength, ppSpnegoToken);

				}

				// Cleanup on failure
				if (SPNEGO_E_SUCCESS != nReturn)
				{
					free(pbTokenData);
				}

			}  // IF alloc succeeded
			else
			{
				nReturn = SPNEGO_E_OUT_OF_MEMORY;
			}

		}  // If calculated token size

	}  // IF Valid Parameters

	return nReturn;
}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    spnegoCreateNegTokenTarg
//
// Parameters:
//    [in]  MechType          -  MechType to specify in supported MechType element
//    [in]  spnegoNegResult   -  NegResult value
//    [in]  pbMechToken       -  Pointer to response MechToken Data
//    [in]  ulMechTokenLen    -  Length of MechToken Data
//    [in]  pbMechListMIC     -  Pointer to binary MechListMIC Data
//    [in]  ulMechListMICLen  -  Length of MechListMIC Data
//    [out] phSpnegoToken     -  SPNEGO_TOKEN_HANDLE pointer
//
// Returns:
//    int   Success - SPNEGO_E_SUCCESS
//          Failure - SPNEGO API Error code
//
// Comments :
//    Initializes a SPNEGO_TOKEN_HANDLE for a NegTokenTarg type
//    from the supplied parameters.  MechToken data can be NULL - if not,
//    it must correspond to the MechType.  MechListMIC can also be NULL.
//    Returned data structure must be freed by calling spnegoFreeData().
//
////////////////////////////////////////////////////////////////////////////

int spnegoCreateNegTokenTarg(SPNEGO_MECH_OID MechType,
	SPNEGO_NEGRESULT spnegoNegResult, unsigned char* pbMechToken,
	unsigned long ulMechTokenLen, unsigned char* pbMechListMIC,
	unsigned long ulMechListMICLen, SPNEGO_TOKEN_HANDLE* phSpnegoToken)
{
	int   nReturn = SPNEGO_E_INVALID_PARAMETER;
	long  nTokenLength = 0L;
	long  nInternalTokenLength = 0L;
	unsigned char* pbTokenData = NULL;
	SPNEGO_TOKEN** ppSpnegoToken = (SPNEGO_TOKEN * *)phSpnegoToken;

	//
	// spnego_mech_oid_NotUsed and spnego_negresult_NotUsed
	// are okay here, however a valid MechOid is required
	// if spnego_negresult_success or spnego_negresult_incomplete
	// is specified.
	//

	if (NULL != ppSpnegoToken &&

		(IsValidMechOid(MechType) ||
			spnego_mech_oid_NotUsed == MechType) &&

			(IsValidNegResult(spnegoNegResult) ||
				spnego_negresult_NotUsed == spnegoNegResult) &&

		!(!IsValidMechOid(MechType) &&
		(spnego_negresult_success == spnegoNegResult ||
			spnego_negresult_incomplete == spnegoNegResult)))
	{

		// Get the actual token size

		if ((nReturn = CalculateMinSpnegoTargTokenSize(MechType, spnegoNegResult, ulMechTokenLen,
			ulMechListMICLen, &nTokenLength,
			&nInternalTokenLength))
			== SPNEGO_E_SUCCESS)
		{
			// Allocate a buffer to hold the data.
			pbTokenData = calloc(1, nTokenLength);

			if (NULL != pbTokenData)
			{

				// Now write the token
				if ((nReturn = CreateSpnegoTargToken(MechType,
					spnegoNegResult, pbMechToken,
					ulMechTokenLen, pbMechListMIC,
					ulMechListMICLen, pbTokenData,
					nTokenLength, nInternalTokenLength))
					== SPNEGO_E_SUCCESS)
				{

					// This will copy our allocated pointer, and ensure that the sructure cleans
					// up the data later
					nReturn = InitTokenFromBinary(SPNEGO_TOKEN_INTERNAL_COPYPTR,
						SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA,
						pbTokenData, nTokenLength, ppSpnegoToken);

				}

				// Cleanup on failure
				if (SPNEGO_E_SUCCESS != nReturn)
				{
					free(pbTokenData);
				}

			}  // IF alloc succeeded
			else
			{
				nReturn = SPNEGO_E_OUT_OF_MEMORY;
			}

		}  // If calculated token size

	}  // IF Valid Parameters

	return nReturn;
}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    spnegoTokenGetBinary
//
// Parameters:
//    [in]     hSpnegoToken   -  Initialized SPNEGO_TOKEN_HANDLE
//    [out]    pbTokenData    -  Buffer to copy token into
//    [in/out] pulDataLen     -  Length of pbTokenData buffer, filled out
//                               with actual size used upon function return.
//
// Returns:
//    int   Success - SPNEGO_E_SUCCESS
//          Failure - SPNEGO API Error code
//
// Comments :
//    Copies binary SPNEGO token data from hSpnegoToken into the user
//    supplied buffer.  If pbTokenData is NULL, or the value in pulDataLen
//    is too small, the function will return SPNEGO_E_BUFFER_TOO_SMALL and
//    fill out pulDataLen with the minimum required buffer size.
//
////////////////////////////////////////////////////////////////////////////

int spnegoTokenGetBinary(SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbTokenData,
	unsigned long* pulDataLen)
{
	int   nReturn = SPNEGO_E_INVALID_PARAMETER;
	SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*)hSpnegoToken;

	// Check parameters - pbTokenData is optional
	if (IsValidSpnegoToken(pSpnegoToken) &&
		NULL != pulDataLen)
	{

		// Check for Buffer too small conditions
		if (NULL == pbTokenData ||
			pSpnegoToken->ulBinaryDataLen > * pulDataLen)
		{
			*pulDataLen = pSpnegoToken->ulBinaryDataLen;
			nReturn = SPNEGO_E_BUFFER_TOO_SMALL;
		}
		else
		{
			memcpy(pbTokenData, pSpnegoToken->pbBinaryData, pSpnegoToken->ulBinaryDataLen);
			*pulDataLen = pSpnegoToken->ulBinaryDataLen;
			nReturn = SPNEGO_E_SUCCESS;
		}

	}  // IF parameters OK

	return nReturn;;
}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    spnegoFreeData
//
// Parameters:
//    [in]     hSpnegoToken   -  Initialized SPNEGO_TOKEN_HANDLE
//
// Returns:
//    void
//
// Comments :
//    Frees up resources consumed by hSpnegoToken.  The supplied data
//    pointer is invalidated by this function.
//
////////////////////////////////////////////////////////////////////////////

void spnegoFreeData(SPNEGO_TOKEN_HANDLE hSpnegoToken)
{
	FreeSpnegoToken((SPNEGO_TOKEN*)hSpnegoToken);
	return;
}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    spnegoGetTokenType
//
// Parameters:
//    [in]  hSpnegoToken      -  Initialized SPNEGO_TOKEN_HANDLE
//    [out] piTokenType       -  Filled out with token type value.
//
// Returns:
//    int   Success - SPNEGO_E_SUCCESS
//          Failure - SPNEGO API Error code
//
// Comments :
//    The function will analyze hSpnegoToken and return the appropriate
//    type in piTokenType.
//
////////////////////////////////////////////////////////////////////////////

int spnegoGetTokenType(SPNEGO_TOKEN_HANDLE hSpnegoToken, int* piTokenType)
{
	int   nReturn = SPNEGO_E_INVALID_PARAMETER;
	SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*)hSpnegoToken;

	// Check parameters
	if (IsValidSpnegoToken(pSpnegoToken) &&
		NULL != piTokenType &&
		pSpnegoToken)
	{

		// Check that the type in the structure makes sense
		if (SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType ||
			SPNEGO_TOKEN_TARG == pSpnegoToken->ucTokenType)
		{
			*piTokenType = pSpnegoToken->ucTokenType;
			nReturn = SPNEGO_E_SUCCESS;
		}

	}  // IF parameters OK

	return nReturn;
}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    spnegoIsMechTypeAvailable
//
// Parameters:
//    [in]  hSpnegoToken      -  Initialized SPNEGO_TOKEN_HANDLE
//    [in]  MechOID           -  MechOID to search MechTypeList for
//    [out] piMechTypeIndex   -  Filled out with index in MechTypeList
//                               element if MechOID is found.
//
// Returns:
//    int   Success - SPNEGO_E_SUCCESS
//          Failure - SPNEGO API Error code
//
// Comments :
//    hSpnegoToken must reference a token of type NegTokenInit.  The
//    function will search the MechTypeList element for an OID corresponding
//    to the specified MechOID.  If one is found, the index (0 based) will
//    be passed into the piMechTypeIndex parameter.
//
////////////////////////////////////////////////////////////////////////////

// Returns the Initial Mech Type in the MechList element in the NegInitToken.
int spnegoIsMechTypeAvailable(SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_MECH_OID MechOID, int* piMechTypeIndex)
{
	int   nReturn = SPNEGO_E_INVALID_PARAMETER;
	SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*)hSpnegoToken;

	// Check parameters
	if (IsValidSpnegoToken(pSpnegoToken) &&
		NULL != piMechTypeIndex &&
		IsValidMechOid(MechOID) &&
		SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType)
	{

		// Check if MechList is available
		if (pSpnegoToken->aElementArray[SPNEGO_INIT_MECHTYPES_ELEMENT].iElementPresent
			== SPNEGO_TOKEN_ELEMENT_AVAILABLE)
		{
			// Locate the MechOID in the list element
			nReturn = FindMechOIDInMechList(
				&pSpnegoToken->aElementArray[SPNEGO_INIT_MECHTYPES_ELEMENT],
				MechOID, piMechTypeIndex);
		}
		else
		{
			nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE;
		}

	}  // IF parameters OK

	return nReturn;;
}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    spnegoGetContextFlags
//
// Parameters:
//    [in]  hSpnegoToken      -  Initialized SPNEGO_TOKEN_HANDLE
//    [out] pucContextFlags   -  Filled out with ContextFlags value.
//
// Returns:
//    int   Success - SPNEGO_E_SUCCESS
//          Failure - SPNEGO API Error code
//
// Comments :
//    hSpnegoToken must reference a token of type NegTokenInit.  The
//    function will copy data from the ContextFlags element into the
//    location pucContextFlags points to.  Note that the function will
//    fail if the actual ContextFlags data appears invalid.
//
////////////////////////////////////////////////////////////////////////////

int spnegoGetContextFlags(SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pucContextFlags)
{
	int   nReturn = SPNEGO_E_INVALID_PARAMETER;
	SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*)hSpnegoToken;

	// Check parameters
	if (IsValidSpnegoToken(pSpnegoToken) &&
		NULL != pucContextFlags &&
		SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType)
	{

		// Check if ContextFlags is available
		if (pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].iElementPresent
			== SPNEGO_TOKEN_ELEMENT_AVAILABLE)
		{
			// The length should be two, the value should show a 1 bit difference in the difference byte, and
			// the value must be valid
			if (pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].nDatalength == SPNEGO_NEGINIT_MAXLEN_REQFLAGS &&
				pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].pbData[0] == SPNEGO_NEGINIT_REQFLAGS_BITDIFF &&
				IsValidContextFlags(pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].pbData[1]))
			{
				*pucContextFlags = pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].pbData[1];
				nReturn = SPNEGO_E_SUCCESS;
			}
			else
			{
				nReturn = SPNEGO_E_INVALID_ELEMENT;
			}

		}
		else
		{
			nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE;
		}

	}  // IF parameters OK

	return nReturn;;
}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    spnegoGetNegotiationResult
//
// Parameters:
//    [in]  hSpnegoToken      -  Initialized SPNEGO_TOKEN_HANDLE
//    [out] pnegResult        -  Filled out with NegResult value.
//
// Returns:
//    int   Success - SPNEGO_E_SUCCESS
//          Failure - SPNEGO API Error code
//
// Comments :
//    hSpnegoToken must reference a token of type NegTokenTarg.  The
//    function will copy data from the NegResult element into the
//    location pointed to by pnegResult.  Note that the function will
//    fail if the actual NegResult data appears invalid.
//
////////////////////////////////////////////////////////////////////////////

int spnegoGetNegotiationResult(SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_NEGRESULT* pnegResult)
{
	int   nReturn = SPNEGO_E_INVALID_PARAMETER;
	SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*)hSpnegoToken;

	// Check parameters
	if (IsValidSpnegoToken(pSpnegoToken) &&
		NULL != pnegResult &&
		SPNEGO_TOKEN_TARG == pSpnegoToken->ucTokenType)
	{

		// Check if NegResult is available
		if (pSpnegoToken->aElementArray[SPNEGO_TARG_NEGRESULT_ELEMENT].iElementPresent
			== SPNEGO_TOKEN_ELEMENT_AVAILABLE)
		{
			// Must be 1 byte long and a valid value
			if (pSpnegoToken->aElementArray[SPNEGO_TARG_NEGRESULT_ELEMENT].nDatalength == SPNEGO_NEGTARG_MAXLEN_NEGRESULT &&
				IsValidNegResult(*pSpnegoToken->aElementArray[SPNEGO_TARG_NEGRESULT_ELEMENT].pbData))
			{
				*pnegResult = *pSpnegoToken->aElementArray[SPNEGO_TARG_NEGRESULT_ELEMENT].pbData;
				nReturn = SPNEGO_E_SUCCESS;
			}
			else
			{
				nReturn = SPNEGO_E_INVALID_ELEMENT;
			}
		}
		else
		{
			nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE;
		}

	}  // IF parameters OK

	return nReturn;;
}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    spnegoGetSupportedMechType
//
// Parameters:
//    [in]  hSpnegoToken      -  Initialized SPNEGO_TOKEN_HANDLE
//    [out] pMechOID          -  Filled out with Supported MechType value.
//
// Returns:
//    int   Success - SPNEGO_E_SUCCESS
//          Failure - SPNEGO API Error code
//
// Comments :
//    hSpnegoToken must reference a token of type NegTokenTarg.  The
//    function will check the Supported MechType element, and if it
//    corresponds to a supported MechType ( spnego_mech_oid_Kerberos_V5_Legacy
//    or spnego_mech_oid_Kerberos_V5 ), will set the location pointed
//    to by pMechOID equal to the appropriate value.
//
////////////////////////////////////////////////////////////////////////////

int spnegoGetSupportedMechType(SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_MECH_OID* pMechOID)
{
	int   nReturn = SPNEGO_E_INVALID_PARAMETER;
	int   nCtr = 0L;
	long  nLength = 0L;
	SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*)hSpnegoToken;

	// Check parameters
	if (IsValidSpnegoToken(pSpnegoToken) &&
		NULL != pMechOID &&
		SPNEGO_TOKEN_TARG == pSpnegoToken->ucTokenType)
	{

		// Check if MechList is available
		if (pSpnegoToken->aElementArray[SPNEGO_TARG_SUPPMECH_ELEMENT].iElementPresent
			== SPNEGO_TOKEN_ELEMENT_AVAILABLE)
		{

			for (nCtr = 0;
				nReturn != SPNEGO_E_SUCCESS &&
				g_stcMechOIDList[nCtr].eMechanismOID != spnego_mech_oid_NotUsed;
				nCtr++)
			{

				if ((nReturn = ASNDerCheckOID(
					pSpnegoToken->aElementArray[SPNEGO_TARG_SUPPMECH_ELEMENT].pbData,
					nCtr,
					pSpnegoToken->aElementArray[SPNEGO_TARG_SUPPMECH_ELEMENT].nDatalength,
					&nLength)) == SPNEGO_E_SUCCESS)
				{
					*pMechOID = nCtr;
				}

			}  // For enum MechOIDs


		}
		else
		{
			nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE;
		}

	}  // IF parameters OK

	return nReturn;;
}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    spnegoTokenGetMechToken
//
// Parameters:
//    [in]     hSpnegoToken   -  Initialized SPNEGO_TOKEN_HANDLE
//    [out]    pbTokenData    -  Buffer to copy MechToken into
//    [in/out] pulDataLen     -  Length of pbTokenData buffer, filled out
//                               with actual size used upon function return.
//
// Returns:
//    int   Success - SPNEGO_E_SUCCESS
//          Failure - SPNEGO API Error code
//
// Comments :
//    hSpnegoToken can point to either NegTokenInit or a NegTokenTarg token.
//    The function will copy the MechToken (the initial MechToken if
//    NegTokenInit, the response MechToken if NegTokenTarg) from the
//    underlying token into the buffer pointed to by pbTokenData.  If
//    pbTokenData is NULL, or the value in pulDataLen is too small, the
//    function will return SPNEGO_E_BUFFER_TOO_SMALL and fill out pulDataLen
//    with the minimum required buffer size.  The token can then be passed
//    to a GSS-API function for processing.
//
////////////////////////////////////////////////////////////////////////////

int spnegoGetMechToken(SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbTokenData, unsigned long* pulDataLen)
{
	int   nReturn = SPNEGO_E_INVALID_PARAMETER;
	SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*)hSpnegoToken;
	SPNEGO_ELEMENT* pSpnegoElement = NULL;

	// Check parameters
	if (IsValidSpnegoToken(pSpnegoToken) &&
		NULL != pulDataLen)
	{

		// Point at the proper Element
		if (SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType)
		{
			pSpnegoElement = &pSpnegoToken->aElementArray[SPNEGO_INIT_MECHTOKEN_ELEMENT];
		}
		else
		{
			pSpnegoElement = &pSpnegoToken->aElementArray[SPNEGO_TARG_RESPTOKEN_ELEMENT];
		}

		// Check if MechType is available
		if (SPNEGO_TOKEN_ELEMENT_AVAILABLE == pSpnegoElement->iElementPresent)
		{
			// Check for Buffer too small conditions
			if (NULL == pbTokenData ||
				pSpnegoElement->nDatalength > * pulDataLen)
			{
				*pulDataLen = pSpnegoElement->nDatalength;
				nReturn = SPNEGO_E_BUFFER_TOO_SMALL;
			}
			else
			{
				// Copy Memory
				memcpy(pbTokenData, pSpnegoElement->pbData, pSpnegoElement->nDatalength);
				*pulDataLen = pSpnegoElement->nDatalength;
				nReturn = SPNEGO_E_SUCCESS;
			}
		}
		else
		{
			nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE;
		}

	}  // IF parameters OK

	return nReturn;;
}

/////////////////////////////////////////////////////////////////////////////
//
// Function:
//    spnegoTokenGetMechListMIC
//
// Parameters:
//    [in]     hSpnegoToken   -  Initialized SPNEGO_TOKEN_HANDLE
//    [out]    pbTokenData    -  Buffer to copy MechListMIC data into
//    [in/out] pulDataLen     -  Length of pbTokenData buffer, filled out
//                               with actual size used upon function return.
//
// Returns:
//    int   Success - SPNEGO_E_SUCCESS
//          Failure - SPNEGO API Error code
//
// Comments :
//    hSpnegoToken can point to either NegTokenInit or a NegTokenTarg token.
//    The function will copy the MechListMIC data from the underlying token
//    into the buffer pointed to by pbTokenData.  If pbTokenData is NULL,
//    or the value in pulDataLen is too small, the function will return
//    SPNEGO_E_BUFFER_TOO_SMALL and fill out pulDataLen with the minimum
//    required buffer size.
//
////////////////////////////////////////////////////////////////////////////

int spnegoGetMechListMIC(SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbMICData, unsigned long* pulDataLen)
{
	int   nReturn = SPNEGO_E_INVALID_PARAMETER;
	SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*)hSpnegoToken;
	SPNEGO_ELEMENT* pSpnegoElement = NULL;

	// Check parameters
	if (IsValidSpnegoToken(pSpnegoToken) &&
		NULL != pulDataLen)
	{

		// Point at the proper Element
		if (SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType)
		{
			pSpnegoElement = &pSpnegoToken->aElementArray[SPNEGO_INIT_MECHLISTMIC_ELEMENT];
		}
		else
		{
			pSpnegoElement = &pSpnegoToken->aElementArray[SPNEGO_TARG_MECHLISTMIC_ELEMENT];
		}

		// Check if MechType is available
		if (SPNEGO_TOKEN_ELEMENT_AVAILABLE == pSpnegoElement->iElementPresent)
		{
			// Check for Buffer too small conditions
			if (NULL == pbMICData ||
				pSpnegoElement->nDatalength > * pulDataLen)
			{
				*pulDataLen = pSpnegoElement->nDatalength;
				nReturn = SPNEGO_E_BUFFER_TOO_SMALL;
			}
			else
			{
				// Copy Memory
				memcpy(pbMICData, pSpnegoElement->pbData, pSpnegoElement->nDatalength);
				*pulDataLen = pSpnegoElement->nDatalength;
				nReturn = SPNEGO_E_SUCCESS;
			}
		}
		else
		{
			nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE;
		}

	}  // IF parameters OK

	return nReturn;;
}

