HTTP-Based Cross-Platform Authentication by Using the Negotiate Protocol

 

Sanj Surati & Michael Muckin
Microsoft Consulting Services

December 2002

Applies to:
   Windows® 2000
   Non-Windows web servers
   Kerberos-capable clients

Summary: Learn how you can parse and create Simple And Protected Negotiate (SPNEGO) tokens on a non-Windows platform with the set of C functions described in this article. This article is the third and last in a series that describes the infrastructure and code required to implement Kerberos-based authentication in a cross-platform environment.

The series consists of three parts:

  • "Network Infrastructure"—Describes the infrastructure required to enable the solution.
  • "SPNEGO Tokens and the Negotiate Protocol"—Describes the negotiate protocol and binary layouts of information sent over the wire.
  • "SPNEGO Token Handler API"—Describes and provides the C source code for an API that will parse and create SPNEGO tokens.

Download Spnegotokenhandler.exe.

Contents

Introduction
SPNEGO Token Handler API
Summary
Appendix A - References

Introduction

Now that you have read the articles on "Network Infrastructure" and "SPNEGO Tokens and the Negotiate Protocol", this article will describe a set of C functions that you can use to parse and create SPNEGO tokens on a non-Windows platform.

Assumptions

Intranet web-based applications

This solution assumes the following to be true:

  • The use of this cross-platform authentication solution is intended for intranet applications only.
  • The Microsoft® Active Directory® directory service Kerberos implementation is the single user and authentication store.
  • MIT V5 Kerberos is implemented on the UNIX hosts.
  • This solution was verified on Solaris 2.8.

This article does NOT cover:

  • Custom Kerberos error handling
  • Ticket/Session expiration handling

This article assumes that the reader is familiar with Kerberos, the HTTP protocol and C. For an overview of Kerberos authentication in Windows® 2000 operating system, as well as definitions of important Kerberos-related terminology such as Key Distribution Center (KDC), Ticket Granting Service (TGS), keytab files, and so on, see Windows 2000 Kerberos Authentication. Additional resources are outlined in Appendix A.

Although this article describes a general cross-platform solution, for ease of reference, we will assume non-Windows 2000 web servers to be running a flavor of UNIX with MIT Kerberos V5—, which is the environment used to develop this solution. Additionally, although we reference Windows 2000 in this article, the same information is applicable to later versions of Windows (for example, Windows 7 and Windows Server 2008 R2).

Although we have supplied you with make files for Microsoft Visual C++® development system, the sample code that is provided with this article makes no platform-specific assumptions and can be compiled into a variety of projects.

SPNEGO Token Handler API

Overview

This section documents a set of function calls that can be used to parse and create SPNEGO tokens. The C source files included with this article implement the functions described here.

The intention of the API set is to provide platform-independent functionality for parsing and creating binary tokens for the Simple And Protected Negotiate (SPNEGO) mechanism. The API calls are documented in alphabetical order, with a parameter list, description, expected return codes and associated comments. Immediately following the function call documentation is an implementation example.

Note The SPNEGO Token Handler API only allows for creation and parsing of SPNEGO tokens. Additional code is required to interact with GSS-API calls, perform the HTTP Header exchanges and encode/decode the actual the SPNEGO tokens by using the base64 algorithm. Finally, after the GSS-API handshake is complete, gss_accept_sec_context() returns a name parameter that can be passed to gss_display_name() to determine the client's user-id and ensure access to resources.

Source Files

The source files are broken out as follows:

  • Spnego.h—Contains prototypes, definitions and enumerations for the SPNEGO Token Handler API. Users of the API must include this file in their projects.
  • Spnegoparse.h—Contains definitions and helper routines for handling SPNEGO encodings.
  • Derparse.h—Contains definitions and helper routines for parsing DER encodings as well as SPNEGO specific definitions.
  • Spnegodata.h—Contains sample data for testing the API.
  • Spnego.c—Contains implementations of SPNEGO Token Handler API functions.
  • Spnegoparse.c—Contains function implementations for handling SPNEGO BLOBs by using ASN.1 DER.
  • Derparse.c—Contains function implementations for handling ASN.1 DER.
  • Main.c—Contains calls to exercise the SPNEGO Token Handler API functions by using the sample data from Spnegodata.h.

The files are fully self-contained and do not use any calls outside of Standard C Library routines. They have all been compiled and linked by using MSVC++ v6.0 SP5 on Windows 2000 with Service Pack 2, as well as GCC v2.96 compiler on Redhat Linux v7.2.

When building the files, there is code that handles lengths differently based on byte-ordering. The default is Big-Endian; however, if you want to compile the code for Little-Endian, you must define __LITTLE_ENDIAN__ (for example, –D__LITTLE_ENDIAN__).

Parsing SPNEGO Tokens

