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