diff options
author | thurlow <none@none> | 2008-02-13 19:51:22 -0800 |
---|---|---|
committer | thurlow <none@none> | 2008-02-13 19:51:22 -0800 |
commit | 4bff34e37def8a90f9194d81bc345c52ba20086a (patch) | |
tree | 7bf2710d9da099e3b07fea38e12788bfd565f3c5 /usr/src/lib/libsmbfs/smb/derparse.c | |
parent | a916d99c7b27a531bf37c57f83b0b74120fd05bb (diff) | |
download | illumos-joyent-4bff34e37def8a90f9194d81bc345c52ba20086a.tar.gz |
PSARC 2005/695 CIFS Client on Solaris
PSARC 2007/303 pam_smb_login
PSARC 2008/073 CIFS Client on Solaris - Updates
6651904 CIFS Client - PSARC 2005/695
Diffstat (limited to 'usr/src/lib/libsmbfs/smb/derparse.c')
-rw-r--r-- | usr/src/lib/libsmbfs/smb/derparse.c | 750 |
1 files changed, 750 insertions, 0 deletions
diff --git a/usr/src/lib/libsmbfs/smb/derparse.c b/usr/src/lib/libsmbfs/smb/derparse.c new file mode 100644 index 0000000000..cc7d61c6bd --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/derparse.c @@ -0,0 +1,750 @@ +/* +// Copyright (C) 2002 Microsoft Corporation +// All rights reserved. +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" +// WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +// OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY +// AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// +// Date - 10/08/2002 +// Author - Sanj Surati + + +///////////////////////////////////////////////////////////// +// +// DERPARSE.C +// +// SPNEGO Token Handler Source File +// +// Contains implementation of ASN.1 DER read/write functions +// as defined in DERPARSE.H. +// +///////////////////////////////////////////////////////////// + +*/ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <stdio.h> +#include <memory.h> +#include <sys/byteorder.h> +#include "spnego.h" +#include "derparse.h" + +/* +// +// The GSS Mechanism OID enumeration values (SPNEGO_MECH_OID) control which offset in +// the array below, that a mechanism can be found. +// +*/ +#pragma error_messages (off,E_INITIALIZATION_TYPE_MISMATCH) +MECH_OID g_stcMechOIDList [] = +{ + {"\x06\x09\x2a\x86\x48\x82\xf7\x12\x01\x02\x02", 11, 9, + spnego_mech_oid_Kerberos_V5_Legacy }, // 1.2.840.48018.1.2.2 + {"\x06\x09\x2a\x86\x48\x86\xf7\x12\x01\x02\x02", 11, 9, + spnego_mech_oid_Kerberos_V5 }, // 1.2.840.113554.1.2.2 + {"\x06\x06\x2b\x06\x01\x05\x05\x02", 8, 6, + spnego_mech_oid_Spnego }, // 1.3.6.1.5.5.2 + {"\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a", 12, 10, + spnego_mech_oid_NTLMSSP }, // 1.3.6.1.4.1.311.2.2.10 + {"", 0, 0, spnego_mech_oid_NotUsed } // Placeholder +}; +#pragma error_messages (default,E_INITIALIZATION_TYPE_MISMATCH) + +/* +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ASNDerGetLength +// +// Parameters: +// [in] pbLengthData - DER Length Data +// [in] nBoundaryLength - Length that value must not exceed. +// [out] pnLength - Filled out with length value +// [out] pnNumLengthBytes - Filled out with number of bytes +// consumed by DER length. +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// Interprets the data at pbLengthData as a DER length. The length must +// fit within the bounds of nBoundary length. We do not currently +// process lengths that take more than 4 bytes. +// +//////////////////////////////////////////////////////////////////////////// +*/ + +int ASNDerGetLength( unsigned char* pbLengthData, long nBoundaryLength, long* pnLength, + long* pnNumLengthBytes ) +{ + int nReturn = SPNEGO_E_INVALID_LENGTH; + int nNumLengthBytes = 0; + + // First check if the extended length bit is set + + if ( *pbLengthData & LEN_XTND ) + { + // Lower 7 bits contain the number of trailing bytes that describe the length + nNumLengthBytes = *pbLengthData & LEN_MASK; + + // Check that the number of bytes we are about to read is within our boundary + // constraints + + if ( nNumLengthBytes <= nBoundaryLength - 1 ) + { + + // For now, our handler won't deal with lengths greater than 4 bytes + if ( nNumLengthBytes >= 1 && nNumLengthBytes <= 4 ) + { + // 0 out the initial length + *pnLength = 0L; + + // Bump by 1 byte + pbLengthData++; + + #ifdef _LITTLE_ENDIAN + + // There may be a cleaner way to do this, but for now, this seems to be + // an easy way to do the transformation + switch ( nNumLengthBytes ) + { + case 1: + { + *( ( (unsigned char*) pnLength ) ) = *pbLengthData; + break; + } + + case 2: + { + *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 1); + *( ( (unsigned char*) pnLength ) + 1 ) = *(pbLengthData); + + break; + } + + case 3: + { + *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 2); + *( ( (unsigned char*) pnLength ) + 2 ) = *(pbLengthData + 1); + *( ( (unsigned char*) pnLength ) + 3 ) = *(pbLengthData); + break; + } + + case 4: + { + *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 3); + *( ( (unsigned char*) pnLength ) + 1 ) = *(pbLengthData + 2); + *( ( (unsigned char*) pnLength ) + 2 ) = *(pbLengthData + 1); + *( ( (unsigned char*) pnLength ) + 3 ) = *(pbLengthData); + break; + } + + } // SWITCH ( nNumLengthBytes ) + + #else + // We are Big-Endian, so the length can be copied in from the source + // as is. Ensure that we adjust for the number of bytes we actually + // copy. + + memcpy( ( (unsigned char *) pnLength ) + ( 4 - nNumLengthBytes ), + pbLengthData, nNumLengthBytes ); + #endif + + // Account for the initial length byte + *pnNumLengthBytes = nNumLengthBytes + 1; + nReturn = SPNEGO_E_SUCCESS; + + } // IF Valid Length + + } // IF num bytes to read is within the boundary length + + } // IF xtended length + else + { + + // Extended bit is not set, so the length is in the value and the one + // byte describes the length + *pnLength = *pbLengthData & LEN_MASK; + *pnNumLengthBytes = 1; + nReturn = SPNEGO_E_SUCCESS; + + } + + return nReturn; +} + + +/* +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ASNDerCheckToken +// +// Parameters: +// [in] pbTokenData - Token Data +// [in] nToken - Token identifier to check for +// [in] nLengthWithToken - Expected token length (with data) +// [in] nBoundaryLength - Length that value must not exceed. +// [out] pnLength - Filled out with data length +// [out] pnTokenLength - Filled out with number of bytes +// consumed by token identifier and length. +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// Checks the data pointed to by pbTokenData for the specified token +// identifier and the length that immediately follows. If +// nLengthWithToken is > 0, the calculated length must match. The +// length must also not exceed the specified boundary length . +// +//////////////////////////////////////////////////////////////////////////// +*/ + +int ASNDerCheckToken( unsigned char* pbTokenData, unsigned char nToken, + long nLengthWithToken, long nBoundaryLength, + long* pnLength, long* pnTokenLength ) +{ + + int nReturn = SPNEGO_E_INVALID_LENGTH; + long nNumLengthBytes = 0L; + + // Make sure that we've at least got 2 bytes of room to work with + + if ( nBoundaryLength >= 2 ) + { + // The first byte of the token data MUST match the specified token + if ( *pbTokenData == nToken ) + { + // Next byte indicates the length + pbTokenData++; + + // Get the length described by the token + if ( ( nReturn = ASNDerGetLength( pbTokenData, nBoundaryLength, pnLength, + &nNumLengthBytes ) ) == SPNEGO_E_SUCCESS ) + { + // Verify that the length is LESS THAN the boundary length + // (this should prevent us walking out of our buffer) + if ( ( nBoundaryLength - ( nNumLengthBytes + 1 ) < *pnLength ) ) + { + + nReturn = SPNEGO_E_INVALID_LENGTH; + + } + + // If we were passed a length to check, do so now + if ( nLengthWithToken > 0L ) + { + + // Check that the expected length matches + if ( ( nLengthWithToken - ( nNumLengthBytes + 1 ) ) != *pnLength ) + { + + nReturn = SPNEGO_E_INVALID_LENGTH; + + } + + } // IF need to validate length + + if ( SPNEGO_E_SUCCESS == nReturn ) + { + *pnTokenLength = nNumLengthBytes + 1; + } + + } // IF ASNDerGetLength + + } // IF token matches + else + { + nReturn = SPNEGO_E_TOKEN_NOT_FOUND; + } + + } // IF Boundary Length is at least 2 bytes + + return nReturn; +} + +/* +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ASNDerCheckOID +// +// Parameters: +// [in] pbTokenData - Token Data +// [in] nMechOID - OID we are looking for +// [in] nBoundaryLength - Length that value must not exceed. +// [out] pnTokenLength - Filled out with number of bytes +// consumed by token and data. +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// Checks the data pointed to by pbTokenData for the specified OID. +// +//////////////////////////////////////////////////////////////////////////// +*/ + +int ASNDerCheckOID( unsigned char* pbTokenData, SPNEGO_MECH_OID nMechOID, long nBoundaryLength, + long* pnTokenLength ) +{ + int nReturn = 0L; + long nLength = 0L; + + // Verify that we have an OID token + if ( ( nReturn = ASNDerCheckToken( pbTokenData, OID, 0L, nBoundaryLength, + &nLength, pnTokenLength ) ) == SPNEGO_E_SUCCESS ) + { + // Add the data length to the Token Length + *pnTokenLength += nLength; + + // Token Lengths plus the actual length must match the length in our OID list element. + // If it doesn't, we're done + if ( *pnTokenLength == g_stcMechOIDList[nMechOID].iLen ) + { + // Memcompare the token and the expected field + if ( memcmp( pbTokenData, g_stcMechOIDList[nMechOID].ucOid, *pnTokenLength ) != 0 ) + { + nReturn = SPNEGO_E_UNEXPECTED_OID; + } + } + else + { + nReturn = SPNEGO_E_UNEXPECTED_OID; + } + + } // IF OID Token CHecks + + return nReturn; +} + +/* +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ASNDerCalcNumLengthBytes +// +// Parameters: +// [in] nLength - Length to calculate length bytes for. +// +// Returns: +// int Number of bytes necessary to represent length +// +// Comments : +// Helper function to calculate the number of length bytes necessary to +// represent a length value. For our purposes, a 32-bit value should be +// enough to describea length. +// +//////////////////////////////////////////////////////////////////////////// +*/ + +int ASNDerCalcNumLengthBytes( long nLength ) +{ + if ( nLength <= 0x7F ) + { + // A single byte will be sufficient for describing this length. + // The byte will simply contain the length + return 1; + } + else if ( nLength <= 0xFF ) + { + // Two bytes are necessary, one to say how many following bytes + // describe the length, and one to give the length + return 2; + } + else if ( nLength <= 0xFFFF ) + { + // Three bytes are necessary, one to say how many following bytes + // describe the length, and two to give the length + return 3; + } + else if ( nLength <= 0xFFFFFF ) + { + // Four bytes are necessary, one to say how many following bytes + // describe the length, and three to give the length + return 4; + } + else + { + // Five bytes are necessary, one to say how many following bytes + // describe the length, and four to give the length + return 5; + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ASNDerCalcTokenLength +// +// Parameters: +// [in] nLength - Length to calculate length bytes for. +// [in] nDataLength - Actual Data length value. +// +// Returns: +// long Number of bytes necessary to represent a token, length and data +// +// Comments : +// Helper function to calculate a token and value size, based on a +// supplied length value, and any binary data that will need to be +// written out. +// +//////////////////////////////////////////////////////////////////////////// + +long ASNDerCalcTokenLength( long nLength, long nDataLength ) +{ + // Add a byte to the length size to account for a single byte to + // hold the token type. + long nTotalLength = ASNDerCalcNumLengthBytes( nLength ) + 1; + + return nTotalLength + nDataLength; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ASNDerCalcElementLength +// +// Parameters: +// [in] nDataLength - Length of data. +// [out] pnInternalLength - Filled out with length of element +// without sequence info. +// +// Returns: +// long Number of bytes necessary to represent an element +// +// Comments : +// Helper function to calculate an element length. An element consists +// of a sequence token, a type token and then the data. +// +//////////////////////////////////////////////////////////////////////////// + +long ASNDerCalcElementLength( long nDataLength, long* pnInternalLength ) +{ + // First the type token and the actual data + long nTotalLength = ASNDerCalcTokenLength( nDataLength, nDataLength ); + + // Internal length is the length without the element sequence token + if ( NULL != pnInternalLength ) + { + *pnInternalLength = nTotalLength; + } + + // Next add in the element's sequence token (remember that its + // length is the total length of the type token and data) + nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L ); + + return nTotalLength; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ASNDerCalcMechListLength +// +// Parameters: +// [in] mechoid - Mech OID to put in list. +// [out] pnInternalLength - Filled out with length of element +// without the primary sequence token. +// +// Returns: +// long Number of bytes necessary to represent a mechList +// +// Comments : +// Helper function to calculate a MechList length. A mechlist consists +// of a NegTokenInit sequence token, a sequence token for the MechList +// and finally a list of OIDs. In our case, we only really have one +// OID. +// +//////////////////////////////////////////////////////////////////////////// + +long ASNDerCalcMechListLength( SPNEGO_MECH_OID mechoid, long* pnInternalLength ) +{ + // First the OID + long nTotalLength = g_stcMechOIDList[mechoid].iLen; + + // Next add in a sequence token + nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L ); + + // Internal length is the length without the element sequence token + if ( NULL != pnInternalLength ) + { + *pnInternalLength = nTotalLength; + } + + // Finally add in the element's sequence token + nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L ); + + return nTotalLength; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ASNDerWriteLength +// +// Parameters: +// [out] pbData - Buffer to write into. +// [in] nLength - Length to write out. +// +// Returns: +// int Number of bytes written out +// +// Comments : +// Helper function to write out a length value following DER rules . +// +//////////////////////////////////////////////////////////////////////////// + +int ASNDerWriteLength( unsigned char* pbData, long nLength ) +{ + int nNumBytesRequired = ASNDerCalcNumLengthBytes( nLength ); + int nNumLengthBytes = nNumBytesRequired - 1; + + + if ( nNumBytesRequired > 1 ) + { + + // Write out the number of bytes following which will be used + *pbData = (unsigned char ) ( LEN_XTND | nNumLengthBytes ); + + // Point to where we'll actually write the length + pbData++; + +#ifdef _LITTLE_ENDIAN + + // There may be a cleaner way to do this, but for now, this seems to be + // an easy way to do the transformation + switch ( nNumLengthBytes ) + { + case 1: + { + // Cast the length to a single byte, since we know that it + // is 0x7F or less (or we wouldn't only need a single byte). + + *pbData = (unsigned char) nLength; + break; + } + + case 2: + { + *pbData = *( ( (unsigned char*) &nLength ) + 1 ); + *( pbData + 1) = *( ( (unsigned char*) &nLength ) ); + break; + } + + case 3: + { + *pbData = *( ( (unsigned char*) &nLength ) + 3 ); + *( pbData + 1) = *( ( (unsigned char*) &nLength ) + 2 ); + *( pbData + 2) = *( ( (unsigned char*) &nLength ) ); + break; + } + + case 4: + { + *pbData = *( ( (unsigned char*) &nLength ) + 3 ); + *( pbData + 1) = *( ( (unsigned char*) &nLength ) + 2 ); + *( pbData + 2) = *( ( (unsigned char*) &nLength ) + 1 ); + *( pbData + 3) = *( ( (unsigned char*) &nLength ) ); + break; + } + + } // SWITCH ( nNumLengthBytes ) + +#else + // We are Big-Endian, so the length can be copied in from the source + // as is. Ensure that we adjust for the number of bytes we actually + // copy. + + memcpy( pbData, + ( (unsigned char *) &nLength ) + ( 4 - nNumLengthBytes ), nNumLengthBytes ); +#endif + + } // IF > 1 byte for length + else + { + // Cast the length to a single byte, since we know that it + // is 0x7F or less (or we wouldn't only need a single byte). + + *pbData = (unsigned char) nLength; + } + + return nNumBytesRequired; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ASNDerWriteToken +// +// Parameters: +// [out] pbData - Buffer to write into. +// [in] ucType - Token Type +// [in] pbTokenValue - Actual Value +// [in] nLength - Length of Data. +// +// Returns: +// int Number of bytes written out +// +// Comments : +// Helper function to write out a token and any associated data. If +// pbTokenValue is non-NULL, then it is written out in addition to the +// token identifier and the length bytes. +// +//////////////////////////////////////////////////////////////////////////// + +int ASNDerWriteToken( unsigned char* pbData, unsigned char ucType, + unsigned char* pbTokenValue, long nLength ) +{ + int nTotalBytesWrittenOut = 0L; + int nNumLengthBytesWritten = 0L; + + // Write out the type + *pbData = ucType; + + // Wrote 1 byte, and move data pointer + nTotalBytesWrittenOut++; + pbData++; + + // Now write out the length and adjust the number of bytes written out + nNumLengthBytesWritten = ASNDerWriteLength( pbData, nLength ); + + nTotalBytesWrittenOut += nNumLengthBytesWritten; + pbData += nNumLengthBytesWritten; + + // Write out the token value if we got one. The assumption is that the + // nLength value indicates how many bytes are in pbTokenValue. + + if ( NULL != pbTokenValue ) + { + memcpy( pbData, pbTokenValue, nLength ); + nTotalBytesWrittenOut += nLength; + } + + return nTotalBytesWrittenOut; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ASNDerWriteOID +// +// Parameters: +// [out] pbData - Buffer to write into. +// [in] eMechOID - OID to write out. +// +// Returns: +// int Number of bytes written out +// +// Comments : +// Helper function to write out an OID. For these we have the raw bytes +// listed in a global structure. The caller simply indicates which OID +// should be written and we will splat out the data. +// +//////////////////////////////////////////////////////////////////////////// + +int ASNDerWriteOID( unsigned char* pbData, SPNEGO_MECH_OID eMechOID ) +{ + + memcpy( pbData, g_stcMechOIDList[eMechOID].ucOid, g_stcMechOIDList[eMechOID].iLen ); + + return g_stcMechOIDList[eMechOID].iLen; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ASNDerWriteMechList +// +// Parameters: +// [out] pbData - Buffer to write into. +// [in] eMechOID - OID to put in MechList. +// +// Returns: +// int Number of bytes written out +// +// Comments : +// Helper function to write out a MechList. A MechList consists of the +// Init Token Sequence, a sequence token and then the list of OIDs. In +// our case the OID is from a global array of known OIDs. +// +//////////////////////////////////////////////////////////////////////////// + +long ASNDerWriteMechList( unsigned char* pbData, SPNEGO_MECH_OID mechoid ) +{ + // First get the length + long nInternalLength = 0L; + long nMechListLength = ASNDerCalcMechListLength( mechoid, &nInternalLength ); + long nTempLength = 0L; + + nTempLength = ASNDerWriteToken( pbData, SPNEGO_NEGINIT_ELEMENT_MECHTYPES, + NULL, nInternalLength ); + + // Adjust the data pointer + pbData += nTempLength; + + // Now write the Sequence token and the OID (the OID is a BLOB in the global + // structure. + + nTempLength = ASNDerWriteToken( pbData, SPNEGO_CONSTRUCTED_SEQUENCE, + g_stcMechOIDList[mechoid].ucOid, + g_stcMechOIDList[mechoid].iLen ); + + return nMechListLength; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ASNDerWriteElement +// +// Parameters: +// [out] pbData - Buffer to write into. +// [in] ucElementSequence - Sequence Token +// [in] ucType - Token Type +// [in] pbTokenValue - Actual Value +// [in] nLength - Length of Data. +// +// Returns: +// int Number of bytes written out +// +// Comments : +// Helper function to write out a SPNEGO Token element. An element +// consists of a sequence token, a type token and the associated data. +// +//////////////////////////////////////////////////////////////////////////// + +int ASNDerWriteElement( unsigned char* pbData, unsigned char ucElementSequence, + unsigned char ucType, unsigned char* pbTokenValue, long nLength ) +{ + // First get the length + long nInternalLength = 0L; + long nElementLength = ASNDerCalcElementLength( nLength, &nInternalLength ); + long nTempLength = 0L; + + // Write out the sequence byte and the length of the type and data + nTempLength = ASNDerWriteToken( pbData, ucElementSequence, NULL, nInternalLength ); + + // Adjust the data pointer + pbData += nTempLength; + + // Now write the type and the data. + nTempLength = ASNDerWriteToken( pbData, ucType, pbTokenValue, nLength ); + + return nElementLength; +} |