The SPNEGO Token Handler API function spnegoInitFromBinary() can be passed a BLOB that is filled with SPNEGO data that it will copy and parse into a SPNEGO_TOKEN_HANDLE that can be passed to helper functions to extract the actual data. Without going into a lot of detail, the primary internal functions used to parse SPNEGO binary data are the following:

BLOB handlers

  • AllocEmptySpnegoToken()
  • FreeSpnegoToken(),InitSpnegoTokenElementArray)
  • InitSpnegoTokenType()
  • InitSpnegoTokenElements()
  • GetSpnegoInitTokenMechList()
  • InitSpnegoTokenElementFromBasicType()
  • InitSpnegoTokenElementFromOID()
  • FindMechOIDInMechList()
  • ValidateMechList()
  • IsValidMechOid()
  • IsValidContextFlags()
  • IsValidNegResult()
  • IsValidSpnegoToken()
  • IsValidSpnegoElement()
  • CalculateElementArrayIndex()

ASN DER readers

  • ASNDerGetLength()
  • ASNDerCheckToken()
  • ASNDerCheckOID()

The BLOB handlers use the ASN DER readers to extract and verify header tokens. Actual token elements (the ones described in the SPNEGO NegTokenInit and NegTokenTarg sequences) are saved off in an element array in the SpnegoToken data structure by storing their type, the element name, the length, and a pointer to an internally allocated BLOB that contains the actual data value.

The tokens are parsed per the details in the section entitled SPNEGO Token Layout.

Building SPNEGO Tokens

The SPNEGO Token Handler API functions spnegoBuildNegTokenInit() and spnegoBuildNegTokenTarg() can be passed data for building SPNEGO NegTokenInit and NegTokenTarg tokens that allocate and fill out an internal BLOB that is then used to initialize a SPNEGO_TOKEN_HANDLE. Following are the primary internal functions used by the API functions to generate the BLOBs:

BLOB handlers

  • CalculateMinSpnegoInitTokenSize()
  • CreateSpnegoInitToken()
  • CalculateMinSpnegoTargTokenSize()
  • CreateSpnegoTargToken()

ASN DER writers

  • ASNDerCalcNumLengthBytes()
  • ASNDerCalcTokenLength()
  • ASNDerCalcElementLength()
  • ASNDerCalcMechListLength()
  • ASNDerWriteLength()
  • ASNDerWriteToken()
  • ASNDerWriteOID()
  • ASNDerWriteMechList()
  • ASNDerWriteElement()

The basic algorithm used here requires us to calculate the size of the BLOB first, allocate the data, then fill it out. Things get a bit tricky when we write out tokens that specify the length of the following data. When this happens, we need to know the length of the following data before we actually write out the length because the number of bytes used to describe the length is dictated by the actual value being written. Because each element of the token can contain a variable length, tokens at the front need to reflect lengths of the elements with the bytes needed to write the lengths. By calculating token length and then writing the tokens out in reverse, we will always know how many bytes we've written so that everything remains accurate.

After the tokens have been created (again refer to SPNEGO Token Layout to see exactly how the tokens are laid out), in the allocated buffer, we use InitSpnegoTokenFromBinary() to initialize the final SPNEGO_TOKEN_HANDLE.

spnegoCreateNegTokenInit

Prototype

  int spnegoCreateNegTokenInit(

     
[in]   SPNEGO_MECH_OID       MechType,
   [in]   unsigned char         ucContextFlags,
   [in]   unsigned char*        pbMechToken,
   [in]   unsigned long         ulMechTokenLen,
   [in]   unsigned char*        pbMechListMIC,
   [in]   unsigned long         ulMechListMICLen,
   [out]  SPNEGO_TOKEN_HANDLE*  phSpnegoToken
   )

Parameters

Table 1. spnegoCreateNegTokenInit Parameters

Parameter Description Comments
MechType A value of the SPNEGO_MECH_OID enumeration that indicates the mechtype to specify in the MechTypeList. This is a required parameter. At this time, the only values that are supported are:
  • spnego_mech_oid_Kerberos_V5_Legacy
  • spnego_mech_oid_Kerberos_V5
ucContextFlags Context flags bit field parameter. This is an optional value, and you can set it to zero. If nonzero, bits can be any combination, as specified in the appropriate definitions. A value of zero indicates that no context flags should be specified in the token.
pbMechToken A pointer to binary data that contains the GSS token that corresponds to the MechType parameter. This is an optional parameter, and you can set it to NULL. This token is established by calling gss_init_sec_context() (GSS-API) or InitializeSecurityContext() (SSPI).
ulMechTokenLen The length of the binary data that the pbMechToken parameter points to. If pbMechToken is non-NULL, this value must be nonzero.  
pbMechListMIC A pointer to binary data that contains Message Integrity Check data. This is an optional parameter, and you can set it to NULL. This is established by calling gss_getMIC() (GSS-API) or MakeSignature() (SSPI) on the MechTypes list.
ulMechListMICLen The length of the binary data pbMechListMIC points to. If pbMechListMIC is non-NULL, this value must be nonzero.  
phSpnegoToken Returns a SPNEGO_TOKEN_HANDLE allocated by the function call. This is a required parameter and must be non-NULL. When you have finished using the handle, free it by calling the spnegoFreeData() function.

Description

This function will create a SPNEGO negTokenInit token by using the supplied parameters and initialize a SPNEGO_TOKEN data structure with the data. The caller can call spnegoTokenGetBinary() to access the underlying binary data—a GSS token that corresponds to the format described in RFC 2478. When you have finished using the token, free it by call the spnegoFreeData() function.

Return values

Table 2. spnegoCreateNegTokenInit Return Values

Return code Comments
SPNEGO_E_SUCCESS The operation completed successfully.
SPNEGO_E_INVALID_PARAMETER One of the supplied parameters was not valid.
SPNEGO_E_OUT_OF_MEMORY An internal memory allocation failed.

spnegoCreateNegTokenTarg

Prototype

  int spnegoCreateNegTokenTarg(

   [in]   SPNEGO_MECH_OID       MechType,
   [in]   SPNEGO_NEGRESULT      spnegoNegResult,
   [in]   unsigned char*        pbMechToken,
   [in]   unsigned long         ulMechTokenLen,
   [in]   unsigned char*        pbMechListMIC,
   [in]   unsigned long         ulMechListMICLen,
   [out]  SPNEGO_TOKEN_HANDLE*  phSpnegoToken
   )

Parameters

Table 3. spnegoCreateNegTokenTarg Parameters

Parameter Description Comments
MechType Indicates the supported MechType. This is an optional parameter, and you can set it to spnego_mech_oid_NotUsed. At this time, the only supported values are:
  • spnego_mech_oid_Kerberos_V5_Legacy
  • spnego_mech_oid_Kerberos_V5
  • spnego_mech_oid_NotUsed
Note   spnego_mech_oid_NotUsed cannot be used if the spnegoNegResult parameter is set to spnego_negresult_success or spnego_negresult_incomplete.
spnegoNegResult The NegResult value. This is an optional parameter, and you can set it to spnego_negresult_NotUsed. The supported values are as follows:
  • spnego_negresult_success
  • spnego_negresult_incomplete
  • spnego_negresult_rejected
  • spnego_negresult_NotUsed
pbMechToken A pointer to binary data that contains a GSS token that corresponds to the MechType parameter. This is an optional parameter, and you can set it to NULL. This token is established by calling gss_init_sec_context() (GSS-API) or InitializeSecurityContext() (SSPI) for an Init type token, or gss_accept_sec_context() (GSS-API) or AcceptSecurityContext() (SSPI) for a response token.
ulMechTokenLen The length of the binary data the pbMechToken parameter points to. If pbMechToken is non-NULL, this value must be nonzero.  
pbMechListMIC A pointer to binary data that contains Message Integrity Check data. This is an optional parameter, and you can set it to NULL. This is established by calling gss_getMIC() (GSS-API) or MakeSignature() (SSPI) on the MechTypes list (from the negTokenInit).
ulMechListMICLen The length of the binary data pbMechListMIC points to. If pbMechListMIC is non-NULL, this value must be nonzero.  
phSpnegoToken A SPNEGO_TOKEN_HANDLE allocated by the function call. This is a required parameter and must be non-NULL. When you have finished using the resulting SPNEGO_TOKEN_HANDLE handle, free it by calling the spnegoFreeData() function.

Description

This function will create a SPNEGO negTokenTarg token by using the supplied parameters and initialize a SPNEGO_TOKEN data structure with the data. The caller can call the spnegoTokenGetBinary() function to access the underlying binary data—a GSS token that corresponds to the format described in RFC 2478. When you have finished using the token, free it by calling the spnegoFreeData() function.

Return values

Table 4. spnegoCreateNegTokenTarg Return Values

Return code Comments
SPNEGO_E_SUCCESS The operation completed successfully.
SPNEGO_E_INVALID_PARAMETER One of the supplied parameters was not valid.
SPNEGO_E_OUT_OF_MEMORY An internal memory allocation failed.

spnegoFreeData

Prototype

  int spnegoFreeData(

   [in]   SPNEGO_TOKEN_HANDLE  hSpnegoToken
   )

Parameters

Table 5. spnegoFreeData Parameters

Parameter Description Comments
hSpnegoToken A properly allocated SPNEGO_TOKEN_HANDLE. This parameter is required. The handle must have been allocated by one of the following calls:
  • spnegoCreateNegTokenInit()
  • spnegoCreateNegTokenTarg()
  • spnegoInitFromBinary()

The behavior of the function is undefined if it is passed a SPNEGO_TOKEN_HANDLE handle that was initialized or allocated by other means.

Description

This function will release all resources associated with the supplied token handle.

Return values

Not applicable. Return type is void.

spnegoGetContextFlags

Prototype

  int spnegoGetContextFlags(

   [in]   SPNEGO_TOKEN_HANDLE  hSpnegoToken,
   [out]  unsigned char*       pucContextFlags
   )

Parameters

Table 6. spnegoGetContextFlags Parameters

Parameter Description Comments
hSpnegoToken A properly allocated SPNEGO_TOKEN_HANDLE. This parameter is required. The handle must have been allocated by one of the following calls:
  • spnegoCreateNegTokenInit()
  • spnegoInitFromBinary()

The behavior of the function is undefined if it is passed a SPNEGO_TOKEN_HANDLE handle that was initialized or allocated by other means.

pucContextFlags A pointer to a location to store the ContextFlags value retrieved from the token. This parameter is required.  

Description

This function will copy the ContextFlags value from a SPNEGO token of type negTokenInit into pucContextFlags. For the function to succeed, the value must exist in the token and must have the proper format and values.

Supported flags for ContextFlags are any combination of the following:

SPNEGO_NEGINIT_CONTEXT_DELEG_FLAG 0x80
SPNEGO_NEGINIT_CONTEXT_MUTUAL_FLAG 0x40
SPNEGO_NEGINIT_CONTEXT_REPLAY_FLAG 0x20
SPNEGO_NEGINIT_CONTEXT_SEQUENCE_FLAG 0x10
SPNEGO_NEGINIT_CONTEXT_ANON_FLAG 0x8
SPNEGO_NEGINIT_CONTEXT_CONF_FLAG 0x4
SPNEGO_NEGINIT_CONTEXT_INTEG_FLAG 0x2

These actual flag values must again be translated into appropriate values for calls to gss_accept_security_context().

Return values

Table 7. spnegoGetContextFlags Return Values

Return code Comments
SPNEGO_E_SUCCESS The operation completed successfully.
SPNEGO_E_INVALID_PARAMETER One of the supplied parameters was not valid.
SPNEGO_E_ELEMENT_UNAVAILABLE The ContextFlags element is not available.
SPNEGO_E_INVALID_ELEMENT The ContextFlags element is available but not valid.

spnegoGetMechListMIC

Prototype

  int spnegoGetMechListMIC(

   [in]      SPNEGO_TOKEN_HANDLE  hSpnegoToken,
   [out]     unsigned char*       pbMICData,
   [in,out]  unsigned long*       pulDataLen,
   )

Parameters

Table 8. spnegoGetMechListMIC Parameters

Parameter Description Comments
hSpnegoToken A properly allocated SPNEGO_TOKEN_HANDLE. This parameter is required. The handle must have been allocated by one of the following calls:
  • spnegoCreateNegTokenInit()
  • spnegoCreateNegTokenTarg()
  • spnegoInitFromBinary()

The behavior of the function is undefined if it is passed a SPNEGO_TOKEN_HANDLE handle that was initialized or allocated by other means.

pbMICData A pointer to copy binary MIC data into. This is an optional parameter, and you can set it to NULL.  
pulDataLen A pointer to a variable that contains the length of the binary MIC data pointed to by the pbMICData parameter. This parameter is required. If pbMICData is non-NULL, pulDataLen must be set to the length of the data buffer. After the function returns, the value will be set equal to the number of bytes copied into the buffer, or in the case of a buffer-too-small error, the buffer size required to hold the data.

Description

This function is used to retrieve the Message Integrity Check (MIC) data from a SPNEGO token of type negTokenInit or negTokenTarg. The token is copied from within the underlying binary SPNEGO data into the buffer pointed to by the pbMICData parameter. If pbMICData is NULL or the value in pulDataLen indicates a buffer size too small to hold the MIC data, the function returns SPNEGO_E_BUFFER_TOO_SMALL and sets pulDataLen equal to the size required to hold the MIC data.

Return values

Table 9. spnegoGetMechListMIC Return Values

Return code Comments
SPNEGO_E_SUCCESS The operation completed successfully.
SPNEGO_E_INVALID_PARAMETER One of the supplied parameters was not valid.
SPNEGO_E_ELEMENT_UNAVAILABLE MIC Data element is not available.
SPNEGO_E_INVALID_ELEMENT MIC Data element is available but not valid.
SPNEGO_E_BUFFER_TOO_SMALL The supplied buffer is too small to contain the MIC Data. The pulDataLen parameter will be set equal to the required value.

spnegoGetMechToken

Prototype

  int spnegoGetMechToken(

   [in]      SPNEGO_TOKEN_HANDLE  hSpnegoToken,
   [out]     unsigned char*       pbTokenData,
   [in,out]  unsigned long*       pulDataLen,
   )

Parameter

Table 10. spnegoGetMechToken Parameters

Parameter Description Comments
hSpnegoToken A properly allocated SPNEGO_TOKEN_HANDLE. This parameter is required. The handle must have been allocated by one of the following calls:
  • spnegoCreateNegTokenInit()
  • spnegoCreateNegTokenTarg()
  • spnegoInitFromBinary()

The behavior of the function is undefined if it is passed a SPNEGO_TOKEN_HANDLE handle that was initialized or allocated by other means.

pbTokenData A pointer to a variable to receive the binary Mech token data. This is an optional parameter, and you can set it to NULL.  
pulDataLen A pointer to a variable that contains the length of pbTokenData. This parameter is required. If the pbTokenData parameter is non-NULL, pulDataLen must be set to the length of the data buffer. After the function returns, the value is set equal to the number of bytes copied into the buffer, or in the case of a buffer-too-small error, the buffer size required to hold the data.

Description

This function is used to retrieve Mech token data from a SPNEGO token of type negTokenInit or negTokenTarg. The token is copied from within the underlying binary SPNEGO data into the buffer pointed to by the pbTokenData parameter. If pbTokenData is NULL or the value in pulDataLen indicates a buffer that is too small to hold the Mech token data, the function returns SPNEGO_E_BUFFER_TOO_SMALL and sets the pulDataLen parameter equal to the size required to hold the Mech.

In a negTokenInit token, the Mech corresponds to the MechToken element. In a negTokenTarg, the Mech corresponds to the responseToken element.

Return values

Table 11. spnegoGetMechToken Return Values

Return code Comments
SPNEGO_E_SUCCESS The operation completed successfully.
SPNEGO_E_INVALID_PARAMETER One of the supplied parameters was not valid.
SPNEGO_E_ELEMENT_UNAVAILABLE The Mech element is not available.
SPNEGO_E_INVALID_ELEMENT The Mech element is available but not valid.
SPNEGO_E_BUFFER_TOO_SMALL The supplied buffer is too small to contain the Mech. The pulDataLen parameter will be set equal to the required value.

spnegoGetNegotiationResult

Prototype

  int spnegoGetNegotiattionResult(

   [in]   SPNEGO_TOKEN_HANDLE  hSpnegoToken,
   [out]  SPNEGO_NEG_RESULT*   pnegResult
   )

Parameters

Table 12. spnegoGetNegotiationResult Parameters

Parameter Description Comments
hSpnegoToken A properly allocated SPNEGO_TOKEN_HANDLE. This parameter is required. The handle must have been allocated by one of the following calls:
  • spnegoCreateNegTokenTarg()
  • spnegoInitFromBinary()

The behavior of the function is undefined if it is passed a SPNEGO_TOKEN_HANDLE handle that was initialized or allocated by other means.

pnegResult A pointer to an address to store the negResult value retrieved from the token. This parameter is required.  

Description

This function will copy the negResult value from a SPNEGO token of type negTokenTarg into pnegTarg. For the function to succeed, the value must exist in the token and must have the proper format and values.

Supported values for negResult are as follows:

typedef enum spnego_negResult
{
   spnego_negresult_success,
   spnego_negresult_incomplete,
   spnego_negresult_rejected
} 

Return values

Table 13. spnegoGetNegotiationResult Return Values

Return code Comments
SPNEGO_E_SUCCESS The operation completed successfully.
SPNEGO_E_INVALID_PARAMETER One of the supplied parameters was not valid.
SPNEGO_E_ELEMENT_UNAVAILABLE negResult element is not available.
SPNEGO_E_INVALID_ELEMENT negResult element is available but not valid.

spnegoGetSupportedMechType

Prototype

  int spnegoGetSupportedMechType(

   [in]   SPNEGO_TOKEN_HANDLE  hSpnegoToken,
   [out]  SPNEGO_MECH_OID*     pMechOID
   )

Parameters

Table 14. spnegoGetSupportedMechType Parameters

Parameter Description Comments
hSpnegoToken A properly allocated SPNEGO_TOKEN_HANDLE. This parameter is required. The handle must have been allocated by one of the following calls:
  • spnegoCreateNegTokenTarg()
  • spnegoInitFromBinary()

The behavior of the function is undefined if it is passed a SPNEGO_TOKEN_HANDLE handle that was initialized or allocated by other means.

pMechOID A pointer to an address in which to store the supported mechtype.  

Description

This function will check the supportedMech element's OID in a negTokenTarg against the known OIDs (spnego_mech_oid_Microsoft_Kerberos, spnego_mech_oid_Kerberos_V5) and if we get a match, sets the pMechOID parameter equal to proper value. For the function to succeed, the value must exist in the token and must have the proper format and values.

Return values

Table 15. spnegoGetSupportedMechType Return Values

Return code Comments
SPNEGO_E_SUCCESS The operation completed successfully.
SPNEGO_E_INVALID_PARAMETER One of the supplied parameters was not valid.
SPNEGO_E_ELEMENT_UNAVAILABLE The supportedMech element is not available.
SPNEGO_E_INVALID_ELEMENT The supportedMech element is available but not valid.

spnegoGetTokenType

Prototype

  int spnegoGetTokenType(

   [in]   SPNEGO_TOKEN_HANDLE  pSpnegoToken,
   [out]  int*                 piTokenType
   )

Parameters

Table 16. spnegoGetTokenType Parameters

Parameter Description Comments
hSpnegoToken A properly allocated SPNEGO_TOKEN_HANDLE. This parameter is required. The handle must have been allocated by one of the following calls:
  • spnegoCreateNegTokenInit()
  • spnegoCreateNegTokenTarg()
  • spnegoInitFromBinary()

The behavior of the function is undefined if it is passed a SPNEGO_TOKEN_HANDLE handle that was initialized or allocated by other means.

piTokenType A pointer to an address in which to store the token type.  

Description

This function will check the underlying data for the hSpnegoToken parameter and return the token type in the piTokenType parameter. Possible values for piTokenType are:

SPNEGO_TOKEN_INIT   0
SPNEGO_TOKEN_TARG   1

Return values

Table 17. spnegoGetTokenType Return Values

Return code Comments
SPNEGO_E_SUCCESS The operation completed successfully.
SPNEGO_E_INVALID_PARAMETER One of the supplied parameters was not valid.

spnegoInitFromBinary

Prototype

  int spnegoInitFromBinary(

   [in]   unsigned char*        pbTokenData,
   [in]   unsigned long         ulLength,
   [out]  SPNEGO_TOKEN_HANDLE*  phSpnegoToken
   )

Parameters

Table 18. spnegoInitFromBinary Parameters

Parameter Description Comments
pbTokenData A pointer to a valid binary SPNEGO token. This is a required parameter. The token can be of type negTokenInit or negTokenTarg.
ulLength The length of the token pointed to by the pbTokenData parameter. This is a required parameter.
phSpnegoToken Returns a SPNEGO_TOKEN_HANDLE handle that was allocated by the function call. This is a required parameter and must be non-NULL. When you have finished using the handle, free it by calling the spnegoFreeData() function.

Description

This function will copy the supplied binary data and initialize a SPNEGO_TOKEN_HANDLE handle on the copy. The token must be a valid negTokenInit or negTokenTarg because some parsing of the token will take place. The caller can call spnegoTokenGetBinary() or spnegoHiPerfGetRawTokenPtr() to access the underlying binary data, which must be a GSS that corresponds to the format described in RFC 2478. When you have finished using the token, free it by calling the spnegoFreeData() function.

Return values

Table 19. spnegoInitFromBinary Return Values

Return code Comments
SPNEGO_E_SUCCESS The operation completed successfully.
SPNEGO_E_INVALID_PARAMETER One of the supplied parameters was not valid.
SPNEGO_E_OUT_OF_MEMORY An internal memory allocation failed.
SPNEGO_E_INVALID_TOKEN The supplied token is not valid.
SPNEGO_E_INVALID_LENGTH A length that is not valid was encountered while parsing the token.
SPNEGO_E_UNEXPECTED_OID The token contains an unexpected OID (for example, the initial OID is not the SPNEGO OID).
SPNEGO_E_TOKEN_NOT_FOUND An expected token inside the buffer could not be found (for example, the tokenizer failed to parse an expected value).
SPNEGO_E_UNEXPECTED_TYPE An unexpected type was encountered (for example, an OID was found where an OCTET STRING was expected).

spnegoIsMechTypeAvailable

Prototype

  int spnegoIsMechTypeAvailable(

   [in]   SPNEGO_TOKEN_HANDLE  hSpnegoToken,
   [in]   MechOID              MechOID,
   [out]  int*                 piMechTypeIndex
   )

Parameters

Table 20. spnegoIsMechTypeAvailable Parameters

Parameter Description Comments
hSpnegoToken A properly allocated SPNEGO_TOKEN_HANDLE. This parameter is required. The handle must have been allocated by one of the following calls:
  • spnegoCreateNegTokenInit()
  • spnegoInitFromBinary()

The behavior of the function is undefined if it is passed a SPNEGO_TOKEN_HANDLE handle that was initialized or allocated by other means.

MechOID Mech Type to search the list for. At this time, the only supported mech types are:
  • spnego_mech_oid_Microsoft_Kerberos
  • spnego_mech_oid_Kerberos_V5
piMechTypeIndex A pointer to an address in which to store the index of the MechType in the list.  

Description

This function will search the negTokenInit's MechTypeList element for the specified Mech OID. If it is found, the zero-based index in the list is returned in the piMechTypeIndex parameter.

Return values

Table 21. spnegoIsMechTypeAvailable Return Values

Return code Comments
SPNEGO_E_SUCCESS The operation completed successfully.
SPNEGO_E_INVALID_PARAMETER One of the supplied parameters was not valid.
SPNEGO_E_NOT_FOUND The requested Mech Type could not be found.
SPNEGO_E_ELEMENT_UNAVAILABLE The MechTypeList element is not available.
SPNEGO_E_INVALID_ELEMENT The MechTypeList element is available but not valid.

spnegoTokenGetBinary

Prototype

  int spnegoTokenGetBinary(

   [in]      SPNEGO_TOKEN_HANDLE  hSpnegoToken,
   [out]     unsigned char*       pbTokenData,
   [in,out]  unsigned long*       pulDataLen
   )

Parameters

Table 22. spnegoTokenGetBinary Parameters

Parameter Description Comments
hSpnegoToken A properly allocated SPNEGO_TOKEN_HANDLE. This parameter is required. The handle must have been allocated by one of the following calls:
  • spnegoCreateNegTokenInit()
  • spnegoCreateNegTokenTarg()
  • spnegoInitFromBinary()

The behavior of the function is undefined if it is passed a SPNEGO_TOKEN_HANDLE handle that was initialized or allocated by other means.

pbTokenData A pointer to copy binary SPNEGO token data into. This is an optional parameter and can be NULL.  
pulDataLen A pointer to a variable that contains the length of the token data pointed to by the pbTokenData parameter. This parameter is required. If pbTokenData is non-NULL, pulDataLen must be set to the length of the data buffer. After the function returns, the value is set equal to the number of bytes copied into the buffer, or in the case of a buffer-too-small error, the buffer size required to hold the data.

Description

This function is used to retrieve a binary copy of the SPNEGO token data from a SPNEGO token of type negTokenInit or negTokenTarg. The token is copied from within the underlying binary SPNEGO data into the buffer pointed to by the pbTokenData parameter. If pbTokenData is NULL or the value in pulDataLen indicates a buffer size too small to hold the MIC data, the function returns SPNEGO_E_BUFFER_TOO_SMALL and sets pulDataLen equal to the size required to hold the SPNEGO token.

Return values

Table 23. spnegoTokenGetBinary Return Values

Return code Comments
SPNEGO_E_SUCCESS The operation completed successfully.
SPNEGO_E_INVALID_PARAMETER One of the supplied parameters was not valid.
SPNEGO_E_BUFFER_TOO_SMALL The supplied buffer is too small to contain the Mech token. The pulDataLen will be set equal to the required value.

Implementation Example

The following example code shows how the SPNEGO Token Handler API could be used to parse a SPNEGO token, extract the Mech token, and generate a response token to send back.

Note This example is intended only to be used as a reference for implementation; it is not complete and represents only one way in which the API may be used.

int HandleSpnegoToken( unsigned char* pbToken, unsigned long,
                      ulTokenSize, unsigned char** ppbResponseToken,
                      unsigned long* pulRespTokenLen,
                      int* pnComplete )
{
   int               nReturn = -1;
   int               nError = 0L;
   SPNEGO_TOKEN_HANDLE     hSpnegoToken = NULL;
   SPNEGO_TOKEN_HANDLE     hSpnegoResponseToken = NULL;
   SPNEGO_MECH_OID   spnegoMechOID = spnego_mech_oid_NotUsed;
   unsigned char*    pbMechToken = NULL;
   unsigned long     ulMechTokenLen = 0L;
   unsigned char*    pbRespToken = NULL;
   unsigned long     ulRespTokenLen = 0L;
   int               nTokenType = 0L;
   int               nOIDIndex = 0L;
   int               nGetMechToken = 1L;

   //
   // Default to incomplete in case we hit a condition in which we
   // have a supported mechtype, but no MechToken
   //

   SPNEGO_NEGRESULT  spnegoNegResult = spnego_negresult_incomplete;

   // First parse the token.
   if ( spnegoInitFromBinary( pbToken, ulTokenSize, &hSpnegoToken )
         != SPNEGO_E_SUCCESS )
   {
      goto xCleanup;
   }

   // Check the type.  If NegTokenInit, see if Microsoft Keberos is in
   // the lead position.

   if ( spnegoGetTokenType( hSpnegoToken, &nTokenType )
         != SPNEGO_E_SUCCESS )
   {
      goto xCleanup;
   }

   if ( SPNEGO_TOKEN_INIT == nTokenType )
   {
      //
      // IF the legacy mechtype is not available, check for the
      // standard Kerberos V5 value.  If we don't find that, send
      // back a rejected response.
      //

      if ( spnegoIsMechTypeAvailable( hSpnegoToken,
            spnego_mech_oid_Kerberos_V5_Legacy, &nOIDIndex )
            != SPNEGO_E_SUCCESS )
      {

         // If we find Kerberos_V5, confirm that it's in the lead position.
         // If not, we can't really do anything with the MechToken.

         if ( spnegoIsMechTypeAvailable( hSpnegoToken,
               spnego_mech_oid_Kerberos_V5, &nOIDIndex )
               != SPNEGO_E_SUCCESS )
         {
            spnegoNegResult = spnego_negresult_rejected;
         }
         else if ( 0 != nOIDIndex )
         {
            // Store the found value.
            spnegoMechOID = spnego_mech_oid_Kerberos_V5;
            nGetMechToken = 0L;
         }

      }
      else
      {
         // Store the found value.
         spnegoMechOID = spnego_mech_oid_Kerberos_V5_Legacy;

         if ( 0 != nOIDIndex )
         {
            // If the OID is not in the first position, there's no 
            // point in trying to retrieve the mechToken because if it's
            // there, it will only correspond to the OID in the first 
            // position.

            nGetMechToken = 0L;
         }
      }

   }

   // Only retrieve the MechToken if appropriate

   if ( nGetMechToken )
   {
      // Retrieve the MechToken - Token Unavailable is okay if
      // nTokenType is an InitToken; otherwise, we expect a
      // buffer too small error.

      nError = spnegoGetMechToken( hSpnegoToken, NULL,
                                   &ulMechTokenLen );

      if ( SPNEGO_E_BUFFER_TOO_SMALL == nError )
      {

         // Allocate a properly sized buffer and retry.

         pbMechToken = malloc( ulMechTokenLen );

         if ( NULL == pbMechToken )
         {
            goto xCleanup;
         }

         if ( spnegoGetMechToken( hSpnegoToken, pbMechToken,
                                  &ulMechTokenLen )
               != SPNEGO_E_SUCCESS )
         {
            goto xCleanup;
         }
      }
      else if ( SPNEGO_TOKEN_TARG == nTokenType ||
                  SPNEGO_E_ELEMENT_UNAVAILABLE != nError )
      {
         goto xCleanup;
      }

   }

   // Only need to pass to GSS if we have a MechToken.

   if ( NULL != pbMechToken )
   {

      //
      // Make the call to gss_accept_sec_context() here. Note that
      // this should record if a Complete status is returned in
      // pnComplete and allocate a response token if one is generated
      // and spnegoNegResult should be set properly.
      //

   }

   // Create the Token and then extract the binary.
   if ( spnegoCreateNegTokenTarg( spnegoMechOID,
            spnegoNegResult, pbRespToken, ulRespTokenLen, NULL,
            0L, &hSpnegoResponseToken )
            != SPNEGO_E_SUCCESS )
   {
      goto xCleanup;
   }

   // Expect a buffer too small error here.

   if ( spnegoTokenGetBinary( hSpnegoResponseToken, NULL,
                              pulRespTokenLen )
         == SPNEGO_E_BUFFER_TOO_SMALL )
   {
      // Now allocate and extract the buffer.

      *ppbResponseToken = malloc( *pulRespTokenLen );

      if ( NULL == *ppbResponseToken )
      {
         goto xCleanup;
      }

      nError = spnegoTokenGetBinary( hSpnegoResponseToken,
                                     ppbResponseToken,
                                     pulRespTokenLen );

      if ( SPNEGO_E_SUCCESS == nError )
      {
         // We're done!
         nReturn = 0L;
      }
      else
      {
         free( *ppbResponseToken );
         *ppbResponseToken = NULL;
      }

   }

xCleanup:

   // Cleanup allocated token info.
   spnegoFreeData( hSpnegoToken );
   spnegoFreeData( hSpnegoResponseToken );

   // Cleanup non-NULL allocated memory.

   if ( NULL != pbRespToken )
   {
      free( pbRespToken );
   }

   if ( NULL != pbMechToken )
   {
      free( pbMechToken );
   }

   return nReturn;

}

Summary

The API set and example code described in this article only represent one way to approach the problem of parsing and creating SPNEGO Tokens. For example, at the time of the writing of this article, as far as we are aware there is no existing GSS-API mechanism for SPNEGO Tokens. Such a mechanism would obviate the need for the code provided in this article. However, until such a thing is written, the provided API set should get you well on your way!

Appendix A—References