📄 ssh2_svr.c
字号:
CRYPT_SESSINFO_USERNAME, stringBuffer,
stringLength, CRYPT_MAX_TEXTSIZE,
ATTR_FLAG_NONE );
/* Get the service name and authentication method name, either
"password" or "none" */
status = readString32( &stream, stringBuffer, &stringLength,
CRYPT_MAX_TEXTSIZE );
if( cryptStatusError( status ) || \
stringLength != 14 || memcmp( stringBuffer, "ssh-connection", 14 ) )
{
sMemDisconnect( &stream );
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid user auth service name" );
}
status = readString32( &stream, stringBuffer, &stringLength,
CRYPT_MAX_TEXTSIZE );
if( cryptStatusError( status ) || \
stringLength <= 0 || stringLength > CRYPT_MAX_TEXTSIZE )
{
sMemDisconnect( &stream );
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid user auth method name" );
}
if( !( ( stringLength == 4 && \
!memcmp( stringBuffer, "none", 4 ) ) || \
( stringLength == 8 && \
!memcmp( stringBuffer, "password", 8 ) ) ) )
{
sMemDisconnect( &stream );
stringBuffer[ stringLength ] = '\0';
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Unknown user auth method name '%s'",
sanitiseString( stringBuffer ) );
}
sgetc( &stream ); /* Skip boolean flag */
/* If the client wants a list of supported authentication mechanisms
(indicated by sending the method name "none" of length 4), tell them
what we allow and await further input:
byte type = SSH2_MSG_USERAUTH_FAILURE
string allowed_authent
boolean partial_success = FALSE */
if( stringLength == 4 )
{
sMemDisconnect( &stream );
openPacketStreamSSH( &stream, sessionInfoPtr, CRYPT_USE_DEFAULT,
SSH2_MSG_USERAUTH_FAILURE );
writeAlgoList( &stream, algoStringUserauthentList );
sputc( &stream, 0 );
status = sendPacketSSH2( sessionInfoPtr, &stream, FALSE );
sMemDisconnect( &stream );
return( CRYPT_OK );
}
/* The client has asked for password auth, save the authentication info
for the caller to check */
status = readString32( &stream, stringBuffer, &stringLength,
CRYPT_MAX_TEXTSIZE );
sMemDisconnect( &stream );
if( cryptStatusError( status ) || \
stringLength <= 0 || stringLength > CRYPT_MAX_TEXTSIZE )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid user auth payload" );
updateSessionAttribute( &sessionInfoPtr->attributeList,
CRYPT_SESSINFO_PASSWORD, stringBuffer,
stringLength, CRYPT_MAX_TEXTSIZE,
ATTR_FLAG_NONE );
return( OK_SPECIAL );
}
/****************************************************************************
* *
* Server-side Connect Functions *
* *
****************************************************************************/
/* Perform the initial part of the handshake with the client */
static int beginServerHandshake( SESSION_INFO *sessionInfoPtr,
SSH_HANDSHAKE_INFO *handshakeInfo )
{
static const FAR_BSS ALGO_STRING_INFO algoStringPubkeyRSATbl[] = {
{ "ssh-rsa", CRYPT_ALGO_RSA },
{ NULL, CRYPT_ALGO_NONE }
};
static const FAR_BSS ALGO_STRING_INFO algoStringPubkeyDSATbl[] = {
{ "ssh-dss", CRYPT_ALGO_DSA },
{ NULL, CRYPT_ALGO_NONE }
};
STREAM stream;
void *serverHelloPtr;
int length, serverHelloLength, clientHelloLength, status;
/* Get the public-key algorithm that we'll be advertising to the client
and set the algorithm table used for processing the client hello to
only match the one that we're offering */
status = krnlSendMessage( sessionInfoPtr->privateKey,
IMESSAGE_GETATTRIBUTE,
&handshakeInfo->pubkeyAlgo,
CRYPT_CTXINFO_ALGO );
if( cryptStatusError( status ) )
return( status );
switch( handshakeInfo->pubkeyAlgo )
{
case CRYPT_ALGO_RSA:
handshakeInfo->algoStringPubkeyTbl = algoStringPubkeyRSATbl;
break;
case CRYPT_ALGO_DSA:
handshakeInfo->algoStringPubkeyTbl = algoStringPubkeyDSATbl;
break;
default:
assert( NOTREACHED );
return( CRYPT_ERROR_NOTAVAIL );
}
/* SSHv2 hashes parts of the handshake messages for integrity-protection
purposes, so before we start we hash the ID strings (first the client
string that we read previously, then our server string) encoded as SSH
string values */
hashAsString( handshakeInfo->iExchangeHashcontext,
sessionInfoPtr->receiveBuffer,
strlen( sessionInfoPtr->receiveBuffer ) );
hashAsString( handshakeInfo->iExchangeHashcontext, SSH2_ID_STRING,
strlen( SSH2_ID_STRING ) );
/* Send the server hello packet:
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 = "none"
string server_compression algorithms = "none"
string client_language = ""
string server_language = ""
boolean first_keyex_packet_follows = FALSE
uint32 reserved = 0
The SSH spec leaves the order in which things happen ambiguous, in
order to save a while round trip it has provisions for both sides
shouting at each other and then a complex interlock process where
bits of the initial exchange can be discarded and retried if necessary.
This is ugly and error-prone. The client code solves this by waiting
for the server hello, choosing known-good algorithms, and then sending
the client hello immediately followed by the client key exchange data.
Since it waits for the server to speak first, it can choose parameters
that are accepted the first time.
Unfortunately, this doesn't work if we're the server, since we'd end
up waiting for the client to speak first while it waits for us to
speak first, so we have to send the server hello in order to prevent
deadlock. This works fine with most clients, which take the same
approach and wait for the server to speak first. The message flow is
then:
server hello;
client hello;
client keyex;
server keyex;
There are one or two exceptions to this, the worst of which is the
F-Secure client, which has the client speak first choosing as its
preference the incompletely specified "x509v3-sign-dss" format (see
the comment in exchangeServerKeys() below) that we can't use since no-
one's quite sure what the format is (this was fixed in mid-2004 when
the x509v3-* schemes were removed from the spec, since no-one could
figure out what they were. F-Secure still specifies them, but after
the standard ssh-* schemes). In this case the message flow is:
server hello;
client hello;
client keyex1;
client keyex2;
server keyex;
This is handled by having the code that reads the client hello return
OK_SPECIAL to indicate that the next packet should be skipped. An
alternative (and simpler) strategy would be to always throw away the
F-Secure client's first keyex, since it's using an algorithm choice
that's impossible to use */
openPacketStreamSSH( &stream, sessionInfoPtr, CRYPT_USE_DEFAULT,
SSH2_MSG_KEXINIT );
streamBookmarkSetFullPacket( &stream, serverHelloPtr,
serverHelloLength );
exportAttributeToStream( &stream, SYSTEM_OBJECT_HANDLE,
CRYPT_IATTRIBUTE_RANDOM_NONCE,
SSH2_COOKIE_SIZE );
writeAlgoList( &stream, algoKeyexList );
writeAlgoString( &stream, handshakeInfo->pubkeyAlgo );
writeAlgoList( &stream, algoEncrList );
writeAlgoList( &stream, algoEncrList );
writeAlgoList( &stream, algoMACList );
writeAlgoList( &stream, algoMACList );
writeAlgoString( &stream, CRYPT_PSEUDOALGO_COPR );
writeAlgoString( &stream, CRYPT_PSEUDOALGO_COPR );
writeUint32( &stream, 0 ); /* No language tag */
writeUint32( &stream, 0 );
sputc( &stream, 0 ); /* Don't try and guess the keyex */
writeUint32( &stream, 0 ); /* Reserved */
streamBookmarkComplete( &stream, serverHelloLength );
status = sendPacketSSH2( sessionInfoPtr, &stream, FALSE );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
return( status );
/* While we wait for the client to digest our hello and send back its
response, create the context with the DH key */
status = initDHcontextSSH( &handshakeInfo->iServerCryptContext,
&handshakeInfo->serverKeySize, NULL, 0,
CRYPT_USE_DEFAULT );
if( cryptStatusError( status ) )
return( status );
/* Process the client hello packet and hash the client and server
hello */
status = processHelloSSH( sessionInfoPtr, handshakeInfo,
&clientHelloLength, TRUE );
if( cryptStatusOK( status ) )
status = hashAsString( handshakeInfo->iExchangeHashcontext,
sessionInfoPtr->receiveBuffer,
clientHelloLength );
else
if( status == OK_SPECIAL )
{
/* There's an incorrectly-guessed keyex following the client
hello, skip it */
hashAsString( handshakeInfo->iExchangeHashcontext,
sessionInfoPtr->receiveBuffer, clientHelloLength );
status = readPacketSSH2( sessionInfoPtr,
( handshakeInfo->requestedServerKeySize > 0 ) ? \
SSH2_MSG_KEXDH_GEX_INIT : SSH2_MSG_KEXDH_INIT,
ID_SIZE + sizeofString32( "", bitsToBytes( MIN_PKCSIZE_BITS ) ) );
}
if( !cryptStatusError( status ) ) /* rPSSH2() returns a byte count */
status = hashAsString( handshakeInfo->iExchangeHashcontext,
serverHelloPtr, serverHelloLength );
if( cryptStatusError( status ) )
return( status );
/* If we're using a nonstandard DH key value, negotiate a new key with
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 */
length = readPacketSSH2( sessionInfoPtr,
( handshakeInfo->requestedServerKeySize > 0 ) ? \
SSH2_MSG_KEXDH_GEX_INIT : SSH2_MSG_KEXDH_INIT,
ID_SIZE + sizeofString32( "", bitsToBytes( MIN_PKCSIZE_BITS ) ) );
if( cryptStatusError( length ) )
return( length );
sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
sgetc( &stream ); /* Skip packet type */
status = readRawObject32( &stream, handshakeInfo->clientKeyexValue,
&handshakeInfo->clientKeyexValueLength,
sizeof( handshakeInfo->clientKeyexValue ) );
sMemDisconnect( &stream );
if( cryptStatusError( status ) || \
!isValidDHsize( handshakeInfo->clientKeyexValueLength,
handshakeInfo->serverKeySize, LENGTH_SIZE ) )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -