1 // Copyright 2012 Nexenta Systems, Inc. All rights reserved.
2 // Copyright (C) 2002 Microsoft Corporation
3 // All rights reserved.
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.
12 // Author - Sanj Surati
15 /////////////////////////////////////////////////////////////
19 // SPNEGO Token Handler Source File
21 // Contains implementation of ASN.1 DER read/write functions
22 // as defined in DERPARSE.H.
24 /////////////////////////////////////////////////////////////
29 #include <sys/byteorder.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 /////////////////////////////////////////////////////////////////////////////
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.
67 // int Success - SPNEGO_E_SUCCESS
68 // Failure - SPNEGO API Error code
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
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
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
)
113 *( ( (unsigned char*) pnLength
) ) = *pbLengthData
;
119 *( ( (unsigned char*) pnLength
) ) = *(pbLengthData
+ 1);
120 *( ( (unsigned char*) pnLength
) + 1 ) = *(pbLengthData
);
127 *( ( (unsigned char*) pnLength
) ) = *(pbLengthData
+ 2);
128 *( ( (unsigned char*) pnLength
) + 2 ) = *(pbLengthData
+ 1);
129 *( ( (unsigned char*) pnLength
) + 3 ) = *(pbLengthData
);
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
);
142 } // SWITCH ( nNumLengthBytes )
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
149 memcpy( ( (unsigned char *) pnLength
) + ( 4 - nNumLengthBytes
),
150 pbLengthData
, nNumLengthBytes
);
153 // Account for the initial length byte
154 *pnNumLengthBytes
= nNumLengthBytes
+ 1;
155 nReturn
= SPNEGO_E_SUCCESS
;
159 } // IF num bytes to read is within the boundary length
161 } // IF xtended length
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
;
177 /////////////////////////////////////////////////////////////////////////////
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.
192 // int Success - SPNEGO_E_SUCCESS
193 // Failure - SPNEGO API Error code
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
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
258 nReturn
= SPNEGO_E_TOKEN_NOT_FOUND
;
261 } // IF Boundary Length is at least 2 bytes
266 /////////////////////////////////////////////////////////////////////////////
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.
279 // int Success - SPNEGO_E_SUCCESS
280 // Failure - SPNEGO API Error code
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
)
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
;
312 nReturn
= SPNEGO_E_UNEXPECTED_OID
;
315 } // IF OID Token CHecks
320 /////////////////////////////////////////////////////////////////////////////
323 // ASNDerCalcNumLengthBytes
326 // [in] nLength - Length to calculate length bytes for.
329 // int Number of bytes necessary to represent length
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
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
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
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
366 // Five bytes are necessary, one to say how many following bytes
367 // describe the length, and four to give the length
373 /////////////////////////////////////////////////////////////////////////////
376 // ASNDerCalcTokenLength
379 // [in] nLength - Length to calculate length bytes for.
380 // [in] nDataLength - Actual Data length value.
383 // long Number of bytes necessary to represent a token, length and data
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
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 /////////////////////////////////////////////////////////////////////////////
405 // ASNDerCalcElementLength
408 // [in] nDataLength - Length of data.
409 // [out] pnInternalLength - Filled out with length of element
410 // without sequence info.
413 // long Number of bytes necessary to represent an element
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 );
439 /////////////////////////////////////////////////////////////////////////////
442 // ASNDerCalcMechListLength
445 // [in] mechoid - Mech OID to put in list.
446 // [out] pnInternalLength - Filled out with length of element
447 // without the primary sequence token.
450 // long Number of bytes necessary to represent a mechList
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
)
463 SPNEGO_MECH_OID oid_idx
;
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 );
489 /////////////////////////////////////////////////////////////////////////////
495 // [out] pbData - Buffer to write into.
496 // [in] nLength - Length to write out.
499 // int Number of bytes written out
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
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
)
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
;
538 *pbData
= *( ( (unsigned char*) &nLength
) + 1 );
539 *( pbData
+ 1) = *( ( (unsigned char*) &nLength
) );
545 *pbData
= *( ( (unsigned char*) &nLength
) + 3 );
546 *( pbData
+ 1) = *( ( (unsigned char*) &nLength
) + 2 );
547 *( pbData
+ 2) = *( ( (unsigned char*) &nLength
) );
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
) );
560 } // SWITCH ( nNumLengthBytes )
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
568 ( (unsigned char *) &nLength
) + ( 4 - nNumLengthBytes
), nNumLengthBytes
);
571 } // IF > 1 byte for length
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 /////////////////////////////////////////////////////////////////////////////
589 // [out] pbData - Buffer to write into.
590 // [in] ucType - Token Type
591 // [in] pbTokenValue - Actual Value
592 // [in] nLength - Length of Data.
595 // int Number of bytes written out
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
613 // Wrote 1 byte, and move data pointer
614 nTotalBytesWrittenOut
++;
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 /////////////////////////////////////////////////////////////////////////////
642 // [out] pbData - Buffer to write into.
643 // [in] eMechOID - OID to write out.
646 // int Number of bytes written out
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 /////////////////////////////////////////////////////////////////////////////
670 // ASNDerWriteMechList
673 // [out] pbData - Buffer to write into.
674 // [in] eMechOID - OID to put in MechList.
677 // int Number of bytes written out
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;
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
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 /////////////////////////////////////////////////////////////////////////////
721 // ASNDerWriteElement
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.
731 // int Number of bytes written out
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
;