📄 ssh1.c
字号:
/* Output the start of the session key packet:
byte cipher_type
byte[8] cookie
mpint double_enc_sessionkey
uint32 protocol_flags */
switch( sessionInfoPtr->cryptAlgo )
{
case CRYPT_ALGO_BLOWFISH:
value = SSH1_CIPHER_BLOWFISH;
break;
case CRYPT_ALGO_DES:
value = SSH1_CIPHER_DES;
break;
case CRYPT_ALGO_IDEA:
value = SSH1_CIPHER_IDEA;
break;
case CRYPT_ALGO_RC4:
value = SSH1_CIPHER_RC4;
break;
default:
retIntError();
}
*bufPtr++ = value;
memcpy( bufPtr, handshakeInfo->cookie, SSH1_COOKIE_SIZE );
bufPtr += SSH1_COOKIE_SIZE;
/* Generate the session ID and secure state information and XOR the
secure state with the session ID */
generateSessionID( handshakeInfo );
setMessageData( &msgData, handshakeInfo->secretValue, SSH1_SECRET_SIZE );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_IATTRIBUTE_RANDOM );
if( cryptStatusError( status ) )
return( status );
handshakeInfo->secretValueLength = SSH1_SECRET_SIZE;
for( i = 0; i < SSH1_SESSIONID_SIZE; i++ )
handshakeInfo->secretValue[ i ] ^= handshakeInfo->sessionID[ i ];
/* Export the secure state information in double-encrypted form,
encrypted first with the server key, then with the host key */
setMechanismWrapInfo( &mechanismInfo, buffer, CRYPT_MAX_PKCSIZE,
handshakeInfo->secretValue, SSH1_SECRET_SIZE,
CRYPT_UNUSED, handshakeInfo->iServerCryptContext,
CRYPT_UNUSED );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_EXPORT,
&mechanismInfo, MECHANISM_ENC_PKCS1_RAW );
if( cryptStatusError( status ) )
return( status );
length = mechanismInfo.wrappedDataLength;
setMechanismWrapInfo( &mechanismInfo,
bufPtr + SSH1_MPI_LENGTH_SIZE, CRYPT_MAX_PKCSIZE,
buffer, length, CRYPT_UNUSED,
sessionInfoPtr->iKeyexCryptContext, CRYPT_UNUSED );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_EXPORT,
&mechanismInfo, MECHANISM_ENC_PKCS1_RAW );
if( cryptStatusError( status ) )
return( status );
length = bytesToBits( mechanismInfo.wrappedDataLength );
mputWord( bufPtr, length );
bufPtr += mechanismInfo.wrappedDataLength;
clearMechanismInfo( &mechanismInfo );
/* XOR the state with the session ID to recover the actual state */
for( i = 0; i < SSH1_SESSIONID_SIZE; i++ )
handshakeInfo->secretValue[ i ] ^= handshakeInfo->sessionID[ i ];
/* Write the various flags */
mputLong( bufPtr, 0 ); /* Protocol flags */
/* Move the data up in the buffer to allow for the variable-length
padding and send it to the server */
dataLength = bufPtr - sessionInfoPtr->sendBuffer;
memmove( sessionInfoPtr->sendBuffer + LENGTH_SIZE + \
getPadLength( dataLength ) + ID_SIZE,
sessionInfoPtr->sendBuffer, dataLength );
return( sendPacketSsh1( sessionInfoPtr, SSH1_CMSG_SESSION_KEY,
dataLength, CRYPT_UNUSED ) );
}
/* Complete the handshake with the server */
static int completeClientHandshake( SESSION_INFO *sessionInfoPtr,
SSH_HANDSHAKE_INFO *handshakeInfo )
{
const ATTRIBUTE_LIST *userNamePtr = \
findSessionInfo( sessionInfoPtr->attributeList,
CRYPT_SESSINFO_USERNAME );
const ATTRIBUTE_LIST *passwordPtr = \
findSessionInfo( sessionInfoPtr->attributeList,
CRYPT_SESSINFO_PASSWORD );
BYTE *bufPtr;
int padLength, modulusLength, length, status;
/* Set up the security information required for the session */
status = initSecurityInfoSSH1( sessionInfoPtr, handshakeInfo );
if( cryptStatusError( status ) )
return( status );
/* Read back the server ack and send the user name:
string username */
status = readPacketSSH1( sessionInfoPtr, SSH1_SMSG_SUCCESS );
if( cryptStatusError( status ) )
return( status );
padLength = getPadLength( LENGTH_SIZE + userNamePtr->valueLength );
bufPtr = sessionInfoPtr->sendBuffer + LENGTH_SIZE + padLength + ID_SIZE;
encodeString( bufPtr, userNamePtr->value, userNamePtr->valueLength );
status = sendPacketSsh1( sessionInfoPtr, SSH1_CMSG_USER,
LENGTH_SIZE + userNamePtr->valueLength,
CRYPT_UNUSED );
if( cryptStatusError( status ) )
return( status );
/* Read back the server ack and send the authentication information if
required. This information is optional, if the server returns a
failure packet (converted to an OK_SPECIAL return status) it means
authentication is required, otherwise it isn't and we're already
logged in */
status = readPacketSSH1( sessionInfoPtr, SSH1_MSG_SPECIAL_USEROPT );
if( status == OK_SPECIAL )
{
/* If there's a password present, we're using password-based
authentication:
string password */
if( passwordPtr != NULL )
{
int maxLen, packetType, i;
/* Since SSHv1 sends the packet length in the clear and uses
implicit-length padding, it reveals the length of the
encrypted password to an observer. To get around this, we
send a series of packets of length 4...maxLen to the server,
one of which is the password, the rest are SSH_MSG_IGNOREs.
It's still possible for an attacker who can perform very
precise timing measurements to determine which one is the
password based on server response time, but it's a lot less
problematic than with a single packet. Unfortunately, it's
also possible for an attacker to determine the password packet
by checking which one generates the password response, unless
the server somehow knows how many packets are coming reads
them all in a loop and only then sends a response, which
means that this defence isn't as effective as it seems */
status = CRYPT_OK;
for( maxLen = 16; maxLen <= passwordPtr->valueLength;
maxLen <<= 1 );
for( i = min( 4, passwordPtr->valueLength );
i < maxLen && cryptStatusOK( status ); i++ )
{
padLength = getPadLength( LENGTH_SIZE + i );
bufPtr = sessionInfoPtr->sendBuffer + LENGTH_SIZE + \
padLength + ID_SIZE;
if( i == passwordPtr->valueLength )
{
encodeString( bufPtr, passwordPtr->value,
passwordPtr->valueLength );
packetType = SSH1_CMSG_AUTH_PASSWORD;
}
else
{
MESSAGE_DATA msgData;
setMessageData( &msgData, sessionInfoPtr->receiveBuffer,
i );
krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_IATTRIBUTE_RANDOM_NONCE );
encodeString( bufPtr, sessionInfoPtr->receiveBuffer, i );
packetType = SSH1_MSG_IGNORE;
}
status = sendPacketSsh1( sessionInfoPtr, packetType,
LENGTH_SIZE + i, CRYPT_UNUSED );
if( cryptStatusOK( status ) && \
packetType == SSH1_CMSG_AUTH_PASSWORD )
status = readPacketSSH1( sessionInfoPtr,
SSH1_MSG_SPECIAL_PWOPT );
}
}
else
{
MECHANISM_WRAP_INFO mechanismInfo;
MESSAGE_DATA msgData;
BYTE challenge[ SSH1_CHALLENGE_SIZE + 8 ];
BYTE response[ SSH1_RESPONSE_SIZE ];
BYTE modulusBuffer[ ( ( SSH1_MPI_LENGTH_SIZE + \
CRYPT_MAX_PKCSIZE ) * 2 ) + 8 ];
BYTE *modulusPtr;
/* We're using RSA authentication, initially we send just the user's
public-key info:
mpint identity_public_modulus
First we get the modulus used to identify the client's key.
For no adequately explored reason this is only the modulus and
not the usual exponent+modulus combination, which means that
there's no way for a cryptlib server to identify the public
key from it, although at a pinch we could try with e=17, 257,
or F4 */
setMessageData( &msgData, modulusBuffer,
( 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 );
/* Skip the key size and exponent to get to the modulus. We
don't have to perform safety checks here since the data is
coming from within cryptlib */
modulusPtr = modulusBuffer + sizeof( LENGTH_SIZE );
length = mgetWord( modulusPtr ); /* Exponent */
length = bitsToBytes( length );
modulusPtr += length;
modulusLength = mgetWord( modulusPtr ); /* Modulus */
modulusLength = bitsToBytes( modulusLength );
length = SSH1_MPI_LENGTH_SIZE + modulusLength;
modulusPtr -= SSH1_MPI_LENGTH_SIZE;
/* Send the modulus to the server */
padLength = getPadLength( length );
bufPtr = sessionInfoPtr->sendBuffer + LENGTH_SIZE + \
padLength + ID_SIZE;
memcpy( bufPtr, modulusPtr, length );
status = sendPacketSsh1( sessionInfoPtr, SSH1_CMSG_AUTH_RSA,
length, CRYPT_UNUSED );
if( cryptStatusOK( status ) )
status = readPacketSSH1( sessionInfoPtr,
SSH1_MSG_SPECIAL_RSAOPT );
if( cryptStatusError( status ) )
return( status );
/* The server recognises our key (no mean feat, considering that
e could be anything) and has sent an RSA challenge, decrypt
it:
mpint encrypted_challenge */
bufPtr = sessionInfoPtr->receiveBuffer;
length = mgetWord( bufPtr );
length = bitsToBytes( length );
if( length < modulusLength - 8 || length > modulusLength )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid encrypted challenge length %d for modulus "
"length %d", length, modulusLength );
setMechanismWrapInfo( &mechanismInfo, bufPtr, length,
challenge, SSH1_CHALLENGE_SIZE, CRYPT_UNUSED,
sessionInfoPtr->privateKey, CRYPT_UNUSED );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_DEV_IMPORT, &mechanismInfo,
MECHANISM_ENC_PKCS1_RAW );
clearMechanismInfo( &mechanismInfo );
if( cryptStatusError( status ) )
return( status );
/* Send the response to the challenge:
byte[16] MD5 of decrypted challenge
Since this completes the authentication, we expect to see
either a success or failure packet once we're done */
generateChallengeResponse( response, handshakeInfo, challenge );
padLength = getPadLength( SSH1_RESPONSE_SIZE );
bufPtr = sessionInfoPtr->sendBuffer + LENGTH_SIZE +
padLength + ID_SIZE;
memcpy( bufPtr, response, SSH1_RESPONSE_SIZE );
status = sendPacketSsh1( sessionInfoPtr,
SSH1_CMSG_AUTH_RSA_RESPONSE,
SSH1_RESPONSE_SIZE, CRYPT_UNUSED );
if( cryptStatusOK( status ) )
status = readPacketSSH1( sessionInfoPtr,
SSH1_MSG_SPECIAL_PWOPT );
}
}
if( cryptStatusError( status ) )
return( status );
/* Tell the server to adjust its maximum packet size if required:
uint32 packet_size */
if( sessionInfoPtr->sendBufSize < EXTRA_PACKET_SIZE + MAX_PACKET_SIZE )
{
const int maxLength = sessionInfoPtr->sendBufSize - \
EXTRA_PACKET_SIZE;
padLength = getPadLength( LENGTH_SIZE );
bufPtr = sessionInfoPtr->sendBuffer + LENGTH_SIZE + padLength + ID_SIZE;
mputLong( bufPtr, maxLength );
status = sendPacketSsh1( sessionInfoPtr, SSH1_CMSG_MAX_PACKET_SIZE,
LENGTH_SIZE, CRYPT_UNUSED );
if( cryptStatusOK( status ) )
status = readPacketSSH1( sessionInfoPtr, SSH1_SMSG_SUCCESS );
if( cryptStatusError( status ) )
return( status );
}
/* Request a pty from the server:
string TERM environment variable = "vt100"
uint32 rows = 24
uint32 cols = 80
uint32 pixel_width = 0
uint32 pixel_height = 0
byte tty_mode_info = 0 */
padLength = getPadLength( ( LENGTH_SIZE + 5 ) + ( UINT_SIZE * 4 ) + 1 );
bufPtr = sessionInfoPtr->sendBuffer + LENGTH_SIZE + padLength + ID_SIZE;
bufPtr += encodeString( bufPtr, "vt100", 0 ); /* Generic terminal type */
mputLong( bufPtr, 24 );
mputLong( bufPtr, 80 ); /* 24 x 80 */
mputLong( bufPtr, 0 );
mputLong( bufPtr, 0 ); /* No graphics capabilities */
*bufPtr = 0; /* No special TTY modes */
status = sendPacketSsh1( sessionInfoPtr, SSH1_CMSG_REQUEST_PTY,
( LENGTH_SIZE + 5 ) + ( UINT_SIZE * 4 ) + 1,
CRYPT_UNUSED );
if( cryptStatusOK( status ) )
status = readPacketSSH1( sessionInfoPtr, SSH1_SMSG_SUCCESS );
if( cryptStatusError( status ) )
return( status );
/* Tell the server to create a shell for us. This moves the server into
the interactive session mode, if we're talking to a standard Unix
server implementing a remote shell we could read the stdout data
response from starting the shell but this may not be the case so we
leave the response for the user to process explicitly */
return( sendPacketSsh1( sessionInfoPtr, SSH1_CMSG_EXEC_SHELL, 0,
CRYPT_UNUSED ) );
}
/****************************************************************************
* *
* Server-side Connect Functions *
* *
****************************************************************************/
/* Perform the initial part of the handshake with the client */
static int beginServerHandshake( SESSION_INFO *sessionInfoPtr,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -