1*6007Sthurlow // Copyright (C) 2002 Microsoft Corporation
2*6007Sthurlow // All rights reserved.
3*6007Sthurlow //
4*6007Sthurlow // THIS CODE AND INFORMATION IS PROVIDED "AS IS"
5*6007Sthurlow // WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
6*6007Sthurlow // OR IMPLIED, INCLUDING BUT NOT LIMITED
7*6007Sthurlow // TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY
8*6007Sthurlow // AND/OR FITNESS FOR A PARTICULAR PURPOSE.
9*6007Sthurlow //
10*6007Sthurlow // Date - 10/08/2002
11*6007Sthurlow // Author - Sanj Surati
12*6007Sthurlow
13*6007Sthurlow /////////////////////////////////////////////////////////////
14*6007Sthurlow //
15*6007Sthurlow // SPNEGO.C
16*6007Sthurlow //
17*6007Sthurlow // SPNEGO Token Handler Source File
18*6007Sthurlow //
19*6007Sthurlow // Contains implementation of SPNEGO Token Handling API
20*6007Sthurlow // as defined in SPNEGO.H.
21*6007Sthurlow //
22*6007Sthurlow /////////////////////////////////////////////////////////////
23*6007Sthurlow
24*6007Sthurlow #pragma ident "%Z%%M% %I% %E% SMI"
25*6007Sthurlow
26*6007Sthurlow #include <stdlib.h>
27*6007Sthurlow #include <stdio.h>
28*6007Sthurlow #include <memory.h>
29*6007Sthurlow #include "spnego.h"
30*6007Sthurlow #include "derparse.h"
31*6007Sthurlow #include "spnegoparse.h"
32*6007Sthurlow
33*6007Sthurlow //
34*6007Sthurlow // Defined in DERPARSE.C
35*6007Sthurlow //
36*6007Sthurlow
37*6007Sthurlow extern MECH_OID g_stcMechOIDList [];
38*6007Sthurlow
39*6007Sthurlow
40*6007Sthurlow /**********************************************************************/
41*6007Sthurlow /** **/
42*6007Sthurlow /** **/
43*6007Sthurlow /** **/
44*6007Sthurlow /** **/
45*6007Sthurlow /** SPNEGO Token Handler API implementation **/
46*6007Sthurlow /** **/
47*6007Sthurlow /** **/
48*6007Sthurlow /** **/
49*6007Sthurlow /** **/
50*6007Sthurlow /**********************************************************************/
51*6007Sthurlow
52*6007Sthurlow
53*6007Sthurlow /////////////////////////////////////////////////////////////////////////////
54*6007Sthurlow //
55*6007Sthurlow // Function:
56*6007Sthurlow // spnegoInitFromBinary
57*6007Sthurlow //
58*6007Sthurlow // Parameters:
59*6007Sthurlow // [in] pbTokenData - Binary Token Data
60*6007Sthurlow // [in] ulLength - Length of binary Token Data
61*6007Sthurlow // [out] phSpnegoToken - SPNEGO_TOKEN_HANDLE pointer
62*6007Sthurlow //
63*6007Sthurlow // Returns:
64*6007Sthurlow // int Success - SPNEGO_E_SUCCESS
65*6007Sthurlow // Failure - SPNEGO API Error code
66*6007Sthurlow //
67*6007Sthurlow // Comments :
68*6007Sthurlow // Initializes a SPNEGO_TOKEN_HANDLE from the supplied
69*6007Sthurlow // binary data. Data is copied locally. Returned data structure
70*6007Sthurlow // must be freed by calling spnegoFreeData().
71*6007Sthurlow //
72*6007Sthurlow ////////////////////////////////////////////////////////////////////////////
73*6007Sthurlow
spnegoInitFromBinary(unsigned char * pbTokenData,unsigned long ulLength,SPNEGO_TOKEN_HANDLE * phSpnegoToken)74*6007Sthurlow int spnegoInitFromBinary( unsigned char* pbTokenData, unsigned long ulLength, SPNEGO_TOKEN_HANDLE* phSpnegoToken )
75*6007Sthurlow {
76*6007Sthurlow int nReturn = SPNEGO_E_INVALID_PARAMETER;
77*6007Sthurlow SPNEGO_TOKEN** ppSpnegoToken = (SPNEGO_TOKEN**) phSpnegoToken;
78*6007Sthurlow
79*6007Sthurlow // Pass off to a handler function that allows tighter control over how the token structure
80*6007Sthurlow // is handled. In this case, we want the token data copied and we want the associated buffer
81*6007Sthurlow // freed.
82*6007Sthurlow nReturn = InitTokenFromBinary( SPNEGO_TOKEN_INTERNAL_COPYDATA,
83*6007Sthurlow SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA, pbTokenData,
84*6007Sthurlow ulLength, ppSpnegoToken );
85*6007Sthurlow
86*6007Sthurlow return nReturn;
87*6007Sthurlow }
88*6007Sthurlow
89*6007Sthurlow /////////////////////////////////////////////////////////////////////////////
90*6007Sthurlow //
91*6007Sthurlow // Function:
92*6007Sthurlow // spnegoCreateNegTokenInit
93*6007Sthurlow //
94*6007Sthurlow // Parameters:
95*6007Sthurlow // [in] MechType - MechType to specify in MechTypeList element
96*6007Sthurlow // [in] ucContextFlags - Context Flags element value
97*6007Sthurlow // [in] pbMechToken - Pointer to binary MechToken Data
98*6007Sthurlow // [in] ulMechTokenLen - Length of MechToken Data
99*6007Sthurlow // [in] pbMechListMIC - Pointer to binary MechListMIC Data
100*6007Sthurlow // [in] ulMechListMICLen - Length of MechListMIC Data
101*6007Sthurlow // [out] phSpnegoToken - SPNEGO_TOKEN_HANDLE pointer
102*6007Sthurlow //
103*6007Sthurlow // Returns:
104*6007Sthurlow // int Success - SPNEGO_E_SUCCESS
105*6007Sthurlow // Failure - SPNEGO API Error code
106*6007Sthurlow //
107*6007Sthurlow // Comments :
108*6007Sthurlow // Initializes a SPNEGO_TOKEN_HANDLE for a NegTokenInit type
109*6007Sthurlow // from the supplied parameters. ucContextFlags may be 0 or must be
110*6007Sthurlow // a valid flag combination. MechToken data can be NULL - if not, it
111*6007Sthurlow // must correspond to the MechType. MechListMIC can also be NULL.
112*6007Sthurlow // Returned data structure must be freed by calling spnegoFreeData().
113*6007Sthurlow //
114*6007Sthurlow ////////////////////////////////////////////////////////////////////////////
115*6007Sthurlow
spnegoCreateNegTokenInit(SPNEGO_MECH_OID MechType,unsigned char ucContextFlags,unsigned char * pbMechToken,unsigned long ulMechTokenLen,unsigned char * pbMechListMIC,unsigned long ulMechListMICLen,SPNEGO_TOKEN_HANDLE * phSpnegoToken)116*6007Sthurlow int spnegoCreateNegTokenInit( SPNEGO_MECH_OID MechType,
117*6007Sthurlow unsigned char ucContextFlags, unsigned char* pbMechToken,
118*6007Sthurlow unsigned long ulMechTokenLen, unsigned char* pbMechListMIC,
119*6007Sthurlow unsigned long ulMechListMICLen, SPNEGO_TOKEN_HANDLE* phSpnegoToken )
120*6007Sthurlow {
121*6007Sthurlow int nReturn = SPNEGO_E_INVALID_PARAMETER;
122*6007Sthurlow long nTokenLength = 0L;
123*6007Sthurlow long nInternalTokenLength = 0L;
124*6007Sthurlow unsigned char* pbTokenData = NULL;
125*6007Sthurlow SPNEGO_TOKEN** ppSpnegoToken = (SPNEGO_TOKEN**) phSpnegoToken;
126*6007Sthurlow
127*6007Sthurlow if ( NULL != ppSpnegoToken &&
128*6007Sthurlow IsValidMechOid( MechType ) &&
129*6007Sthurlow IsValidContextFlags( ucContextFlags ) )
130*6007Sthurlow {
131*6007Sthurlow // Get the actual token size
132*6007Sthurlow
133*6007Sthurlow if ( ( nReturn = CalculateMinSpnegoInitTokenSize( ulMechTokenLen, ulMechListMICLen,
134*6007Sthurlow MechType, ( ucContextFlags != 0L ),
135*6007Sthurlow &nTokenLength, &nInternalTokenLength ) )
136*6007Sthurlow == SPNEGO_E_SUCCESS )
137*6007Sthurlow {
138*6007Sthurlow // Allocate a buffer to hold the data.
139*6007Sthurlow pbTokenData = calloc( 1, nTokenLength );
140*6007Sthurlow
141*6007Sthurlow if ( NULL != pbTokenData )
142*6007Sthurlow {
143*6007Sthurlow
144*6007Sthurlow // Now write the token
145*6007Sthurlow if ( ( nReturn = CreateSpnegoInitToken( MechType,
146*6007Sthurlow ucContextFlags, pbMechToken,
147*6007Sthurlow ulMechTokenLen, pbMechListMIC,
148*6007Sthurlow ulMechListMICLen, pbTokenData,
149*6007Sthurlow nTokenLength, nInternalTokenLength ) )
150*6007Sthurlow == SPNEGO_E_SUCCESS )
151*6007Sthurlow {
152*6007Sthurlow
153*6007Sthurlow // This will copy our allocated pointer, and ensure that the sructure cleans
154*6007Sthurlow // up the data later
155*6007Sthurlow nReturn = InitTokenFromBinary( SPNEGO_TOKEN_INTERNAL_COPYPTR,
156*6007Sthurlow SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA,
157*6007Sthurlow pbTokenData, nTokenLength, ppSpnegoToken );
158*6007Sthurlow
159*6007Sthurlow }
160*6007Sthurlow
161*6007Sthurlow // Cleanup on failure
162*6007Sthurlow if ( SPNEGO_E_SUCCESS != nReturn )
163*6007Sthurlow {
164*6007Sthurlow free( pbTokenData );
165*6007Sthurlow }
166*6007Sthurlow
167*6007Sthurlow } // IF alloc succeeded
168*6007Sthurlow else
169*6007Sthurlow {
170*6007Sthurlow nReturn = SPNEGO_E_OUT_OF_MEMORY;
171*6007Sthurlow }
172*6007Sthurlow
173*6007Sthurlow } // If calculated token size
174*6007Sthurlow
175*6007Sthurlow } // IF Valid Parameters
176*6007Sthurlow
177*6007Sthurlow return nReturn;
178*6007Sthurlow }
179*6007Sthurlow
180*6007Sthurlow /////////////////////////////////////////////////////////////////////////////
181*6007Sthurlow //
182*6007Sthurlow // Function:
183*6007Sthurlow // spnegoCreateNegTokenTarg
184*6007Sthurlow //
185*6007Sthurlow // Parameters:
186*6007Sthurlow // [in] MechType - MechType to specify in supported MechType element
187*6007Sthurlow // [in] spnegoNegResult - NegResult value
188*6007Sthurlow // [in] pbMechToken - Pointer to response MechToken Data
189*6007Sthurlow // [in] ulMechTokenLen - Length of MechToken Data
190*6007Sthurlow // [in] pbMechListMIC - Pointer to binary MechListMIC Data
191*6007Sthurlow // [in] ulMechListMICLen - Length of MechListMIC Data
192*6007Sthurlow // [out] phSpnegoToken - SPNEGO_TOKEN_HANDLE pointer
193*6007Sthurlow //
194*6007Sthurlow // Returns:
195*6007Sthurlow // int Success - SPNEGO_E_SUCCESS
196*6007Sthurlow // Failure - SPNEGO API Error code
197*6007Sthurlow //
198*6007Sthurlow // Comments :
199*6007Sthurlow // Initializes a SPNEGO_TOKEN_HANDLE for a NegTokenTarg type
200*6007Sthurlow // from the supplied parameters. MechToken data can be NULL - if not,
201*6007Sthurlow // it must correspond to the MechType. MechListMIC can also be NULL.
202*6007Sthurlow // Returned data structure must be freed by calling spnegoFreeData().
203*6007Sthurlow //
204*6007Sthurlow ////////////////////////////////////////////////////////////////////////////
205*6007Sthurlow
spnegoCreateNegTokenTarg(SPNEGO_MECH_OID MechType,SPNEGO_NEGRESULT spnegoNegResult,unsigned char * pbMechToken,unsigned long ulMechTokenLen,unsigned char * pbMechListMIC,unsigned long ulMechListMICLen,SPNEGO_TOKEN_HANDLE * phSpnegoToken)206*6007Sthurlow int spnegoCreateNegTokenTarg( SPNEGO_MECH_OID MechType,
207*6007Sthurlow SPNEGO_NEGRESULT spnegoNegResult, unsigned char* pbMechToken,
208*6007Sthurlow unsigned long ulMechTokenLen, unsigned char* pbMechListMIC,
209*6007Sthurlow unsigned long ulMechListMICLen, SPNEGO_TOKEN_HANDLE* phSpnegoToken )
210*6007Sthurlow {
211*6007Sthurlow int nReturn = SPNEGO_E_INVALID_PARAMETER;
212*6007Sthurlow long nTokenLength = 0L;
213*6007Sthurlow long nInternalTokenLength = 0L;
214*6007Sthurlow unsigned char* pbTokenData = NULL;
215*6007Sthurlow SPNEGO_TOKEN** ppSpnegoToken = (SPNEGO_TOKEN**) phSpnegoToken;
216*6007Sthurlow
217*6007Sthurlow //
218*6007Sthurlow // spnego_mech_oid_NotUsed and spnego_negresult_NotUsed
219*6007Sthurlow // are okay here, however a valid MechOid is required
220*6007Sthurlow // if spnego_negresult_success or spnego_negresult_incomplete
221*6007Sthurlow // is specified.
222*6007Sthurlow //
223*6007Sthurlow
224*6007Sthurlow if ( NULL != ppSpnegoToken &&
225*6007Sthurlow
226*6007Sthurlow ( IsValidMechOid( MechType ) ||
227*6007Sthurlow spnego_mech_oid_NotUsed == MechType ) &&
228*6007Sthurlow
229*6007Sthurlow ( IsValidNegResult( spnegoNegResult ) ||
230*6007Sthurlow spnego_negresult_NotUsed == spnegoNegResult ) &&
231*6007Sthurlow
232*6007Sthurlow !( !IsValidMechOid( MechType ) &&
233*6007Sthurlow ( spnego_negresult_success == spnegoNegResult ||
234*6007Sthurlow spnego_negresult_incomplete == spnegoNegResult ) ) )
235*6007Sthurlow {
236*6007Sthurlow
237*6007Sthurlow // Get the actual token size
238*6007Sthurlow
239*6007Sthurlow if ( ( nReturn = CalculateMinSpnegoTargTokenSize( MechType, spnegoNegResult, ulMechTokenLen,
240*6007Sthurlow ulMechListMICLen, &nTokenLength,
241*6007Sthurlow &nInternalTokenLength ) )
242*6007Sthurlow == SPNEGO_E_SUCCESS )
243*6007Sthurlow {
244*6007Sthurlow // Allocate a buffer to hold the data.
245*6007Sthurlow pbTokenData = calloc( 1, nTokenLength );
246*6007Sthurlow
247*6007Sthurlow if ( NULL != pbTokenData )
248*6007Sthurlow {
249*6007Sthurlow
250*6007Sthurlow // Now write the token
251*6007Sthurlow if ( ( nReturn = CreateSpnegoTargToken( MechType,
252*6007Sthurlow spnegoNegResult, pbMechToken,
253*6007Sthurlow ulMechTokenLen, pbMechListMIC,
254*6007Sthurlow ulMechListMICLen, pbTokenData,
255*6007Sthurlow nTokenLength, nInternalTokenLength ) )
256*6007Sthurlow == SPNEGO_E_SUCCESS )
257*6007Sthurlow {
258*6007Sthurlow
259*6007Sthurlow // This will copy our allocated pointer, and ensure that the sructure cleans
260*6007Sthurlow // up the data later
261*6007Sthurlow nReturn = InitTokenFromBinary( SPNEGO_TOKEN_INTERNAL_COPYPTR,
262*6007Sthurlow SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA,
263*6007Sthurlow pbTokenData, nTokenLength, ppSpnegoToken );
264*6007Sthurlow
265*6007Sthurlow }
266*6007Sthurlow
267*6007Sthurlow // Cleanup on failure
268*6007Sthurlow if ( SPNEGO_E_SUCCESS != nReturn )
269*6007Sthurlow {
270*6007Sthurlow free( pbTokenData );
271*6007Sthurlow }
272*6007Sthurlow
273*6007Sthurlow } // IF alloc succeeded
274*6007Sthurlow else
275*6007Sthurlow {
276*6007Sthurlow nReturn = SPNEGO_E_OUT_OF_MEMORY;
277*6007Sthurlow }
278*6007Sthurlow
279*6007Sthurlow } // If calculated token size
280*6007Sthurlow
281*6007Sthurlow } // IF Valid Parameters
282*6007Sthurlow
283*6007Sthurlow return nReturn;
284*6007Sthurlow }
285*6007Sthurlow
286*6007Sthurlow /////////////////////////////////////////////////////////////////////////////
287*6007Sthurlow //
288*6007Sthurlow // Function:
289*6007Sthurlow // spnegoTokenGetBinary
290*6007Sthurlow //
291*6007Sthurlow // Parameters:
292*6007Sthurlow // [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE
293*6007Sthurlow // [out] pbTokenData - Buffer to copy token into
294*6007Sthurlow // [in/out] pulDataLen - Length of pbTokenData buffer, filled out
295*6007Sthurlow // with actual size used upon function return.
296*6007Sthurlow //
297*6007Sthurlow // Returns:
298*6007Sthurlow // int Success - SPNEGO_E_SUCCESS
299*6007Sthurlow // Failure - SPNEGO API Error code
300*6007Sthurlow //
301*6007Sthurlow // Comments :
302*6007Sthurlow // Copies binary SPNEGO token data from hSpnegoToken into the user
303*6007Sthurlow // supplied buffer. If pbTokenData is NULL, or the value in pulDataLen
304*6007Sthurlow // is too small, the function will return SPNEGO_E_BUFFER_TOO_SMALL and
305*6007Sthurlow // fill out pulDataLen with the minimum required buffer size.
306*6007Sthurlow //
307*6007Sthurlow ////////////////////////////////////////////////////////////////////////////
308*6007Sthurlow
spnegoTokenGetBinary(SPNEGO_TOKEN_HANDLE hSpnegoToken,unsigned char * pbTokenData,unsigned long * pulDataLen)309*6007Sthurlow int spnegoTokenGetBinary( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbTokenData,
310*6007Sthurlow unsigned long * pulDataLen )
311*6007Sthurlow {
312*6007Sthurlow int nReturn = SPNEGO_E_INVALID_PARAMETER;
313*6007Sthurlow SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken;
314*6007Sthurlow
315*6007Sthurlow // Check parameters - pbTokenData is optional
316*6007Sthurlow if ( IsValidSpnegoToken( pSpnegoToken ) &&
317*6007Sthurlow NULL != pulDataLen )
318*6007Sthurlow {
319*6007Sthurlow
320*6007Sthurlow // Check for Buffer too small conditions
321*6007Sthurlow if ( NULL == pbTokenData ||
322*6007Sthurlow pSpnegoToken->ulBinaryDataLen > *pulDataLen )
323*6007Sthurlow {
324*6007Sthurlow *pulDataLen = pSpnegoToken->ulBinaryDataLen;
325*6007Sthurlow nReturn = SPNEGO_E_BUFFER_TOO_SMALL;
326*6007Sthurlow }
327*6007Sthurlow else
328*6007Sthurlow {
329*6007Sthurlow memcpy( pbTokenData, pSpnegoToken->pbBinaryData, pSpnegoToken->ulBinaryDataLen );
330*6007Sthurlow *pulDataLen = pSpnegoToken->ulBinaryDataLen;
331*6007Sthurlow nReturn = SPNEGO_E_SUCCESS;
332*6007Sthurlow }
333*6007Sthurlow
334*6007Sthurlow } // IF parameters OK
335*6007Sthurlow
336*6007Sthurlow return nReturn;;
337*6007Sthurlow }
338*6007Sthurlow
339*6007Sthurlow /////////////////////////////////////////////////////////////////////////////
340*6007Sthurlow //
341*6007Sthurlow // Function:
342*6007Sthurlow // spnegoFreeData
343*6007Sthurlow //
344*6007Sthurlow // Parameters:
345*6007Sthurlow // [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE
346*6007Sthurlow //
347*6007Sthurlow // Returns:
348*6007Sthurlow // void
349*6007Sthurlow //
350*6007Sthurlow // Comments :
351*6007Sthurlow // Frees up resources consumed by hSpnegoToken. The supplied data
352*6007Sthurlow // pointer is invalidated by this function.
353*6007Sthurlow //
354*6007Sthurlow ////////////////////////////////////////////////////////////////////////////
355*6007Sthurlow
spnegoFreeData(SPNEGO_TOKEN_HANDLE hSpnegoToken)356*6007Sthurlow void spnegoFreeData( SPNEGO_TOKEN_HANDLE hSpnegoToken )
357*6007Sthurlow {
358*6007Sthurlow FreeSpnegoToken( (SPNEGO_TOKEN*) hSpnegoToken);
359*6007Sthurlow return;
360*6007Sthurlow }
361*6007Sthurlow
362*6007Sthurlow /////////////////////////////////////////////////////////////////////////////
363*6007Sthurlow //
364*6007Sthurlow // Function:
365*6007Sthurlow // spnegoGetTokenType
366*6007Sthurlow //
367*6007Sthurlow // Parameters:
368*6007Sthurlow // [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE
369*6007Sthurlow // [out] piTokenType - Filled out with token type value.
370*6007Sthurlow //
371*6007Sthurlow // Returns:
372*6007Sthurlow // int Success - SPNEGO_E_SUCCESS
373*6007Sthurlow // Failure - SPNEGO API Error code
374*6007Sthurlow //
375*6007Sthurlow // Comments :
376*6007Sthurlow // The function will analyze hSpnegoToken and return the appropriate
377*6007Sthurlow // type in piTokenType.
378*6007Sthurlow //
379*6007Sthurlow ////////////////////////////////////////////////////////////////////////////
380*6007Sthurlow
spnegoGetTokenType(SPNEGO_TOKEN_HANDLE hSpnegoToken,int * piTokenType)381*6007Sthurlow int spnegoGetTokenType( SPNEGO_TOKEN_HANDLE hSpnegoToken, int * piTokenType )
382*6007Sthurlow {
383*6007Sthurlow int nReturn = SPNEGO_E_INVALID_PARAMETER;
384*6007Sthurlow SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken;
385*6007Sthurlow
386*6007Sthurlow // Check parameters
387*6007Sthurlow if ( IsValidSpnegoToken( pSpnegoToken ) &&
388*6007Sthurlow NULL != piTokenType &&
389*6007Sthurlow pSpnegoToken)
390*6007Sthurlow {
391*6007Sthurlow
392*6007Sthurlow // Check that the type in the structure makes sense
393*6007Sthurlow if ( SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType ||
394*6007Sthurlow SPNEGO_TOKEN_TARG == pSpnegoToken->ucTokenType )
395*6007Sthurlow {
396*6007Sthurlow *piTokenType = pSpnegoToken->ucTokenType;
397*6007Sthurlow nReturn = SPNEGO_E_SUCCESS;
398*6007Sthurlow }
399*6007Sthurlow
400*6007Sthurlow } // IF parameters OK
401*6007Sthurlow
402*6007Sthurlow return nReturn;
403*6007Sthurlow }
404*6007Sthurlow
405*6007Sthurlow /////////////////////////////////////////////////////////////////////////////
406*6007Sthurlow //
407*6007Sthurlow // Function:
408*6007Sthurlow // spnegoIsMechTypeAvailable
409*6007Sthurlow //
410*6007Sthurlow // Parameters:
411*6007Sthurlow // [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE
412*6007Sthurlow // [in] MechOID - MechOID to search MechTypeList for
413*6007Sthurlow // [out] piMechTypeIndex - Filled out with index in MechTypeList
414*6007Sthurlow // element if MechOID is found.
415*6007Sthurlow //
416*6007Sthurlow // Returns:
417*6007Sthurlow // int Success - SPNEGO_E_SUCCESS
418*6007Sthurlow // Failure - SPNEGO API Error code
419*6007Sthurlow //
420*6007Sthurlow // Comments :
421*6007Sthurlow // hSpnegoToken must reference a token of type NegTokenInit. The
422*6007Sthurlow // function will search the MechTypeList element for an OID corresponding
423*6007Sthurlow // to the specified MechOID. If one is found, the index (0 based) will
424*6007Sthurlow // be passed into the piMechTypeIndex parameter.
425*6007Sthurlow //
426*6007Sthurlow ////////////////////////////////////////////////////////////////////////////
427*6007Sthurlow
428*6007Sthurlow // Returns the Initial Mech Type in the MechList element in the NegInitToken.
spnegoIsMechTypeAvailable(SPNEGO_TOKEN_HANDLE hSpnegoToken,SPNEGO_MECH_OID MechOID,int * piMechTypeIndex)429*6007Sthurlow int spnegoIsMechTypeAvailable( SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_MECH_OID MechOID, int * piMechTypeIndex )
430*6007Sthurlow {
431*6007Sthurlow int nReturn = SPNEGO_E_INVALID_PARAMETER;
432*6007Sthurlow SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken;
433*6007Sthurlow
434*6007Sthurlow // Check parameters
435*6007Sthurlow if ( IsValidSpnegoToken( pSpnegoToken ) &&
436*6007Sthurlow NULL != piMechTypeIndex &&
437*6007Sthurlow IsValidMechOid( MechOID ) &&
438*6007Sthurlow SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType )
439*6007Sthurlow {
440*6007Sthurlow
441*6007Sthurlow // Check if MechList is available
442*6007Sthurlow if ( pSpnegoToken->aElementArray[SPNEGO_INIT_MECHTYPES_ELEMENT].iElementPresent
443*6007Sthurlow == SPNEGO_TOKEN_ELEMENT_AVAILABLE )
444*6007Sthurlow {
445*6007Sthurlow // Locate the MechOID in the list element
446*6007Sthurlow nReturn = FindMechOIDInMechList(
447*6007Sthurlow &pSpnegoToken->aElementArray[SPNEGO_INIT_MECHTYPES_ELEMENT],
448*6007Sthurlow MechOID, piMechTypeIndex );
449*6007Sthurlow }
450*6007Sthurlow else
451*6007Sthurlow {
452*6007Sthurlow nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE;
453*6007Sthurlow }
454*6007Sthurlow
455*6007Sthurlow } // IF parameters OK
456*6007Sthurlow
457*6007Sthurlow return nReturn;;
458*6007Sthurlow }
459*6007Sthurlow
460*6007Sthurlow /////////////////////////////////////////////////////////////////////////////
461*6007Sthurlow //
462*6007Sthurlow // Function:
463*6007Sthurlow // spnegoGetContextFlags
464*6007Sthurlow //
465*6007Sthurlow // Parameters:
466*6007Sthurlow // [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE
467*6007Sthurlow // [out] pucContextFlags - Filled out with ContextFlags value.
468*6007Sthurlow //
469*6007Sthurlow // Returns:
470*6007Sthurlow // int Success - SPNEGO_E_SUCCESS
471*6007Sthurlow // Failure - SPNEGO API Error code
472*6007Sthurlow //
473*6007Sthurlow // Comments :
474*6007Sthurlow // hSpnegoToken must reference a token of type NegTokenInit. The
475*6007Sthurlow // function will copy data from the ContextFlags element into the
476*6007Sthurlow // location pucContextFlags points to. Note that the function will
477*6007Sthurlow // fail if the actual ContextFlags data appears invalid.
478*6007Sthurlow //
479*6007Sthurlow ////////////////////////////////////////////////////////////////////////////
480*6007Sthurlow
spnegoGetContextFlags(SPNEGO_TOKEN_HANDLE hSpnegoToken,unsigned char * pucContextFlags)481*6007Sthurlow int spnegoGetContextFlags( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pucContextFlags )
482*6007Sthurlow {
483*6007Sthurlow int nReturn = SPNEGO_E_INVALID_PARAMETER;
484*6007Sthurlow SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken;
485*6007Sthurlow
486*6007Sthurlow // Check parameters
487*6007Sthurlow if ( IsValidSpnegoToken( pSpnegoToken ) &&
488*6007Sthurlow NULL != pucContextFlags &&
489*6007Sthurlow SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType )
490*6007Sthurlow {
491*6007Sthurlow
492*6007Sthurlow // Check if ContextFlags is available
493*6007Sthurlow if ( pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].iElementPresent
494*6007Sthurlow == SPNEGO_TOKEN_ELEMENT_AVAILABLE )
495*6007Sthurlow {
496*6007Sthurlow // The length should be two, the value should show a 1 bit difference in the difference byte, and
497*6007Sthurlow // the value must be valid
498*6007Sthurlow if ( pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].nDatalength == SPNEGO_NEGINIT_MAXLEN_REQFLAGS &&
499*6007Sthurlow pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].pbData[0] == SPNEGO_NEGINIT_REQFLAGS_BITDIFF &&
500*6007Sthurlow IsValidContextFlags( pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].pbData[1] ) )
501*6007Sthurlow {
502*6007Sthurlow *pucContextFlags = pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].pbData[1];
503*6007Sthurlow nReturn = SPNEGO_E_SUCCESS;
504*6007Sthurlow }
505*6007Sthurlow else
506*6007Sthurlow {
507*6007Sthurlow nReturn = SPNEGO_E_INVALID_ELEMENT;
508*6007Sthurlow }
509*6007Sthurlow
510*6007Sthurlow }
511*6007Sthurlow else
512*6007Sthurlow {
513*6007Sthurlow nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE;
514*6007Sthurlow }
515*6007Sthurlow
516*6007Sthurlow } // IF parameters OK
517*6007Sthurlow
518*6007Sthurlow return nReturn;;
519*6007Sthurlow }
520*6007Sthurlow
521*6007Sthurlow /////////////////////////////////////////////////////////////////////////////
522*6007Sthurlow //
523*6007Sthurlow // Function:
524*6007Sthurlow // spnegoGetNegotiationResult
525*6007Sthurlow //
526*6007Sthurlow // Parameters:
527*6007Sthurlow // [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE
528*6007Sthurlow // [out] pnegResult - Filled out with NegResult value.
529*6007Sthurlow //
530*6007Sthurlow // Returns:
531*6007Sthurlow // int Success - SPNEGO_E_SUCCESS
532*6007Sthurlow // Failure - SPNEGO API Error code
533*6007Sthurlow //
534*6007Sthurlow // Comments :
535*6007Sthurlow // hSpnegoToken must reference a token of type NegTokenTarg. The
536*6007Sthurlow // function will copy data from the NegResult element into the
537*6007Sthurlow // location pointed to by pnegResult. Note that the function will
538*6007Sthurlow // fail if the actual NegResult data appears invalid.
539*6007Sthurlow //
540*6007Sthurlow ////////////////////////////////////////////////////////////////////////////
541*6007Sthurlow
spnegoGetNegotiationResult(SPNEGO_TOKEN_HANDLE hSpnegoToken,SPNEGO_NEGRESULT * pnegResult)542*6007Sthurlow int spnegoGetNegotiationResult( SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_NEGRESULT* pnegResult )
543*6007Sthurlow {
544*6007Sthurlow int nReturn = SPNEGO_E_INVALID_PARAMETER;
545*6007Sthurlow SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken;
546*6007Sthurlow
547*6007Sthurlow // Check parameters
548*6007Sthurlow if ( IsValidSpnegoToken( pSpnegoToken ) &&
549*6007Sthurlow NULL != pnegResult &&
550*6007Sthurlow SPNEGO_TOKEN_TARG == pSpnegoToken->ucTokenType )
551*6007Sthurlow {
552*6007Sthurlow
553*6007Sthurlow // Check if NegResult is available
554*6007Sthurlow if ( pSpnegoToken->aElementArray[SPNEGO_TARG_NEGRESULT_ELEMENT].iElementPresent
555*6007Sthurlow == SPNEGO_TOKEN_ELEMENT_AVAILABLE )
556*6007Sthurlow {
557*6007Sthurlow // Must be 1 byte long and a valid value
558*6007Sthurlow if ( pSpnegoToken->aElementArray[SPNEGO_TARG_NEGRESULT_ELEMENT].nDatalength == SPNEGO_NEGTARG_MAXLEN_NEGRESULT &&
559*6007Sthurlow IsValidNegResult( *pSpnegoToken->aElementArray[SPNEGO_TARG_NEGRESULT_ELEMENT].pbData ) )
560*6007Sthurlow {
561*6007Sthurlow *pnegResult = *pSpnegoToken->aElementArray[SPNEGO_TARG_NEGRESULT_ELEMENT].pbData;
562*6007Sthurlow nReturn = SPNEGO_E_SUCCESS;
563*6007Sthurlow }
564*6007Sthurlow else
565*6007Sthurlow {
566*6007Sthurlow nReturn = SPNEGO_E_INVALID_ELEMENT;
567*6007Sthurlow }
568*6007Sthurlow }
569*6007Sthurlow else
570*6007Sthurlow {
571*6007Sthurlow nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE;
572*6007Sthurlow }
573*6007Sthurlow
574*6007Sthurlow } // IF parameters OK
575*6007Sthurlow
576*6007Sthurlow return nReturn;;
577*6007Sthurlow }
578*6007Sthurlow
579*6007Sthurlow /////////////////////////////////////////////////////////////////////////////
580*6007Sthurlow //
581*6007Sthurlow // Function:
582*6007Sthurlow // spnegoGetSupportedMechType
583*6007Sthurlow //
584*6007Sthurlow // Parameters:
585*6007Sthurlow // [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE
586*6007Sthurlow // [out] pMechOID - Filled out with Supported MechType value.
587*6007Sthurlow //
588*6007Sthurlow // Returns:
589*6007Sthurlow // int Success - SPNEGO_E_SUCCESS
590*6007Sthurlow // Failure - SPNEGO API Error code
591*6007Sthurlow //
592*6007Sthurlow // Comments :
593*6007Sthurlow // hSpnegoToken must reference a token of type NegTokenTarg. The
594*6007Sthurlow // function will check the Supported MechType element, and if it
595*6007Sthurlow // corresponds to a supported MechType ( spnego_mech_oid_Kerberos_V5_Legacy
596*6007Sthurlow // or spnego_mech_oid_Kerberos_V5 ), will set the location pointed
597*6007Sthurlow // to by pMechOID equal to the appropriate value.
598*6007Sthurlow //
599*6007Sthurlow ////////////////////////////////////////////////////////////////////////////
600*6007Sthurlow
spnegoGetSupportedMechType(SPNEGO_TOKEN_HANDLE hSpnegoToken,SPNEGO_MECH_OID * pMechOID)601*6007Sthurlow int spnegoGetSupportedMechType( SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_MECH_OID* pMechOID )
602*6007Sthurlow {
603*6007Sthurlow int nReturn = SPNEGO_E_INVALID_PARAMETER;
604*6007Sthurlow int nCtr = 0L;
605*6007Sthurlow long nLength = 0L;
606*6007Sthurlow SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken;
607*6007Sthurlow
608*6007Sthurlow // Check parameters
609*6007Sthurlow if ( IsValidSpnegoToken( pSpnegoToken ) &&
610*6007Sthurlow NULL != pMechOID &&
611*6007Sthurlow SPNEGO_TOKEN_TARG == pSpnegoToken->ucTokenType )
612*6007Sthurlow {
613*6007Sthurlow
614*6007Sthurlow // Check if MechList is available
615*6007Sthurlow if ( pSpnegoToken->aElementArray[SPNEGO_TARG_SUPPMECH_ELEMENT].iElementPresent
616*6007Sthurlow == SPNEGO_TOKEN_ELEMENT_AVAILABLE )
617*6007Sthurlow {
618*6007Sthurlow
619*6007Sthurlow for ( nCtr = 0;
620*6007Sthurlow nReturn != SPNEGO_E_SUCCESS &&
621*6007Sthurlow g_stcMechOIDList[nCtr].eMechanismOID != spnego_mech_oid_NotUsed;
622*6007Sthurlow nCtr++ )
623*6007Sthurlow {
624*6007Sthurlow
625*6007Sthurlow if ( ( nReturn = ASNDerCheckOID(
626*6007Sthurlow pSpnegoToken->aElementArray[SPNEGO_TARG_SUPPMECH_ELEMENT].pbData,
627*6007Sthurlow nCtr,
628*6007Sthurlow pSpnegoToken->aElementArray[SPNEGO_TARG_SUPPMECH_ELEMENT].nDatalength,
629*6007Sthurlow &nLength ) ) == SPNEGO_E_SUCCESS )
630*6007Sthurlow {
631*6007Sthurlow *pMechOID = nCtr;
632*6007Sthurlow }
633*6007Sthurlow
634*6007Sthurlow } // For enum MechOIDs
635*6007Sthurlow
636*6007Sthurlow
637*6007Sthurlow }
638*6007Sthurlow else
639*6007Sthurlow {
640*6007Sthurlow nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE;
641*6007Sthurlow }
642*6007Sthurlow
643*6007Sthurlow } // IF parameters OK
644*6007Sthurlow
645*6007Sthurlow return nReturn;;
646*6007Sthurlow }
647*6007Sthurlow
648*6007Sthurlow /////////////////////////////////////////////////////////////////////////////
649*6007Sthurlow //
650*6007Sthurlow // Function:
651*6007Sthurlow // spnegoTokenGetMechToken
652*6007Sthurlow //
653*6007Sthurlow // Parameters:
654*6007Sthurlow // [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE
655*6007Sthurlow // [out] pbTokenData - Buffer to copy MechToken into
656*6007Sthurlow // [in/out] pulDataLen - Length of pbTokenData buffer, filled out
657*6007Sthurlow // with actual size used upon function return.
658*6007Sthurlow //
659*6007Sthurlow // Returns:
660*6007Sthurlow // int Success - SPNEGO_E_SUCCESS
661*6007Sthurlow // Failure - SPNEGO API Error code
662*6007Sthurlow //
663*6007Sthurlow // Comments :
664*6007Sthurlow // hSpnegoToken can point to either NegTokenInit or a NegTokenTarg token.
665*6007Sthurlow // The function will copy the MechToken (the initial MechToken if
666*6007Sthurlow // NegTokenInit, the response MechToken if NegTokenTarg) from the
667*6007Sthurlow // underlying token into the buffer pointed to by pbTokenData. If
668*6007Sthurlow // pbTokenData is NULL, or the value in pulDataLen is too small, the
669*6007Sthurlow // function will return SPNEGO_E_BUFFER_TOO_SMALL and fill out pulDataLen
670*6007Sthurlow // with the minimum required buffer size. The token can then be passed
671*6007Sthurlow // to a GSS-API function for processing.
672*6007Sthurlow //
673*6007Sthurlow ////////////////////////////////////////////////////////////////////////////
674*6007Sthurlow
spnegoGetMechToken(SPNEGO_TOKEN_HANDLE hSpnegoToken,unsigned char * pbTokenData,unsigned long * pulDataLen)675*6007Sthurlow int spnegoGetMechToken( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbTokenData, unsigned long* pulDataLen )
676*6007Sthurlow {
677*6007Sthurlow int nReturn = SPNEGO_E_INVALID_PARAMETER;
678*6007Sthurlow SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken;
679*6007Sthurlow SPNEGO_ELEMENT* pSpnegoElement = NULL;
680*6007Sthurlow
681*6007Sthurlow // Check parameters
682*6007Sthurlow if ( IsValidSpnegoToken( pSpnegoToken ) &&
683*6007Sthurlow NULL != pulDataLen )
684*6007Sthurlow {
685*6007Sthurlow
686*6007Sthurlow // Point at the proper Element
687*6007Sthurlow if ( SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType )
688*6007Sthurlow {
689*6007Sthurlow pSpnegoElement = &pSpnegoToken->aElementArray[SPNEGO_INIT_MECHTOKEN_ELEMENT];
690*6007Sthurlow }
691*6007Sthurlow else
692*6007Sthurlow {
693*6007Sthurlow pSpnegoElement = &pSpnegoToken->aElementArray[SPNEGO_TARG_RESPTOKEN_ELEMENT];
694*6007Sthurlow }
695*6007Sthurlow
696*6007Sthurlow // Check if MechType is available
697*6007Sthurlow if ( SPNEGO_TOKEN_ELEMENT_AVAILABLE == pSpnegoElement->iElementPresent )
698*6007Sthurlow {
699*6007Sthurlow // Check for Buffer too small conditions
700*6007Sthurlow if ( NULL == pbTokenData ||
701*6007Sthurlow pSpnegoElement->nDatalength > *pulDataLen )
702*6007Sthurlow {
703*6007Sthurlow *pulDataLen = pSpnegoElement->nDatalength;
704*6007Sthurlow nReturn = SPNEGO_E_BUFFER_TOO_SMALL;
705*6007Sthurlow }
706*6007Sthurlow else
707*6007Sthurlow {
708*6007Sthurlow // Copy Memory
709*6007Sthurlow memcpy( pbTokenData, pSpnegoElement->pbData, pSpnegoElement->nDatalength );
710*6007Sthurlow *pulDataLen = pSpnegoElement->nDatalength;
711*6007Sthurlow nReturn = SPNEGO_E_SUCCESS;
712*6007Sthurlow }
713*6007Sthurlow }
714*6007Sthurlow else
715*6007Sthurlow {
716*6007Sthurlow nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE;
717*6007Sthurlow }
718*6007Sthurlow
719*6007Sthurlow } // IF parameters OK
720*6007Sthurlow
721*6007Sthurlow return nReturn;;
722*6007Sthurlow }
723*6007Sthurlow
724*6007Sthurlow /////////////////////////////////////////////////////////////////////////////
725*6007Sthurlow //
726*6007Sthurlow // Function:
727*6007Sthurlow // spnegoTokenGetMechListMIC
728*6007Sthurlow //
729*6007Sthurlow // Parameters:
730*6007Sthurlow // [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE
731*6007Sthurlow // [out] pbTokenData - Buffer to copy MechListMIC data into
732*6007Sthurlow // [in/out] pulDataLen - Length of pbTokenData buffer, filled out
733*6007Sthurlow // with actual size used upon function return.
734*6007Sthurlow //
735*6007Sthurlow // Returns:
736*6007Sthurlow // int Success - SPNEGO_E_SUCCESS
737*6007Sthurlow // Failure - SPNEGO API Error code
738*6007Sthurlow //
739*6007Sthurlow // Comments :
740*6007Sthurlow // hSpnegoToken can point to either NegTokenInit or a NegTokenTarg token.
741*6007Sthurlow // The function will copy the MechListMIC data from the underlying token
742*6007Sthurlow // into the buffer pointed to by pbTokenData. If pbTokenData is NULL,
743*6007Sthurlow // or the value in pulDataLen is too small, the function will return
744*6007Sthurlow // SPNEGO_E_BUFFER_TOO_SMALL and fill out pulDataLen with the minimum
745*6007Sthurlow // required buffer size.
746*6007Sthurlow //
747*6007Sthurlow ////////////////////////////////////////////////////////////////////////////
748*6007Sthurlow
spnegoGetMechListMIC(SPNEGO_TOKEN_HANDLE hSpnegoToken,unsigned char * pbMICData,unsigned long * pulDataLen)749*6007Sthurlow int spnegoGetMechListMIC( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbMICData, unsigned long* pulDataLen )
750*6007Sthurlow {
751*6007Sthurlow int nReturn = SPNEGO_E_INVALID_PARAMETER;
752*6007Sthurlow SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken;
753*6007Sthurlow SPNEGO_ELEMENT* pSpnegoElement = NULL;
754*6007Sthurlow
755*6007Sthurlow // Check parameters
756*6007Sthurlow if ( IsValidSpnegoToken( pSpnegoToken ) &&
757*6007Sthurlow NULL != pulDataLen )
758*6007Sthurlow {
759*6007Sthurlow
760*6007Sthurlow // Point at the proper Element
761*6007Sthurlow if ( SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType )
762*6007Sthurlow {
763*6007Sthurlow pSpnegoElement = &pSpnegoToken->aElementArray[SPNEGO_INIT_MECHLISTMIC_ELEMENT];
764*6007Sthurlow }
765*6007Sthurlow else
766*6007Sthurlow {
767*6007Sthurlow pSpnegoElement = &pSpnegoToken->aElementArray[SPNEGO_TARG_MECHLISTMIC_ELEMENT];
768*6007Sthurlow }
769*6007Sthurlow
770*6007Sthurlow // Check if MechType is available
771*6007Sthurlow if ( SPNEGO_TOKEN_ELEMENT_AVAILABLE == pSpnegoElement->iElementPresent )
772*6007Sthurlow {
773*6007Sthurlow // Check for Buffer too small conditions
774*6007Sthurlow if ( NULL == pbMICData ||
775*6007Sthurlow pSpnegoElement->nDatalength > *pulDataLen )
776*6007Sthurlow {
777*6007Sthurlow *pulDataLen = pSpnegoElement->nDatalength;
778*6007Sthurlow nReturn = SPNEGO_E_BUFFER_TOO_SMALL;
779*6007Sthurlow }
780*6007Sthurlow else
781*6007Sthurlow {
782*6007Sthurlow // Copy Memory
783*6007Sthurlow memcpy( pbMICData, pSpnegoElement->pbData, pSpnegoElement->nDatalength );
784*6007Sthurlow *pulDataLen = pSpnegoElement->nDatalength;
785*6007Sthurlow nReturn = SPNEGO_E_SUCCESS;
786*6007Sthurlow }
787*6007Sthurlow }
788*6007Sthurlow else
789*6007Sthurlow {
790*6007Sthurlow nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE;
791*6007Sthurlow }
792*6007Sthurlow
793*6007Sthurlow } // IF parameters OK
794*6007Sthurlow
795*6007Sthurlow return nReturn;;
796*6007Sthurlow }
797*6007Sthurlow
798