xref: /illumos-gate/usr/src/lib/libsmbfs/smb/derparse.c (revision 228c5c17dd30d7640072462f5334f96835507e11)
1 // Copyright (C) 2002 Microsoft Corporation
2 // All rights reserved.
3 //
4 // THIS CODE AND INFORMATION IS PROVIDED "AS IS"
5 // WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
6 // OR IMPLIED, INCLUDING BUT NOT LIMITED
7 // TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY
8 // AND/OR FITNESS FOR A PARTICULAR PURPOSE.
9 //
10 // Date    - 10/08/2002
11 // Author  - Sanj Surati
12 
13 
14 /////////////////////////////////////////////////////////////
15 //
16 // DERPARSE.C
17 //
18 // SPNEGO Token Handler Source File
19 //
20 // Contains implementation of ASN.1 DER read/write functions
21 // as defined in DERPARSE.H.
22 //
23 /////////////////////////////////////////////////////////////
24 
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <memory.h>
28 #include <sys/byteorder.h>
29 #include "spnego.h"
30 #include "derparse.h"
31 
32 //
33 // The GSS Mechanism OID enumeration values (SPNEGO_MECH_OID) control which offset in
34 // the array below, that a mechanism can be found.
35 //
36 
37 #pragma error_messages (off,E_INITIALIZATION_TYPE_MISMATCH)
38 MECH_OID g_stcMechOIDList [] =
39 {
40         {(unsigned char *)"\x06\x09\x2a\x86\x48\x82\xf7\x12\x01\x02\x02",
41          11,  9, spnego_mech_oid_Kerberos_V5_Legacy},	// 1.2.840.48018.1.2.2
42 	{(unsigned char *)"\x06\x09\x2a\x86\x48\x86\xf7\x12\x01\x02\x02",
43          11,  9, spnego_mech_oid_Kerberos_V5}, // 1.2.840.113554.1.2.2
44 	{(unsigned char *)"\x06\x06\x2b\x06\x01\x05\x05\x02",
45          8,  6, spnego_mech_oid_Spnego}, // 1.3.6.1.5.5.2
46 	{(unsigned char *)"\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a",
47          12, 10, spnego_mech_oid_NTLMSSP}, // 1.3.6.1.4.1.311.2.2.10
48 	{(unsigned char *)"", 0,  0, spnego_mech_oid_NotUsed  // Placeholder
49         }
50 };
51 #pragma error_messages (default,E_INITIALIZATION_TYPE_MISMATCH)
52 
53 /////////////////////////////////////////////////////////////////////////////
54 //
55 // Function:
56 //    ASNDerGetLength
57 //
58 // Parameters:
59 //    [in]  pbLengthData      -  DER Length Data
60 //    [in]  nBoundaryLength   -  Length that value must not exceed.
61 //    [out] pnLength          -  Filled out with length value
62 //    [out] pnNumLengthBytes  -  Filled out with number of bytes
63 //                               consumed by DER length.
64 //
65 // Returns:
66 //    int   Success - SPNEGO_E_SUCCESS
67 //          Failure - SPNEGO API Error code
68 //
69 // Comments :
70 //    Interprets the data at pbLengthData as a DER length.  The length must
71 //    fit within the bounds of nBoundary length.  We do not currently
72 //    process lengths that take more than 4 bytes.
73 //
74 ////////////////////////////////////////////////////////////////////////////
75 
76 int ASNDerGetLength( unsigned char* pbLengthData, long nBoundaryLength, long* pnLength,
77                      long* pnNumLengthBytes )
78 {
79    int   nReturn = SPNEGO_E_INVALID_LENGTH;
80    int   nNumLengthBytes = 0;
81 
82    // First check if the extended length bit is set
83 
84    if ( *pbLengthData & LEN_XTND )
85    {
86       // Lower 7 bits contain the number of trailing bytes that describe the length
87       nNumLengthBytes = *pbLengthData & LEN_MASK;
88 
89       // Check that the number of bytes we are about to read is within our boundary
90       // constraints
91 
92       if ( nNumLengthBytes <= nBoundaryLength - 1 )
93       {
94 
95          // For now, our handler won't deal with lengths greater than 4 bytes
96          if ( nNumLengthBytes >= 1 && nNumLengthBytes <= 4 )
97          {
98             // 0 out the initial length
99             *pnLength = 0L;
100 
101             // Bump by 1 byte
102             pbLengthData++;
103 
104    #ifdef _LITTLE_ENDIAN
105 
106             // There may be a cleaner way to do this, but for now, this seems to be
107             // an easy way to do the transformation
108             switch ( nNumLengthBytes )
109             {
110                case 1:
111                {
112                   *( ( (unsigned char*) pnLength ) ) = *pbLengthData;
113                   break;
114                }
115 
116                case 2:
117                {
118                   *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 1);
119                   *( ( (unsigned char*) pnLength ) + 1 ) = *(pbLengthData);
120 
121                   break;
122                }
123 
124                case 3:
125                {
126                   *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 2);
127                   *( ( (unsigned char*) pnLength ) + 2 ) = *(pbLengthData + 1);
128                   *( ( (unsigned char*) pnLength ) + 3 ) = *(pbLengthData);
129                   break;
130                }
131 
132                case 4:
133                {
134                   *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 3);
135                   *( ( (unsigned char*) pnLength ) + 1 ) = *(pbLengthData + 2);
136                   *( ( (unsigned char*) pnLength ) + 2 ) = *(pbLengthData + 1);
137                   *( ( (unsigned char*) pnLength ) + 3 ) = *(pbLengthData);
138                   break;
139                }
140 
141             }  // SWITCH ( nNumLengthBytes )
142 
143    #else
144             // We are Big-Endian, so the length can be copied in from the source
145             // as is.  Ensure that we adjust for the number of bytes we actually
146             // copy.
147 
148             memcpy( ( (unsigned char *) pnLength ) + ( 4 - nNumLengthBytes ),
149                      pbLengthData, nNumLengthBytes );
150    #endif
151 
152             // Account for the initial length byte
153             *pnNumLengthBytes = nNumLengthBytes + 1;
154             nReturn = SPNEGO_E_SUCCESS;
155 
156          }  // IF Valid Length
157 
158       }  // IF num bytes to read is within the boundary length
159 
160    }  // IF xtended length
161    else
162    {
163 
164       // Extended bit is not set, so the length is in the value and the one
165       // byte describes the length
166       *pnLength = *pbLengthData & LEN_MASK;
167       *pnNumLengthBytes = 1;
168       nReturn = SPNEGO_E_SUCCESS;
169 
170    }
171 
172    return nReturn;
173 }
174 
175 
176 /////////////////////////////////////////////////////////////////////////////
177 //
178 // Function:
179 //    ASNDerCheckToken
180 //
181 // Parameters:
182 //    [in]  pbTokenData       -  Token Data
183 //    [in]  nToken            -  Token identifier to check for
184 //    [in]  nLengthWithToken  -  Expected token length (with data)
185 //    [in]  nBoundaryLength   -  Length that value must not exceed.
186 //    [out] pnLength          -  Filled out with data length
187 //    [out] pnTokenLength     -  Filled out with number of bytes
188 //                               consumed by token identifier and length.
189 //
190 // Returns:
191 //    int   Success - SPNEGO_E_SUCCESS
192 //          Failure - SPNEGO API Error code
193 //
194 // Comments :
195 //    Checks the data pointed to by pbTokenData for the specified token
196 //    identifier and the length that immediately follows.  If
197 //    nLengthWithToken is > 0, the calculated length must match.  The
198 //    length must also not exceed the specified boundary length .
199 //
200 ////////////////////////////////////////////////////////////////////////////
201 
202 int ASNDerCheckToken( unsigned char* pbTokenData, unsigned char nToken,
203                         long nLengthWithToken, long nBoundaryLength,
204                         long* pnLength, long* pnTokenLength )
205 {
206 
207    int   nReturn = SPNEGO_E_INVALID_LENGTH;
208    long  nNumLengthBytes = 0L;
209 
210    // Make sure that we've at least got 2 bytes of room to work with
211 
212    if ( nBoundaryLength >= 2 )
213    {
214       // The first byte of the token data MUST match the specified token
215       if ( *pbTokenData == nToken )
216       {
217          // Next byte indicates the length
218          pbTokenData++;
219 
220          // Get the length described by the token
221          if ( ( nReturn = ASNDerGetLength( pbTokenData, nBoundaryLength, pnLength,
222                                              &nNumLengthBytes )  ) == SPNEGO_E_SUCCESS )
223          {
224             // Verify that the length is LESS THAN the boundary length
225             // (this should prevent us walking out of our buffer)
226             if ( ( nBoundaryLength - ( nNumLengthBytes + 1 ) < *pnLength ) )
227             {
228 
229                nReturn = SPNEGO_E_INVALID_LENGTH;
230 
231             }
232 
233             // If we were passed a length to check, do so now
234             if ( nLengthWithToken > 0L )
235             {
236 
237                // Check that the expected length matches
238                if ( ( nLengthWithToken - ( nNumLengthBytes + 1 ) ) != *pnLength )
239                {
240 
241                   nReturn = SPNEGO_E_INVALID_LENGTH;
242 
243                }
244 
245             }  // IF need to validate length
246 
247             if ( SPNEGO_E_SUCCESS == nReturn )
248             {
249                *pnTokenLength = nNumLengthBytes + 1;
250             }
251 
252          }  // IF ASNDerGetLength
253 
254       }  // IF token matches
255       else
256       {
257          nReturn = SPNEGO_E_TOKEN_NOT_FOUND;
258       }
259 
260    }  // IF Boundary Length is at least 2 bytes
261 
262    return nReturn;
263 }
264 
265 /////////////////////////////////////////////////////////////////////////////
266 //
267 // Function:
268 //    ASNDerCheckOID
269 //
270 // Parameters:
271 //    [in]  pbTokenData       -  Token Data
272 //    [in]  nMechOID          -  OID we are looking for
273 //    [in]  nBoundaryLength   -  Length that value must not exceed.
274 //    [out] pnTokenLength     -  Filled out with number of bytes
275 //                               consumed by token and data.
276 //
277 // Returns:
278 //    int   Success - SPNEGO_E_SUCCESS
279 //          Failure - SPNEGO API Error code
280 //
281 // Comments :
282 //    Checks the data pointed to by pbTokenData for the specified OID.
283 //
284 ////////////////////////////////////////////////////////////////////////////
285 
286 int ASNDerCheckOID( unsigned char* pbTokenData, SPNEGO_MECH_OID nMechOID, long nBoundaryLength,
287                      long* pnTokenLength )
288 {
289    int   nReturn = 0L;
290    long  nLength = 0L;
291 
292    // Verify that we have an OID token
293    if ( ( nReturn = ASNDerCheckToken( pbTokenData, OID, 0L, nBoundaryLength,
294                                        &nLength, pnTokenLength ) ) == SPNEGO_E_SUCCESS )
295    {
296       // Add the data length to the Token Length
297       *pnTokenLength += nLength;
298 
299       // Token Lengths plus the actual length must match the length in our OID list element.
300       // If it doesn't, we're done
301       if ( *pnTokenLength == g_stcMechOIDList[nMechOID].iLen )
302       {
303          // Memcompare the token and the expected field
304          if ( memcmp( pbTokenData, g_stcMechOIDList[nMechOID].ucOid, *pnTokenLength ) != 0 )
305          {
306             nReturn = SPNEGO_E_UNEXPECTED_OID;
307          }
308       }
309       else
310       {
311          nReturn = SPNEGO_E_UNEXPECTED_OID;
312       }
313 
314    }  // IF OID Token CHecks
315 
316    return nReturn;
317 }
318 
319 /////////////////////////////////////////////////////////////////////////////
320 //
321 // Function:
322 //    ASNDerCalcNumLengthBytes
323 //
324 // Parameters:
325 //    [in]  nLength           -  Length to calculate length bytes for.
326 //
327 // Returns:
328 //    int   Number of bytes necessary to represent length
329 //
330 // Comments :
331 //    Helper function to calculate the number of length bytes necessary to
332 //    represent a length value.  For our purposes, a 32-bit value should be
333 //    enough to describea length.
334 //
335 ////////////////////////////////////////////////////////////////////////////
336 
337 int ASNDerCalcNumLengthBytes( long nLength )
338 {
339       if ( nLength <= 0x7F )
340       {
341          // A single byte will be sufficient for describing this length.
342          // The byte will simply contain the length
343          return 1;
344       }
345       else if ( nLength <= 0xFF )
346       {
347          // Two bytes are necessary, one to say how many following bytes
348          // describe the length, and one to give the length
349          return 2;
350       }
351       else if ( nLength <= 0xFFFF )
352       {
353          // Three bytes are necessary, one to say how many following bytes
354          // describe the length, and two to give the length
355          return 3;
356       }
357       else if ( nLength <= 0xFFFFFF )
358       {
359          // Four bytes are necessary, one to say how many following bytes
360          // describe the length, and three to give the length
361          return 4;
362       }
363       else
364       {
365          // Five bytes are necessary, one to say how many following bytes
366          // describe the length, and four to give the length
367          return 5;
368       }
369 }
370 
371 
372 /////////////////////////////////////////////////////////////////////////////
373 //
374 // Function:
375 //    ASNDerCalcTokenLength
376 //
377 // Parameters:
378 //    [in]  nLength           -  Length to calculate length bytes for.
379 //    [in]  nDataLength       -  Actual Data length value.
380 //
381 // Returns:
382 //    long  Number of bytes necessary to represent a token, length and data
383 //
384 // Comments :
385 //    Helper function to calculate a token and value size, based on a
386 //    supplied length value, and any binary data that will need to be
387 //    written out.
388 //
389 ////////////////////////////////////////////////////////////////////////////
390 
391 long ASNDerCalcTokenLength( long nLength, long nDataLength )
392 {
393    // Add a byte to the length size to account for a single byte to
394    // hold the token type.
395    long  nTotalLength = ASNDerCalcNumLengthBytes( nLength ) + 1;
396 
397    return nTotalLength + nDataLength;
398 }
399 
400 
401 /////////////////////////////////////////////////////////////////////////////
402 //
403 // Function:
404 //    ASNDerCalcElementLength
405 //
406 // Parameters:
407 //    [in]  nDataLength       -  Length of data.
408 //    [out] pnInternalLength  -  Filled out with length of element
409 //                               without sequence info.
410 //
411 // Returns:
412 //    long  Number of bytes necessary to represent an element
413 //
414 // Comments :
415 //    Helper function to calculate an element length.  An element consists
416 //    of a sequence token, a type token and then the data.
417 //
418 ////////////////////////////////////////////////////////////////////////////
419 
420 long ASNDerCalcElementLength( long nDataLength, long* pnInternalLength )
421 {
422    // First the type token and the actual data
423    long  nTotalLength = ASNDerCalcTokenLength( nDataLength, nDataLength );
424 
425    // Internal length is the length without the element sequence token
426    if ( NULL != pnInternalLength )
427    {
428       *pnInternalLength = nTotalLength;
429    }
430 
431    // Next add in the element's sequence token (remember that its
432    // length is the total length of the type token and data)
433    nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L );
434 
435    return nTotalLength;
436 }
437 
438 /////////////////////////////////////////////////////////////////////////////
439 //
440 // Function:
441 //    ASNDerCalcMechListLength
442 //
443 // Parameters:
444 //    [in]  mechoid           -  Mech OID to put in list.
445 //    [out] pnInternalLength  -  Filled out with length of element
446 //                               without the primary sequence token.
447 //
448 // Returns:
449 //    long  Number of bytes necessary to represent a mechList
450 //
451 // Comments :
452 //    Helper function to calculate a MechList length.  A mechlist consists
453 //    of a NegTokenInit sequence token, a sequence token for the MechList
454 //    and finally a list of OIDs.  In our case, we only really have one
455 //    OID.
456 //
457 ////////////////////////////////////////////////////////////////////////////
458 
459 long ASNDerCalcMechListLength( SPNEGO_MECH_OID mechoid, long* pnInternalLength )
460 {
461    // First the OID
462    long  nTotalLength = g_stcMechOIDList[mechoid].iLen;
463 
464    // Next add in a sequence token
465    nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L );
466 
467    // Internal length is the length without the element sequence token
468    if ( NULL != pnInternalLength )
469    {
470       *pnInternalLength = nTotalLength;
471    }
472 
473    // Finally add in the element's sequence token
474    nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L );
475 
476    return nTotalLength;
477 }
478 
479 
480 /////////////////////////////////////////////////////////////////////////////
481 //
482 // Function:
483 //    ASNDerWriteLength
484 //
485 // Parameters:
486 //    [out] pbData            -  Buffer to write into.
487 //    [in]  nLength           -  Length to write out.
488 //
489 // Returns:
490 //    int   Number of bytes written out
491 //
492 // Comments :
493 //    Helper function to write out a length value following DER rules .
494 //
495 ////////////////////////////////////////////////////////////////////////////
496 
497 int ASNDerWriteLength( unsigned char* pbData, long nLength )
498 {
499    int   nNumBytesRequired = ASNDerCalcNumLengthBytes( nLength );
500    int   nNumLengthBytes = nNumBytesRequired - 1;
501 
502 
503    if ( nNumBytesRequired > 1 )
504    {
505 
506       // Write out the number of bytes following which will be used
507       *pbData = (unsigned char ) ( LEN_XTND | nNumLengthBytes );
508 
509       // Point to where we'll actually write the length
510       pbData++;
511 
512 #ifdef  _LITTLE_ENDIAN
513 
514       // There may be a cleaner way to do this, but for now, this seems to be
515       // an easy way to do the transformation
516       switch ( nNumLengthBytes )
517       {
518          case 1:
519          {
520             // Cast the length to a single byte, since we know that it
521             // is 0x7F or less (or we wouldn't only need a single byte).
522 
523             *pbData = (unsigned char) nLength;
524             break;
525          }
526 
527          case 2:
528          {
529             *pbData = *( ( (unsigned char*) &nLength ) + 1 );
530             *( pbData + 1) = *( ( (unsigned char*) &nLength ) );
531             break;
532          }
533 
534          case 3:
535          {
536             *pbData = *( ( (unsigned char*) &nLength ) + 3 );
537             *( pbData + 1) = *( ( (unsigned char*) &nLength ) + 2 );
538             *( pbData + 2) = *( ( (unsigned char*) &nLength ) );
539             break;
540          }
541 
542          case 4:
543          {
544             *pbData = *( ( (unsigned char*) &nLength ) + 3 );
545             *( pbData + 1) = *( ( (unsigned char*) &nLength ) + 2 );
546             *( pbData + 2) = *( ( (unsigned char*) &nLength ) + 1 );
547             *( pbData + 3) = *( ( (unsigned char*) &nLength ) );
548             break;
549          }
550 
551       }  // SWITCH ( nNumLengthBytes )
552 
553 #else
554       // We are Big-Endian, so the length can be copied in from the source
555       // as is.  Ensure that we adjust for the number of bytes we actually
556       // copy.
557 
558       memcpy( pbData,
559                ( (unsigned char *) &nLength ) + ( 4 - nNumLengthBytes ), nNumLengthBytes );
560 #endif
561 
562    }  // IF > 1 byte for length
563    else
564    {
565       // Cast the length to a single byte, since we know that it
566       // is 0x7F or less (or we wouldn't only need a single byte).
567 
568       *pbData = (unsigned char) nLength;
569    }
570 
571    return nNumBytesRequired;
572 }
573 
574 /////////////////////////////////////////////////////////////////////////////
575 //
576 // Function:
577 //    ASNDerWriteToken
578 //
579 // Parameters:
580 //    [out] pbData            -  Buffer to write into.
581 //    [in]  ucType            -  Token Type
582 //    [in]  pbTokenValue      -  Actual Value
583 //    [in]  nLength           -  Length of Data.
584 //
585 // Returns:
586 //    int   Number of bytes written out
587 //
588 // Comments :
589 //    Helper function to write out a token and any associated data.  If
590 //    pbTokenValue is non-NULL, then it is written out in addition to the
591 //    token identifier and the length bytes.
592 //
593 ////////////////////////////////////////////////////////////////////////////
594 
595 int ASNDerWriteToken( unsigned char* pbData, unsigned char ucType,
596                      unsigned char* pbTokenValue, long nLength )
597 {
598    int   nTotalBytesWrittenOut = 0L;
599    int   nNumLengthBytesWritten = 0L;
600 
601    // Write out the type
602    *pbData = ucType;
603 
604    // Wrote 1 byte, and move data pointer
605    nTotalBytesWrittenOut++;
606    pbData++;
607 
608    // Now write out the length and adjust the number of bytes written out
609    nNumLengthBytesWritten = ASNDerWriteLength( pbData, nLength );
610 
611    nTotalBytesWrittenOut += nNumLengthBytesWritten;
612    pbData += nNumLengthBytesWritten;
613 
614    // Write out the token value if we got one.  The assumption is that the
615    // nLength value indicates how many bytes are in pbTokenValue.
616 
617    if ( NULL != pbTokenValue )
618    {
619       memcpy( pbData, pbTokenValue, nLength );
620       nTotalBytesWrittenOut += nLength;
621    }
622 
623    return nTotalBytesWrittenOut;
624 }
625 
626 
627 /////////////////////////////////////////////////////////////////////////////
628 //
629 // Function:
630 //    ASNDerWriteOID
631 //
632 // Parameters:
633 //    [out] pbData            -  Buffer to write into.
634 //    [in]  eMechOID          -  OID to write out.
635 //
636 // Returns:
637 //    int   Number of bytes written out
638 //
639 // Comments :
640 //    Helper function to write out an OID.  For these we have the raw bytes
641 //    listed in a global structure.  The caller simply indicates which OID
642 //    should be written and we will splat out the data.
643 //
644 ////////////////////////////////////////////////////////////////////////////
645 
646 int ASNDerWriteOID( unsigned char* pbData, SPNEGO_MECH_OID eMechOID )
647 {
648 
649    memcpy( pbData, g_stcMechOIDList[eMechOID].ucOid, g_stcMechOIDList[eMechOID].iLen );
650 
651    return g_stcMechOIDList[eMechOID].iLen;
652 }
653 
654 
655 /////////////////////////////////////////////////////////////////////////////
656 //
657 // Function:
658 //    ASNDerWriteMechList
659 //
660 // Parameters:
661 //    [out] pbData            -  Buffer to write into.
662 //    [in]  eMechOID          -  OID to put in MechList.
663 //
664 // Returns:
665 //    int   Number of bytes written out
666 //
667 // Comments :
668 //    Helper function to write out a MechList.  A MechList consists of the
669 //    Init Token Sequence, a sequence token and then the list of OIDs.  In
670 //    our case the OID is from a global array of known OIDs.
671 //
672 ////////////////////////////////////////////////////////////////////////////
673 
674 long ASNDerWriteMechList( unsigned char* pbData, SPNEGO_MECH_OID mechoid )
675 {
676    // First get the length
677    long  nInternalLength = 0L;
678    long  nMechListLength = ASNDerCalcMechListLength( mechoid, &nInternalLength );
679    long  nTempLength = 0L;
680 
681    nTempLength = ASNDerWriteToken( pbData, SPNEGO_NEGINIT_ELEMENT_MECHTYPES,
682                                     NULL, nInternalLength );
683 
684    // Adjust the data pointer
685    pbData += nTempLength;
686 
687    // Now write the Sequence token and the OID (the OID is a BLOB in the global
688    // structure.
689 
690    nTempLength = ASNDerWriteToken( pbData, SPNEGO_CONSTRUCTED_SEQUENCE,
691                                     g_stcMechOIDList[mechoid].ucOid,
692                                     g_stcMechOIDList[mechoid].iLen );
693 
694    return nMechListLength;
695 }
696 
697 
698 /////////////////////////////////////////////////////////////////////////////
699 //
700 // Function:
701 //    ASNDerWriteElement
702 //
703 // Parameters:
704 //    [out] pbData            -  Buffer to write into.
705 //    [in]  ucElementSequence -  Sequence Token
706 //    [in]  ucType            -  Token Type
707 //    [in]  pbTokenValue      -  Actual Value
708 //    [in]  nLength           -  Length of Data.
709 //
710 // Returns:
711 //    int   Number of bytes written out
712 //
713 // Comments :
714 //    Helper function to write out a SPNEGO Token element.  An element
715 //    consists of a sequence token, a type token and the associated data.
716 //
717 ////////////////////////////////////////////////////////////////////////////
718 
719 int ASNDerWriteElement( unsigned char* pbData, unsigned char ucElementSequence,
720                         unsigned char ucType, unsigned char* pbTokenValue, long nLength )
721 {
722    // First get the length
723    long  nInternalLength = 0L;
724    long  nElementLength = ASNDerCalcElementLength( nLength, &nInternalLength );
725    long  nTempLength = 0L;
726 
727    // Write out the sequence byte and the length of the type and data
728    nTempLength = ASNDerWriteToken( pbData, ucElementSequence, NULL, nInternalLength );
729 
730    // Adjust the data pointer
731    pbData += nTempLength;
732 
733    // Now write the type and the data.
734    nTempLength = ASNDerWriteToken( pbData, ucType, pbTokenValue, nLength );
735 
736    return nElementLength;
737 }
738