📄 ssh2_svr.c
字号:
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid user auth user name" ) );
}
attributeListPtr = findSessionInfo( sessionInfoPtr->attributeList,
CRYPT_SESSINFO_USERNAME );
if( attributeListPtr != NULL )
{
/* There's user name info present, make sure that the newly-
submitted one matches one of the existing ones */
attributeListPtr = \
findSessionInfoEx( attributeListPtr,
CRYPT_SESSINFO_USERNAME,
userNameBuffer, userNameLength );
if( attributeListPtr == NULL )
{
sMemDisconnect( &stream );
if( attributeListPtr == NULL )
{
retExt( CRYPT_ERROR_WRONGKEY,
( CRYPT_ERROR_WRONGKEY, SESSION_ERRINFO,
"Unknown user name '%s'",
sanitiseString( userNameBuffer, CRYPT_MAX_TEXTSIZE,
userNameLength ) ) );
}
}
/* We've matched an existing user name, select the attribute that
contains it */
sessionInfoPtr->attributeListCurrent = \
( ATTRIBUTE_LIST * ) attributeListPtr;
/* If it's just a saved name that was entered during a previous
round of the authentication process (so there's no associated
password) then we treat it as a newly-entered name. Otherwise,
it's a match to a caller-supplied list of allowed { username,
password } pairs, and we move on to the corresponding password */
if( attributeListPtr->next != NULL )
{
/* Move on to the associated password */
attributeListPtr = attributeListPtr->next;
if( attributeListPtr->attributeID != CRYPT_SESSINFO_PASSWORD )
retIntError();
/* If it's a caller-supplied name, remember to check it later */
if( !( attributeListPtr->flags & ATTR_FLAG_EPHEMERAL ) )
userNamePresent = TRUE;
}
}
else
{
status = addSessionInfo( &sessionInfoPtr->attributeList,
CRYPT_SESSINFO_USERNAME,
userNameBuffer, userNameLength );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
retExt( status,
( status, SESSION_ERRINFO,
"Error recording user name '%s'",
sanitiseString( userNameBuffer, CRYPT_MAX_TEXTSIZE,
userNameLength ) ) );
}
}
/* Get the service name and authentication method name, either
"password" or "none" */
status = readString32( &stream, stringBuffer, CRYPT_MAX_TEXTSIZE,
&stringLength );
if( cryptStatusError( status ) || \
stringLength != 14 || memcmp( stringBuffer, "ssh-connection", 14 ) )
{
sMemDisconnect( &stream );
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid user auth service name" ) );
}
status = readString32( &stream, stringBuffer, CRYPT_MAX_TEXTSIZE,
&stringLength );
if( cryptStatusError( status ) || \
stringLength <= 0 || stringLength > CRYPT_MAX_TEXTSIZE )
{
sMemDisconnect( &stream );
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid user auth method name" ) );
}
if( !( ( stringLength == 4 && \
!memcmp( stringBuffer, "none", 4 ) ) || \
( stringLength == 8 && \
!memcmp( stringBuffer, "password", 8 ) ) ) )
{
sMemDisconnect( &stream );
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Unknown user auth method name '%s'",
sanitiseString( stringBuffer, CRYPT_MAX_TEXTSIZE,
stringLength ) ) );
}
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 );
status = openPacketStreamSSH( &stream, sessionInfoPtr,
CRYPT_USE_DEFAULT,
SSH2_MSG_USERAUTH_FAILURE );
if( cryptStatusError( status ) )
return( status );
writeAlgoList( &stream, algoStringUserauthentList );
status = sputc( &stream, 0 );
if( cryptStatusOK( status ) )
status = sendPacketSSH2( sessionInfoPtr, &stream, FALSE );
sMemDisconnect( &stream );
return( status );
}
/* The client has asked for password auth, either check the password
against the one we have for this user or save the info for the caller
to check */
status = readString32( &stream, stringBuffer, CRYPT_MAX_TEXTSIZE,
&stringLength );
sMemDisconnect( &stream );
if( cryptStatusError( status ) || \
stringLength <= 0 || stringLength > CRYPT_MAX_TEXTSIZE )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid user auth payload" ) );
}
if( userNamePresent )
{
if( stringLength != attributeListPtr->valueLength || \
memcmp( stringBuffer, attributeListPtr->value, stringLength ) )
{
retExt( CRYPT_ERROR_WRONGKEY,
( CRYPT_ERROR_WRONGKEY, SESSION_ERRINFO,
"Invalid password for user '%s'",
sanitiseString( userNameBuffer, CRYPT_MAX_TEXTSIZE,
userNameLength ) ) );
}
}
else
{
/* If it's a password from the client, we make it an ephemeral
attribute since they could try and re-enter it on a sunsequent
iteration if we tell them that it's incorrect */
status = updateSessionInfo( &sessionInfoPtr->attributeList,
CRYPT_SESSINFO_PASSWORD,
stringBuffer, stringLength,
CRYPT_MAX_TEXTSIZE, ATTR_FLAG_EPHEMERAL );
if( cryptStatusError( status ) )
{
retExt( status,
( status, SESSION_ERRINFO,
"Error recording password for user '%s'",
sanitiseString( userNameBuffer, CRYPT_MAX_TEXTSIZE,
userNameLength ) ) );
}
}
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 ALGO_STRING_INFO FAR_BSS algoStringPubkeyRSATbl[] = {
{ "ssh-rsa", 7, CRYPT_ALGO_RSA },
{ NULL, CRYPT_ALGO_NONE }, { NULL, CRYPT_ALGO_NONE }
};
static const ALGO_STRING_INFO FAR_BSS algoStringPubkeyDSATbl[] = {
{ "ssh-dss", 7, CRYPT_ALGO_DSA },
{ NULL, CRYPT_ALGO_NONE }, { NULL, CRYPT_ALGO_NONE }
};
STREAM stream;
void *serverHelloPtr = DUMMY_INIT_PTR;
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;
handshakeInfo->algoStringPubkeyTblNoEntries = \
FAILSAFE_ARRAYSIZE( algoStringPubkeyRSATbl, ALGO_STRING_INFO );
break;
case CRYPT_ALGO_DSA:
handshakeInfo->algoStringPubkeyTbl = algoStringPubkeyDSATbl;
handshakeInfo->algoStringPubkeyTblNoEntries = \
FAILSAFE_ARRAYSIZE( algoStringPubkeyDSATbl, ALGO_STRING_INFO );
break;
default:
retIntError();
}
/* 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 */
status = hashAsString( handshakeInfo->iExchangeHashcontext,
sessionInfoPtr->receiveBuffer,
strlen( sessionInfoPtr->receiveBuffer ) );
if( cryptStatusOK( status ) )
status = hashAsString( handshakeInfo->iExchangeHashcontext,
SSH2_ID_STRING, SSH_ID_STRING_SIZE );
if( cryptStatusError( status ) )
return( status );
/* 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 */
status = openPacketStreamSSH( &stream, sessionInfoPtr, CRYPT_USE_DEFAULT,
SSH2_MSG_KEXINIT );
if( cryptStatusError( status ) )
return( status );
streamBookmarkSetFullPacket( &stream, serverHelloLength );
status = exportVarsizeAttributeToStream( &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 */
if( cryptStatusOK( status ) )
status = writeUint32( &stream, 0 ); /* Reserved */
if( cryptStatusOK( status ) )
{
status = streamBookmarkComplete( &stream, &serverHelloPtr,
&serverHelloLength,
serverHelloLength );
}
if( cryptStatusOK( status ) )
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 */
status = hashAsString( handshakeInfo->iExchangeHashcontext,
sessionInfoPtr->receiveBuffer,
clientHelloLength );
if( cryptStatusOK( status ) )
{
status = readHSPacketSSH2( sessionInfoPtr,
( handshakeInfo->requestedServerKeySize > 0 ) ? \
SSH2_MSG_KEXDH_GEX_INIT : SSH2_MSG_KEXDH_INIT,
ID_SIZE + sizeofString32( "", MIN_PKCSIZE ) );
}
}
}
if( !cryptStatusError( status ) ) /* rHSPSSH2() 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -