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