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