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