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