📄 ssh1.c
字号:
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,
0 );
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 ], *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_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_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, 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, 0 );
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 );
memcpy( sessionInfoPtr->userName, bufPtr, stringLength );
sessionInfoPtr->userNameLength = stringLength;
/* 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, 0 );
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 );
memcpy( sessionInfoPtr->password, bufPtr, stringLength );
sessionInfoPtr->passwordLength = stringLength;
/* 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, 0 );
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, 0 );
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, 0 );
}
}
while( !cryptStatusError( status ) && \
( packetType != SSH1_CMSG_EXEC_SHELL && \
packetType != SSH1_CMSG_EXEC_CMD ) );
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,
READSTATE_INFO *readInfo )
{
BYTE *bufPtr = sessionInfoPtr->receiveBuffer + \
sessionInfoPtr->receiveBufPos;
BYTE *bufStartPtr = bufPtr;
long length;
int padLength, status;
/* All errors processing the payload are fatal */
*readInfo = READINFO_FATAL;
/* Decrypt the packet in the buffer and checksum the payload */
padLength = 8 - ( sessionInfoPtr->pendingPacketLength & 7 );
assert( bufPtr == sessionInfoPtr->receiveBuffer + \
sessionInfoPtr->receiveBufEnd - \
( padLength + sessionInfoPtr->pendingPacketLength ) );
status = decryptPayload( sessionInfoPtr, bufPtr,
padLength + sessionInfoPtr->pendingPacketLength );
if( cryptStatusError( status ) )
return( status );
if( !checksumPayload( sessionInfoPtr, bufPtr,
padLength + sessionInfoPtr->pendingPacketLength ) )
retExt( sessionInfoPtr, CRYPT_ERROR_SIGNATURE,
"Bad message checksum" );
/* See what we got */
bufPtr += padLength;
if( *bufPtr == SSH1_MSG_IGNORE || *bufPtr == SSH1_MSG_DEBUG )
{
/* Nothing to see here, move along, move along */
sessionInfoPtr->receiveBufEnd = sessionInfoPtr->receiveBufPos;
sessionInfoPtr->pendingPacketLength = 0;
*readInfo = READINFO_NOOP;
return( OK_SPECIAL ); /* Tell the caller to try again */
}
if( *bufPtr == SSH1_MSG_DISCONNECT )
return( getDisconnectInfo( sessionInfoPtr,
sessionInfoPtr->receiveBuffer + padLength + ID_SIZE ) );
if( *bufPtr == SSH1_SMSG_EXITSTATUS )
{
/* Confirm the server exit and bail out */
sendPacketSsh1( sessionInfoPtr, SSH1_CMSG_EXIT_CONFIRMATION, 0, 0 );
sessionInfoPtr->receiveBufEnd = sessionInfoPtr->receiveBufPos;
sessionInfoPtr->pendingPacketLength = 0;
return( 0 );
}
bufPtr++;
/* It's payload data, move it down next to the previous data. This
doesn't help performance much, but it shouldn't be a major problem
since SSHv1 is deprecated anyway */
length = mgetLong( bufPtr );
if( length < 0 || length > sessionInfoPtr->pendingPacketLength - \
( ID_SIZE + LENGTH_SIZE + SSH1_CRC_SIZE ) )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid payload length %d", length );
if( length > 0 )
memmove( bufStartPtr, bufPtr, length );
sessionInfoPtr->receiveBufEnd -= padLength + ID_SIZE + LENGTH_SIZE + \
SSH1_CRC_SIZE;
s
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -