📄 ssh2.c
字号:
padBlockSize ) - length;
assert( padLength >= SSH2_MIN_PADLENGTH_SIZE && padLength < 256 );
/* Add the SSH packet header:
uint32 length
byte padLen
byte[] data
byte[] padding
byte[] MAC */
mputLong( bufPtr, ( long ) ( length - LENGTH_SIZE ) + padLength );
*bufPtr++ = padLength;
bufPtr += dataLength;
if( sessionInfoPtr->flags & SESSION_ISSECURE )
{
RESOURCE_DATA msgData;
BYTE seqBuffer[ 8 ], *seqBufPtr = seqBuffer;
const int payloadLength = SSH2_HEADER_SIZE + dataLength + padLength;
/* Append the padding */
setMessageData( &msgData, bufPtr, padLength );
krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
bufPtr += padLength;
assert( bufPtr == bufStartPtr + payloadLength );
/* MAC the data:
HMAC( seqNo || payload ) */
mputLong( seqBufPtr, sessionInfoPtr->writeSeqNo );
krnlSendMessage( sessionInfoPtr->iAuthOutContext,
IMESSAGE_DELETEATTRIBUTE, NULL,
CRYPT_CTXINFO_HASHVALUE );
krnlSendMessage( sessionInfoPtr->iAuthOutContext,
IMESSAGE_CTX_HASH, seqBuffer, LENGTH_SIZE );
krnlSendMessage( sessionInfoPtr->iAuthOutContext,
IMESSAGE_CTX_HASH, ( void * ) bufStartPtr,
payloadLength );
krnlSendMessage( sessionInfoPtr->iAuthOutContext,
IMESSAGE_CTX_HASH, "", 0 );
setMessageData( &msgData, bufPtr, CRYPT_MAX_HASHSIZE );
status = krnlSendMessage( sessionInfoPtr->iAuthOutContext,
IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_CTXINFO_HASHVALUE );
if( cryptStatusError( status ) )
return( status );
/* Encrypt the entire packet except for the MAC */
status = krnlSendMessage( sessionInfoPtr->iCryptOutContext,
IMESSAGE_CTX_ENCRYPT, ( void * ) bufStartPtr,
payloadLength );
if( cryptStatusError( status ) )
return( status );
}
else
/* If there's no security in effect yet, the padding is all zeroes */
memset( bufPtr, 0, padLength );
sessionInfoPtr->writeSeqNo++;
return( SSH2_HEADER_SIZE + dataLength + padLength + \
( ( sessionInfoPtr->flags & SESSION_ISSECURE ) ? \
sessionInfoPtr->authBlocksize : 0 ) );
}
int sendPacketSSH2( SESSION_INFO *sessionInfoPtr, const int dataLength,
const BOOLEAN sendOnly )
{
int length = dataLength, status;
if( !sendOnly )
{
length = wrapPacket( sessionInfoPtr, sessionInfoPtr->sendBuffer,
dataLength );
if( cryptStatusError( length ) )
return( length );
}
status = swrite( &sessionInfoPtr->stream, sessionInfoPtr->sendBuffer,
length );
if( cryptStatusError( status ) )
sNetGetErrorInfo( &sessionInfoPtr->stream,
sessionInfoPtr->errorMessage,
&sessionInfoPtr->errorCode );
return( CRYPT_OK );
}
/* Process a client/server hello packet */
int processHello( SESSION_INFO *sessionInfoPtr,
SSH_HANDSHAKE_INFO *handshakeInfo, int *keyexLength,
const BOOLEAN isServer )
{
ALGOID_INFO algoIDInfo;
BYTE *bufPtr;
BOOLEAN preferredAlgoMismatch = FALSE;
int length, stringLength, status;
/* Process the client/server hello:
byte type = SSH2_MSG_KEXINIT
byte[16] cookie
string keyex algorithms
string pubkey algorithms
string client_crypto algorithms
string server_crypto algorithms
string client_mac algorithms
string server_mac algorithms
string client_compression algorithms
string server_compression algorithms
string client_language
string server_language
boolean first_keyex_packet_follows
uint32 reserved
The cookie isn't explicitly processed as with SSHv1 since SSHv2
hashes the entire server hello message */
length = readPacketSSH2( sessionInfoPtr, SSH2_MSG_KEXINIT );
if( cryptStatusError( length ) )
return( length );
if( length < ID_SIZE + SSH2_COOKIE_SIZE + \
( ( LENGTH_SIZE + SSH2_MIN_ALGOID_SIZE ) * 6 ) + \
( LENGTH_SIZE * 4 ) + BOOLEAN_SIZE + UINT_SIZE )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid hello packet length %d", length );
*keyexLength = length;
bufPtr = sessionInfoPtr->receiveBuffer + ID_SIZE + SSH2_COOKIE_SIZE;
length -= ID_SIZE + SSH2_COOKIE_SIZE;
if( isServer )
{
/* DES is a placeholder for EDH (as opposed to the standard static
DH) */
setAlgoIDInfo( &algoIDInfo, algoStringKeyexTbl, CRYPT_ALGO_DES,
GETALGO_FIRST_MATCH_WARN );
}
else
{
setAlgoIDInfo( &algoIDInfo, algoStringKeyexTbl, CRYPT_ALGO_NONE,
GETALGO_BEST_MATCH );
}
status = getAlgoIDEx( &algoIDInfo, bufPtr, length, sessionInfoPtr );
if( cryptStatusError( status ) )
return( status );
if( algoIDInfo.prefAlgoMismatch )
/* We didn't get a match for our first choice, remember that we have
to discard any guessed keyex that may follow */
preferredAlgoMismatch = TRUE;
if( algoIDInfo.algo == CRYPT_ALGO_DES )
/* If the keyex algorithm is the DES placeholder, we're using
ephemeral rather than static DH keys and need to negotiate the
keyex key before we can perform the exchange */
handshakeInfo->requestedServerKeySize = SSH2_DEFAULT_KEYSIZE;
bufPtr += algoIDInfo.algoStringLength;
length -= algoIDInfo.algoStringLength;
if( isServer )
{
setAlgoIDInfo( &algoIDInfo, handshakeInfo->algoStringPubkeyTbl,
handshakeInfo->pubkeyAlgo, GETALGO_FIRST_MATCH_WARN );
}
else
{
setAlgoIDInfo( &algoIDInfo, handshakeInfo->algoStringPubkeyTbl,
CRYPT_ALGO_NONE, GETALGO_BEST_MATCH );
}
status = getAlgoIDEx( &algoIDInfo, bufPtr, length, sessionInfoPtr );
if( cryptStatusError( status ) )
return( status );
if( !isServer )
handshakeInfo->pubkeyAlgo = algoIDInfo.algo;
if( algoIDInfo.prefAlgoMismatch )
/* We didn't get a match for our first choice, remember that we have
to discard any guessed keyex that may follow */
preferredAlgoMismatch = TRUE;
bufPtr += algoIDInfo.algoStringLength;
length -= algoIDInfo.algoStringLength;
stringLength = getAlgoIDpair( ( sessionInfoPtr->flags & SESSION_ISSERVER ) ? \
algoStringEncrTblServer : \
algoStringEncrTblClient,
&sessionInfoPtr->cryptAlgo, bufPtr,
length, isServer, sessionInfoPtr );
if( cryptStatusError( stringLength ) )
return( stringLength );
bufPtr += stringLength;
length -= stringLength;
stringLength = getAlgoIDpair( algoStringMACTbl,
&sessionInfoPtr->integrityAlgo, bufPtr,
length, isServer, sessionInfoPtr );
if( cryptStatusError( stringLength ) )
return( stringLength );
bufPtr += stringLength;
length -= stringLength;
stringLength = getAlgoIDpair( algoStringCoprTbl, NULL, bufPtr,
length, isServer, sessionInfoPtr );
if( cryptStatusError( stringLength ) )
return( stringLength );
bufPtr += stringLength;
length -= stringLength;
stringLength = mgetLong( bufPtr );
if( stringLength < 0 || stringLength > length + LENGTH_SIZE )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid hello packet client language string length %d",
stringLength );
bufPtr += stringLength;
length -= stringLength + LENGTH_SIZE;
stringLength = mgetLong( bufPtr );
if( stringLength < 0 || stringLength > length + LENGTH_SIZE )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid hello packet server language string length %d",
stringLength );
bufPtr += stringLength;
length -= stringLength + LENGTH_SIZE;
if( length != BOOLEAN_SIZE + UINT_SIZE )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid hello packet length remainder size %d, expected "
"%d", length, BOOLEAN_SIZE + UINT_SIZE );
if( *bufPtr && preferredAlgoMismatch )
/* There's a guessed keyex following this packet and we didn't match
the first-choice keyex/pubkey algorithm, tell the caller to skip
it */
return( OK_SPECIAL );
return( CRYPT_OK );
}
/* Process a global or channel 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 that submit things that cause problems */
static int sendRequestResponse( SESSION_INFO *sessionInfoPtr,
const BOOLEAN isChannelRequest,
const BOOLEAN isSuccessful )
{
BYTE *bufPtr = sessionInfoPtr->sendBuffer + SSH2_HEADER_SIZE;
/* Indicate that the request succeeded/was denied:
byte type = SSH2_MSG_CHANNEL/GLOBAL_SUCCESS/FAILURE
[ uint32 channel_no - For channel reqs ] */
if( isChannelRequest )
{
*bufPtr++ = isSuccessful ? SSH2_MSG_CHANNEL_SUCCESS : \
SSH2_MSG_CHANNEL_FAILURE;
mputLong( bufPtr, sessionInfoPtr->sshChannel );
return( sendPacketSSH2( sessionInfoPtr, ID_SIZE + UINT_SIZE,
FALSE ) );
}
*bufPtr++ = isSuccessful ? SSH2_MSG_GLOBAL_SUCCESS : \
SSH2_MSG_GLOBAL_FAILURE;
return( sendPacketSSH2( sessionInfoPtr, ID_SIZE, FALSE ) );
}
int processRequest( SESSION_INFO *sessionInfoPtr, const BYTE *data,
const int dataLength )
{
static const FAR_BSS char *invalidRequests[] = \
{ "x11-req", NULL };
static const FAR_BSS char *validRequests[] = \
{ "shell", "exec", "subsystem", NULL };
#if 0 /* Anything not matched defaults to being treated as a no-op */
static const FAR_BSS char *noopRequests[] = \
{ "pty-req", "env", "window-change", "xon-xoff", NULL };
#endif /* 0 */
const BOOLEAN isChannelRequest = \
( sessionInfoPtr->sshPacketType == SSH2_MSG_CHANNEL_REQUEST );
BOOLEAN wantReply = FALSE;
const char *requestNamePtr;
int length = dataLength, stringLength, i;
int extraLength = isChannelRequest ? UINT_SIZE : 0;
/* Process the channel/global request:
byte type = SSH2_MSG_CHANNEL_REQUEST
[ uint32 recipient_channel - For channel reqs ]
string request_type
boolean want_reply
[...] */
if( length < extraLength + ( LENGTH_SIZE + 1 ) + BOOLEAN_SIZE )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid global/channel request packet length %d",
length );
if( isChannelRequest )
{
long channelNo;
channelNo = mgetLong( data );
if( channelNo != sessionInfoPtr->sshChannel )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid channel number %d, expected %d",
channelNo, sessionInfoPtr->sshChannel );
}
stringLength = mgetLong( data );
if( stringLength <= 0 || stringLength > CRYPT_MAX_TEXTSIZE || \
length < extraLength + ( LENGTH_SIZE + stringLength ) + \
BOOLEAN_SIZE )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid global/channel request packet length %d, "
"string length %d", length, stringLength );
length -= extraLength + ( LENGTH_SIZE + stringLength ) + BOOLEAN_SIZE;
if( data[ stringLength ] )
wantReply = TRUE;
requestNamePtr = data;
data += stringLength + BOOLEAN_SIZE;
/* Check for requests that we don't allow */
for( i = 0; invalidRequests[ i ] != NULL; i++ )
if( stringLength == strlen( invalidRequests[ i ] ) && \
!memcmp( requestNamePtr, invalidRequests[ i ], stringLength ) )
return( sendRequestResponse( sessionInfoPtr,
isChannelRequest, FALSE ) );
/* If we're being asked for a subsystem, record the type */
if( stringLength == 9 && !memcmp( requestNamePtr, "subsystem", 9 ) )
{
const int subsystemLength = mgetLong( data );
/* [...]
string subsystem_name */
if( length != ( LENGTH_SIZE + subsystemLength ) || \
subsystemLength <= 0 || subsystemLength > CRYPT_MAX_TEXTSIZE )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid channel request payload length %d, "
"subsystem length %d", length, subsystemLength );
memcpy( sessionInfoPtr->sshSubsystem, data, subsystemLength );
sessionInfoPtr->sshSubsystemLength = subsystemLength;
}
/* If we're being asked for port forwarding, get the address and port
information */
if( stringLength == 13 && !memcmp( requestNamePtr, "tcpip-forward", 13 ) )
{
int status;
/* [...]
string address_to_bind (e.g. "0.0.0.0")
uint32 port_to_bind */
status = getAddressAndPort( sessionInfoPtr, data, length );
if( cryp
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -