import less(1)
[unleashed/tickless.git] / usr / src / lib / libsmbfs / smb / derparse.c
blobf21a277d5a85f69ecb1af766964a7684092e816f
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.
11 // Date - 10/08/2002
12 // Author - Sanj Surati
15 /////////////////////////////////////////////////////////////
17 // DERPARSE.C
19 // SPNEGO Token Handler Source File
21 // Contains implementation of ASN.1 DER read/write functions
22 // as defined in DERPARSE.H.
24 /////////////////////////////////////////////////////////////
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"
34 // The GSS Mechanism OID enumeration values (SPNEGO_MECH_OID) control which offset in
35 // the array below, that a mechanism can be found.
38 #pragma error_messages (off,E_INITIALIZATION_TYPE_MISMATCH)
39 MECH_OID g_stcMechOIDList [] =
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
52 #pragma error_messages (default,E_INITIALIZATION_TYPE_MISMATCH)
54 /////////////////////////////////////////////////////////////////////////////
56 // Function:
57 // ASNDerGetLength
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.
66 // Returns:
67 // int Success - SPNEGO_E_SUCCESS
68 // Failure - SPNEGO API Error code
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.
75 ////////////////////////////////////////////////////////////////////////////
77 int ASNDerGetLength( unsigned char* pbLengthData, long nBoundaryLength, long* pnLength,
78 long* pnNumLengthBytes )
80 int nReturn = SPNEGO_E_INVALID_LENGTH;
81 int nNumLengthBytes = 0;
83 // First check if the extended length bit is set
85 if ( *pbLengthData & LEN_XTND )
87 // Lower 7 bits contain the number of trailing bytes that describe the length
88 nNumLengthBytes = *pbLengthData & LEN_MASK;
90 // Check that the number of bytes we are about to read is within our boundary
91 // constraints
93 if ( nNumLengthBytes <= nBoundaryLength - 1 )
96 // For now, our handler won't deal with lengths greater than 4 bytes
97 if ( nNumLengthBytes >= 1 && nNumLengthBytes <= 4 )
99 // 0 out the initial length
100 *pnLength = 0L;
102 // Bump by 1 byte
103 pbLengthData++;
105 #ifdef _LITTLE_ENDIAN
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 )
111 case 1:
113 *( ( (unsigned char*) pnLength ) ) = *pbLengthData;
114 break;
117 case 2:
119 *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 1);
120 *( ( (unsigned char*) pnLength ) + 1 ) = *(pbLengthData);
122 break;
125 case 3:
127 *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 2);
128 *( ( (unsigned char*) pnLength ) + 2 ) = *(pbLengthData + 1);
129 *( ( (unsigned char*) pnLength ) + 3 ) = *(pbLengthData);
130 break;
133 case 4:
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;
142 } // SWITCH ( nNumLengthBytes )
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.
149 memcpy( ( (unsigned char *) pnLength ) + ( 4 - nNumLengthBytes ),
150 pbLengthData, nNumLengthBytes );
151 #endif
153 // Account for the initial length byte
154 *pnNumLengthBytes = nNumLengthBytes + 1;
155 nReturn = SPNEGO_E_SUCCESS;
157 } // IF Valid Length
159 } // IF num bytes to read is within the boundary length
161 } // IF xtended length
162 else
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;
173 return nReturn;
177 /////////////////////////////////////////////////////////////////////////////
179 // Function:
180 // ASNDerCheckToken
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.
191 // Returns:
192 // int Success - SPNEGO_E_SUCCESS
193 // Failure - SPNEGO API Error code
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 .
201 ////////////////////////////////////////////////////////////////////////////
203 int ASNDerCheckToken( unsigned char* pbTokenData, unsigned char nToken,
204 long nLengthWithToken, long nBoundaryLength,
205 long* pnLength, long* pnTokenLength )
208 int nReturn = SPNEGO_E_INVALID_LENGTH;
209 long nNumLengthBytes = 0L;
211 // Make sure that we've at least got 2 bytes of room to work with
213 if ( nBoundaryLength >= 2 )
215 // The first byte of the token data MUST match the specified token
216 if ( *pbTokenData == nToken )
218 // Next byte indicates the length
219 pbTokenData++;
221 // Get the length described by the token
222 if ( ( nReturn = ASNDerGetLength( pbTokenData, nBoundaryLength, pnLength,
223 &nNumLengthBytes ) ) == SPNEGO_E_SUCCESS )
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 ) )
230 nReturn = SPNEGO_E_INVALID_LENGTH;
234 // If we were passed a length to check, do so now
235 if ( nLengthWithToken > 0L )
238 // Check that the expected length matches
239 if ( ( nLengthWithToken - ( nNumLengthBytes + 1 ) ) != *pnLength )
242 nReturn = SPNEGO_E_INVALID_LENGTH;
246 } // IF need to validate length
248 if ( SPNEGO_E_SUCCESS == nReturn )
250 *pnTokenLength = nNumLengthBytes + 1;
253 } // IF ASNDerGetLength
255 } // IF token matches
256 else
258 nReturn = SPNEGO_E_TOKEN_NOT_FOUND;
261 } // IF Boundary Length is at least 2 bytes
263 return nReturn;
266 /////////////////////////////////////////////////////////////////////////////
268 // Function:
269 // ASNDerCheckOID
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.
278 // Returns:
279 // int Success - SPNEGO_E_SUCCESS
280 // Failure - SPNEGO API Error code
282 // Comments :
283 // Checks the data pointed to by pbTokenData for the specified OID.
285 ////////////////////////////////////////////////////////////////////////////
287 int ASNDerCheckOID( unsigned char* pbTokenData, SPNEGO_MECH_OID nMechOID, long nBoundaryLength,
288 long* pnTokenLength )
290 int nReturn = 0L;
291 long nLength = 0L;
293 // Verify that we have an OID token
294 if ( ( nReturn = ASNDerCheckToken( pbTokenData, OID, 0L, nBoundaryLength,
295 &nLength, pnTokenLength ) ) == SPNEGO_E_SUCCESS )
297 // Add the data length to the Token Length
298 *pnTokenLength += nLength;
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 )
304 // Memcompare the token and the expected field
305 if ( memcmp( pbTokenData, g_stcMechOIDList[nMechOID].ucOid, *pnTokenLength ) != 0 )
307 nReturn = SPNEGO_E_UNEXPECTED_OID;
310 else
312 nReturn = SPNEGO_E_UNEXPECTED_OID;
315 } // IF OID Token CHecks
317 return nReturn;
320 /////////////////////////////////////////////////////////////////////////////
322 // Function:
323 // ASNDerCalcNumLengthBytes
325 // Parameters:
326 // [in] nLength - Length to calculate length bytes for.
328 // Returns:
329 // int Number of bytes necessary to represent length
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.
336 ////////////////////////////////////////////////////////////////////////////
338 int ASNDerCalcNumLengthBytes( long nLength )
340 if ( nLength <= 0x7F )
342 // A single byte will be sufficient for describing this length.
343 // The byte will simply contain the length
344 return 1;
346 else if ( nLength <= 0xFF )
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;
352 else if ( nLength <= 0xFFFF )
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;
358 else if ( nLength <= 0xFFFFFF )
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;
364 else
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;
373 /////////////////////////////////////////////////////////////////////////////
375 // Function:
376 // ASNDerCalcTokenLength
378 // Parameters:
379 // [in] nLength - Length to calculate length bytes for.
380 // [in] nDataLength - Actual Data length value.
382 // Returns:
383 // long Number of bytes necessary to represent a token, length and data
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.
390 ////////////////////////////////////////////////////////////////////////////
392 long ASNDerCalcTokenLength( long nLength, long nDataLength )
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;
398 return nTotalLength + nDataLength;
402 /////////////////////////////////////////////////////////////////////////////
404 // Function:
405 // ASNDerCalcElementLength
407 // Parameters:
408 // [in] nDataLength - Length of data.
409 // [out] pnInternalLength - Filled out with length of element
410 // without sequence info.
412 // Returns:
413 // long Number of bytes necessary to represent an element
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.
419 ////////////////////////////////////////////////////////////////////////////
421 long ASNDerCalcElementLength( long nDataLength, long* pnInternalLength )
423 // First the type token and the actual data
424 long nTotalLength = ASNDerCalcTokenLength( nDataLength, nDataLength );
426 // Internal length is the length without the element sequence token
427 if ( NULL != pnInternalLength )
429 *pnInternalLength = nTotalLength;
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 );
436 return nTotalLength;
439 /////////////////////////////////////////////////////////////////////////////
441 // Function:
442 // ASNDerCalcMechListLength
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.
449 // Returns:
450 // long Number of bytes necessary to represent a mechList
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.
457 ////////////////////////////////////////////////////////////////////////////
459 long ASNDerCalcMechListLength( SPNEGO_MECH_OID *mechOidLst, int mechOidCnt,
460 long* pnInternalLength )
462 // First the OID
463 SPNEGO_MECH_OID oid_idx;
464 long nTotalLength;
465 int i;
467 nTotalLength = 0;
468 for (i = 0; i < mechOidCnt; i++) {
469 oid_idx = mechOidLst[i];
470 nTotalLength += g_stcMechOIDList[oid_idx].iLen;
473 // Next add in a sequence token
474 nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L );
476 // Internal length is the length without the element sequence token
477 if ( NULL != pnInternalLength )
479 *pnInternalLength = nTotalLength;
482 // Finally add in the element's sequence token
483 nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L );
485 return nTotalLength;
489 /////////////////////////////////////////////////////////////////////////////
491 // Function:
492 // ASNDerWriteLength
494 // Parameters:
495 // [out] pbData - Buffer to write into.
496 // [in] nLength - Length to write out.
498 // Returns:
499 // int Number of bytes written out
501 // Comments :
502 // Helper function to write out a length value following DER rules .
504 ////////////////////////////////////////////////////////////////////////////
506 int ASNDerWriteLength( unsigned char* pbData, long nLength )
508 int nNumBytesRequired = ASNDerCalcNumLengthBytes( nLength );
509 int nNumLengthBytes = nNumBytesRequired - 1;
512 if ( nNumBytesRequired > 1 )
515 // Write out the number of bytes following which will be used
516 *pbData = (unsigned char ) ( LEN_XTND | nNumLengthBytes );
518 // Point to where we'll actually write the length
519 pbData++;
521 #ifdef _LITTLE_ENDIAN
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 )
527 case 1:
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).
532 *pbData = (unsigned char) nLength;
533 break;
536 case 2:
538 *pbData = *( ( (unsigned char*) &nLength ) + 1 );
539 *( pbData + 1) = *( ( (unsigned char*) &nLength ) );
540 break;
543 case 3:
545 *pbData = *( ( (unsigned char*) &nLength ) + 3 );
546 *( pbData + 1) = *( ( (unsigned char*) &nLength ) + 2 );
547 *( pbData + 2) = *( ( (unsigned char*) &nLength ) );
548 break;
551 case 4:
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;
560 } // SWITCH ( nNumLengthBytes )
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.
567 memcpy( pbData,
568 ( (unsigned char *) &nLength ) + ( 4 - nNumLengthBytes ), nNumLengthBytes );
569 #endif
571 } // IF > 1 byte for length
572 else
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).
577 *pbData = (unsigned char) nLength;
580 return nNumBytesRequired;
583 /////////////////////////////////////////////////////////////////////////////
585 // Function:
586 // ASNDerWriteToken
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.
594 // Returns:
595 // int Number of bytes written out
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.
602 ////////////////////////////////////////////////////////////////////////////
604 int ASNDerWriteToken( unsigned char* pbData, unsigned char ucType,
605 unsigned char* pbTokenValue, long nLength )
607 int nTotalBytesWrittenOut = 0L;
608 int nNumLengthBytesWritten = 0L;
610 // Write out the type
611 *pbData = ucType;
613 // Wrote 1 byte, and move data pointer
614 nTotalBytesWrittenOut++;
615 pbData++;
617 // Now write out the length and adjust the number of bytes written out
618 nNumLengthBytesWritten = ASNDerWriteLength( pbData, nLength );
620 nTotalBytesWrittenOut += nNumLengthBytesWritten;
621 pbData += nNumLengthBytesWritten;
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.
626 if ( NULL != pbTokenValue )
628 memcpy( pbData, pbTokenValue, nLength );
629 nTotalBytesWrittenOut += nLength;
632 return nTotalBytesWrittenOut;
636 /////////////////////////////////////////////////////////////////////////////
638 // Function:
639 // ASNDerWriteOID
641 // Parameters:
642 // [out] pbData - Buffer to write into.
643 // [in] eMechOID - OID to write out.
645 // Returns:
646 // int Number of bytes written out
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.
653 ////////////////////////////////////////////////////////////////////////////
655 int ASNDerWriteOID( unsigned char* pbData, SPNEGO_MECH_OID eMechOID )
658 if (pbData != NULL) {
659 memcpy( pbData, g_stcMechOIDList[eMechOID].ucOid,
660 g_stcMechOIDList[eMechOID].iLen );
663 return g_stcMechOIDList[eMechOID].iLen;
667 /////////////////////////////////////////////////////////////////////////////
669 // Function:
670 // ASNDerWriteMechList
672 // Parameters:
673 // [out] pbData - Buffer to write into.
674 // [in] eMechOID - OID to put in MechList.
676 // Returns:
677 // int Number of bytes written out
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.
684 ////////////////////////////////////////////////////////////////////////////
686 long ASNDerWriteMechList( unsigned char* pbData, SPNEGO_MECH_OID *mechOidLst, int mechOidCnt )
688 // First get the length
689 long nInternalLength = 0L;
690 long nMechListLength;
691 long nTempLength = 0L;
692 int i;
694 nMechListLength = ASNDerCalcMechListLength(mechOidLst, mechOidCnt, &nInternalLength);
695 nTempLength = ASNDerWriteToken( pbData, SPNEGO_NEGINIT_ELEMENT_MECHTYPES,
696 NULL, nInternalLength );
698 // Adjust the data pointer
699 pbData += nTempLength;
700 nInternalLength -= nTempLength;
702 // Now write the Sequence token and the OID (the OID is a BLOB in the global
703 // structure.
705 nTempLength = ASNDerWriteToken( pbData, SPNEGO_CONSTRUCTED_SEQUENCE,
706 NULL, nInternalLength);
707 pbData += nTempLength;
709 for (i = 0; i < mechOidCnt; i++) {
710 nTempLength = ASNDerWriteOID( pbData, mechOidLst[i] );
711 pbData += nTempLength;
714 return nMechListLength;
718 /////////////////////////////////////////////////////////////////////////////
720 // Function:
721 // ASNDerWriteElement
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.
730 // Returns:
731 // int Number of bytes written out
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.
737 ////////////////////////////////////////////////////////////////////////////
739 int ASNDerWriteElement( unsigned char* pbData, unsigned char ucElementSequence,
740 unsigned char ucType, unsigned char* pbTokenValue, long nLength )
742 // First get the length
743 long nInternalLength = 0L;
744 long nElementLength = ASNDerCalcElementLength( nLength, &nInternalLength );
745 long nTempLength = 0L;
747 // Write out the sequence byte and the length of the type and data
748 nTempLength = ASNDerWriteToken( pbData, ucElementSequence, NULL, nInternalLength );
750 // Adjust the data pointer
751 pbData += nTempLength;
753 // Now write the type and the data.
754 nTempLength = ASNDerWriteToken( pbData, ucType, pbTokenValue, nLength );
756 return nElementLength;