📄 ssh2_svr.c
字号:
the client */
if( handshakeInfo->requestedServerKeySize > 0 )
{
status = processDHE( sessionInfoPtr, handshakeInfo );
if( cryptStatusError( status ) )
return( status );
}
/* Process the client keyex:
byte type = SSH2_MSG_KEXDH_INIT / SSH2_MSG_KEXDH_GEX_INIT
mpint y */
status = length = \
readHSPacketSSH2( sessionInfoPtr,
( handshakeInfo->requestedServerKeySize > 0 ) ? \
SSH2_MSG_KEXDH_GEX_INIT : SSH2_MSG_KEXDH_INIT,
ID_SIZE + sizeofString32( "", MIN_PKCSIZE ) );
if( cryptStatusError( status ) )
return( status );
sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
sgetc( &stream ); /* Skip packet type */
status = readRawObject32( &stream, handshakeInfo->clientKeyexValue,
CRYPT_MAX_PKCSIZE + 16,
&handshakeInfo->clientKeyexValueLength );
sMemDisconnect( &stream );
if( cryptStatusError( status ) || \
!isValidDHsize( handshakeInfo->clientKeyexValueLength,
handshakeInfo->serverKeySize, LENGTH_SIZE ) )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid DH phase 1 keyex value" ) );
}
return( CRYPT_OK );
}
/* Exchange keys with the client */
static int exchangeServerKeys( SESSION_INFO *sessionInfoPtr,
SSH_HANDSHAKE_INFO *handshakeInfo )
{
KEYAGREE_PARAMS keyAgreeParams;
STREAM stream;
void *keyPtr = DUMMY_INIT_PTR, *dataPtr;
int keyLength, dataLength, sigLength = DUMMY_INIT, packetOffset, status;
/* Create the server DH value */
memset( &keyAgreeParams, 0, sizeof( KEYAGREE_PARAMS ) );
status = krnlSendMessage( handshakeInfo->iServerCryptContext,
IMESSAGE_CTX_ENCRYPT, &keyAgreeParams,
sizeof( KEYAGREE_PARAMS ) );
if( cryptStatusError( status ) )
return( status );
sMemOpen( &stream, handshakeInfo->serverKeyexValue,
sizeof( handshakeInfo->serverKeyexValue ) );
status = writeInteger32( &stream, keyAgreeParams.publicValue,
keyAgreeParams.publicValueLen );
if( cryptStatusOK( status ) )
handshakeInfo->serverKeyexValueLength = stell( &stream );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
return( status );
/* Build 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
...
The specification also makes provision for using X.509 and PGP keys,
but only so far as to say that keys and signatures are in "X.509 DER"
and "PGP" formats, neither of which actually explain what it is
that's sent or signed (and no-one on the SSH list can agree on what
they're supposed to look like), so we can't use either of them */
status = openPacketStreamSSH( &stream, sessionInfoPtr, CRYPT_USE_DEFAULT,
handshakeInfo->requestedServerKeySize ? \
SSH2_MSG_KEXDH_GEX_REPLY : \
SSH2_MSG_KEXDH_REPLY );
if( cryptStatusError( status ) )
return( status );
streamBookmarkSet( &stream, keyLength );
status = exportAttributeToStream( &stream, sessionInfoPtr->privateKey,
CRYPT_IATTRIBUTE_KEY_SSH );
if( cryptStatusOK( status ) )
status = streamBookmarkComplete( &stream, &keyPtr, &keyLength,
keyLength );
if( cryptStatusOK( status ) )
status = krnlSendMessage( handshakeInfo->iExchangeHashcontext,
IMESSAGE_CTX_HASH, keyPtr, keyLength );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
swrite( &stream, handshakeInfo->serverKeyexValue,
handshakeInfo->serverKeyexValueLength );
/* Complete phase 2 of the DH key agreement process to obtain the shared
secret value */
status = completeKeyex( sessionInfoPtr, handshakeInfo, TRUE );
if( cryptStatusError( status ) )
return( status );
/* Sign the hash. The reason for the min() part of the expression is
that iCryptCreateSignature() gets suspicious of very large buffer
sizes, for example when the user has specified the use of a 1MB send
buffer */
status = sMemGetDataBlockRemaining( &stream, &dataPtr, &dataLength );
if( cryptStatusOK( status ) )
{
status = iCryptCreateSignature( dataPtr,
min( dataLength, MAX_INTLENGTH_SHORT - 1 ),
&sigLength, CRYPT_IFORMAT_SSH,
sessionInfoPtr->privateKey,
handshakeInfo->iExchangeHashcontext,
CRYPT_UNUSED, CRYPT_UNUSED );
}
krnlSendNotifier( handshakeInfo->iExchangeHashcontext,
IMESSAGE_DECREFCOUNT );
handshakeInfo->iExchangeHashcontext = CRYPT_ERROR;
if( cryptStatusOK( status ) )
status = sSkip( &stream, sigLength );
if( cryptStatusOK( status ) )
status = wrapPacketSSH2( sessionInfoPtr, &stream, 0, FALSE, TRUE );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
/* Build our change cipherspec message and send the whole mess through
to the client:
...
byte type = SSH2_MSG_NEWKEYS.
After this point the write channel is in the secure state */
status = continuePacketStreamSSH( &stream, SSH2_MSG_NEWKEYS,
&packetOffset );
if( cryptStatusOK( status ) )
status = wrapPacketSSH2( sessionInfoPtr, &stream, packetOffset,
FALSE, TRUE );
if( cryptStatusOK( status ) )
status = sendPacketSSH2( sessionInfoPtr, &stream, TRUE );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
return( status );
sessionInfoPtr->flags |= SESSION_ISSECURE_WRITE;
return( CRYPT_OK );
}
/* Complete the handshake with the client */
static int completeServerHandshake( SESSION_INFO *sessionInfoPtr,
SSH_HANDSHAKE_INFO *handshakeInfo )
{
STREAM stream;
int length, iterationCount = 0, status = CRYPT_OK;
/* If this is the first time through, set up the security info and wait
for the first part of the authentication */
if( !( sessionInfoPtr->flags & SESSION_PARTIALOPEN ) )
{
BYTE stringBuffer[ CRYPT_MAX_TEXTSIZE + 8 ];
int stringLength;
/* Set up the security information required for the session */
status = initSecurityInfo( sessionInfoPtr, handshakeInfo );
if( cryptStatusError( status ) )
return( status );
/* Wait for the client's change cipherspec message. From this point
on the read channel is in the secure state */
status = readHSPacketSSH2( sessionInfoPtr, SSH2_MSG_NEWKEYS,
ID_SIZE );
if( cryptStatusError( status ) )
return( status );
sessionInfoPtr->flags |= SESSION_ISSECURE_READ;
/* Wait for the client's authentication packets. For some reason
SSHv2 requires the use of two authentication messages, an "I'm
about to authenticate" packet and an "I'm authenticating" packet.
First we handle the "I'm about to authenticate":
byte type = SSH2_MSG_SERVICE_REQUEST
string service_name = "ssh-userauth"
byte type = SSH2_MSG_SERVICE_ACCEPT
string service_name = "ssh-userauth" */
status = length = \
readHSPacketSSH2( sessionInfoPtr, SSH2_MSG_SERVICE_REQUEST,
ID_SIZE + sizeofString32( "", 8 ) );
if( cryptStatusError( status ) )
return( status );
sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
sgetc( &stream ); /* Skip packet type */
status = readString32( &stream, stringBuffer, CRYPT_MAX_TEXTSIZE,
&stringLength );
sMemDisconnect( &stream );
if( cryptStatusError( status ) || \
stringLength != 12 || memcmp( stringBuffer, "ssh-userauth", 12 ) )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid service request packet" ) );
}
status = openPacketStreamSSH( &stream, sessionInfoPtr,
CRYPT_USE_DEFAULT,
SSH2_MSG_SERVICE_ACCEPT );
if( cryptStatusError( status ) )
return( status );
status = writeString32( &stream, "ssh-userauth", 12 );
if( cryptStatusOK( status ) )
status = sendPacketSSH2( sessionInfoPtr, &stream, FALSE );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
return( status );
}
/* Wait for the second part of the authentication, optionally letting the
caller determine whether to allow the authentication or not */
do
{
SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
/* If we don't have authentication info ready to act upon,
read it now */
if( !sshInfo->authRead )
{
int retryCount;
/* Since the userAuth negotiation can (in theory) go on
indefinitely, we limit it to three iterations to avoid
potential DoS problems */
for( retryCount = 0; status != OK_SPECIAL && retryCount < 3;
retryCount++ )
{
status = processUserAuth( sessionInfoPtr, handshakeInfo );
if( cryptStatusError( status ) && status != OK_SPECIAL )
return( status );
}
if( retryCount >= 3 )
{
retExt( CRYPT_ERROR_PERMISSION,
( CRYPT_ERROR_PERMISSION, SESSION_ERRINFO,
"Too many iterations of negotiation during user "
"auth request processing" ) );
}
/* We got a userAuth request, if the caller will handle it, let
them know that they have to react on it */
sshInfo->authRead = TRUE;
if( sessionInfoPtr->authResponse == CRYPT_UNUSED )
return( CRYPT_ENVELOPE_RESOURCE );
}
/* Acknowledge the authentication:
byte type = SSH2_MSG_USERAUTH_SUCCESS
or
byte type = SSH2_MSG_USERAUTH_FAILURE
string allowed_authent
boolean partial_success = FALSE */
status = openPacketStreamSSH( &stream, sessionInfoPtr,
CRYPT_USE_DEFAULT,
sessionInfoPtr->authResponse ? \
SSH2_MSG_USERAUTH_SUCCESS : \
SSH2_MSG_USERAUTH_FAILURE );
if( cryptStatusError( status ) )
return( status );
if( !sessionInfoPtr->authResponse )
{
/* If it was a failed auth, tell the client what their options
are */
writeAlgoList( &stream, algoStringUserauthentList );
status = sputc( &stream, 0 );
}
if( cryptStatusOK( status ) )
status = sendPacketSSH2( sessionInfoPtr, &stream, FALSE );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
return( status );
/* If the caller denied the authentication, go back to asking what
to do on the next authentication attempt */
if( sessionInfoPtr->authResponse == FALSE )
sessionInfoPtr->authResponse = CRYPT_UNUSED;
sshInfo->authRead = FALSE;
}
while( sessionInfoPtr->authResponse != TRUE && \
iterationCount++ < FAILSAFE_ITERATIONS_MED );
if( iterationCount >= FAILSAFE_ITERATIONS_MED )
retIntError();
/* Handle the channel open */
status = length = \
readHSPacketSSH2( sessionInfoPtr, SSH2_MSG_CHANNEL_OPEN,
ID_SIZE + sizeofString32( "", 4 ) + \
UINT32_SIZE + UINT32_SIZE + UINT32_SIZE );
if( cryptStatusError( status ) )
return( status );
sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
assert( sPeek( &stream ) == SSH2_MSG_CHANNEL_OPEN );
status = sgetc( &stream ); /* Skip packet type */
if( !cryptStatusError( status ) )
status = processChannelOpen( sessionInfoPtr, &stream );
sMemDisconnect( &stream );
#if 1
return( status );
#else /* If we handle the following inline as part of the general read code
it requires that the user try and read some data (with a non-zero
timeout) right after the connect completes. Because it's awkward
to have to rely on this, we provide optional code to explicitly
clear the pipe here. This code stops as soon as the first data
channel-opening request is received, with further requests being
handled inline as part of the standard data-read handling. The
reason why this isn't enabled by default is that it's possible to
encounter a client that doesn't send anything beyond the initial
channel open, which means that we'd hang around waiting for a
control message until we time out */
if( cryptStatusError( status ) )
return( status );
/* Process any further junk that the caller may throw at us until we get
a request that we can handle, indicated by an OK_SPECIAL response */
do
{
status = length = \
readHSPacketSSH2( sessionInfoPtr, SSH2_MSG_SPECIAL_REQUEST, 8 );
if( !cryptStatusError( status ) )
{
sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
sgetc( &stream );
status = processChannelControlMessage( sessionInfoPtr, &stream );
sMemDisconnect( &stream );
}
}
while( cryptStatusOK( status ) );
return( ( status == OK_SPECIAL ) ? CRYPT_OK : status );
#endif /* 1 */
}
/****************************************************************************
* *
* Session Access Routines *
* *
****************************************************************************/
void initSSH2serverProcessing( SESSION_INFO *sessionInfoPtr,
SSH_HANDSHAKE_INFO *handshakeInfo )
{
UNUSED_ARG( sessionInfoPtr );
handshakeInfo->beginHandshake = beginServerHandshake;
handshakeInfo->exchangeKeys = exchangeServerKeys;
handshakeInfo->completeHandshake = completeServerHandshake;
}
#endif /* USE_SSH */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -