📄 ssh.c
字号:
/* If we're expecting any kind of data, save the type at the start
of the buffer, move the data down in the buffer to get rid of
the padding, and return */
sessionInfoPtr->receiveBuffer[ 0 ] = packetType;
memmove( sessionInfoPtr->receiveBuffer + 1,
sessionInfoPtr->receiveBuffer + padLength + ID_SIZE,
length - ID_SIZE );
return( length - ID_SIZE );
}
if( packetType != expectedType )
return( CRYPT_ERROR_BADDATA );
/* Move the data down in the buffer to get rid of the padding */
memmove( sessionInfoPtr->receiveBuffer,
sessionInfoPtr->receiveBuffer + padLength + ID_SIZE,
length - ID_SIZE );
return( length - ID_SIZE );
}
/* Send an SSHv1 packet. SSHv1 uses an awkward variable-length padding at
the start, when we're sending short control packets we can pre-calculate
the data start position or memmove() it into place, with longer data
quantities which arrive in multiple segments we can no longer do this so
we build the header backwards from the start of the data in the buffer.
Because of this we perform all operations on the data relative to a
variable start position given by the parameter delta */
static int sendPacket1( SESSION_INFO *sessionInfoPtr,
const int packetType, const int dataLength,
const int delta )
{
BYTE *bufStartPtr = sessionInfoPtr->sendBuffer + delta;
BYTE *bufPtr = bufStartPtr;
LONG crc32;
const int length = getEffectiveLength1( dataLength );
const int padLength = getPadLength1( dataLength );
int status;
/* Add the SSH packet header:
uint32 length
byte[] padding, 8 - ( length & 7 ) bytes
byte type
byte[] data
uint32 crc32 - Calculated over padding, type, and data */
mputBLong( bufPtr, ( long ) length );
getNonce( bufPtr, padLength );
bufPtr[ padLength ] = packetType;
if( ( sessionInfoPtr->flags & ( SESSION_ISCRYPTLIB | SESSION_ISSECURE ) ) == \
( SESSION_ISCRYPTLIB | SESSION_ISSECURE ) )
crc32 = calculateTruncatedMAC( sessionInfoPtr->iAuthInContext,
bufPtr,
padLength + ID_SIZE + dataLength );
else
crc32 = calculateCRC( bufPtr, padLength + ID_SIZE + dataLength );
bufPtr += padLength + ID_SIZE + dataLength;
mputBLong( bufPtr, crc32 );
if( sessionInfoPtr->flags & SESSION_ISSECURE )
{
/* Encrypt the payload with handling for SSH's Blowfish
endianness bug */
if( sessionInfoPtr->cryptAlgo == CRYPT_ALGO_BLOWFISH )
longReverse( ( LONG * ) ( bufStartPtr + LENGTH_SIZE ),
padLength + length );
status = krnlSendMessage( sessionInfoPtr->iCryptOutContext,
RESOURCE_IMESSAGE_CTX_ENCRYPT,
bufStartPtr + LENGTH_SIZE,
padLength + length );
if( sessionInfoPtr->cryptAlgo == CRYPT_ALGO_BLOWFISH )
longReverse( ( LONG * ) ( bufStartPtr + LENGTH_SIZE ),
padLength + length );
if( cryptStatusError( status ) )
return( status );
}
status = swrite( &sessionInfoPtr->stream, bufStartPtr,
LENGTH_SIZE + padLength + length );
if( cryptStatusError( status ) )
sNetGetErrorInfo( &sessionInfoPtr->stream,
sessionInfoPtr->errorMessage,
&sessionInfoPtr->errorCode );
return( status );
}
/* Send a disconnect packet */
static int sendDisconnect1( SESSION_INFO *sessionInfoPtr,
const char *message )
{
BYTE *bufPtr;
const int messageLength = strlen( message );
int padLength;
/* Send a disconnect packet to the remote system:
string disconnection_reason */
padLength = getPadLength1( LENGTH_SIZE + messageLength );
bufPtr = sessionInfoPtr->sendBuffer + LENGTH_SIZE + padLength + ID_SIZE;
mputBLong( bufPtr, messageLength );
memcpy( bufPtr, message, messageLength );
return( sendPacket1( sessionInfoPtr, SSH_MSG_DISCONNECT,
LENGTH_SIZE + messageLength, 0 ) );
}
/****************************************************************************
* *
* SSHv2 Functions *
* *
****************************************************************************/
/* Load the fixed SSHv2 DH key into a context. The prime is the value
2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }, from the Oakley spec.
Unfortunately this value of q leads to a horribly inefficient key
agreement process, since it's 860 bits larger than it needs to be */
static const BYTE pValue[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
static const BYTE qValue[] = {
0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
0x24, 0x94, 0x33, 0x28, 0xF6, 0x73, 0x29, 0xC0,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
static const BYTE gValue[] = { 0x02 };
static int loadDHcontext( const CRYPT_CONTEXT iCryptContext,
const BOOLEAN isKey1 )
{
CRYPT_PKCINFO_DLP dhKey, *dhKeyPtr = &dhKey;
RESOURCE_DATA msgData;
int status;
/* Load the key into the context */
cryptInitComponents( dhKeyPtr, CRYPT_KEYTYPE_PUBLIC );
cryptSetComponent( dhKeyPtr->p, pValue, 1024 );
cryptSetComponent( dhKeyPtr->q, qValue, 1024 );
cryptSetComponent( dhKeyPtr->g, gValue, 2 );
setResourceData( &msgData, isKey1 ? \
"SSH DH key #1" : "SSH DH key #2", 13 );
status = krnlSendMessage( iCryptContext, RESOURCE_IMESSAGE_SETATTRIBUTE_S,
&msgData, CRYPT_CTXINFO_LABEL );
if( cryptStatusOK( status ) )
{
setResourceData( &msgData, dhKeyPtr, sizeof( CRYPT_PKCINFO_DLP ) );
status = krnlSendMessage( iCryptContext,
RESOURCE_IMESSAGE_SETATTRIBUTE_S,
&msgData, CRYPT_CTXINFO_KEY_COMPONENTS );
}
cryptDestroyComponents( dhKeyPtr );
return( status );
}
/* Convert an SSHv2 algorithm list to a cryptlib ID in preferred-algorithm
order, and return a list of available algorithms in SSHv2 algorithm list
format. For some bizarre reason the algorithm information is communicated
as a comma-delimited list (in an otherwise binary protocol), so we have
to unpack and pack them into this cumbersome format alongside just
choosing which algorithm to use */
static int getAlgoID( const ALGO_STRING_INFO *algoInfo, CRYPT_ALGO *algo,
const CRYPT_ALGO preferredAlgo, const BYTE *string,
const int maxLength )
{
int stringPos = 0, stringLen, algoIndex = 1000;
/* Clear return value */
if( algo != NULL )
*algo = CRYPT_ALGO_NONE;
/* Get the string length and make sure it's valid */
stringLen = mgetBLong( string );
if( stringLen <= 0 || stringLen > maxLength + LENGTH_SIZE )
return( CRYPT_ERROR_BADDATA );
/* Walk down the string looking for a recognised algorithm. Since our
preference may not match the other side's preferences, we have to walk
down the entire list to find our preferred choice */
while( stringPos < stringLen )
{
int len, i;
/* Find the length of the next algorithm name */
for( len = stringPos; len < stringLen && string[ len ] != ','; len++ );
len -= stringPos;
if( !len )
{
stringPos++;
continue;
}
/* Check whether it's something we can handle. SSH algorithms are
used in pairs, one for incoming and the other for outgoing data.
To keep things simple, we always force these to be the same (all
implementation seem to do this anyway), so if the algorithm choice
for one direction has already been specified we make sure that the
algorithm for the other direction is the same */
for( i = 0; algoInfo[ i ].name != NULL; i++ )
if( !memcmp( algoInfo[ i ].name, string + stringPos, len ) )
/* If we've found a new, more-preferred algorithm, remember
its entry position */
if( preferredAlgo == CRYPT_ALGO_NONE && i < algoIndex && \
algoAvailable( algoInfo[ i ].algo ) )
algoIndex = i;
else
/* We already know what we want, make sure it matches our
existing choice */
if( preferredAlgo == algoInfo[ i ].algo )
{
if( algo != NULL )
*algo = preferredAlgo;
return( LENGTH_SIZE + stringLen );
}
/* No match, move on to the next name */
stringPos += len + 1;
}
if( algoIndex != 1000 )
{
if( algo != NULL )
*algo = algoInfo[ algoIndex ].algo;
return( LENGTH_SIZE + stringLen );
}
return( CRYPT_ERROR_NOTAVAIL );
}
static int getAlgoIDpair( const ALGO_STRING_INFO *algoInfo, CRYPT_ALGO *algo,
const BYTE *string, const int maxLength )
{
CRYPT_ALGO preferredAlgo;
int length1, length2;
length1 = getAlgoID( algoInfo, &preferredAlgo, CRYPT_ALGO_NONE, string,
maxLength );
if( cryptStatusError( length1 ) )
return( length1 );
length2 = getAlgoID( algoInfo, algo, preferredAlgo, string + length1,
maxLength - length1 );
return( cryptStatusError( length2 ) ? length2 : length1 + length2 );
}
static void putAlgoID( BYTE **bufPtrPtr, const CRYPT_ALGO algo )
{
BYTE *bufPtr = *bufPtrPtr;
int length, i;
/* Locate the name for this algorithm and encode it as an SSH string */
for( i = 0; algoStringMapTbl[ i ].name != NULL && \
algoStringMapTbl[ i ].algo != algo; i++ );
assert( algoStringMapTbl[ i ].name != NULL );
length = strlen( algoStringMapTbl[ i ].name );
mputBLong( bufPtr, length );
memcpy( bufPtr, algoStringMapTbl[ i ].name, length );
*bufPtrPtr += LENGTH_SIZE + length;
}
/* Encode a value as an SSHv2 MPI or string */
static int encodeMPI( BYTE *buffer, const BYTE *value,
const int valueLength )
{
BYTE *bufPtr = buffer;
const int mpiValueLength = valueLength + \
( ( value[ 0 ] & 0x80 ) ? 1 : 0 );
if( buffer != NULL )
{
mputBLong( bufPtr, mpiValueLength );
if( value[ 0 ] & 0x80 )
*bufPtr++ = 0;
memcpy( bufPtr, value, valueLength );
}
return( LENGTH_SIZE + mpiValueLength );
}
static int encodeString( BYTE *buffer, const BYTE *string,
const int stringLength )
{
BYTE *bufPtr = buffer;
const int length = ( stringLength ) ? stringLength : strlen( string );
if( buffer != NULL )
{
mputBLong( bufPtr, length );
memcpy( bufPtr, string, length );
}
return( LENGTH_SIZE + length );
}
/* Hash a value encoded as an SSH string and MPI */
static int hashAsString( const CRYPT_CONTEXT iHashContext,
const BYTE *data, const int dataLength )
{
BYTE buffer[ 64 ], *bufPtr = buffer;
int status;
/* Prepend the string length to the data and hash it. If it'll fit in
the buffer we copy it over to save a kernel call */
mputBLong( bufPtr, dataLength );
if( dataLength < 64 - LENGTH_SIZE )
{
memcpy( buffer + LENGTH_SIZE, data, dataLength );
status = krnlSendMessage( iHashContext, RESOURCE_IMESSAGE_CTX_HASH,
buffer, LENGTH_SIZE + dataLength );
}
else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -