📄 ssh2.c
字号:
static int readAlgoStringPair( INOUT STREAM *stream,
IN_ARRAY( noAlgoStringEntries ) \
const ALGO_STRING_INFO *algoInfo,
const int noAlgoStringEntries,
OUT CRYPT_ALGO_TYPE *algo, const BOOLEAN isServer,
INOUT ERROR_INFO *errorInfo )
{
CRYPT_ALGO_TYPE pairPreferredAlgo;
ALGOID_INFO algoIDInfo;
int status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isReadPtr( algoInfo, sizeof( ALGO_STRING_INFO ) * \
noAlgoStringEntries ) );
assert( isWritePtr( algo, sizeof( CRYPT_ALGO_TYPE ) ) );
/* Clear return value */
*algo = CRYPT_ALGO_NONE;
/* Get the first algorithm */
setAlgoIDInfo( &algoIDInfo, algoInfo, noAlgoStringEntries,
CRYPT_ALGO_NONE, isServer ? GETALGO_FIRST_MATCH : \
GETALGO_BEST_MATCH );
status = readAlgoStringEx( stream, &algoIDInfo, errorInfo );
if( cryptStatusError( status ) )
return( status );
pairPreferredAlgo = algoIDInfo.algo;
/* Get the matched second algorithm */
setAlgoIDInfo( &algoIDInfo, algoInfo, noAlgoStringEntries,
pairPreferredAlgo, GETALGO_FIRST_MATCH );
status = readAlgoStringEx( stream, &algoIDInfo, errorInfo );
if( cryptStatusError( status ) )
return( status );
if( pairPreferredAlgo != algoIDInfo.algo )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, errorInfo,
"Client algorithm %d doesn't match server algorithm %d "
"in algorithm pair", pairPreferredAlgo,
algoIDInfo.algo ) );
}
*algo = algoIDInfo.algo;
return( status );
}
/* Convert a cryptlib algorithm ID to an SSHv2 algorithm name */
int writeAlgoString( STREAM *stream, const CRYPT_ALGO_TYPE algo )
{
static const ALGO_STRING_INFO FAR_BSS algoStringMapTbl[] = {
{ "ssh-rsa", 7, CRYPT_ALGO_RSA },
{ "ssh-dss", 7, CRYPT_ALGO_DSA },
{ "3des-cbc", 8, CRYPT_ALGO_3DES },
{ "aes128-cbc", 10, CRYPT_ALGO_AES },
{ "blowfish-cbc", 12, CRYPT_ALGO_BLOWFISH },
{ "cast128-cbc", 11, CRYPT_ALGO_CAST },
{ "idea-cbc", 8, CRYPT_ALGO_IDEA },
{ "arcfour", 7, CRYPT_ALGO_RC4 },
{ "diffie-hellman-group-exchange-sha1", 34, CRYPT_PSEUDOALGO_DHE },
{ "diffie-hellman-group1-sha1", 26, CRYPT_ALGO_DH },
{ "hmac-sha1", 9, CRYPT_ALGO_HMAC_SHA },
{ "hmac-md5", 8, CRYPT_ALGO_HMAC_MD5 },
{ "none", 4, CRYPT_PSEUDOALGO_COPR },
{ "none", 4, CRYPT_ALGO_LAST }, /* Catch-all */
{ NULL, 0, CRYPT_ALGO_LAST }, { NULL, 0, CRYPT_ALGO_LAST }
};
int i;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( algo >= CRYPT_ALGO_NONE && algo < CRYPT_ALGO_LAST );
/* Locate the name for this algorithm and encode it as an SSH string */
for( i = 0; algoStringMapTbl[ i ].algo != CRYPT_ALGO_LAST && \
algoStringMapTbl[ i ].algo != algo && \
i < FAILSAFE_ARRAYSIZE( algoStringMapTbl, ALGO_STRING_INFO );
i++ );
if( i >= FAILSAFE_ARRAYSIZE( algoStringMapTbl, ALGO_STRING_INFO ) )
retIntError();
assert( algoStringMapTbl[ i ].algo != CRYPT_ALGO_LAST );
return( writeString32( stream, algoStringMapTbl[ i ].name,
algoStringMapTbl[ i ].nameLen ) );
}
/****************************************************************************
* *
* Miscellaneous Functions *
* *
****************************************************************************/
/* Process a client/server hello packet */
int processHelloSSH( SESSION_INFO *sessionInfoPtr,
SSH_HANDSHAKE_INFO *handshakeInfo, int *keyexLength,
const BOOLEAN isServer )
{
CRYPT_ALGO_TYPE dummyAlgo;
STREAM stream;
ALGOID_INFO algoIDInfo;
BOOLEAN preferredAlgoMismatch = FALSE, guessedKeyex = FALSE;
int length, status;
/* Process the client/server hello:
byte type = SSH2_MSG_KEXINIT
byte[16] cookie
string keyex algorithms
string pubkey algorithms
string client_crypto algorithms
string server_crypto algorithms
string client_mac algorithms
string server_mac algorithms
string client_compression algorithms
string server_compression algorithms
string client_language
string server_language
boolean first_keyex_packet_follows
uint32 reserved
The cookie isn't explicitly processed as with SSHv1 since SSHv2
hashes the entire hello message */
status = length = \
readHSPacketSSH2( sessionInfoPtr, SSH2_MSG_KEXINIT, 128 );
if( cryptStatusError( status ) )
return( status );
*keyexLength = length;
sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
sSkip( &stream, ID_SIZE + SSH2_COOKIE_SIZE );
/* Read the keyex algorithm info */
if( isServer )
{
setAlgoIDInfo( &algoIDInfo, algoStringKeyexTbl,
FAILSAFE_ARRAYSIZE( algoStringKeyexTbl, \
ALGO_STRING_INFO ),
CRYPT_PSEUDOALGO_DHE, GETALGO_FIRST_MATCH_WARN );
}
else
{
setAlgoIDInfo( &algoIDInfo, algoStringKeyexTbl,
FAILSAFE_ARRAYSIZE( algoStringKeyexTbl, \
ALGO_STRING_INFO ),
CRYPT_ALGO_NONE, GETALGO_BEST_MATCH );
}
status = readAlgoStringEx( &stream, &algoIDInfo, SESSION_ERRINFO );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
if( algoIDInfo.prefAlgoMismatch )
/* We didn't get a match for our first choice, remember that we have
to discard any guessed keyex that may follow */
preferredAlgoMismatch = TRUE;
if( algoIDInfo.algo == CRYPT_PSEUDOALGO_DHE )
/* If we're using ephemeral rather than static DH keys, we need to
negotiate the keyex key before we can perform the exchange */
handshakeInfo->requestedServerKeySize = SSH2_DEFAULT_KEYSIZE;
/* Read the pubkey (signature) algorithm info */
if( isServer )
{
setAlgoIDInfo( &algoIDInfo, handshakeInfo->algoStringPubkeyTbl,
handshakeInfo->algoStringPubkeyTblNoEntries,
handshakeInfo->pubkeyAlgo, GETALGO_FIRST_MATCH_WARN );
}
else
{
setAlgoIDInfo( &algoIDInfo, handshakeInfo->algoStringPubkeyTbl,
handshakeInfo->algoStringPubkeyTblNoEntries,
CRYPT_ALGO_NONE, GETALGO_BEST_MATCH );
}
status = readAlgoStringEx( &stream, &algoIDInfo, SESSION_ERRINFO );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
if( !isServer )
handshakeInfo->pubkeyAlgo = algoIDInfo.algo;
if( algoIDInfo.prefAlgoMismatch )
{
/* We didn't get a match for our first choice, remember that we have
to discard any guessed keyex that may follow */
preferredAlgoMismatch = TRUE;
}
/* Read the encryption and MAC algorithm info */
if( isServer )
{
status = readAlgoStringPair( &stream, algoStringEncrTblServer,
FAILSAFE_ARRAYSIZE( algoStringEncrTblServer, \
ALGO_STRING_INFO ),
&sessionInfoPtr->cryptAlgo, isServer,
SESSION_ERRINFO );
}
else
{
status = readAlgoStringPair( &stream, algoStringEncrTblClient,
FAILSAFE_ARRAYSIZE( algoStringEncrTblClient, \
ALGO_STRING_INFO ),
&sessionInfoPtr->cryptAlgo, isServer,
SESSION_ERRINFO );
}
if( cryptStatusOK( status ) )
{
status = readAlgoStringPair( &stream, algoStringMACTbl,
FAILSAFE_ARRAYSIZE( algoStringMACTbl, \
ALGO_STRING_INFO ),
&sessionInfoPtr->integrityAlgo,
isServer, SESSION_ERRINFO );
}
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
/* Read the remaining algorithm info. The final reserved value should
always be zero, but we don't specifically check for this since at
some point in the future it may become non-zero */
status = readAlgoStringPair( &stream, algoStringCoprTbl,
FAILSAFE_ARRAYSIZE( algoStringCoprTbl, \
ALGO_STRING_INFO ),
&dummyAlgo, isServer, SESSION_ERRINFO );
if( cryptStatusOK( status ) )
status = readUniversal32( &stream );
if( cryptStatusOK( status ) )
status = readUniversal32( &stream );
if( cryptStatusOK( status ) )
{
if( sgetc( &stream ) )
guessedKeyex = TRUE;
status = readUint32( &stream ); /* Reserved value */
}
if( cryptStatusError( status ) )
{
retExt( status,
( status, SESSION_ERRINFO,
"Invalid hello packet compression algorithm/language "
"string/trailer" ) );
}
/* If there's a guessed keyex following this packet and we didn't match
the first-choice keyex/pubkey algorithm, tell the caller to skip it */
if( guessedKeyex && preferredAlgoMismatch )
return( OK_SPECIAL );
return( CRYPT_OK );
}
/****************************************************************************
* *
* Get/Put Data Functions *
* *
****************************************************************************/
/* Read data over the SSHv2 link */
static int readHeaderFunction( SESSION_INFO *sessionInfoPtr,
READSTATE_INFO *readInfo )
{
SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
BYTE *bufPtr = sessionInfoPtr->receiveBuffer + \
sessionInfoPtr->receiveBufPos;
long length;
int extraLength, removedDataLength = ( ID_SIZE + PADLENGTH_SIZE );
int status;
/* Clear return value */
*readInfo = READINFO_NONE;
/* Make sure that there's room left to handle the speculative read */
if( sessionInfoPtr->receiveBufPos >= \
sessionInfoPtr->receiveBufSize - 128 )
return( 0 );
/* Try and read the header data from the remote system */
assert( sessionInfoPtr->receiveBufPos == sessionInfoPtr->receiveBufEnd );
status = readPacketHeaderSSH2( sessionInfoPtr, SSH2_MSG_CHANNEL_DATA,
&length, &extraLength, readInfo );
if( cryptStatusError( status ) )
{
/* OK_SPECIAL means that we got a soft timeout before the entire
header was read */
return( ( status == OK_SPECIAL ) ? 0 : status );
}
assert( length >= ID_SIZE + PADLENGTH_SIZE + SSH2_MIN_PADLENGTH_SIZE );
status = checkMacSSH( sessionInfoPtr->iAuthInContext, sshInfo->readSeqNo,
bufPtr, MIN_PACKET_SIZE - LENGTH_SIZE,
MIN_PACKET_SIZE - LENGTH_SIZE, length,
MAC_START, sessionInfoPtr->authBlocksize );
if( cryptStatusError( status ) )
{
/* We don't return an extended status at this point because we
haven't completed the message MAC calculation/check yet, so
any errors will be cryptlib-internal ones */
return( status );
}
/* Extract fixed information (the pad length and packet type) */
sshInfo->padLength = bufPtr[ 0 ];
sshInfo->packetType = bufPtr[ 1 ];
/* If it's channel data, strip the encapsulation, which allows us to
process the payload directly without having to move it around in
the buffer */
if( sshInfo->packetType == SSH2_MSG_CHANNEL_DATA )
{
STREAM stream;
long payloadLength;
/* Process the channel header and make sure that the payload length
matches the packet length */
sMemConnect( &stream, bufPtr, SSH2_HEADER_REMAINDER_SIZE );
sSkip( &stream, ID_SIZE + PADLENGTH_SIZE );
status = processChannelControlMessage( sessionInfoPtr, &stream );
if( cryptStatusError( status ) )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -