xref: /illumos-gate/usr/src/lib/libsmbfs/smb/spnegoparse.c (revision 300fdee27f8b59b381c1a0316bdee52fdfdb9213)
1  // Copyright 2012 Nexenta Systems, Inc.  All rights reserved.
2  // Copyright (C) 2002 Microsoft Corporation
3  // All rights reserved.
4  //
5  // THIS CODE AND INFORMATION IS PROVIDED "AS IS"
6  // WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
7  // OR IMPLIED, INCLUDING BUT NOT LIMITED
8  // TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY
9  // AND/OR FITNESS FOR A PARTICULAR PURPOSE.
10  //
11  // Date    - 10/08/2002
12  // Author  - Sanj Surati
13  
14  /////////////////////////////////////////////////////////////
15  //
16  // SPNEGOPARSE.C
17  //
18  // SPNEGO Token Handler Source File
19  //
20  // Contains implementation of SPNEGO Token parsing functions.
21  //
22  /////////////////////////////////////////////////////////////
23  
24  #include <stdlib.h>
25  #include <stdio.h>
26  #include <memory.h>
27  #include "spnego.h"
28  #include "derparse.h"
29  #include "spnegoparse.h"
30  
31  //
32  // Defined in DERPARSE.C
33  //
34  
35  extern MECH_OID g_stcMechOIDList [];
36  
37  /**********************************************************************/
38  /**                                                                  **/
39  /**                                                                  **/
40  /**                                                                  **/
41  /**                                                                  **/
42  /**                 Local SPNEGO Helper definitions                  **/
43  /**                                                                  **/
44  /**                                                                  **/
45  /**                                                                  **/
46  /**                                                                  **/
47  /**********************************************************************/
48  
49  
50  /////////////////////////////////////////////////////////////////////////////
51  //
52  // Function:
53  //    CalculateMinSpnegoInitTokenSize
54  //
55  // Parameters:
56  //    [in]  nMechTokenLength        -  Length of the MechToken Element
57  //    [in]  nMechListMICLength      -  Length of the MechListMIC Element
58  //                                     (or negHints, if no MechToken)
59  //    [in]  mechOID                 -  OID for MechList
60  //    [in]  nReqFlagsAvailable      -  Is ContextFlags element available
61  //    [out] pnTokenSize             -  Filled out with total size of token
62  //    [out] pnInternalTokenLength   -  Filled out with length minus length
63  //                                     for initial token.
64  //
65  // Returns:
66  //    int   Success - SPNEGO_E_SUCCESS
67  //          Failure - SPNEGO API Error code
68  //
69  // Comments :
70  //    Calculates the required length for a SPNEGO NegTokenInit token based
71  //    on the supplied variable length values and which elements are present.
72  //    Note that because the lengths can be represented by an arbitrary
73  //    number of bytes in DER encodings, we actually calculate the lengths
74  //    backwards, so we always know how many bytes we will potentially be
75  //    writing out.
76  //
77  ////////////////////////////////////////////////////////////////////////////
78  
79  int CalculateMinSpnegoInitTokenSize( long nMechTokenLength,
80        long nMechListMICLength, SPNEGO_MECH_OID *mechOidLst, int mechOidCnt,
81                                   int nReqFlagsAvailable, long* pnTokenSize,
82                                   long* pnInternalTokenLength )
83  {
84     int   nReturn = SPNEGO_E_INVALID_LENGTH;
85  
86     // Start at 0.
87     long  nTotalLength = 0;
88     long  nTempLength= 0L;
89  
90     // We will calculate this by walking the token backwards
91  
92     // Start with MIC Element (or negHints)
93     if ( nMechListMICLength > 0L )
94     {
95        nTempLength = ASNDerCalcElementLength( nMechListMICLength, NULL );
96  
97        // Check for rollover error
98        if ( nTempLength < nMechListMICLength )
99        {
100           goto xEndTokenInitLength;
101        }
102  
103        nTotalLength += nTempLength;
104     }
105  
106     // Next is the MechToken
107     if ( nMechTokenLength > 0L )
108     {
109        nTempLength += ASNDerCalcElementLength( nMechTokenLength, NULL );
110  
111        // Check for rollover error
112        if ( nTempLength < nTotalLength )
113        {
114           goto xEndTokenInitLength;
115        }
116  
117        nTotalLength = nTempLength;
118     }
119  
120     // Next is the ReqFlags
121     if ( nReqFlagsAvailable )
122     {
123        nTempLength += ASNDerCalcElementLength( SPNEGO_NEGINIT_MAXLEN_REQFLAGS, NULL );
124  
125        // Check for rollover error
126        if ( nTempLength < nTotalLength )
127        {
128           goto xEndTokenInitLength;
129        }
130  
131        nTotalLength = nTempLength;
132     }
133  
134     // Next is the MechList - This is REQUIRED
135     nTempLength += ASNDerCalcMechListLength( mechOidLst, mechOidCnt, NULL );
136  
137     // Check for rollover error
138     if ( nTempLength < nTotalLength )
139     {
140        goto xEndTokenInitLength;
141     }
142  
143     nTotalLength = nTempLength;
144  
145     // Following four fields are the basic header tokens
146  
147     // Sequence Token
148     nTempLength += ASNDerCalcTokenLength( nTotalLength, 0L );
149  
150     // Check for rollover error
151     if ( nTempLength < nTotalLength )
152     {
153        goto xEndTokenInitLength;
154     }
155  
156     nTotalLength = nTempLength;
157  
158     // Neg Token Identifier Token
159     nTempLength += ASNDerCalcTokenLength( nTotalLength, 0L );
160  
161     // Check for rollover error
162     if ( nTempLength < nTotalLength )
163     {
164        goto xEndTokenInitLength;
165     }
166  
167     nTotalLength = nTempLength;
168  
169     // SPNEGO OID Token
170     nTempLength += g_stcMechOIDList[spnego_mech_oid_Spnego].iLen;
171  
172     // Check for rollover error
173     if ( nTempLength < nTotalLength )
174     {
175        goto xEndTokenInitLength;
176     }
177  
178     nTotalLength = nTempLength;
179  
180     // App Constructed Token
181     nTempLength += ASNDerCalcTokenLength( nTotalLength, 0L );
182  
183     // Check for rollover error
184     if ( nTempLength < nTotalLength )
185     {
186        goto xEndTokenInitLength;
187     }
188  
189     // The internal length doesn't include the number of bytes
190     // for the initial token
191     *pnInternalTokenLength = nTotalLength;
192     nTotalLength = nTempLength;
193  
194     // We're done
195     *pnTokenSize = nTotalLength;
196     nReturn = SPNEGO_E_SUCCESS;
197  
198  xEndTokenInitLength:
199  
200     return nReturn;
201  
202  }
203  
204  /////////////////////////////////////////////////////////////////////////////
205  //
206  // Function:
207  //    CreateSpnegoInitToken
208  //
209  // Parameters:
210  //    [in]  pMechTypeList           -  OID array
211  //    [in]  MechTypeCnt             -  OID array length
212  //    [in]  ucContextFlags          -  ContextFlags value
213  //    [in]  pbMechToken             -  Mech Token Binary Data
214  //    [in]  ulMechTokenLen          -  Length of Mech Token
215  //    [in]  pbMechListMIC           -  MechListMIC Binary Data (or negHints)
216  //    [in]  ulMechListMICn          -  Length of MechListMIC
217  //    [out] pbTokenData             -  Buffer to write token into.
218  //    [in]  nTokenLength            -  Length of pbTokenData buffer
219  //    [in]  nInternalTokenLength    -  Length of full token without leading
220  //                                     token bytes.
221  //
222  // Returns:
223  //    int   Success - SPNEGO_E_SUCCESS
224  //          Failure - SPNEGO API Error code
225  //
226  // Comments :
227  //    Uses DER to fill out pbTokenData with a SPNEGO NegTokenInit Token
228  //    Note that because the lengths can be represented by an arbitrary
229  //    number of bytes in DER encodings, we actually calculate the lengths
230  //    backwards, so we always know how many bytes we will potentially be
231  //    writing out.
232  //
233  //    This function is also used to create an SPNEGO "hint", as described in
234  //    [MS-SPNG] sec. 2.2.1 negTokenInit2.  The "hint" looks almost identical
235  //    to a NegTokenInit, but has a "negHints" field inserted before the MIC.
236  //    A normal SPNEGO negTokenInit2 contains only the mech list and the
237  //    negHints.  To avoid a giant copy/paste of this function, we pass the
238  //    negHints as the MIC arg, and pass NULL as the MechToken to indicate
239  //    that we're creating a Hint rather than an Init, and use the correct
240  //    type when writing out the MIC (or negHints) element.
241  //
242  ////////////////////////////////////////////////////////////////////////////
243  
244  int CreateSpnegoInitToken( SPNEGO_MECH_OID *pMechTypeList, long MechTypeCnt,
245            unsigned char ucContextFlags, unsigned char* pbMechToken,
246            unsigned long ulMechTokenLen, unsigned char* pbMechListMIC,
247            unsigned long ulMechListMICLen, unsigned char* pbTokenData,
248            long nTokenLength, long nInternalTokenLength )
249  {
250     int   nReturn = SPNEGO_E_INVALID_LENGTH;
251  
252     // Start at 0.
253     long  nTempLength= 0L;
254     long  nTotalBytesWritten = 0L;
255     long  nInternalLength = 0L;
256  
257     unsigned char* pbWriteTokenData = pbTokenData + nTokenLength;
258  
259     // Temporary buffer to hold the REQ Flags as BIT String Data
260     unsigned char  abTempReqFlags[SPNEGO_NEGINIT_MAXLEN_REQFLAGS];
261  
262  
263     // We will write the token out backwards to properly handle the cases
264     // where the length bytes become adjustable
265  
266     // Start with MIC Element (or negHints)
267     if ( ulMechListMICLen > 0L )
268     {
269        unsigned char ucType;
270        nTempLength = ASNDerCalcElementLength( ulMechListMICLen, &nInternalLength );
271  
272        // Decrease the pbWriteTokenData, now we know the length and write it out.
273        // Note: When MechTokenLen == 0, we're writing a negTokenInit2 and the
274        // MIC arg is really negHints, written as a constructed sequence.
275        // Otherwise we're writing a negTokenInit, and the MIC is an OCTETSTRING.
276        ucType = (ulMechTokenLen == 0) ?
277           SPNEGO_CONSTRUCTED_SEQUENCE : OCTETSTRING;
278  
279        pbWriteTokenData -= nTempLength;
280        nTempLength = ASNDerWriteElement( pbWriteTokenData, SPNEGO_NEGINIT_ELEMENT_MECHLISTMIC,
281                                ucType, pbMechListMIC, ulMechListMICLen );
282  
283        // Adjust Values and sanity check
284        nTotalBytesWritten += nTempLength;
285        nInternalTokenLength -= nTempLength;
286  
287        if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 )
288        {
289           goto xEndWriteNegTokenInit;
290        }
291  
292     }  // IF MechListMIC is present
293  
294     // Next is the MechToken
295     if ( ulMechTokenLen > 0L )
296     {
297        nTempLength = ASNDerCalcElementLength( ulMechTokenLen, &nInternalLength );
298  
299        // Decrease the pbWriteTokenData, now we know the length and
300        // write it out.
301        pbWriteTokenData -= nTempLength;
302        nTempLength = ASNDerWriteElement( pbWriteTokenData, SPNEGO_NEGINIT_ELEMENT_MECHTOKEN,
303                                OCTETSTRING, pbMechToken, ulMechTokenLen );
304        // Adjust Values and sanity check
305        nTotalBytesWritten += nTempLength;
306        nInternalTokenLength -= nTempLength;
307  
308        if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 )
309        {
310           goto xEndWriteNegTokenInit;
311        }
312  
313     }  // IF MechToken Length is present
314  
315     // Next is the ReqFlags
316     if ( ucContextFlags > 0L )
317     {
318  
319        nTempLength = ASNDerCalcElementLength( SPNEGO_NEGINIT_MAXLEN_REQFLAGS, &nInternalLength );
320  
321        // We need a byte that indicates how many bits difference between the number
322        // of bits used in final octet (we only have one) and the max (8)
323  
324        abTempReqFlags[0] = SPNEGO_NEGINIT_REQFLAGS_BITDIFF;
325        abTempReqFlags[1] = ucContextFlags;
326  
327        // Decrease the pbWriteTokenData, now we know the length and
328        // write it out.
329        pbWriteTokenData -= nTempLength;
330        nTempLength = ASNDerWriteElement( pbWriteTokenData, SPNEGO_NEGINIT_ELEMENT_REQFLAGS,
331                                BITSTRING, abTempReqFlags, SPNEGO_NEGINIT_MAXLEN_REQFLAGS );
332  
333        // Adjust Values and sanity check
334        nTotalBytesWritten += nTempLength;
335        nInternalTokenLength -= nTempLength;
336  
337        if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 )
338        {
339           goto xEndWriteNegTokenInit;
340        }
341  
342     }  // IF ContextFlags
343  
344     // Next is the MechList - This is REQUIRED
345     nTempLength = ASNDerCalcMechListLength( pMechTypeList, MechTypeCnt, &nInternalLength );
346  
347     // Decrease the pbWriteTokenData, now we know the length and
348     // write it out.
349     pbWriteTokenData -= nTempLength;
350     nTempLength = ASNDerWriteMechList( pbWriteTokenData, pMechTypeList, MechTypeCnt );
351  
352     // Adjust Values and sanity check
353     nTotalBytesWritten += nTempLength;
354     nInternalTokenLength -= nTempLength;
355  
356     if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 )
357     {
358        goto xEndWriteNegTokenInit;
359     }
360  
361     // The next tokens we're writing out reflect the total number of bytes
362     // we have actually written out.
363  
364     // Sequence Token
365     nTempLength = ASNDerCalcTokenLength( nTotalBytesWritten, 0L );
366  
367     // Decrease the pbWriteTokenData, now we know the length and
368     // write it out.
369     pbWriteTokenData -= nTempLength;
370     nTempLength = ASNDerWriteToken( pbWriteTokenData, SPNEGO_CONSTRUCTED_SEQUENCE,
371                                      NULL, nTotalBytesWritten );
372  
373     // Adjust Values and sanity check
374     nTotalBytesWritten += nTempLength;
375     nInternalTokenLength -= nTempLength;
376  
377     if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 )
378     {
379        goto xEndWriteNegTokenInit;
380     }
381  
382     // Neg Init Token Identifier Token
383     nTempLength = ASNDerCalcTokenLength( nTotalBytesWritten, 0L );
384  
385     // Decrease the pbWriteTokenData, now we know the length and
386     // write it out.
387     pbWriteTokenData -= nTempLength;
388     nTempLength = ASNDerWriteToken( pbWriteTokenData, SPNEGO_NEGINIT_TOKEN_IDENTIFIER,
389                                      NULL, nTotalBytesWritten );
390  
391     // Adjust Values and sanity check
392     nTotalBytesWritten += nTempLength;
393     nInternalTokenLength -= nTempLength;
394  
395     if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 )
396     {
397        goto xEndWriteNegTokenInit;
398     }
399  
400     // SPNEGO OID Token
401     nTempLength = g_stcMechOIDList[spnego_mech_oid_Spnego].iLen;
402  
403     // Decrease the pbWriteTokenData, now we know the length and
404     // write it out.
405     pbWriteTokenData -= nTempLength;
406     nTempLength = ASNDerWriteOID( pbWriteTokenData, spnego_mech_oid_Spnego );
407  
408     // Adjust Values and sanity check
409     nTotalBytesWritten += nTempLength;
410     nInternalTokenLength -= nTempLength;
411  
412     if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 )
413     {
414        goto xEndWriteNegTokenInit;
415     }
416  
417     // App Constructed Token
418     nTempLength = ASNDerCalcTokenLength( nTotalBytesWritten, 0L );
419  
420     // Decrease the pbWriteTokenData, now we know the length and
421     // write it out.
422     pbWriteTokenData -= nTempLength;
423     nTempLength = ASNDerWriteToken( pbWriteTokenData, SPNEGO_NEGINIT_APP_CONSTRUCT,
424                                      NULL, nTotalBytesWritten );
425  
426     // Adjust Values and sanity check
427     nTotalBytesWritten += nTempLength;
428  
429     // Don't adjust the internal token length here, it doesn't account
430     // the initial bytes written out (we really don't need to keep
431     // a running count here, but for debugging, it helps to be able
432     // to see the total number of bytes written out as well as the
433     // number of bytes left to write).
434  
435     if ( nTotalBytesWritten == nTokenLength && nInternalTokenLength == 0 &&
436           pbWriteTokenData == pbTokenData )
437     {
438        nReturn = SPNEGO_E_SUCCESS;
439     }
440  
441  xEndWriteNegTokenInit:
442  
443     return nReturn;
444  
445  }
446  
447  /////////////////////////////////////////////////////////////////////////////
448  //
449  // Function:
450  //    CalculateMinSpnegoTargTokenSize
451  //
452  // Parameters:
453  //    [in]  MechType                -  Supported MechType
454  //    [in]  spnegoNegResult         -  Neg Result
455  //    [in]  nMechTokenLength        -  Length of the MechToken Element
456  //    [in]  nMechListMICLength      -  Length of the MechListMIC Element
457  //    [out] pnTokenSize             -  Filled out with total size of token
458  //    [out] pnInternalTokenLength   -  Filled out with length minus length
459  //                                     for initial token.
460  //
461  // Returns:
462  //    int   Success - SPNEGO_E_SUCCESS
463  //          Failure - SPNEGO API Error code
464  //
465  // Comments :
466  //    Calculates the required length for a SPNEGO NegTokenTarg token based
467  //    on the supplied variable length values and which elements are present.
468  //    Note that because the lengths can be represented by an arbitrary
469  //    number of bytes in DER encodings, we actually calculate the lengths
470  //    backwards, so we always know how many bytes we will potentially be
471  //    writing out.
472  //
473  ////////////////////////////////////////////////////////////////////////////
474  
475  int CalculateMinSpnegoTargTokenSize( SPNEGO_MECH_OID MechType,
476                                      SPNEGO_NEGRESULT spnegoNegResult, long nMechTokenLen,
477                                      long nMechListMICLen, long* pnTokenSize,
478                                      long* pnInternalTokenLength )
479  {
480     int   nReturn = SPNEGO_E_INVALID_LENGTH;
481  
482     // Start at 0.
483     long  nTotalLength = 0;
484     long  nTempLength= 0L;
485  
486     // We will calculate this by walking the token backwards
487  
488     // Start with MIC Element
489     if ( nMechListMICLen > 0L )
490     {
491        nTempLength = ASNDerCalcElementLength( nMechListMICLen, NULL );
492  
493        // Check for rollover error
494        if ( nTempLength < nMechListMICLen )
495        {
496           goto xEndTokenTargLength;
497        }
498  
499        nTotalLength += nTempLength;
500     }
501  
502     // Next is the MechToken
503     if ( nMechTokenLen > 0L )
504     {
505        nTempLength += ASNDerCalcElementLength( nMechTokenLen, NULL );
506  
507        // Check for rollover error
508        if ( nTempLength < nTotalLength )
509        {
510           goto xEndTokenTargLength;
511        }
512  
513        nTotalLength = nTempLength;
514     }
515  
516     // Supported MechType
517     if ( spnego_mech_oid_NotUsed != MechType )
518     {
519        // Supported MechOID element - we use the token function since
520        // we already know the size of the OID token and value
521        nTempLength += ASNDerCalcElementLength( g_stcMechOIDList[MechType].iActualDataLen,
522                                               NULL );
523  
524        // Check for rollover error
525        if ( nTempLength < nTotalLength )
526        {
527           goto xEndTokenTargLength;
528        }
529  
530        nTotalLength = nTempLength;
531  
532     }  // IF MechType is available
533  
534     // NegResult Element
535     if ( spnego_negresult_NotUsed != spnegoNegResult )
536     {
537        nTempLength += ASNDerCalcElementLength( SPNEGO_NEGTARG_MAXLEN_NEGRESULT, NULL );
538  
539        // Check for rollover error
540        if ( nTempLength < nTotalLength )
541        {
542           goto xEndTokenTargLength;
543        }
544  
545        nTotalLength = nTempLength;
546  
547     }  // IF negResult is available
548  
549     // Following two fields are the basic header tokens
550  
551     // Sequence Token
552     nTempLength += ASNDerCalcTokenLength( nTotalLength, 0L );
553  
554     // Check for rollover error
555     if ( nTempLength < nTotalLength )
556     {
557        goto xEndTokenTargLength;
558     }
559  
560     nTotalLength = nTempLength;
561  
562     // Neg Token Identifier Token
563     nTempLength += ASNDerCalcTokenLength( nTotalLength, 0L );
564  
565     // Check for rollover error
566     if ( nTempLength < nTotalLength )
567     {
568        goto xEndTokenTargLength;
569     }
570  
571     // The internal length doesn't include the number of bytes
572     // for the initial token
573     *pnInternalTokenLength = nTotalLength;
574     nTotalLength = nTempLength;
575  
576     // We're done
577     *pnTokenSize = nTotalLength;
578     nReturn = SPNEGO_E_SUCCESS;
579  
580  xEndTokenTargLength:
581  
582     return nReturn;
583  
584  }
585  
586  /////////////////////////////////////////////////////////////////////////////
587  //
588  // Function:
589  //    CreateSpnegoTargToken
590  //
591  // Parameters:
592  //    [in]  MechType                -  Supported MechType
593  //    [in]  eNegResult              -  NegResult value
594  //    [in]  pbMechToken             -  Mech Token Binary Data
595  //    [in]  ulMechTokenLen          -  Length of Mech Token
596  //    [in]  pbMechListMIC           -  MechListMIC Binary Data
597  //    [in]  ulMechListMICn          -  Length of MechListMIC
598  //    [out] pbTokenData             -  Buffer to write token into.
599  //    [in]  nTokenLength            -  Length of pbTokenData buffer
600  //    [in]  nInternalTokenLength    -  Length of full token without leading
601  //                                     token bytes.
602  //
603  // Returns:
604  //    int   Success - SPNEGO_E_SUCCESS
605  //          Failure - SPNEGO API Error code
606  //
607  // Comments :
608  //    Uses DER to fill out pbTokenData with a SPNEGO NegTokenTarg Token
609  //    Note that because the lengths can be represented by an arbitrary
610  //    number of bytes in DER encodings, we actually calculate the lengths
611  //    backwards, so we always know how many bytes we will potentially be
612  //    writing out.
613  //
614  ////////////////////////////////////////////////////////////////////////////
615  
616  int CreateSpnegoTargToken( SPNEGO_MECH_OID MechType,
617            SPNEGO_NEGRESULT eNegResult, unsigned char* pbMechToken,
618            unsigned long ulMechTokenLen, unsigned char* pbMechListMIC,
619            unsigned long ulMechListMICLen, unsigned char* pbTokenData,
620            long nTokenLength, long nInternalTokenLength )
621  {
622     int   nReturn = SPNEGO_E_INVALID_LENGTH;
623  
624     // Start at 0.
625     long  nTempLength= 0L;
626     long  nTotalBytesWritten = 0L;
627     long  nInternalLength = 0L;
628  
629     unsigned char  ucTemp = 0;
630  
631     // We will write the token out backwards to properly handle the cases
632     // where the length bytes become adjustable, so the write location
633     // is initialized to point *just* past the end of the buffer.
634  
635     unsigned char* pbWriteTokenData = pbTokenData + nTokenLength;
636  
637  
638     // Start with MIC Element
639     if ( ulMechListMICLen > 0L )
640     {
641        nTempLength = ASNDerCalcElementLength( ulMechListMICLen, &nInternalLength );
642  
643        // Decrease the pbWriteTokenData, now we know the length and
644        // write it out.
645  
646        pbWriteTokenData -= nTempLength;
647        nTempLength = ASNDerWriteElement( pbWriteTokenData, SPNEGO_NEGTARG_ELEMENT_MECHLISTMIC,
648                                OCTETSTRING, pbMechListMIC, ulMechListMICLen );
649  
650        // Adjust Values and sanity check
651        nTotalBytesWritten += nTempLength;
652        nInternalTokenLength -= nTempLength;
653  
654        if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 )
655        {
656           goto xEndWriteNegTokenTarg;
657        }
658  
659     }  // IF MechListMIC is present
660  
661     // Next is the MechToken
662     if ( ulMechTokenLen > 0L )
663     {
664        nTempLength = ASNDerCalcElementLength( ulMechTokenLen, &nInternalLength );
665  
666        // Decrease the pbWriteTokenData, now we know the length and
667        // write it out.
668        pbWriteTokenData -= nTempLength;
669        nTempLength = ASNDerWriteElement( pbWriteTokenData, SPNEGO_NEGTARG_ELEMENT_RESPONSETOKEN,
670                                OCTETSTRING, pbMechToken, ulMechTokenLen );
671        // Adjust Values and sanity check
672        nTotalBytesWritten += nTempLength;
673        nInternalTokenLength -= nTempLength;
674  
675        if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 )
676        {
677           goto xEndWriteNegTokenTarg;
678        }
679  
680     }  // IF MechToken Length is present
681  
682     // Supported Mech Type
683     if ( spnego_mech_oid_NotUsed != MechType )
684     {
685  
686        nTempLength = ASNDerCalcElementLength( g_stcMechOIDList[MechType].iActualDataLen,
687                                               &nInternalLength );
688  
689        // Decrease the pbWriteTokenData, now we know the length and
690        // write it out.
691        pbWriteTokenData -= nTempLength;
692        nTempLength = ASNDerWriteToken( pbWriteTokenData, SPNEGO_NEGTARG_ELEMENT_SUPPORTEDMECH,
693                                         g_stcMechOIDList[MechType].ucOid,
694                                         g_stcMechOIDList[MechType].iLen );
695  
696        // Adjust Values and sanity check
697        nTotalBytesWritten += nTempLength;
698        nInternalTokenLength -= nTempLength;
699  
700        if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 )
701        {
702           goto xEndWriteNegTokenTarg;
703        }
704  
705     }  // IF MechType is present
706  
707     // Neg Result
708     // NegResult Element
709     if ( spnego_negresult_NotUsed != eNegResult )
710     {
711        ucTemp = (unsigned char) eNegResult;
712  
713        nTempLength = ASNDerCalcElementLength( SPNEGO_NEGTARG_MAXLEN_NEGRESULT, &nInternalLength );
714  
715        // Decrease the pbWriteTokenData, now we know the length and
716        // write it out.
717        pbWriteTokenData -= nTempLength;
718        nTempLength = ASNDerWriteElement( pbWriteTokenData, SPNEGO_NEGTARG_ELEMENT_NEGRESULT,
719                                ENUMERATED, &ucTemp, SPNEGO_NEGTARG_MAXLEN_NEGRESULT );
720  
721        // Adjust Values and sanity check
722        nTotalBytesWritten += nTempLength;
723        nInternalTokenLength -= nTempLength;
724  
725        if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 )
726        {
727           goto xEndWriteNegTokenTarg;
728        }
729  
730     }  // If eNegResult is available
731  
732     // The next tokens we're writing out reflect the total number of bytes
733     // we have actually written out.
734  
735     // Sequence Token
736     nTempLength = ASNDerCalcTokenLength( nTotalBytesWritten, 0L );
737  
738     // Decrease the pbWriteTokenData, now we know the length and
739     // write it out.
740     pbWriteTokenData -= nTempLength;
741     nTempLength = ASNDerWriteToken( pbWriteTokenData, SPNEGO_CONSTRUCTED_SEQUENCE,
742                                      NULL, nTotalBytesWritten );
743  
744     // Adjust Values and sanity check
745     nTotalBytesWritten += nTempLength;
746     nInternalTokenLength -= nTempLength;
747  
748     if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 )
749     {
750        goto xEndWriteNegTokenTarg;
751     }
752  
753     // Neg Targ Token Identifier Token
754     nTempLength = ASNDerCalcTokenLength( nTotalBytesWritten, 0L );
755  
756     // Decrease the pbWriteTokenData, now we know the length and
757     // write it out.
758     pbWriteTokenData -= nTempLength;
759     nTempLength = ASNDerWriteToken( pbWriteTokenData, SPNEGO_NEGTARG_TOKEN_IDENTIFIER,
760                                      NULL, nTotalBytesWritten );
761  
762     // Adjust Values and sanity check
763     nTotalBytesWritten += nTempLength;
764  
765     // Don't adjust the internal token length here, it doesn't account
766     // the initial bytes written out (we really don't need to keep
767     // a running count here, but for debugging, it helps to be able
768     // to see the total number of bytes written out as well as the
769     // number of bytes left to write).
770  
771     if ( nTotalBytesWritten == nTokenLength && nInternalTokenLength == 0 &&
772           pbWriteTokenData == pbTokenData )
773     {
774        nReturn = SPNEGO_E_SUCCESS;
775     }
776  
777  
778  xEndWriteNegTokenTarg:
779  
780     return nReturn;
781  
782  
783  }
784  
785  
786  /////////////////////////////////////////////////////////////////////////////
787  //
788  // Function:
789  //    AllocEmptySpnegoToken
790  //
791  // Parameters:
792  //    [in]  ucCopyData        -  Flag to copy data or pointer.
793  //    [in]  ulFlags           -  Flags for SPNEGO_TOKEN data member.
794  //    [in]  pbTokenData       -  Binary token data.
795  //    [in]  ulTokenSize       -  Size of pbTokenData.
796  //
797  // Returns:
798  //    SPNEGO_TOKEN*  Success - Pointer to initialized SPNEGO_TOKEN struct
799  //                   Failure - NULL
800  //
801  // Comments :
802  //    Allocates a SPNEGO_TOKEN data structure and initializes it.  Based on
803  //    the value of ucCopyData, if non-zero, we copy the data into a buffer
804  //    we allocate in this function, otherwise, we copy the data pointer
805  //    direcly.
806  //
807  ////////////////////////////////////////////////////////////////////////////
808  
809  SPNEGO_TOKEN* AllocEmptySpnegoToken( unsigned char ucCopyData, unsigned long ulFlags,
810                                      unsigned char * pbTokenData, unsigned long ulTokenSize )
811  {
812     SPNEGO_TOKEN*  pSpnegoToken = (SPNEGO_TOKEN*) calloc( 1, sizeof(SPNEGO_TOKEN) );
813  
814     if ( NULL != pSpnegoToken )
815     {
816        // Set the token size
817        pSpnegoToken->nStructSize = SPNEGO_TOKEN_SIZE;
818  
819        // Initialize the element array
820        InitSpnegoTokenElementArray( pSpnegoToken );
821  
822        // Assign the flags value
823        pSpnegoToken->ulFlags = ulFlags;
824  
825        //
826        // IF ucCopyData is TRUE, we will allocate a buffer and copy data into it.
827        // Otherwise, we will just copy the pointer and the length.  This is so we
828        // can cut out additional allocations for performance reasons
829        //
830  
831        if ( SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA == ucCopyData )
832        {
833           // Alloc the internal buffer.  Cleanup on failure.
834           pSpnegoToken->pbBinaryData = (unsigned char*) calloc( ulTokenSize, sizeof(unsigned char) );
835  
836           if ( NULL != pSpnegoToken->pbBinaryData )
837           {
838              // We must ALWAYS free this buffer
839              pSpnegoToken->ulFlags |= SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA;
840  
841              // Copy the data locally
842              memcpy( pSpnegoToken->pbBinaryData, pbTokenData, ulTokenSize );
843              pSpnegoToken->ulBinaryDataLen = ulTokenSize;
844           }
845           else
846           {
847              free( pSpnegoToken );
848              pSpnegoToken = NULL;
849           }
850  
851        }  // IF ucCopyData
852        else
853        {
854           // Copy the pointer and the length directly - ulFlags will control whether or not
855           // we are allowed to free the value
856  
857           pSpnegoToken->pbBinaryData = pbTokenData;
858           pSpnegoToken->ulBinaryDataLen = ulTokenSize;
859        }
860  
861     }
862  
863     return pSpnegoToken;
864  }
865  
866  /////////////////////////////////////////////////////////////////////////////
867  //
868  // Function:
869  //    FreeSpnegoToken
870  //
871  // Parameters:
872  //    [in]  pSpnegoToken      -  Points to SPNEGO_TOKEN to free.
873  //
874  // Returns:
875  //    void
876  //
877  // Comments :
878  //    If non-NULL, interprets pSpnegoToken, freeing any internal allocations
879  //    and finally the actual structure.
880  //
881  ////////////////////////////////////////////////////////////////////////////
882  
883  void FreeSpnegoToken( SPNEGO_TOKEN* pSpnegoToken )
884  {
885     if ( NULL != pSpnegoToken )
886     {
887  
888        // Cleanup internal allocation per the flags
889        if ( pSpnegoToken->ulFlags & SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA &&
890           NULL != pSpnegoToken->pbBinaryData )
891        {
892           free( pSpnegoToken->pbBinaryData );
893           pSpnegoToken->pbBinaryData = NULL;
894        }
895  
896        free ( pSpnegoToken );
897     }
898  }
899  
900  /////////////////////////////////////////////////////////////////////////////
901  //
902  // Function:
903  //    InitSpnegoTokenElementArray
904  //
905  // Parameters:
906  //    [in]  pSpnegoToken      -  Points to SPNEGO_TOKEN structure.
907  //
908  // Returns:
909  //    void
910  //
911  // Comments :
912  //    Initializes the element array data member of a SPNEGO_TOKEN data
913  //    structure.
914  //
915  ////////////////////////////////////////////////////////////////////////////
916  
917  void InitSpnegoTokenElementArray( SPNEGO_TOKEN* pSpnegoToken )
918  {
919     int   nCtr;
920  
921     // Set the number of elemnts
922     pSpnegoToken->nNumElements = MAX_NUM_TOKEN_ELEMENTS;
923  
924     //
925     // Initially, all elements are unavailable
926     //
927  
928     for ( nCtr = 0; nCtr < MAX_NUM_TOKEN_ELEMENTS; nCtr++ )
929     {
930        // Set the element size as well
931        pSpnegoToken->aElementArray[ nCtr ].nStructSize = SPNEGO_ELEMENT_SIZE;
932        pSpnegoToken->aElementArray[ nCtr ].iElementPresent = SPNEGO_TOKEN_ELEMENT_UNAVAILABLE;
933     }
934  
935  }
936  
937  /////////////////////////////////////////////////////////////////////////////
938  //
939  // Function:
940  //    InitSpnegoTokenType
941  //
942  // Parameters:
943  //    [in]  pSpnegoToken            -  Points to SPNEGO_TOKEN structure.
944  //    [out] pnTokenLength           -  Filled out with total token length
945  //    [out] pnRemainingTokenLength  -  Filled out with remaining length
946  //                                     after header is parsed
947  //    [out] ppbFirstElement         -  Filled out with pointer to first
948  //                                     element after header info.
949  //
950  // Returns:
951  //    int   Success - SPNEGO_E_SUCCESS
952  //          Failure - SPNEGO API Error code
953  //
954  // Comments :
955  //    Walks the underlying binary data for a SPNEGO_TOKEN data structure
956  //    and determines the type of the underlying token based on token header
957  //    information.
958  //
959  ////////////////////////////////////////////////////////////////////////////
960  
961  int InitSpnegoTokenType( SPNEGO_TOKEN* pSpnegoToken, long* pnTokenLength,
962                             long* pnRemainingTokenLength, unsigned char** ppbFirstElement )
963  {
964     int   nReturn = SPNEGO_E_INVALID_TOKEN;
965     long  nActualTokenLength = 0L;
966     long  nBoundaryLength = pSpnegoToken->ulBinaryDataLen;
967     unsigned char* pbTokenData = pSpnegoToken->pbBinaryData;
968  
969     //
970     // First byte MUST be either an APP_CONSTRUCT or the NEGTARG_TOKEN_TARG
971     //
972  
973     if ( SPNEGO_NEGINIT_APP_CONSTRUCT == *pbTokenData )
974     {
975        // Validate the above token - this will tell us the actual length of the token
976        // per the encoding (minus the actual token bytes)
977        if ( ( nReturn = ASNDerCheckToken( pbTokenData, SPNEGO_NEGINIT_APP_CONSTRUCT, 0L, nBoundaryLength,
978                                            pnTokenLength, &nActualTokenLength ) )
979                         == SPNEGO_E_SUCCESS )
980        {
981           // Initialize the remaining token length value.  This will be used
982           // to tell the caller how much token there is left once we've parsed
983           // the header (they could calculate it from the other values, but this
984           // is a bit friendlier)
985           *pnRemainingTokenLength = *pnTokenLength;
986  
987           // Make adjustments to next token
988           pbTokenData += nActualTokenLength;
989           nBoundaryLength -= nActualTokenLength;
990  
991           // The next token should be an OID
992           if ( ( nReturn = ASNDerCheckOID( pbTokenData, spnego_mech_oid_Spnego, nBoundaryLength,
993                                            &nActualTokenLength ) ) == SPNEGO_E_SUCCESS )
994           {
995              // Make adjustments to next token
996              pbTokenData += nActualTokenLength;
997              nBoundaryLength -= nActualTokenLength;
998              *pnRemainingTokenLength -= nActualTokenLength;
999  
1000              // The next token should specify the NegTokenInit
1001              if ( ( nReturn = ASNDerCheckToken( pbTokenData, SPNEGO_NEGINIT_TOKEN_IDENTIFIER,
1002                                                  *pnRemainingTokenLength, nBoundaryLength, pnTokenLength,
1003                                                  &nActualTokenLength ) )
1004                               == SPNEGO_E_SUCCESS )
1005              {
1006                 // Make adjustments to next token
1007                 pbTokenData += nActualTokenLength;
1008                 nBoundaryLength -= nActualTokenLength;
1009                 *pnRemainingTokenLength -= nActualTokenLength;
1010  
1011                 // The next token should specify the start of a sequence
1012                 if ( ( nReturn = ASNDerCheckToken( pbTokenData, SPNEGO_CONSTRUCTED_SEQUENCE,
1013                                                     *pnRemainingTokenLength, nBoundaryLength, pnTokenLength,
1014                                                     &nActualTokenLength ) )
1015                                  == SPNEGO_E_SUCCESS )
1016                 {
1017                    // NegTokenInit header is now checked out!
1018  
1019                    // Make adjustments to next token
1020                    *pnRemainingTokenLength -= nActualTokenLength;
1021  
1022                    // Store pointer to first element
1023                    *ppbFirstElement = pbTokenData + nActualTokenLength;
1024                    pSpnegoToken->ucTokenType = SPNEGO_TOKEN_INIT;
1025                 }  // IF Check Sequence Token
1026  
1027              }  // IF Check NegTokenInit token
1028  
1029  
1030           }  // IF Check for SPNEGO OID
1031  
1032  
1033        }  // IF check app construct token
1034  
1035     }
1036     else if ( SPNEGO_NEGTARG_TOKEN_IDENTIFIER == *pbTokenData )
1037     {
1038  
1039        // The next token should specify the NegTokenInit
1040        if ( ( nReturn = ASNDerCheckToken( pbTokenData, SPNEGO_NEGTARG_TOKEN_IDENTIFIER,
1041                                            *pnRemainingTokenLength, nBoundaryLength, pnTokenLength,
1042                                            &nActualTokenLength ) )
1043                         == SPNEGO_E_SUCCESS )
1044        {
1045           // Initialize the remaining token length value.  This will be used
1046           // to tell the caller how much token there is left once we've parsed
1047           // the header (they could calculate it from the other values, but this
1048           // is a bit friendlier)
1049           *pnRemainingTokenLength = *pnTokenLength;
1050  
1051           // Make adjustments to next token
1052           pbTokenData += nActualTokenLength;
1053           nBoundaryLength -= nActualTokenLength;
1054  
1055           // The next token should specify the start of a sequence
1056           if ( ( nReturn = ASNDerCheckToken( pbTokenData, SPNEGO_CONSTRUCTED_SEQUENCE,
1057                                               *pnRemainingTokenLength, nBoundaryLength, pnTokenLength,
1058                                               &nActualTokenLength ) )
1059                            == SPNEGO_E_SUCCESS )
1060           {
1061              // NegTokenInit header is now checked out!
1062  
1063              // Make adjustments to next token
1064              *pnRemainingTokenLength -= nActualTokenLength;
1065  
1066              // Store pointer to first element
1067              *ppbFirstElement = pbTokenData + nActualTokenLength;
1068              pSpnegoToken->ucTokenType = SPNEGO_TOKEN_TARG;
1069           }  // IF Check Sequence Token
1070  
1071        }  // IF Check NegTokenInit token
1072  
1073     }  // ELSE IF it's a NegTokenTarg
1074  
1075     return nReturn;
1076  }
1077  
1078  
1079  /////////////////////////////////////////////////////////////////////////////
1080  //
1081  // Function:
1082  //    GetSpnegoInitTokenMechList
1083  //
1084  // Parameters:
1085  //    [in]  pbTokenData             -  Points to binary MechList element
1086  //                                     in NegTokenInit.
1087  //    [in]  nMechListLength         -  Length of the MechList
1088  //    [out] pSpnegoElement          -  Filled out with MechList Element
1089  //                                     data.
1090  //
1091  // Returns:
1092  //    int   Success - SPNEGO_E_SUCCESS
1093  //          Failure - SPNEGO API Error code
1094  //
1095  // Comments :
1096  //    Checks that pbTokenData is pointing at something that at least
1097  //    *looks* like a MechList and then fills out the supplied
1098  //    SPNEGO_ELEMENT structure.
1099  //
1100  ////////////////////////////////////////////////////////////////////////////
1101  
1102  int GetSpnegoInitTokenMechList( unsigned char* pbTokenData, int nMechListLength,
1103                                   SPNEGO_ELEMENT* pSpnegoElement )
1104  {
1105     int   nReturn = SPNEGO_E_INVALID_TOKEN;
1106     long  nLength = 0L;
1107     long  nActualTokenLength = 0L;
1108  
1109     // Actual MechList is prepended by a Constructed Sequence Token
1110     if ( ( nReturn = ASNDerCheckToken( pbTokenData, SPNEGO_CONSTRUCTED_SEQUENCE,
1111                                         nMechListLength, nMechListLength,
1112                                         &nLength, &nActualTokenLength ) )
1113                               == SPNEGO_E_SUCCESS )
1114     {
1115        // Adjust for this token
1116        nMechListLength -= nActualTokenLength;
1117        pbTokenData += nActualTokenLength;
1118  
1119        // Perform simple validation of the actual MechList (i.e. ensure that
1120        // the OIDs in the MechList are reasonable).
1121  
1122        if ( ( nReturn = ValidateMechList( pbTokenData, nLength ) ) == SPNEGO_E_SUCCESS )
1123        {
1124           // Initialize the element now
1125           pSpnegoElement->eElementType = spnego_init_mechtypes;
1126           pSpnegoElement->iElementPresent = SPNEGO_TOKEN_ELEMENT_AVAILABLE;
1127           pSpnegoElement->type = SPNEGO_MECHLIST_TYPE;
1128           pSpnegoElement->nDatalength = nLength;
1129           pSpnegoElement->pbData = pbTokenData;
1130        }
1131  
1132     }  // IF Check Token
1133  
1134     return nReturn;
1135  }
1136  
1137  /////////////////////////////////////////////////////////////////////////////
1138  //
1139  // Function:
1140  //    InitSpnegoTokenElementFromBasicType
1141  //
1142  // Parameters:
1143  //    [in]  pbTokenData             -  Points to binary element data in
1144  //                                     a SPNEGO token.
1145  //    [in]  nElementLength          -  Length of the element
1146  //    [in]  ucExpectedType          -  Expected DER type.
1147  //    [in]  spnegoElementType       -  Which element is this?
1148  //    [out] pSpnegoElement          -  Filled out with element data.
1149  //
1150  // Returns:
1151  //    int   Success - SPNEGO_E_SUCCESS
1152  //          Failure - SPNEGO API Error code
1153  //
1154  // Comments :
1155  //    Checks that pbTokenData is pointing at the specified DER type.  If so,
1156  //    then we verify that lengths are proper and then fill out the
1157  //    SPNEGO_ELEMENT data structure.
1158  //
1159  ////////////////////////////////////////////////////////////////////////////
1160  
1161  int InitSpnegoTokenElementFromBasicType( unsigned char* pbTokenData, int nElementLength,
1162                                            unsigned char ucExpectedType,
1163                                            SPNEGO_ELEMENT_TYPE spnegoElementType,
1164                                            SPNEGO_ELEMENT* pSpnegoElement )
1165  {
1166     int   nReturn = SPNEGO_E_UNEXPECTED_TYPE;
1167     long  nLength = 0L;
1168     long  nActualTokenLength = 0L;
1169  
1170     // The type BYTE must match our token data or something is badly wrong
1171     if ( *pbTokenData == ucExpectedType )
1172     {
1173  
1174        // Check that we are pointing at the specified type
1175        if ( ( nReturn = ASNDerCheckToken( pbTokenData, ucExpectedType,
1176                                            nElementLength, nElementLength,
1177                                            &nLength, &nActualTokenLength ) )
1178                                  == SPNEGO_E_SUCCESS )
1179        {
1180           // Adjust for this token
1181           nElementLength -= nActualTokenLength;
1182           pbTokenData += nActualTokenLength;
1183  
1184           // Initialize the element now
1185           pSpnegoElement->eElementType = spnegoElementType;
1186           pSpnegoElement->iElementPresent = SPNEGO_TOKEN_ELEMENT_AVAILABLE;
1187           pSpnegoElement->type = ucExpectedType;
1188           pSpnegoElement->nDatalength = nLength;
1189           pSpnegoElement->pbData = pbTokenData;
1190        }
1191  
1192     }  // IF type makes sense
1193  
1194     return nReturn;
1195  }
1196  
1197  
1198  /////////////////////////////////////////////////////////////////////////////
1199  //
1200  // Function:
1201  //    InitSpnegoTokenElementFromOID
1202  //
1203  // Parameters:
1204  //    [in]  pbTokenData             -  Points to binary element data in
1205  //                                     a SPNEGO token.
1206  //    [in]  nElementLength          -  Length of the element
1207  //    [in]  spnegoElementType       -  Which element is this?
1208  //    [out] pSpnegoElement          -  Filled out with element data.
1209  //
1210  // Returns:
1211  //    int   Success - SPNEGO_E_SUCCESS
1212  //          Failure - SPNEGO API Error code
1213  //
1214  // Comments :
1215  //    Initializes a SpnegoElement from an OID - normally, this would have
1216  //    used the Basic Type function above, but since we do binary compares
1217  //    on the OIDs against the DER information as well as the OID, we need
1218  //    to account for that.
1219  //
1220  ////////////////////////////////////////////////////////////////////////////
1221  
1222  int InitSpnegoTokenElementFromOID( unsigned char* pbTokenData, int nElementLength,
1223                                     SPNEGO_ELEMENT_TYPE spnegoElementType,
1224                                     SPNEGO_ELEMENT* pSpnegoElement )
1225  {
1226     int   nReturn = SPNEGO_E_UNEXPECTED_TYPE;
1227     long  nLength = 0L;
1228     long  nActualTokenLength = 0L;
1229  
1230     // The type BYTE must match our token data or something is badly wrong
1231     if ( *pbTokenData == OID )
1232     {
1233  
1234        // Check that we are pointing at an OID type
1235        if ( ( nReturn = ASNDerCheckToken( pbTokenData, OID,
1236                                            nElementLength, nElementLength,
1237                                            &nLength, &nActualTokenLength ) )
1238                                  == SPNEGO_E_SUCCESS )
1239        {
1240           // Don't adjust any values for this function
1241  
1242           // Initialize the element now
1243           pSpnegoElement->eElementType = spnegoElementType;
1244           pSpnegoElement->iElementPresent = SPNEGO_TOKEN_ELEMENT_AVAILABLE;
1245           pSpnegoElement->type = OID;
1246           pSpnegoElement->nDatalength = nElementLength;
1247           pSpnegoElement->pbData = pbTokenData;
1248        }
1249  
1250     }  // IF type makes sense
1251  
1252     return nReturn;
1253  }
1254  
1255  
1256  /////////////////////////////////////////////////////////////////////////////
1257  //
1258  // Function:
1259  //    InitSpnegoTokenElements
1260  //
1261  // Parameters:
1262  //    [in]  pSpnegoToken            -  Points to SPNEGO_TOKEN struct
1263  //    [in]  pbTokenData             -  Points to initial binary element
1264  //                                     data in a SPNEGO token.
1265  //    [in]  nRemainingTokenLength   -  Length remaining past header
1266  //
1267  // Returns:
1268  //    int   Success - SPNEGO_E_SUCCESS
1269  //          Failure - SPNEGO API Error code
1270  //
1271  // Comments :
1272  //    Interprets the data at pbTokenData based on the TokenType in
1273  //    pSpnegoToken.  Since some elements are optional (technically all are
1274  //    but the token becomes quite useless if this is so), we check if
1275  //    an element exists before filling out the element in the array.
1276  //
1277  ////////////////////////////////////////////////////////////////////////////
1278  
1279  int InitSpnegoTokenElements( SPNEGO_TOKEN* pSpnegoToken, unsigned char* pbTokenData,
1280                             long nRemainingTokenLength  )
1281  {
1282     //
1283     // The following arrays contain the token identifiers for the elements
1284     // comprising the actual token.  All values are optional, and there are
1285     // no defaults.
1286     //
1287  
1288     static unsigned char abNegTokenInitElements[] =
1289        { SPNEGO_NEGINIT_ELEMENT_MECHTYPES, SPNEGO_NEGINIT_ELEMENT_REQFLAGS,
1290           SPNEGO_NEGINIT_ELEMENT_MECHTOKEN, SPNEGO_NEGINIT_ELEMENT_MECHLISTMIC };
1291  
1292     static unsigned char abNegTokenTargElements[] =
1293        { SPNEGO_NEGTARG_ELEMENT_NEGRESULT, SPNEGO_NEGTARG_ELEMENT_SUPPORTEDMECH,
1294           SPNEGO_NEGTARG_ELEMENT_RESPONSETOKEN, SPNEGO_NEGTARG_ELEMENT_MECHLISTMIC };
1295  
1296     int   nReturn = SPNEGO_E_SUCCESS;
1297     int   nCtr = 0L;
1298     long  nElementLength = 0L;
1299     long  nActualTokenLength = 0L;
1300     unsigned char* pbElements = NULL;
1301  
1302     // Point to the correct array
1303     switch( pSpnegoToken->ucTokenType )
1304     {
1305        case SPNEGO_TOKEN_INIT:
1306        {
1307           pbElements = abNegTokenInitElements;
1308        }
1309        break;
1310  
1311        case SPNEGO_TOKEN_TARG:
1312        {
1313           pbElements = abNegTokenTargElements;
1314        }
1315        break;
1316  
1317     }  // SWITCH tokentype
1318  
1319     //
1320     // Enumerate the element arrays and look for the tokens at our current location
1321     //
1322  
1323     for ( nCtr = 0L;
1324           SPNEGO_E_SUCCESS == nReturn &&
1325           nCtr < MAX_NUM_TOKEN_ELEMENTS &&
1326           nRemainingTokenLength > 0L;
1327           nCtr++ )
1328     {
1329  
1330        // Check if the token exists
1331        if ( ( nReturn = ASNDerCheckToken( pbTokenData, pbElements[nCtr],
1332                                            0L, nRemainingTokenLength,
1333                                            &nElementLength, &nActualTokenLength ) )
1334                                  == SPNEGO_E_SUCCESS )
1335        {
1336  
1337           // Token data should skip over the sequence token and then
1338           // call the appropriate function to initialize the element
1339           pbTokenData += nActualTokenLength;
1340  
1341           // Lengths in the elements should NOT go beyond the element
1342           // length
1343  
1344           // Different tokens mean different elements
1345           if ( SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType )
1346           {
1347  
1348              // Handle each element as appropriate
1349              switch( pbElements[nCtr] )
1350              {
1351  
1352                 case SPNEGO_NEGINIT_ELEMENT_MECHTYPES:
1353                 {
1354                    //
1355                    // This is a Mech List that specifies which OIDs the
1356                    // originator of the Init Token supports.
1357                    //
1358  
1359                    nReturn = GetSpnegoInitTokenMechList( pbTokenData, nElementLength,
1360                                                           &pSpnegoToken->aElementArray[nCtr] );
1361  
1362                 }
1363                 break;
1364  
1365                 case SPNEGO_NEGINIT_ELEMENT_REQFLAGS:
1366                 {
1367                    //
1368                    // This is a BITSTRING which specifies the flags that the receiver
1369                    // pass to the gss_accept_sec_context() function.
1370                    //
1371  
1372                    nReturn = InitSpnegoTokenElementFromBasicType( pbTokenData, nElementLength,
1373                                                                    BITSTRING, spnego_init_reqFlags,
1374                                                                    &pSpnegoToken->aElementArray[nCtr] );
1375                 }
1376                 break;
1377  
1378                 case SPNEGO_NEGINIT_ELEMENT_MECHTOKEN:
1379                 {
1380                    //
1381                    // This is an OCTETSTRING which contains a GSSAPI token corresponding
1382                    // to the first OID in the MechList.
1383                    //
1384  
1385                    nReturn = InitSpnegoTokenElementFromBasicType( pbTokenData, nElementLength,
1386                                                                    OCTETSTRING, spnego_init_mechToken,
1387                                                                    &pSpnegoToken->aElementArray[nCtr] );
1388                 }
1389                 break;
1390  
1391                 case SPNEGO_NEGINIT_ELEMENT_MECHLISTMIC:  // xA3
1392                 {
1393                    //
1394                    // Don't yet know if this is a negTokenInit, or negTokenInit2.
1395                    // Unfortunately, both have the same type: SPNEGO_TOKEN_INIT
1396                    // If it's negTokenInit, this element should be an OCTETSTRING
1397                    // containing the MIC.  If it's a negTokenInit2, this element
1398                    // should be an SPNEGO_CONSTRUCTED_SEQUENCE containing the
1399                    // negHints (GENERALSTR, ignored)
1400                    //
1401  
1402                    nReturn = InitSpnegoTokenElementFromBasicType( pbTokenData, nElementLength,
1403                                                                   OCTETSTRING, spnego_init_mechListMIC,
1404                                                                   &pSpnegoToken->aElementArray[nCtr] );
1405  
1406                    if (nReturn == SPNEGO_E_UNEXPECTED_TYPE) {
1407                       // This is really a negHints element.  Check the type and length,
1408                       // but otherwise just ignore it.
1409                       long  elen, tlen;
1410                       nReturn = ASNDerCheckToken( pbTokenData, SPNEGO_CONSTRUCTED_SEQUENCE,
1411                                            nElementLength, nElementLength,
1412                                            &elen, &tlen );
1413                    }
1414                 }
1415                 break;
1416  
1417              }  // SWITCH Element
1418           }
1419           else
1420           {
1421              /* pSpnegoToken->ucTokenType == SPNEGO_TOKEN_TARG */
1422  
1423              switch( pbElements[nCtr] )
1424              {
1425  
1426                 case SPNEGO_NEGTARG_ELEMENT_NEGRESULT:
1427                 {
1428                    //
1429                    // This is an ENUMERATION which specifies result of the last GSS
1430                    // token negotiation call.
1431                    //
1432  
1433                    nReturn = InitSpnegoTokenElementFromBasicType( pbTokenData, nElementLength,
1434                                                                    ENUMERATED, spnego_targ_negResult,
1435                                                                    &pSpnegoToken->aElementArray[nCtr] );
1436                 }
1437                 break;
1438  
1439                 case SPNEGO_NEGTARG_ELEMENT_SUPPORTEDMECH:
1440                 {
1441                    //
1442                    // This is an OID which specifies a supported mechanism.
1443                    //
1444  
1445                    nReturn = InitSpnegoTokenElementFromOID( pbTokenData, nElementLength,
1446                                                             spnego_targ_mechListMIC,
1447                                                             &pSpnegoToken->aElementArray[nCtr] );
1448                 }
1449                 break;
1450  
1451                 case SPNEGO_NEGTARG_ELEMENT_RESPONSETOKEN:
1452                 {
1453                    //
1454                    // This is an OCTETSTRING which specifies results of the last GSS
1455                    // token negotiation call.
1456                    //
1457  
1458                    nReturn = InitSpnegoTokenElementFromBasicType( pbTokenData, nElementLength,
1459                                                                    OCTETSTRING, spnego_targ_responseToken,
1460                                                                    &pSpnegoToken->aElementArray[nCtr] );
1461                 }
1462                 break;
1463  
1464                 case SPNEGO_NEGTARG_ELEMENT_MECHLISTMIC:
1465                 {
1466                    //
1467                    // This is an OCTETSTRING, typically 16 bytes,
1468                    // which contains a message integrity BLOB.
1469                    //
1470  
1471                    nReturn = InitSpnegoTokenElementFromBasicType( pbTokenData, nElementLength,
1472                                                                   OCTETSTRING, spnego_targ_mechListMIC,
1473                                                                   &pSpnegoToken->aElementArray[nCtr] );
1474                 }
1475                 break;
1476  
1477              }  // SWITCH Element
1478  
1479           }  // ELSE !NegTokenInit
1480  
1481           // Account for the entire token and following data
1482           nRemainingTokenLength -= ( nActualTokenLength + nElementLength );
1483  
1484           // Token data should skip past the element length now
1485           pbTokenData += nElementLength;
1486  
1487        }  // IF Token found
1488        else if ( SPNEGO_E_TOKEN_NOT_FOUND == nReturn )
1489        {
1490           // For now, this is a benign error (remember, all elements are optional, so
1491           // if we don't find one, it's okay).
1492  
1493           nReturn = SPNEGO_E_SUCCESS;
1494        }
1495  
1496     }  // FOR enum elements
1497  
1498     //
1499     // We should always run down to 0 remaining bytes in the token.  If not, we've got
1500     // a bad token.
1501     //
1502  
1503     if ( SPNEGO_E_SUCCESS == nReturn && nRemainingTokenLength != 0L )
1504     {
1505        nReturn = SPNEGO_E_INVALID_TOKEN;
1506     }
1507  
1508     return nReturn;
1509  }
1510  
1511  
1512  /////////////////////////////////////////////////////////////////////////////
1513  //
1514  // Function:
1515  //    FindMechOIDInMechList
1516  //
1517  // Parameters:
1518  //    [in]  pSpnegoElement          -  SPNEGO_ELEMENT for MechList
1519  //    [in]  MechOID                 -  OID we're looking for.
1520  //    [out] piMechTypeIndex         -  Index in the list where OID was
1521  //                                     found
1522  //
1523  // Returns:
1524  //    int   Success - SPNEGO_E_SUCCESS
1525  //          Failure - SPNEGO API Error code
1526  //
1527  // Comments :
1528  //    Walks the MechList for MechOID.  When it is found, the index in the
1529  //    list is written to piMechTypeIndex.
1530  //
1531  ////////////////////////////////////////////////////////////////////////////
1532  
1533  int FindMechOIDInMechList( SPNEGO_ELEMENT* pSpnegoElement, SPNEGO_MECH_OID MechOID,
1534                            int * piMechTypeIndex )
1535  {
1536     int   nReturn = SPNEGO_E_NOT_FOUND;
1537     int   nCtr = 0;
1538     long  nLength = 0L;
1539     long  nBoundaryLength = pSpnegoElement->nDatalength;
1540     unsigned char* pbMechListData = pSpnegoElement->pbData;
1541  
1542     while( SPNEGO_E_SUCCESS != nReturn && nBoundaryLength > 0L )
1543     {
1544  
1545        // Use the helper function to check the OID
1546        if ( ( nReturn = ASNDerCheckOID( pbMechListData, MechOID, nBoundaryLength, &nLength ) )
1547                       == SPNEGO_E_SUCCESS )
1548        {
1549           *piMechTypeIndex = nCtr;
1550        }
1551  
1552        // Adjust for the current OID
1553        pbMechListData += nLength;
1554        nBoundaryLength -= nLength;
1555        nCtr++;
1556  
1557     }  // WHILE enuming OIDs
1558  
1559     return nReturn;
1560  
1561  }
1562  
1563  
1564  /////////////////////////////////////////////////////////////////////////////
1565  //
1566  // Function:
1567  //    ValidateMechList
1568  //
1569  // Parameters:
1570  //    [in]  pbMechListData          -  Pointer to binary MechList data
1571  //    [in]  nBoundaryLength         -  Length we must not exceed
1572  //
1573  // Returns:
1574  //    int   Success - SPNEGO_E_SUCCESS
1575  //          Failure - SPNEGO API Error code
1576  //
1577  // Comments :
1578  //    Checks the data at pbMechListData to see if it looks like a MechList.
1579  //    As part of this, we walk the list and ensure that none of the OIDs
1580  //    have a length that takes us outside of nBoundaryLength.
1581  //
1582  ////////////////////////////////////////////////////////////////////////////
1583  
1584  int ValidateMechList( unsigned char* pbMechListData, long nBoundaryLength )
1585  {
1586     int   nReturn = SPNEGO_E_SUCCESS;
1587     long  nLength = 0L;
1588     long  nTokenLength = 0L;
1589  
1590     while( SPNEGO_E_SUCCESS == nReturn && nBoundaryLength > 0L )
1591     {
1592        // Verify that we have something that at least *looks* like an OID - in other
1593        // words it has an OID identifier and specifies a length that doesn't go beyond
1594        // the size of the list.
1595        nReturn = ASNDerCheckToken( pbMechListData, OID, 0L, nBoundaryLength,
1596                                    &nLength, &nTokenLength );
1597  
1598        // Adjust for the current OID
1599        pbMechListData += ( nLength + nTokenLength );
1600        nBoundaryLength -= ( nLength + nTokenLength );
1601  
1602     }  // WHILE enuming OIDs
1603  
1604     return nReturn;
1605  
1606  }
1607  
1608  /////////////////////////////////////////////////////////////////////////////
1609  //
1610  // Function:
1611  //    IsValidMechOid
1612  //
1613  // Parameters:
1614  //    [in]  mechOid  -  mechOID id enumeration
1615  //
1616  // Returns:
1617  //    int   Success - 1
1618  //          Failure - 0
1619  //
1620  // Comments :
1621  //    Checks for a valid mechOid value.
1622  //
1623  ////////////////////////////////////////////////////////////////////////////
1624  
1625  int IsValidMechOid( SPNEGO_MECH_OID mechOid )
1626  {
1627     return ( mechOid >= spnego_mech_oid_Kerberos_V5_Legacy &&
1628              mechOid <= spnego_mech_oid_NTLMSSP );
1629  }
1630  
1631  /////////////////////////////////////////////////////////////////////////////
1632  //
1633  // Function:
1634  //    IsValidContextFlags
1635  //
1636  // Parameters:
1637  //    [in]  ucContextFlags -  ContextFlags value
1638  //
1639  // Returns:
1640  //    int   Success - 1
1641  //          Failure - 0
1642  //
1643  // Comments :
1644  //    Checks for a valid ContextFlags value.
1645  //
1646  ////////////////////////////////////////////////////////////////////////////
1647  
1648  int IsValidContextFlags( unsigned char ucContextFlags )
1649  {
1650     // Mask out our valid bits.  If there is anything leftover, this
1651     // is not a valid value for Context Flags
1652     return ( ( ucContextFlags & ~SPNEGO_NEGINIT_CONTEXT_MASK ) == 0 );
1653  }
1654  
1655  /////////////////////////////////////////////////////////////////////////////
1656  //
1657  // Function:
1658  //    IsValidNegResult
1659  //
1660  // Parameters:
1661  //    [in]  negResult   -  NegResult value
1662  //
1663  // Returns:
1664  //    int   Success - 1
1665  //          Failure - 0
1666  //
1667  // Comments :
1668  //    Checks for a valid NegResult value.
1669  //
1670  ////////////////////////////////////////////////////////////////////////////
1671  
1672  int IsValidNegResult( SPNEGO_NEGRESULT negResult )
1673  {
1674     return ( negResult >= spnego_negresult_success &&
1675              negResult <= spnego_negresult_rejected );
1676  }
1677  
1678  /////////////////////////////////////////////////////////////////////////////
1679  //
1680  // Function:
1681  //    IsValidSpnegoToken
1682  //
1683  // Parameters:
1684  //    [in]  pSpnegoToken   -  Points to SPNEGO_TOKEN data structure
1685  //
1686  // Returns:
1687  //    int   Success - 1
1688  //          Failure - 0
1689  //
1690  // Comments :
1691  //    Performs simple heuristic on location pointed to by pSpnegoToken.
1692  //
1693  ////////////////////////////////////////////////////////////////////////////
1694  
1695  int IsValidSpnegoToken( SPNEGO_TOKEN* pSpnegoToken )
1696  {
1697     int   nReturn = 0;
1698  
1699     // Parameter should be non-NULL
1700     if ( NULL != pSpnegoToken )
1701     {
1702        // Length should be at least the size defined in the header
1703        if ( pSpnegoToken->nStructSize >= SPNEGO_TOKEN_SIZE )
1704        {
1705           // Number of elements should be >= our maximum - if it's greater, that's
1706           // okay, since we'll only be accessing the elements up to MAX_NUM_TOKEN_ELEMENTS
1707           if ( pSpnegoToken->nNumElements >= MAX_NUM_TOKEN_ELEMENTS )
1708           {
1709              // Check for proper token type
1710              if ( SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType ||
1711                 SPNEGO_TOKEN_TARG == pSpnegoToken->ucTokenType )
1712              {
1713                 nReturn = 1;
1714              }
1715           }
1716  
1717        }  // IF struct size makes sense
1718  
1719     }  // IF non-NULL spnego Token
1720  
1721     return nReturn;
1722  }
1723  
1724  /////////////////////////////////////////////////////////////////////////////
1725  //
1726  // Function:
1727  //    IsValidSpnegoElement
1728  //
1729  // Parameters:
1730  //    [in]  pSpnegoToken   -  Points to SPNEGO_TOKEN data structure
1731  //    [in]  spnegoElement  -  spnegoElement Type from enumeration
1732  //
1733  // Returns:
1734  //    int   Success - 1
1735  //          Failure - 0
1736  //
1737  // Comments :
1738  //    Checks that spnegoElement has a valid value and is appropriate for
1739  //    the SPNEGO token encapsulated by pSpnegoToken.
1740  //
1741  ////////////////////////////////////////////////////////////////////////////
1742  
1743  int IsValidSpnegoElement( SPNEGO_TOKEN* pSpnegoToken,SPNEGO_ELEMENT_TYPE spnegoElement )
1744  {
1745     int   nReturn = 0;
1746  
1747     // Check boundaries
1748     if ( spnegoElement > spnego_element_min &&
1749        spnegoElement < spnego_element_max )
1750     {
1751  
1752        // Check for appropriateness to token type
1753        if ( SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType )
1754        {
1755           nReturn = ( spnegoElement >= spnego_init_mechtypes &&
1756                       spnegoElement <= spnego_init_mechListMIC );
1757        }
1758        else
1759        {
1760           nReturn = ( spnegoElement >= spnego_targ_negResult &&
1761                       spnegoElement <= spnego_targ_mechListMIC );
1762        }
1763  
1764     }  // IF boundary conditions are met
1765  
1766     return nReturn;
1767  }
1768  
1769  /////////////////////////////////////////////////////////////////////////////
1770  //
1771  // Function:
1772  //    CalculateElementArrayIndex
1773  //
1774  // Parameters:
1775  //    [in]  pSpnegoToken   -  Points to SPNEGO_TOKEN data structure
1776  //    [in]  spnegoElement  -  spnegoElement Type from enumeration
1777  //
1778  // Returns:
1779  //    int   index in the SPNEGO_TOKEN element array that the element can
1780  //          can be found
1781  //
1782  // Comments :
1783  //    Based on the Token Type, calculates the index in the element array
1784  //    at which the specified element can be found.
1785  //
1786  ////////////////////////////////////////////////////////////////////////////
1787  
1788  int CalculateElementArrayIndex( SPNEGO_TOKEN* pSpnegoToken,SPNEGO_ELEMENT_TYPE spnegoElement )
1789  {
1790     int   nReturn = 0;
1791  
1792     // Offset is difference between value and initial element identifier
1793     // (these differ based on ucTokenType)
1794  
1795     if ( SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType )
1796     {
1797        nReturn = spnegoElement - spnego_init_mechtypes;
1798     }
1799     else
1800     {
1801        nReturn = spnegoElement - spnego_targ_negResult;
1802     }
1803  
1804     return nReturn;
1805  }
1806  
1807  /////////////////////////////////////////////////////////////////////////////
1808  //
1809  // Function:
1810  //    InitTokenFromBinary
1811  //
1812  // Parameters:
1813  //    [in]  ucCopyData     -  Flag indicating if data should be copied
1814  //    [in]  ulFlags        -  Flags value for structure
1815  //    [in]  pnTokenData    -  Binary Token Data
1816  //    [in]  ulLength       -  Length of the data
1817  //    [out] ppSpnegoToken  -  Pointer to call allocated SPNEGO Token
1818  //                            data structure
1819  //
1820  // Returns:
1821  //    int   Success - SPNEGO_E_SUCCESS
1822  //          Failure - SPNEGO API Error code
1823  //
1824  // Comments :
1825  //    Allocates a SPNEGO_TOKEN data structure and fills it out as
1826  //    appropriate based in the flags passed into the function.
1827  //
1828  ////////////////////////////////////////////////////////////////////////////
1829  
1830  
1831  // Initializes SPNEGO_TOKEN structure from DER encoded binary data
1832  int InitTokenFromBinary( unsigned char ucCopyData, unsigned long ulFlags,
1833                          unsigned char* pbTokenData, unsigned long ulLength,
1834                          SPNEGO_TOKEN** ppSpnegoToken )
1835  {
1836     int            nReturn = SPNEGO_E_INVALID_PARAMETER;
1837     SPNEGO_TOKEN*  pSpnegoToken = NULL;
1838     unsigned char* pbFirstElement = NULL;
1839     long           nTokenLength = 0L;
1840     long           nRemainingTokenLength = 0L;
1841  
1842     // Basic Parameter Validation
1843  
1844     if (  NULL != pbTokenData &&
1845           NULL != ppSpnegoToken &&
1846           0L != ulLength )
1847     {
1848  
1849        //
1850        // Allocate the empty token, then initialize the data structure.
1851        //
1852  
1853        pSpnegoToken = AllocEmptySpnegoToken( ucCopyData, ulFlags, pbTokenData, ulLength );
1854  
1855        if ( NULL != pSpnegoToken )
1856        {
1857  
1858           // Copy the binary data locally
1859  
1860  
1861           // Initialize the token type
1862           if ( ( nReturn = InitSpnegoTokenType( pSpnegoToken, &nTokenLength,
1863                                                  &nRemainingTokenLength, &pbFirstElement ) )
1864                          == SPNEGO_E_SUCCESS )
1865           {
1866  
1867              // Initialize the element array
1868              if ( ( nReturn = InitSpnegoTokenElements( pSpnegoToken, pbFirstElement,
1869                                                        nRemainingTokenLength ) )
1870                             == SPNEGO_E_SUCCESS )
1871              {
1872                 *ppSpnegoToken = pSpnegoToken;
1873              }
1874  
1875           }  // IF Init Token Type
1876  
1877           // Cleanup on error condition
1878           if ( SPNEGO_E_SUCCESS != nReturn )
1879           {
1880              spnegoFreeData( pSpnegoToken );
1881           }
1882  
1883        }
1884        else
1885        {
1886           nReturn = SPNEGO_E_OUT_OF_MEMORY;
1887        }
1888  
1889     }  // IF Valid parameters
1890  
1891  
1892     return nReturn;
1893  }
1894