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