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