📄 ssh2_cli.c
字号:
writeAlgoString( &stream, sessionInfoPtr->cryptAlgo );
writeAlgoString( &stream, sessionInfoPtr->cryptAlgo );
writeAlgoString( &stream, sessionInfoPtr->integrityAlgo );
writeAlgoString( &stream, sessionInfoPtr->integrityAlgo );
writeAlgoString( &stream, CRYPT_PSEUDOALGO_COPR );
writeAlgoString( &stream, CRYPT_PSEUDOALGO_COPR );
writeUint32( &stream, 0 ); /* No language tag */
writeUint32( &stream, 0 );
sputc( &stream, 0 ); /* Tell the server not to discard the packet */
status = writeUint32( &stream, 0 ); /* Reserved */
if( cryptStatusOK( status ) )
{
status = streamBookmarkComplete( &stream, &clientHelloPtr,
&clientHelloLength,
clientHelloLength );
}
if( cryptStatusOK( status ) )
status = wrapPacketSSH2( sessionInfoPtr, &stream, 0, FALSE, TRUE );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
/* Hash the client and server hello messages. We have to do this now
(rather than deferring it until we're waiting on network traffic from
the server) because they may get overwritten by the keyex negotiation
data if we're using a non-builtin DH key value */
status = hashAsString( handshakeInfo->iExchangeHashcontext,
clientHelloPtr, clientHelloLength );
if( cryptStatusOK( status ) )
status = hashAsString( handshakeInfo->iExchangeHashcontext,
sessionInfoPtr->receiveBuffer,
serverHelloLength );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
/* If we're using a non-builtin DH key value, request the keyex key from
the server. This requires disconnecting and re-connecting the stream
since it exchanges further data with the server, so if there's an
error return we don't disconnect the stream before we exit */
if( handshakeInfo->requestedServerKeySize > 0 )
{
status = processDHE( sessionInfoPtr, handshakeInfo, &stream,
&keyAgreeParams );
if( cryptStatusError( status ) )
{
/* processDHE() has already disconnected the stream */
return( status );
}
}
/* ...
byte type = SSH2_MSG_KEXDH_INIT / SSH2_MSG_KEXDH_GEX_INIT
mpint y */
status = continuePacketStreamSSH( &stream, \
( handshakeInfo->requestedServerKeySize > 0 ) ? \
SSH2_MSG_KEXDH_GEX_INIT : SSH2_MSG_KEXDH_INIT,
&packetOffset );
if( cryptStatusOK( status ) )
{
streamBookmarkSet( &stream, keyexLength );
status = writeInteger32( &stream, keyAgreeParams.publicValue,
keyAgreeParams.publicValueLen );
}
if( cryptStatusOK( status ) )
status = streamBookmarkComplete( &stream, &keyexPtr, &keyexLength,
keyexLength );
if( cryptStatusOK( status ) )
status = wrapPacketSSH2( sessionInfoPtr, &stream, packetOffset,
FALSE, TRUE );
if( cryptStatusOK( status ) )
{
/* 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, &stream, TRUE );
}
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
return( status );
/* Save the MPI-encoded client DH keyex value for later, when we need to
hash it */
memcpy( handshakeInfo->clientKeyexValue, keyexPtr, keyexLength );
handshakeInfo->clientKeyexValueLength = keyexLength;
/* 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 = DUMMY_INIT;
STREAM stream;
MESSAGE_DATA msgData;
void *keyPtr = DUMMY_INIT_PTR, *keyBlobPtr = DUMMY_INIT_PTR;
void *sigPtr = DUMMY_INIT_PTR;
int keyLength = DUMMY_INIT, keyBlobLength, sigLength, length;
int dummy, 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 */
status = length = \
readHSPacketSSH2( sessionInfoPtr,
( handshakeInfo->requestedServerKeySize > 0 ) ? \
SSH2_MSG_KEXDH_GEX_REPLY : SSH2_MSG_KEXDH_REPLY,
ID_SIZE + LENGTH_SIZE + sizeofString32( "", 6 ) + \
sizeofString32( "", 1 ) + \
sizeofString32( "", MIN_PKCSIZE ) + \
sizeofString32( "", MIN_PKCSIZE ) + \
LENGTH_SIZE + sizeofString32( "", 6 ) + 40 );
if( cryptStatusError( status ) )
return( status );
sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
status = sgetc( &stream ); /* Skip packet type */
if( !cryptStatusError( status ) )
{
streamBookmarkSet( &stream, keyLength );
status = readUint32( &stream ); /* Server key data size */
}
if( !cryptStatusError( status ) )
{
status = readAlgoString( &stream, handshakeInfo->algoStringPubkeyTbl,
handshakeInfo->algoStringPubkeyTblNoEntries,
&pubkeyAlgo, TRUE, SESSION_ERRINFO );
}
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
if( pubkeyAlgo != handshakeInfo->pubkeyAlgo )
{
sMemDisconnect( &stream );
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid DH phase 2 public key algorithm %d, expected %d",
pubkeyAlgo, handshakeInfo->pubkeyAlgo ) );
}
streamBookmarkSet( &stream, keyBlobLength );
if( pubkeyAlgo == CRYPT_ALGO_RSA )
{
/* RSA e, n */
readInteger32( &stream, NULL, &dummy, 1, CRYPT_MAX_PKCSIZE );
status = readInteger32Checked( &stream, NULL, &dummy,
MIN_PKCSIZE, CRYPT_MAX_PKCSIZE );
}
else
{
/* DSA p, q, g, y */
status = readInteger32Checked( &stream, NULL, &dummy,
MIN_PKCSIZE, CRYPT_MAX_PKCSIZE );
if( cryptStatusOK( status ) )
{
readInteger32( &stream, NULL, &dummy, 1, CRYPT_MAX_PKCSIZE );
readInteger32( &stream, NULL, &dummy, 1, CRYPT_MAX_PKCSIZE );
status = readInteger32Checked( &stream, NULL, &dummy,
MIN_PKCSIZE, CRYPT_MAX_PKCSIZE );
}
}
if( cryptStatusOK( status ) )
status = streamBookmarkComplete( &stream, &keyBlobPtr,
&keyBlobLength, keyBlobLength );
if( cryptStatusOK( status ) )
status = streamBookmarkComplete( &stream, &keyPtr, &keyLength,
keyLength );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
/* Some misconfigured servers may use very short keys, we perform
a special-case check for these and return a more specific message
than the generic bad-data */
if( status == CRYPT_ERROR_NOSECURE )
{
retExt( CRYPT_ERROR_NOSECURE,
( CRYPT_ERROR_NOSECURE, SESSION_ERRINFO,
"Insecure key used in key exchange" ) );
}
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid DH phase 2 packet" ) );
}
setMessageData( &msgData, keyPtr, keyLength );
status = krnlSendMessage( sessionInfoPtr->iKeyexAuthContext,
IMESSAGE_SETATTRIBUTE_S, &msgData,
CRYPT_IATTRIBUTE_KEY_SSH );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
retExt( cryptArgError( status ) ? \
CRYPT_ERROR_BADDATA : status,
( cryptArgError( status ) ? \
CRYPT_ERROR_BADDATA : status, SESSION_ERRINFO,
"Invalid server key/certificate" ) );
}
status = krnlSendMessage( handshakeInfo->iExchangeHashcontext,
IMESSAGE_CTX_HASH, keyPtr, keyLength );
if( cryptStatusOK( status ) )
{
/* The fingerprint is computed from the "key blob", which is
different from the server key. The server key is the full key,
while the "key blob" is only the raw key components (e, n for
RSA, p, q, g, y for DSA). Note that, as with the old PGP 2.x key
hash mechanism, this allows key spoofing (although it isn't quite
as bad as the PGP 2.x key fingerprint mechanism) since it doesn't
hash an indication of the key type or format */
status = processKeyFingerprint( sessionInfoPtr,
keyBlobPtr, keyBlobLength );
}
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
/* Read the server DH keyex value and complete the DH key agreement */
status = readRawObject32( &stream, handshakeInfo->serverKeyexValue,
CRYPT_MAX_PKCSIZE + 16,
&handshakeInfo->serverKeyexValueLength );
if( cryptStatusError( status ) || \
!isValidDHsize( handshakeInfo->clientKeyexValueLength,
handshakeInfo->serverKeySize, LENGTH_SIZE ) )
{
sMemDisconnect( &stream );
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid DH phase 2 keyex value" ) );
}
status = completeKeyex( sessionInfoPtr, handshakeInfo, FALSE );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
/* Prepare to process the handshake packet signature */
streamBookmarkSet( &stream, sigLength );
status = length = readUint32( &stream );
if( !cryptStatusError( status ) )
status = sSkip( &stream, length );
if( cryptStatusOK( status ) )
status = streamBookmarkComplete( &stream, &sigPtr, &sigLength,
sigLength );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid DH phase 2 packet signature data" ) );
}
/* 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 [ nothing ]
string "ssh-dss"
string signature signature
If we're talking to one of these versions, we check to see whether
the packet is correctly formatted (that is, that it has the
algorithm-type string present as required) and if it isn't present
rewrite it into the correct form 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( ( BYTE * ) sigPtr + LENGTH_SIZE + LENGTH_SIZE,
"ssh-dss", 7 ) && \
memcmp( ( BYTE * ) sigPtr + LENGTH_SIZE + LENGTH_SIZE,
"x509v3-sign-dss", 15 ) && \
memcmp( ( BYTE * ) sigPtr + LENGTH_SIZE + LENGTH_SIZE,
"spki-sign-dss", 13 ) && \
memcmp( ( BYTE * ) sigPtr + LENGTH_SIZE + LENGTH_SIZE,
"pgp-sign-dss", 12 ) ) )
{
int headerSize = DUMMY_INIT;
/* Rewrite the signature to fix up the overall length at the start
and insert the algorithm name and signature length. We can
safely reuse the receive buffer for this because the start
contains the server key/certificate and DH keyex value, which is
far longer than the 12 bytes of header plus signature that we'll
be writing there */
sMemOpen( &stream, sessionInfoPtr->receiveBuffer,
LENGTH_SIZE + sizeofString32( "ssh-dsa", 6 ) + sigLength );
writeUint32( &stream, sizeofString32( "ssh-dsa", 6 ) );
writeAlgoString( &stream, CRYPT_ALGO_DSA );
status = swrite( &stream, sigPtr, sigLength );
if( cryptStatusOK( status ) )
headerSize = stell( &stream );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
return( status );
/* The rewritten signature is now at the start of the buffer, update
the sig. pointer and size to accomodate the added header */
sigPtr = sessionInfoPtr->receiveBuffer;
sigLength += headerSize;
}
/* Finally, verify the server's signature on the exchange hash */
status = iCryptCheckSignature( sigPtr, sigLength, CRYPT_IFORMAT_SSH,
sessionInfoPtr->iKeyexAuthContext,
handshakeInfo->iExchangeHashcontext,
CRYPT_UNUSED, NULL );
if( cryptStatusError( status ) )
retExt( status,
( status, SESSION_ERRINFO,
"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 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -