📄 ssh2_cli.c
字号:
clientKeyexLength );
status = hashAsString( handshakeInfo->iExchangeHashcontext,
sessionInfoPtr->receiveBuffer, serverKeyexLength );
if( cryptStatusError( status ) )
return( status );
/* If we're using a non-builtin DH key value, request the keyex key from
the server */
if( handshakeInfo->requestedServerKeySize > 0 )
{
BYTE *dataStartPtr;
const int extraLength = LENGTH_SIZE + ( LENGTH_SIZE + 6 );
/* ...
byte type = SSH2_MSG_KEXDH_GEX_REQUEST
uint32 keySize = 1024
There is an alternative format that allows the client to specify
a range of key sizes:
byte type = SSH2_MSG_KEXDH_GEX_REQUEST_NEW
uint32 minSize = 1024 - 16
uint32 preferredSize = 1024
uint32 maxSize = 1024 + 16
but few implementations currently seem to support this, with some
servers just dropping the connection without any error response if
they encounter the newer packet type */
bufPtr = dataStartPtr = sessionInfoPtr->sendBuffer + length + \
SSH2_HEADER_SIZE;
#if 1
*bufPtr++ = SSH2_MSG_KEXDH_GEX_REQUEST;
mputLong( bufPtr, bytesToBits( SSH2_DEFAULT_KEYSIZE ) );
status = length2 = wrapPacket( sessionInfoPtr,
sessionInfoPtr->sendBuffer + length,
ID_SIZE + UINT_SIZE );
#else
*bufPtr++ = SSH2_MSG_KEXDH_GEX_REQUEST_NEW;
mputLong( bufPtr, 1024 );
mputLong( bufPtr, bytesToBits( SSH2_DEFAULT_KEYSIZE ) );
mputLong( bufPtr, bytesToBits( CRYPT_MAX_PKCSIZE ) );
status = length2 = wrapPacket( sessionInfoPtr,
sessionInfoPtr->sendBuffer + length,
ID_SIZE + ( 3 * UINT_SIZE ) );
#endif /* 1 */
if( !cryptStatusError( status ) )
status = sendPacketSSH2( sessionInfoPtr, length + length2,
TRUE );
if( cryptStatusError( status ) )
return( status );
/* Remember the encoded key size info for later when we generate
the exchange hash */
#if 1
memcpy( handshakeInfo->encodedReqKeySizes, dataStartPtr + 1,
UINT_SIZE );
handshakeInfo->encodedReqKeySizesLength = UINT_SIZE;
#else
memcpy( handshakeInfo->encodedReqKeySizes, dataStartPtr + 1,
3 * UINT_SIZE );
handshakeInfo->encodedReqKeySizesLength = 3 * UINT_SIZE;
#endif /* 1 */
/* Process the ephemeral DH key:
byte type = SSH2_MSG_KEXDH_GEX_GROUP
mpint p
mpint g */
length = readPacketSSH2( sessionInfoPtr, SSH2_MSG_KEXDH_GEX_GROUP );
if( cryptStatusError( length ) )
return( length );
bufPtr = dataStartPtr = sessionInfoPtr->receiveBuffer + ID_SIZE;
length -= ID_SIZE;
if( length < ( LENGTH_SIZE + bitsToBytes( MIN_PKCSIZE_BITS ) ) + \
( LENGTH_SIZE + 1 ) || \
length > ( ( LENGTH_SIZE + CRYPT_MAX_PKCSIZE ) * 2 ) )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid DH ephemeral key packet length %d",
length );
/* Since this phase of the key negotiation exchanges raw key
components rather than the standard SSH public-key format, we
have to rewrite the raw key components into a standard SSH key so
that we can import it:
string "ssh-dh"
mpint p
mpint g */
memmove( bufPtr + extraLength, bufPtr, length );
mputLong( bufPtr, ( extraLength - LENGTH_SIZE ) + length );
encodeString( bufPtr, "ssh-dh", 0 );
/* Destroy the existing static DH key, load the new one, and
re-perform phase 1 of the DH key agreement process */
krnlSendNotifier( handshakeInfo->iServerCryptContext,
IMESSAGE_DECREFCOUNT );
status = initDHcontext( &handshakeInfo->iServerCryptContext,
&handshakeInfo->serverKeySize,
dataStartPtr, extraLength + length,
CRYPT_UNUSED );
if( cryptStatusOK( status ) )
{
memset( &keyAgreeParams, 0, sizeof( KEYAGREE_PARAMS ) );
status = krnlSendMessage( handshakeInfo->iServerCryptContext,
IMESSAGE_CTX_ENCRYPT, &keyAgreeParams,
sizeof( KEYAGREE_PARAMS ) );
}
if( cryptStatusError( status ) )
return( status );
/* We've already sent the client hello as part of the keyex
negotiation so there's no need to bundle it with the client
keyex, reset the start position in the send buffer */
length = 0;
}
/* ...
byte type = SSH2_MSG_KEXDH_INIT / SSH2_MSG_KEXDH_GEX_INIT
mpint y */
bufPtr = sessionInfoPtr->sendBuffer + length + SSH2_HEADER_SIZE;
*bufPtr++ = ( handshakeInfo->requestedServerKeySize > 0 ) ? \
SSH2_MSG_KEXDH_GEX_INIT : SSH2_MSG_KEXDH_INIT;
mpiLength = encodeMPI( bufPtr, keyAgreeParams.publicValue,
keyAgreeParams.publicValueLen );
status = length2 = wrapPacket( sessionInfoPtr,
sessionInfoPtr->sendBuffer + length,
ID_SIZE + mpiLength );
if( cryptStatusError( status ) )
return( status );
/* Save the MPI-encoded client DH keyex value for later, when we need to
hash it */
memcpy( handshakeInfo->clientKeyexValue,
sessionInfoPtr->sendBuffer + length + SSH2_HEADER_SIZE + ID_SIZE,
mpiLength );
handshakeInfo->clientKeyexValueLength = mpiLength;
/* Send the whole mess to the server. Since SSH, unlike SSL, requires
that each packet in a multi-packet group be wrapped as a separate
packet, we have to first assemble the packets via wrapPacket() and
then send them in a group via sendPacket() with the send-only
flag set */
status = sendPacketSSH2( sessionInfoPtr, length + length2, TRUE );
if( cryptStatusError( status ) )
return( status );
/* Set up PKC info while we wait for the server to process our
response */
setMessageCreateObjectInfo( &createInfo, handshakeInfo->pubkeyAlgo );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_DEV_CREATEOBJECT, &createInfo,
OBJECT_TYPE_CONTEXT );
if( cryptStatusOK( status ) )
sessionInfoPtr->iKeyexAuthContext = createInfo.cryptHandle;
return( status );
}
/* Exchange keys with the server */
static int exchangeClientKeys( SESSION_INFO *sessionInfoPtr,
SSH_HANDSHAKE_INFO *handshakeInfo )
{
CRYPT_ALGO_TYPE pubkeyAlgo;
RESOURCE_DATA msgData;
BYTE *bufPtr, *dataStartPtr;
int length, dataLength, stringLength, status;
/* Process the DH phase 2 keyex packet:
byte type = SSH2_MSG_KEXDH_REPLY / SSH2_MSG_KEXDH_GEX_REPLY
string server key/certificate
string "ssh-rsa" "ssh-dss"
mpint e p
mpint n q
mpint g
mpint y
mpint y'
string signature of handshake data
string "ssh-rsa" "ssh-dss"
string signature signature
First, we read and hash the server key/certificate. Since this is
already encoded as an SSH string, we can hash it directly */
length = readPacketSSH2( sessionInfoPtr,
( handshakeInfo->requestedServerKeySize > 0 ) ? \
SSH2_MSG_KEXDH_GEX_REPLY : SSH2_MSG_KEXDH_REPLY );
if( cryptStatusError( length ) )
return( length );
bufPtr = dataStartPtr = sessionInfoPtr->receiveBuffer + ID_SIZE;
dataLength = ( int ) mgetLong( bufPtr ); /* Server key size */
length -= ID_SIZE + LENGTH_SIZE + dataLength;
if( length < ( LENGTH_SIZE + bitsToBytes( MIN_PKCSIZE_BITS ) ) + \
LENGTH_SIZE + ( LENGTH_SIZE + 7 ) + ( LENGTH_SIZE + 40 ) || \
dataLength < ( LENGTH_SIZE + 7 ) + ( LENGTH_SIZE + 1 ) + \
( LENGTH_SIZE + bitsToBytes( MIN_PKCSIZE_BITS ) ) || \
dataLength > ( LENGTH_SIZE + 7 ) + \
( ( LENGTH_SIZE + CRYPT_MAX_PKCSIZE ) * 4 ) )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid DH phase 2 packet length %d, data length %d",
length, dataLength );
stringLength = getAlgoID( handshakeInfo->algoStringPubkeyTbl,
&pubkeyAlgo, CRYPT_ALGO_NONE, bufPtr,
dataLength, sessionInfoPtr );
if( cryptStatusError( stringLength ) )
return( stringLength );
if( pubkeyAlgo != handshakeInfo->pubkeyAlgo )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid DH phase 2 public key algorithm %d, expected %d",
pubkeyAlgo, handshakeInfo->pubkeyAlgo );
setMessageData( &msgData, dataStartPtr, LENGTH_SIZE + dataLength );
status = krnlSendMessage( sessionInfoPtr->iKeyexAuthContext,
IMESSAGE_SETATTRIBUTE_S, &msgData,
CRYPT_IATTRIBUTE_KEY_SSH2 );
if( cryptStatusError( status ) )
retExt( sessionInfoPtr, cryptArgError( status ) ? \
CRYPT_ERROR_BADDATA : status,
"Invalid server key/certificate" );
status = krnlSendMessage( handshakeInfo->iExchangeHashcontext,
IMESSAGE_CTX_HASH, dataStartPtr,
LENGTH_SIZE + dataLength );
if( cryptStatusOK( status ) )
status = processKeyFingerprint( sessionInfoPtr,
dataStartPtr + LENGTH_SIZE,
dataLength );
if( cryptStatusError( status ) )
return( status );
bufPtr += dataLength;
/* Then we read the server DH keyex value and complete the DH key
agreement */
dataStartPtr = bufPtr;
dataLength = ( int ) mgetLong( bufPtr ); /* DH keyex value size */
length -= LENGTH_SIZE + dataLength;
if( length < LENGTH_SIZE + ( LENGTH_SIZE + 7 ) + ( LENGTH_SIZE + 40 ) || \
dataLength < bitsToBytes( MIN_PKCSIZE_BITS ) || \
dataLength > 4 + CRYPT_MAX_PKCSIZE ) /* +4 for zero-pad */
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid DH phase 2 keyex value length %d, packet length "
"%d", dataLength, length );
bufPtr += dataLength;
memcpy( handshakeInfo->serverKeyexValue, dataStartPtr,
LENGTH_SIZE + dataLength );
handshakeInfo->serverKeyexValueLength = LENGTH_SIZE + dataLength;
status = completeKeyex( sessionInfoPtr, handshakeInfo, FALSE );
if( cryptStatusError( status ) )
return( status );
/* Some implementations incorrectly format the signature packet,
omitting the algorithm name and signature blob length for DSA sigs
(that is, they just encode two 20-byte values instead of a properly-
formatted signature):
Right Wrong
string signature data string signature data
string "ssh-dss" signature
string signature
If we're talking to one of these versions, we check to see whether
the packet is correctly formatted and if it isn't rewrite it into the
correct format so that we can verify the signature. This check
requires that the signature format be one of the SSHv2 standard
types, but since we can't (by definition) handle proprietary formats
this isn't a problem */
if( ( sessionInfoPtr->protocolFlags & SSH_PFLAG_SIGFORMAT ) && \
( pubkeyAlgo == CRYPT_ALGO_DSA ) && \
( memcmp( bufPtr + LENGTH_SIZE + LENGTH_SIZE, "ssh-", 4 ) && \
memcmp( bufPtr + LENGTH_SIZE + LENGTH_SIZE, "x509v3-", 7 ) && \
memcmp( bufPtr + LENGTH_SIZE + LENGTH_SIZE, "spki-", 5 ) && \
memcmp( bufPtr + LENGTH_SIZE + LENGTH_SIZE, "pgp-", 4 ) ) )
{
BYTE *sigInfoPtr = bufPtr;
const int extraLength = putAlgoID( NULL, CRYPT_ALGO_DSA ) + \
LENGTH_SIZE;
/* Make sure that the rewritten packet still fits into the buffer */
if( ( bufPtr - sessionInfoPtr->receiveBuffer ) + length + extraLength >= \
sessionInfoPtr->receiveBufSize )
retExt( sessionInfoPtr, CRYPT_ERROR_OVERFLOW,
"Invalid DH phase 2 keyex value length %d, need extra %d "
"bytes", length, extraLength );
/* Rewrite the packet to fix up the overall length at the start and
insert the algorithm name and signature length */
memmove( sigInfoPtr + extraLength, sigInfoPtr, length );
mputLong( sigInfoPtr, extraLength + length );
putAlgoID( &sigInfoPtr, pubkeyAlgo );
mputLong( sigInfoPtr, length );
}
/* Finally, verify the server's signature on the exchange hash */
status = iCryptCheckSignatureEx( bufPtr, length, CRYPT_IFORMAT_SSH,
sessionInfoPtr->iKeyexAuthContext,
handshakeInfo->iExchangeHashcontext, NULL );
if( cryptStatusError( status ) )
retExt( sessionInfoPtr, status, "Bad handshake data signature" );
/* We don't need the hash context any more, get rid of it */
krnlSendNotifier( handshakeInfo->iExchangeHashcontext,
IMESSAGE_DECREFCOUNT );
handshakeInfo->iExchangeHashcontext = CRYPT_ERROR;
return( CRYPT_OK );
}
/* Complete the handshake with the server */
static int completeClientHandshake( SESSION_INFO *sessionInfoPtr,
SSH_HANDSHAKE_INFO *handshakeInfo )
{
BYTE *bufPtr;
int length, totalLength, status;
/* Set up the security information required for the session */
status = initSecurityInfo( sessionInfoPtr, handshakeInfo );
if( cryptStatusError( status ) )
return( status );
/* Wait for the server's change cipherspec message */
status = readPacketSSH2( sessionInfoPtr, SSH2_MSG_NEWKEYS );
if( cryptStatusError( status ) )
return( status );
/* Build our change cipherspec message and request authentication with
the server:
byte type = SSH2_MSG_NEWKEYS
... */
bufPtr = sessionInfoPtr->sendBuffer + SSH2_HEADER_SIZE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -