?? ssh1.c
字號:
static int processKeyFingerprint( SESSION_INFO *sessionInfoPtr,
const void *n, const int nLength,
const void *e, int eLength )
{
HASHFUNCTION hashFunction;
HASHINFO hashInfo;
const ATTRIBUTE_LIST *attributeListPtr = \
findSessionInfo( sessionInfoPtr->attributeList,
CRYPT_SESSINFO_SERVER_FINGERPRINT );
BYTE fingerPrint[ CRYPT_MAX_HASHSIZE + 8 ];
int hashSize;
getHashParameters( CRYPT_ALGO_MD5, &hashFunction, &hashSize );
hashFunction( hashInfo, NULL, n, nLength, HASH_START );
hashFunction( hashInfo, fingerPrint, e, eLength, HASH_END );
if( attributeListPtr == NULL )
/* Remember the value for the caller */
return( addSessionInfo( &sessionInfoPtr->attributeList,
CRYPT_SESSINFO_SERVER_FINGERPRINT,
fingerPrint, hashSize ) );
/* There's an existing fingerprint value, make sure that it matches what
we just calculated */
if( attributeListPtr->valueLength != hashSize || \
memcmp( attributeListPtr->value, fingerPrint, hashSize ) )
retExt( sessionInfoPtr, CRYPT_ERROR_WRONGKEY,
"Server key fingerprint doesn't match requested "
"fingerprint" );
return( CRYPT_OK );
}
/* Generate a response to an RSA authentication challenge */
static void generateChallengeResponse( BYTE *response,
const SSH_HANDSHAKE_INFO *handshakeInfo,
const BYTE *challenge )
{
HASHFUNCTION hashFunction;
HASHINFO hashInfo;
/* Hash the session ID and challenge:
hash( sessionID || challenge ) */
getHashParameters( CRYPT_ALGO_MD5, &hashFunction, NULL );
hashFunction( hashInfo, NULL, ( BYTE * ) handshakeInfo->sessionID,
handshakeInfo->sessionIDlength, HASH_START );
hashFunction( hashInfo, response, ( BYTE * ) challenge,
SSH1_CHALLENGE_SIZE, HASH_END );
}
/* Process the public key data. The preceding key length value isn't useful
because it contains the nominal key size in bits rather than the size of
the following data, so we have to dig into the data to find out how much
there is. In addition we need to take a copy of the key modulus since
it's needed later for calculating the session ID */
static int processPublickeyData( SSH_HANDSHAKE_INFO *handshakeInfo,
const void *data, const int dataLength,
const BOOLEAN isServerKey,
SESSION_INFO *sessionInfoPtr )
{
BYTE *dataPtr = ( BYTE * ) data, *ePtr;
int nominalLength, eLength, nLength;
nominalLength = ( int ) mgetLong( dataPtr );
nominalLength = bitsToBytes( nominalLength );
if( nominalLength < bitsToBytes( MIN_PKCSIZE_BITS ) || \
nominalLength > CRYPT_MAX_PKCSIZE )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid public key size %d", nominalLength );
eLength = mgetWord( dataPtr );
eLength = bitsToBytes( eLength );
if( LENGTH_SIZE + SSH1_MPI_LENGTH_SIZE + eLength + \
SSH1_MPI_LENGTH_SIZE + nominalLength > dataLength )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid exponent size %d for key size %d", eLength,
nominalLength );
ePtr = dataPtr;
dataPtr += eLength;
nLength = mgetWord( dataPtr );
nLength = bitsToBytes( nLength );
if( nLength != nominalLength )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Public key size %d doesn't match modulus size %d",
nominalLength, nLength );
if( isServerKey )
{
memcpy( handshakeInfo->serverModulus, dataPtr, nLength );
handshakeInfo->serverModulusLength = nLength;
}
else
{
memcpy( handshakeInfo->hostModulus, dataPtr, nLength );
handshakeInfo->hostModulusLength = nLength;
}
if( sessionInfoPtr != NULL )
{
int status;
status = processKeyFingerprint( sessionInfoPtr, dataPtr, nLength,
ePtr, eLength );
if( cryptStatusError( status ) )
return( status );
}
return( LENGTH_SIZE + SSH1_MPI_LENGTH_SIZE + eLength + \
SSH1_MPI_LENGTH_SIZE + nLength );
}
/* Set up the security information required for the session */
static int initSecurityInfoSSH1( SESSION_INFO *sessionInfoPtr,
SSH_HANDSHAKE_INFO *handshakeInfo )
{
MESSAGE_DATA msgData;
int keySize, ivSize, status;
/* Create the security contexts required for the session */
status = initSecurityContextsSSH( sessionInfoPtr );
if( cryptStatusError( status ) )
return( status );
if( sessionInfoPtr->cryptAlgo == CRYPT_ALGO_BLOWFISH )
/* For Blowfish the session key size doesn't match the default
Blowfish key size so we explicitly specify its length */
keySize = SSH1_SECRET_SIZE;
else
krnlSendMessage( sessionInfoPtr->iCryptInContext,
IMESSAGE_GETATTRIBUTE, &keySize,
CRYPT_CTXINFO_KEYSIZE );
if( krnlSendMessage( sessionInfoPtr->iCryptInContext,
IMESSAGE_GETATTRIBUTE, &ivSize,
CRYPT_CTXINFO_IVSIZE ) == CRYPT_ERROR_NOTAVAIL )
/* It's a stream cipher */
ivSize = 0;
/* Load the keys. For RC4, which is IV-less, the session key is split
into two parts, with the first part being the receive key and the
second part being the send key. For other algorithms, the entire
session key is used for both send and receive contexts, leading to
a simple attack on the first data block since the initial IV is all
zeroes */
setMessageData( &msgData, ( sessionInfoPtr->cryptAlgo == CRYPT_ALGO_RC4 ) ? \
handshakeInfo->secretValue + 16 : handshakeInfo->secretValue,
keySize );
status = krnlSendMessage( sessionInfoPtr->iCryptOutContext,
IMESSAGE_SETATTRIBUTE_S, &msgData,
CRYPT_CTXINFO_KEY );
if( cryptStatusOK( status ) )
{
setMessageData( &msgData, handshakeInfo->secretValue, keySize );
status = krnlSendMessage( sessionInfoPtr->iCryptInContext,
IMESSAGE_SETATTRIBUTE_S, &msgData,
CRYPT_CTXINFO_KEY );
}
if( cryptStatusOK( status ) && ivSize > 0 )
{
static const char iv[ CRYPT_MAX_IVSIZE ] = { 0 };
setMessageData( &msgData, ( void * ) iv, ivSize );
krnlSendMessage( sessionInfoPtr->iCryptOutContext,
IMESSAGE_SETATTRIBUTE_S, &msgData, CRYPT_CTXINFO_IV );
setMessageData( &msgData, ( void * ) iv, ivSize );
krnlSendMessage( sessionInfoPtr->iCryptInContext,
IMESSAGE_SETATTRIBUTE_S, &msgData, CRYPT_CTXINFO_IV );
}
if( cryptStatusError( status ) )
return( status );
/* If we're talking to a cryptlib peer, set up the MAC context which is
used instead of a CRC32. The key we use for this is taken from the
end of the SSH secret data, which isn't used for any cipher except
Blowfish */
if( sessionInfoPtr->flags & SESSION_ISCRYPTLIB )
{
setMessageData( &msgData,
handshakeInfo->secretValue + ( SSH1_SECRET_SIZE - 16 ), 16 );
status = krnlSendMessage( sessionInfoPtr->iAuthInContext,
IMESSAGE_SETATTRIBUTE_S, &msgData,
CRYPT_CTXINFO_KEY );
if( cryptStatusError( status ) )
return( status );
}
/* We've set up the security info, from now on all data is encrypted */
sessionInfoPtr->flags |= SESSION_ISSECURE_READ | SESSION_ISSECURE_WRITE;
return( CRYPT_OK );
}
/* Read an SSH packet */
static int decryptPayload( SESSION_INFO *sessionInfoPtr, BYTE *buffer,
const int length )
{
int status;
/* Decrypt the payload, with handling for SSH's Blowfish endianness bug.
This may not be a true bug but more a problem in the spec, since the
original was rather vague about the endianness of the byte -> long
conversion */
if( sessionInfoPtr->cryptAlgo == CRYPT_ALGO_BLOWFISH )
longReverse( ( unsigned long * ) buffer, length );
status = krnlSendMessage( sessionInfoPtr->iCryptInContext,
IMESSAGE_CTX_DECRYPT, buffer, length );
if( sessionInfoPtr->cryptAlgo == CRYPT_ALGO_BLOWFISH )
longReverse( ( unsigned long * ) buffer, length );
return( status );
}
static BOOLEAN checksumPayload( SESSION_INFO *sessionInfoPtr,
const BYTE *buffer, const int length )
{
const int dataLength = length - SSH1_CRC_SIZE; /* CRC isn't part of payload */
BYTE *bufPtr = ( BYTE * ) buffer + dataLength;
unsigned long crc32, storedCrc32;
/* Calculate the checksum over the padding, type, and data and make sure
that it matches the transmitted value */
if( ( sessionInfoPtr->flags & ( SESSION_ISCRYPTLIB | SESSION_ISSECURE_READ ) ) == \
( SESSION_ISCRYPTLIB | SESSION_ISSECURE_READ ) )
crc32 = calculateTruncatedMAC( sessionInfoPtr->iAuthInContext,
buffer, dataLength );
else
crc32 = calculateCRC( buffer, dataLength );
storedCrc32 = mgetLong( bufPtr );
return( ( crc32 == storedCrc32 ) ? TRUE : FALSE );
}
static int getDisconnectInfoSSH1( SESSION_INFO *sessionInfoPtr, BYTE *bufPtr )
{
int length;
/* Server is disconnecting, find out why */
length = mgetLong( bufPtr );
if( length > MAX_ERRMSG_SIZE - 32 )
retExt( sessionInfoPtr, CRYPT_ERROR_OVERFLOW,
"Invalid error information size %d", length );
strlcpy_s( sessionInfoPtr->errorMessage, MAX_ERRMSG_SIZE,
"Received SSHv1 server message: " );
memcpy( sessionInfoPtr->errorMessage + 31, bufPtr, length );
sessionInfoPtr->errorMessage[ 31 + length ] = '\0';
return( CRYPT_ERROR_READ );
}
static int readPacketSSH1( SESSION_INFO *sessionInfoPtr, int expectedType )
{
BYTE *bufPtr = sessionInfoPtr->receiveBuffer;
long length;
int padLength, packetType, iterationCount = 0;
/* Alongside the expected packets the server can also send us all sorts
of no-op messages, ranging from explicit no-ops (SSH_MSG_IGNORE)
through to general chattiness (SSH_MSG_DEBUG). Because we can
receive any quantity of these at any time, we have to run the receive
code in a loop to strip them out */
do
{
const BYTE *lengthPtr = bufPtr;
int status;
/* Read the SSHv1 packet header:
uint32 length (excluding padding)
byte[] padding
byte type
byte[] data
uint32 crc32
The padding length is implicitly calculated as
8 - ( length & 7 ) bytes, and the CRC is calculated over the
padding, type, and data */
assert( sessionInfoPtr->receiveBufEnd == 0 );
status = readFixedHeader( sessionInfoPtr, LENGTH_SIZE );
if( cryptStatusError( status ) )
return( status );
assert( status == LENGTH_SIZE );
length = mgetLong( lengthPtr );
padLength = 8 - ( length & 7 );
if( length < SSH1_HEADER_SIZE || \
length + padLength >= sessionInfoPtr->receiveBufSize )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid packet length %d", length );
status = sread( &sessionInfoPtr->stream,
sessionInfoPtr->receiveBuffer, padLength + length );
if( cryptStatusError( status ) )
{
sNetGetErrorInfo( &sessionInfoPtr->stream,
&sessionInfoPtr->errorInfo );
return( status );
}
if( status != padLength + length )
retExt( sessionInfoPtr, CRYPT_ERROR_TIMEOUT,
"Timeout during packet remainder read, only got %d of "
"%d bytes", status, padLength + length );
if( sessionInfoPtr->flags & SESSION_ISSECURE_READ )
{
status = decryptPayload( sessionInfoPtr,
sessionInfoPtr->receiveBuffer,
padLength + length );
if( cryptStatusError( status ) )
return( status );
}
if( !checksumPayload( sessionInfoPtr, sessionInfoPtr->receiveBuffer,
padLength + length ) )
/* If we're expecting a success packet after a key exchange or an
immediate post key-exchange packet and don't get it then it's
more likely that the problem is due to the wrong key being
used than data corruption, so we return a wrong key error
instead of bad data */
retExt( sessionInfoPtr, ( expectedType == SSH1_SMSG_SUCCESS ) ? \
CRYPT_ERROR_WRONGKEY : CRYPT_ERROR_BADDATA,
"Bad message checksum" );
packetType = sessionInfoPtr->receiveBuffer[ padLength ];
}
while( ( packetType == SSH1_MSG_IGNORE || \
packetType == SSH1_MSG_DEBUG ) && iterationCount++ < 1000 );
if( iterationCount >= 1000 )
retExt( sessionInfoPtr, CRYPT_ERROR_OVERFLOW,
"Peer sent excessive number of no-op packets" );
length -= ID_SIZE + UINT_SIZE; /* Remove fixed fields */
/* Make sure we either got what we asked for or one of the allowed
special-case packets */
if( packetType == SSH1_MSG_DISCONNECT )
return( getDisconnectInfoSSH1( sessionInfoPtr,
sessionInfoPtr->receiveBuffer + padLength + ID_SIZE ) );
if( expectedType == SSH1_MSG_SPECIAL_USEROPT )
{
/* Sending an SSH1_CMSG_USER can result in an SSH1_SMSG_FAILURE if the
user needs some form of authentiction to log on, so we have to
filter this and convert it into a CRYPT_OK/OK_SPECIAL value to
let the caller know whether they have to send a password or not */
if( packetType == SSH1_SMSG_SUCCESS )
return( CRYPT_OK );
if( packetType == SSH1_SMSG_FAILURE )
return( OK_SPECIAL );
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -