?? ssh1.c
字號:
SSH_HANDSHAKE_INFO *handshakeInfo )
{
MESSAGE_CREATEOBJECT_INFO createInfo;
MESSAGE_DATA msgData;
BYTE *bufPtr = sessionInfoPtr->sendBuffer;
static const int keyLength = bitsToBytes( 768 );
long value;
int length, status;
krnlSendMessage( sessionInfoPtr->privateKey, IMESSAGE_GETATTRIBUTE,
&handshakeInfo->serverKeySize, CRYPT_CTXINFO_KEYSIZE );
/* Generate the 768-bit RSA server key. It would be better to do this
before the listen on the socket, but we can't do it until we know that
the client is v1, which we only know after the initial message
exchange */
setMessageCreateObjectInfo( &createInfo, CRYPT_ALGO_RSA );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_CREATEOBJECT,
&createInfo, OBJECT_TYPE_CONTEXT );
if( cryptStatusError( status ) )
return( status );
setMessageData( &msgData, "SSH server key", 14 );
status = krnlSendMessage( createInfo.cryptHandle,
IMESSAGE_SETATTRIBUTE_S, &msgData,
CRYPT_CTXINFO_LABEL );
if( cryptStatusOK( status ) )
status = krnlSendMessage( createInfo.cryptHandle,
IMESSAGE_SETATTRIBUTE, ( int * ) &keyLength,
CRYPT_CTXINFO_KEYSIZE );
if( cryptStatusOK( status ) )
status = krnlSendMessage( createInfo.cryptHandle,
IMESSAGE_CTX_GENKEY, NULL, FALSE );
if( cryptStatusError( status ) )
{
krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
return( status );
}
handshakeInfo->iServerCryptContext = createInfo.cryptHandle;
/* Send the server public key packet:
byte[8] cookie
uint32 keysize_bits - Usually 768 bits
mpint serverkey_exponent
mpint serverkey_modulus
uint32 keysize_bits - Usually 1024 bits
mpint hostkey_exponent
mpint hostkey_modulus
uint32 protocol_flags - Not used
uint32 offered_ciphers
uint32 offered_authent */
setMessageData( &msgData, handshakeInfo->cookie, SSH1_COOKIE_SIZE );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
if( cryptStatusError( status ) )
return( status );
memcpy( bufPtr, handshakeInfo->cookie, SSH1_COOKIE_SIZE );
bufPtr += SSH1_COOKIE_SIZE;
setMessageData( &msgData, bufPtr, LENGTH_SIZE + \
( ( SSH1_MPI_LENGTH_SIZE + CRYPT_MAX_PKCSIZE ) * 2 ) );
status = krnlSendMessage( handshakeInfo->iServerCryptContext,
IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_IATTRIBUTE_KEY_SSH1 );
if( cryptStatusError( status ) )
return( status );
length = processPublickeyData( handshakeInfo, bufPtr, msgData.length,
TRUE, NULL );
bufPtr += length;
setMessageData( &msgData, bufPtr, LENGTH_SIZE + \
( ( SSH1_MPI_LENGTH_SIZE + CRYPT_MAX_PKCSIZE ) * 2 ) );
status = krnlSendMessage( sessionInfoPtr->privateKey,
IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_IATTRIBUTE_KEY_SSH1 );
if( cryptStatusError( status ) )
return( status );
length = processPublickeyData( handshakeInfo, bufPtr, msgData.length,
FALSE, NULL );
bufPtr += length;
mputLong( bufPtr, 0 ); /* No protocol flags */
value = getAlgorithmMask();
mputLong( bufPtr, value ); /* Cipher algorithms */
value = 1 << SSH1_AUTH_PASSWORD;
if( sessionInfoPtr->cryptKeyset != CRYPT_ERROR )
value |= 1 << SSH1_AUTH_RSA;
mputLong( bufPtr, value ); /* Authent algorithms */
/* Move the data up in the buffer to allow for the variable-length
padding and send it to the client */
length = bufPtr - sessionInfoPtr->sendBuffer;
memmove( sessionInfoPtr->sendBuffer + LENGTH_SIZE + \
getPadLength( length ) + ID_SIZE, sessionInfoPtr->sendBuffer,
length );
status = sendPacketSsh1( sessionInfoPtr, SSH1_SMSG_PUBLIC_KEY, length,
CRYPT_UNUSED );
if( cryptStatusError( status ) )
return( status );
/* If the peer is using cryptlib, we use HMAC-SHA instead of CRC32 */
if( sessionInfoPtr->flags & SESSION_ISCRYPTLIB )
{
setMessageCreateObjectInfo( &createInfo, CRYPT_ALGO_HMAC_SHA );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_DEV_CREATEOBJECT, &createInfo,
OBJECT_TYPE_CONTEXT );
if( cryptStatusError( status ) )
return( status );
sessionInfoPtr->iAuthInContext = createInfo.cryptHandle;
}
return( CRYPT_OK );
}
/* Exchange keys with the client */
static int exchangeServerKeys( SESSION_INFO *sessionInfoPtr,
SSH_HANDSHAKE_INFO *handshakeInfo )
{
MECHANISM_WRAP_INFO mechanismInfo;
BYTE buffer[ CRYPT_MAX_PKCSIZE + 8 ];
BYTE *bufPtr = sessionInfoPtr->receiveBuffer;
int length, keyLength, i, status;
/* Read the client's encrypted session key info:
byte cipher_type
byte[8] cookie
mpint double_enc_sessionkey
uint32 protocol_flags */
length = readPacketSSH1( sessionInfoPtr, SSH1_CMSG_SESSION_KEY );
if( cryptStatusError( length ) )
return( length );
sessionInfoPtr->cryptAlgo = sshIDToAlgoID( bufPtr[ 0 ] );
if( sessionInfoPtr->cryptAlgo == CRYPT_ALGO_NONE )
retExt( sessionInfoPtr, CRYPT_ERROR_NOTAVAIL,
"No crypto algorithm compatible with the remote system "
"could be found" );
if( memcmp( bufPtr + 1, handshakeInfo->cookie, SSH1_COOKIE_SIZE ) )
retExt( sessionInfoPtr, CRYPT_ERROR_INVALID,
"Client cookie doesn't match server cookie" );
bufPtr += 1 + SSH1_COOKIE_SIZE;
length -= 1 + SSH1_COOKIE_SIZE;
keyLength = mgetWord( bufPtr );
keyLength = bitsToBytes( keyLength );
if( length != SSH1_MPI_LENGTH_SIZE + keyLength + UINT_SIZE || \
keyLength < handshakeInfo->serverKeySize - 8 || \
keyLength > handshakeInfo->serverKeySize )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid encrypted session key packet length %d, key "
"length %d", length, keyLength );
/* Import the double-encrypted secure state information, first decrypting
with the host key, then with the server key */
setMechanismWrapInfo( &mechanismInfo, bufPtr, keyLength,
buffer, CRYPT_MAX_PKCSIZE, CRYPT_UNUSED,
sessionInfoPtr->privateKey, CRYPT_UNUSED );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_IMPORT,
&mechanismInfo, MECHANISM_ENC_PKCS1_RAW );
if( cryptStatusError( status ) )
return( status );
setMechanismWrapInfo( &mechanismInfo, buffer, mechanismInfo.keyDataLength,
handshakeInfo->secretValue, SSH1_SECRET_SIZE, CRYPT_UNUSED,
handshakeInfo->iServerCryptContext, CRYPT_UNUSED );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_IMPORT,
&mechanismInfo, MECHANISM_ENC_PKCS1_RAW );
if( cryptStatusOK( status ) && \
mechanismInfo.keyDataLength != SSH1_SECRET_SIZE )
return( CRYPT_ERROR_BADDATA );
clearMechanismInfo( &mechanismInfo );
if( cryptStatusError( status ) )
return( status );
handshakeInfo->secretValueLength = SSH1_SECRET_SIZE;
/* Generate the session ID from the handshake info and XOR it with the
recovered secure state information to get the final secure state
data */
generateSessionID( handshakeInfo );
for( i = 0; i < SSH1_SESSIONID_SIZE; i++ )
handshakeInfo->secretValue[ i ] ^= handshakeInfo->sessionID[ i ];
return( CRYPT_OK );
}
/* Complete the handshake with the client */
static int completeServerHandshake( SESSION_INFO *sessionInfoPtr,
SSH_HANDSHAKE_INFO *handshakeInfo )
{
BYTE *bufPtr;
int packetType, stringLength, length, iterationCount = 0, status;
/* Set up the security information required for the session */
status = initSecurityInfoSSH1( sessionInfoPtr, handshakeInfo );
if( cryptStatusError( status ) )
return( status );
/* Send the server ack and read back the user name:
string username */
status = sendPacketSsh1( sessionInfoPtr, SSH1_SMSG_SUCCESS, 0,
CRYPT_UNUSED );
if( cryptStatusOK( status ) )
length = status = readPacketSSH1( sessionInfoPtr, SSH1_CMSG_USER );
if( cryptStatusError( status ) )
return( status );
bufPtr = sessionInfoPtr->receiveBuffer;
stringLength = ( int ) mgetLong( bufPtr );
if( length != LENGTH_SIZE + stringLength || \
stringLength <= 0 || stringLength > CRYPT_MAX_TEXTSIZE )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid user name packet length %d, name length %d",
length, stringLength );
updateSessionInfo( &sessionInfoPtr->attributeList,
CRYPT_SESSINFO_USERNAME, bufPtr,
stringLength, CRYPT_MAX_TEXTSIZE, ATTR_FLAG_NONE );
/* Send the server ack (which is actually a nack since the user needs
to submit a password) and read back the password */
status = sendPacketSsh1( sessionInfoPtr, SSH1_SMSG_FAILURE, 0,
CRYPT_UNUSED );
if( cryptStatusOK( status ) )
length = status = readPacketSSH1( sessionInfoPtr,
SSH1_CMSG_AUTH_PASSWORD );
if( cryptStatusError( status ) )
return( status );
bufPtr = sessionInfoPtr->receiveBuffer;
stringLength = ( int ) mgetLong( bufPtr );
if( length != LENGTH_SIZE + stringLength || \
stringLength <= 0 || stringLength > CRYPT_MAX_TEXTSIZE )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid password packet length %d, password length %d",
length, stringLength );
updateSessionInfo( &sessionInfoPtr->attributeList,
CRYPT_SESSINFO_PASSWORD, bufPtr,
stringLength, CRYPT_MAX_TEXTSIZE, ATTR_FLAG_NONE );
/* Send the server ack and process any further junk that the caller may
throw at us until we get an exec shell or command request. At the
moment it's set up in allow-all mode, it may be necessary to switch
to deny-all instead if clients pop up which submit things that cause
problems */
status = sendPacketSsh1( sessionInfoPtr, SSH1_SMSG_SUCCESS, 0,
CRYPT_UNUSED );
if( cryptStatusError( status ) )
return( status );
do
{
status = readPacketSSH1( sessionInfoPtr, SSH1_MSG_SPECIAL_ANY );
if( cryptStatusError( status ) )
break;
packetType = sessionInfoPtr->receiveBuffer[ 0 ];
switch( packetType )
{
case SSH1_CMSG_REQUEST_COMPRESSION:
case SSH1_CMSG_X11_REQUEST_FORWARDING:
case SSH1_CMSG_PORT_FORWARD_REQUEST:
case SSH1_CMSG_AGENT_REQUEST_FORWARDING:
/* Special operations aren't supported by cryptlib */
status = sendPacketSsh1( sessionInfoPtr, SSH1_SMSG_FAILURE,
0, CRYPT_UNUSED );
break;
case SSH1_CMSG_EXEC_SHELL:
case SSH1_CMSG_EXEC_CMD:
/* These commands move the server into the interactive
session mode and aren't explicitly acknowledged */
break;
case SSH1_CMSG_REQUEST_PTY:
default:
status = sendPacketSsh1( sessionInfoPtr, SSH1_SMSG_SUCCESS,
0, CRYPT_UNUSED );
}
}
while( !cryptStatusError( status ) && \
( packetType != SSH1_CMSG_EXEC_SHELL && \
packetType != SSH1_CMSG_EXEC_CMD ) && iterationCount++ < 50 );
if( iterationCount >= 50 )
retExt( sessionInfoPtr, CRYPT_ERROR_OVERFLOW,
"Peer sent excessive number of session open packets" );
return( cryptStatusError( status ) ? status : CRYPT_OK );
}
/****************************************************************************
* *
* Get/Put Data Functions *
* *
****************************************************************************/
/* Read data over the SSHv1 link */
static int readHeaderFunction( SESSION_INFO *sessionInfoPtr,
READSTATE_INFO *readInfo )
{
BYTE *bufPtr = sessionInfoPtr->receiveBuffer + \
sessionInfoPtr->receiveBufPos;
long length;
int status;
/* Clear return value */
*readInfo = READINFO_NONE;
/* Try and read the header data from the remote system */
assert( sessionInfoPtr->receiveBufPos == sessionInfoPtr->receiveBufEnd );
status = readFixedHeader( sessionInfoPtr, LENGTH_SIZE );
if( status <= 0 )
return( status );
/* Process the header data. Since data errors are always fatal, we make
all errors fatal until we've finished handling the header */
*readInfo = READINFO_FATAL;
assert( status == LENGTH_SIZE );
length = mgetLong( bufPtr );
if( length < SSH1_HEADER_SIZE || \
length > sessionInfoPtr->receiveBufSize - 8 )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid packet length %d", length );
/* Determine how much data we'll be expecting. We set the remaining
data length to the actual length plus the padding length since we
need to read this much to get to the end of the packet */
sessionInfoPtr->pendingPacketLength = length;
sessionInfoPtr->pendingPacketRemaining = length + \
( 8 - ( sessionInfoPtr->pendingPacketLength & 7 ) );
/* Indicate that we got the header. Since the header is out-of-band data
in SSHv1, we mark it as a no-op read */
*readInfo = READINFO_NOOP;
return( OK_SPECIAL );
}
static int processBodyFunction( SESSION_INFO *sessionInfoPtr,
READSTAT
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -