📄 ssh.c
字号:
OpenSSH:
Omits hashing the exchange hash length when creating the hash
to be signed for client auth for version 2.0 (all subversions).
ssh.com:
This implementation puts the version number first, so if we find
something without a vendor name at the start we treat it as an
ssh.com version. However, Van Dyke's SSH server VShell also
uses the ssh.com-style identification (fronti nulla fides), so
when we check for the ssh.com implementation we make sure that
it isn't really VShell. In addition CuteFTP advertises whatever
it's using as "1.x" (without and vendor name), which is going to
cause problems in the future if they ever move to 2.x of
whatever it is.
Omits the DH-derived shared secret when hashing the keying
material for versions identified as "2.0.0" (all
sub-versions) and "2.0.10" .
Uses an SSH2_FIXED_KEY_SIZE-sized key for HMAC instead of the de
facto 160 bits for versions identified as "2.0.", "2.1 ", "2.1.",
and "2.2." (i.e. all sub-versions of 2.0, 2.1, and 2.2), and
specifically version "2.3.0". This was fixed in 2.3.1.
Omits the signature algorithm name for versions identified as
"2.0" and "2.1" (all sub-versions).
Requires a window adjust for every 32K sent even if the window is
advertised as being (effectively) infinite in size for versions
identified as "2.0" and "2.1" (all sub-versions).
Omits hashing the exchange hash length when creating the hash
to be signed for client auth for versions 2.1 and 2.2 (all
subversions).
Dumps text diagnostics (that is, raw text strings rather than
SSH error packets) onto the connection if something unexpected
occurs, for uncertain versions probably in the 2.x range.
Van Dyke:
Omits hashing the exchange hash length when creating the hash to
be signed for client auth for version 3.0 (SecureCRT = SSH) and
1.7 (SecureFX = SFTP).
Further quirks and peculiarities exist, but fortunately these are rare
enough (mostly for SSHv1) that we don't have to go out of our way to
handle them */
if( !memcmp( versionStringPtr, "OpenSSH_", 8 ) )
{
const char *subVersionStringPtr = versionStringPtr + 8;
if( !memcmp( subVersionStringPtr, "2.0", 3 ) )
sessionInfoPtr->protocolFlags |= SSH_PFLAG_NOHASHLENGTH;
}
if( *versionStringPtr == '2' && \
strstr( versionStringPtr, "VShell" ) == NULL )
{
/* ssh.com 2.x versions have quite a number of bugs so we check for
them as a group */
if( !memcmp( versionStringPtr, "2.0.0", 5 ) || \
!memcmp( versionStringPtr, "2.0.10", 6 ) )
sessionInfoPtr->protocolFlags |= SSH_PFLAG_NOHASHSECRET;
if( !memcmp( versionStringPtr, "2.0", 3 ) || \
!memcmp( versionStringPtr, "2.1", 3 ) )
sessionInfoPtr->protocolFlags |= SSH_PFLAG_SIGFORMAT;
if( !memcmp( versionStringPtr, "2.0", 3 ) || \
!memcmp( versionStringPtr, "2.1", 3 ) )
sessionInfoPtr->protocolFlags |= SSH_PFLAG_WINDOWBUG;
if( !memcmp( versionStringPtr, "2.1", 3 ) || \
!memcmp( versionStringPtr, "2.2", 3 ) )
sessionInfoPtr->protocolFlags |= SSH_PFLAG_NOHASHLENGTH;
if( !memcmp( versionStringPtr, "2.0", 3 ) || \
!memcmp( versionStringPtr, "2.1", 3 ) || \
!memcmp( versionStringPtr, "2.2", 3 ) || \
!memcmp( versionStringPtr, "2.3.0", 5 ) )
sessionInfoPtr->protocolFlags |= SSH_PFLAG_HMACKEYSIZE;
if( !memcmp( versionStringPtr, "2.", 2 ) )
/* Not sure of the exact versions where this occurs */
sessionInfoPtr->protocolFlags |= SSH_PFLAG_TEXTDIAGS;
}
if( !memcmp( versionStringPtr, "3.0 SecureCRT", 13 ) || \
!memcmp( versionStringPtr, "1.7 SecureFX", 12 ) )
sessionInfoPtr->protocolFlags |= SSH_PFLAG_NOHASHLENGTH;
return( CRYPT_OK );
}
/* Encode a value as an SSH string */
int encodeString( BYTE *buffer, const BYTE *string, const int stringLength )
{
BYTE *bufPtr = buffer;
const int length = ( stringLength > 0 ) ? stringLength : strlen( string );
if( buffer != NULL )
{
mputLong( bufPtr, length );
memcpy( bufPtr, string, length );
}
return( LENGTH_SIZE + length );
}
/****************************************************************************
* *
* Init/Shutdown Functions *
* *
****************************************************************************/
/* Connect to an SSH server */
static int initVersion( SESSION_INFO *sessionInfoPtr,
SSH_HANDSHAKE_INFO *handshakeInfo )
{
MESSAGE_CREATEOBJECT_INFO createInfo;
int status;
/* Set up handshake function pointers based on the protocol version */
status = readVersionString( sessionInfoPtr );
if( cryptStatusError( status ) )
return( status );
if( sessionInfoPtr->version == 1 )
{
initSSH1processing( sessionInfoPtr, handshakeInfo,
( sessionInfoPtr->flags & SESSION_ISSERVER) ? \
TRUE : FALSE );
return( CRYPT_OK );
}
initSSH2processing( sessionInfoPtr, handshakeInfo,
( sessionInfoPtr->flags & SESSION_ISSERVER) ? \
TRUE : FALSE );
/* SSHv2 hashes parts of the handshake messages for integrity-protection
purposes, so if we're talking to an SSHv2 peer we create a context
for the hash */
setMessageCreateObjectInfo( &createInfo, CRYPT_ALGO_SHA );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_CREATEOBJECT,
&createInfo, OBJECT_TYPE_CONTEXT );
if( cryptStatusOK( status ) )
handshakeInfo->iExchangeHashcontext = createInfo.cryptHandle;
return( status );
}
static int completeStartup( SESSION_INFO *sessionInfoPtr )
{
SSH_HANDSHAKE_INFO handshakeInfo;
int status;
/* Initialise the handshake info and begin the handshake. Since we don't
know what type of peer we're talking to and since the protocols aren't
compatible in anything but name, we have to peek at the peer's initial
communication and redirect function pointers based on that */
status = initHandshakeInfo( &handshakeInfo );
if( cryptStatusOK( status ) )
status = initVersion( sessionInfoPtr, &handshakeInfo );
if( cryptStatusOK( status ) )
status = handshakeInfo.beginHandshake( sessionInfoPtr,
&handshakeInfo );
if( cryptStatusError( status ) )
{
destroyHandshakeInfo( &handshakeInfo );
sessionInfoPtr->shutdownFunction( sessionInfoPtr );
return( status );
}
/* Exchange a key with the server */
status = handshakeInfo.exchangeKeys( sessionInfoPtr, &handshakeInfo );
if( cryptStatusError( status ) )
{
destroySecurityContexts( sessionInfoPtr );
destroyHandshakeInfo( &handshakeInfo );
sessionInfoPtr->shutdownFunction( sessionInfoPtr );
return( status );
}
/* Complete the handshake */
status = handshakeInfo.completeHandshake( sessionInfoPtr,
&handshakeInfo );
destroyHandshakeInfo( &handshakeInfo );
if( cryptStatusError( status ) )
{
/* At this point we could be in the secure state, so we have to
keep the security info around until after we've called the
shutdown function, which could require sending secured data */
sessionInfoPtr->shutdownFunction( sessionInfoPtr );
destroySecurityContexts( sessionInfoPtr );
return( status );
}
sioctl( &sessionInfoPtr->stream, STREAM_IOCTL_HANDSHAKETIMEOUT, NULL, 0 );
return( status );
}
/* Start an SSH server */
static int serverStartup( SESSION_INFO *sessionInfoPtr )
{
const char *idString = ( sessionInfoPtr->version == 1 ) ? \
SSH1_ID_STRING "\n" : SSH2_ID_STRING "\r\n";
int status;
/* Send the ID string to the client before we continue with the
handshake. We don't have to wait for any input from the client since
we know that if we got here there's a client listening. Note that
standard cryptlib practice for sessions is to wait for input from the
client, make sure that it looks reasonable, and only then send back a
reply of any kind. If anything that doesn't look right arrives, we
close the connection immediately without any response. Unfortunately
this isn't possible with SSH, which requires that the server send data
before the client does */
status = swrite( &sessionInfoPtr->stream, idString, strlen( idString ) );
if( cryptStatusError( status ) )
return( status );
/* Complete the handshake in the shared code */
return( completeStartup( sessionInfoPtr ) );
}
/****************************************************************************
* *
* Control Information Management Functions *
* *
****************************************************************************/
static int getAttributeFunction( SESSION_INFO *sessionInfoPtr,
void *data, const CRYPT_ATTRIBUTE_TYPE type )
{
RESOURCE_DATA *msgData = data;
assert( type == CRYPT_SESSINFO_SSH_SUBSYSTEM || \
type == CRYPT_SESSINFO_SSH_PORTFORWARD );
switch( type )
{
case CRYPT_SESSINFO_SSH_SUBSYSTEM:
if( sessionInfoPtr->sshSubsystemLength <= 0 )
return( CRYPT_ERROR_NOTINITED );
return( attributeCopy( msgData, sessionInfoPtr->sshSubsystem,
sessionInfoPtr->sshSubsystemLength ) );
case CRYPT_SESSINFO_SSH_PORTFORWARD:
if( sessionInfoPtr->sshPortForwardLength <= 0 )
return( CRYPT_ERROR_NOTINITED );
return( attributeCopy( msgData, sessionInfoPtr->sshPortForward,
sessionInfoPtr->sshPortForwardLength ) );
}
assert( NOTREACHED );
return( CRYPT_ARGERROR_NUM1 );
}
static int setAttributeFunction( SESSION_INFO *sessionInfoPtr,
const void *data,
const CRYPT_ATTRIBUTE_TYPE type )
{
const RESOURCE_DATA *msgData = data;
assert( type == CRYPT_SESSINFO_SSH_SUBSYSTEM || \
type == CRYPT_SESSINFO_SSH_PORTFORWARD );
switch( type )
{
case CRYPT_SESSINFO_SSH_SUBSYSTEM:
if( sessionInfoPtr->sshSubsystemLength > 0 )
return( CRYPT_ERROR_INITED );
memcpy( sessionInfoPtr->sshSubsystem, msgData->data,
msgData->length );
sessionInfoPtr->sshSubsystemLength = msgData->length;
break;
case CRYPT_SESSINFO_SSH_PORTFORWARD:
{
URL_INFO urlInfo;
int status;
/* Make sure that we've been given a valid URL for forwarding */
if( sessionInfoPtr->sshPortForwardLength > 0 )
return( CRYPT_ERROR_INITED );
status = sNetParseURL( &urlInfo, msgData->data,
msgData->length );
if( cryptStatusError( status ) )
return( status );
memcpy( sessionInfoPtr->sshPortForward, msgData->data,
msgData->length );
sessionInfoPtr->sshPortForwardLength = msgData->length;
break;
}
default:
assert( NOTREACHED );
return( CRYPT_ARGERROR_NUM1 );
}
return( CRYPT_OK );
}
/****************************************************************************
* *
* Session Access Routines *
* *
****************************************************************************/
int setAccessMethodSSH( SESSION_INFO *sessionInfoPtr )
{
/* Set the access method pointers. Since the protocol version is
negotiable, we default to SSHv2, which is the one most commonly
used */
sessionInfoPtr->getAttributeFunction = getAttributeFunction;
sessionInfoPtr->setAttributeFunction = setAttributeFunction;
if( sessionInfoPtr->flags & SESSION_ISSERVER )
{
sessionInfoPtr->transactFunction = serverStartup;
initSSH2processing( sessionInfoPtr, NULL, TRUE );
}
else
{
sessionInfoPtr->transactFunction = completeStartup;
initSSH2processing( sessionInfoPtr, NULL, FALSE );
}
return( CRYPT_OK );
}
#endif /* USE_SSH1 || USE_SSH2 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -