📄 ssh.c
字号:
sessionInfoPtr->protocolFlags |= SSH_PFLAG_TEXTDIAGS;
}
if( !memcmp( versionStringPtr, "3.0 SecureCRT", 13 ) || \
!memcmp( versionStringPtr, "1.7 SecureFX", 12 ) )
sessionInfoPtr->protocolFlags |= SSH_PFLAG_NOHASHLENGTH;
if( !memcmp( versionStringPtr, "1.0", 3 ) )
sessionInfoPtr->protocolFlags |= SSH_PFLAG_CUTEFTP;
return( CRYPT_OK );
}
/****************************************************************************
* *
* 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 );
#ifdef USE_SSH1
if( sessionInfoPtr->version == 1 )
{
initSSH1processing( sessionInfoPtr, handshakeInfo,
( sessionInfoPtr->flags & SESSION_ISSERVER) ? \
TRUE : FALSE );
sessionInfoPtr->sendBufStartOfs = \
sessionInfoPtr->receiveBufStartOfs = \
sessionInfoPtr->protocolInfo->sendBufStartOfs;
return( CRYPT_OK );
}
#endif /* USE_SSH1 */
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 ) )
{
/* If we run into an error at this point we need to disable error-
reporting during the shutdown phase since we've already got
error information present from the already-encountered error */
destroyHandshakeInfo( &handshakeInfo );
sessionInfoPtr->flags |= SESSION_NOREPORTERROR;
sessionInfoPtr->shutdownFunction( sessionInfoPtr );
return( status );
}
/* Exchange a key with the server */
status = handshakeInfo.exchangeKeys( sessionInfoPtr, &handshakeInfo );
if( cryptStatusError( status ) )
{
destroySecurityContextsSSH( sessionInfoPtr );
destroyHandshakeInfo( &handshakeInfo );
sessionInfoPtr->flags |= SESSION_NOREPORTERROR;
sessionInfoPtr->shutdownFunction( sessionInfoPtr );
return( status );
}
/* Complete the handshake */
status = handshakeInfo.completeHandshake( sessionInfoPtr,
&handshakeInfo );
destroyHandshakeInfo( &handshakeInfo );
if( cryptStatusError( status ) )
{
/* If we need confirmation from the user before continuing, let
them know */
if( status == CRYPT_ENVELOPE_RESOURCE )
return( 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->flags |= SESSION_NOREPORTERROR;
sessionInfoPtr->shutdownFunction( sessionInfoPtr );
destroySecurityContextsSSH( sessionInfoPtr );
return( status );
}
return( CRYPT_OK );
}
/* 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;
/* If we're completing a handshake that was interrupted while we got
confirmation of the client auth, skip the initial handshake stages
and go straight to the handshake completion stage */
if( sessionInfoPtr->flags & SESSION_PARTIALOPEN )
{
SSH_HANDSHAKE_INFO handshakeInfo;
initHandshakeInfo( &handshakeInfo );
initSSH2processing( sessionInfoPtr, &handshakeInfo, TRUE );
status = handshakeInfo.completeHandshake( sessionInfoPtr,
&handshakeInfo );
destroyHandshakeInfo( &handshakeInfo );
return( 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 )
{
int status;
assert( type == CRYPT_SESSINFO_SSH_CHANNEL ||\
type == CRYPT_SESSINFO_SSH_CHANNEL_TYPE || \
type == CRYPT_SESSINFO_SSH_CHANNEL_ARG1 || \
type == CRYPT_SESSINFO_SSH_CHANNEL_ARG2 || \
type == CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE );
if( type == CRYPT_SESSINFO_SSH_CHANNEL || \
type == CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE )
status = getChannelAttribute( sessionInfoPtr, type,
NULL, data );
else
{
RESOURCE_DATA *msgData = data;
status = getChannelAttribute( sessionInfoPtr, type,
msgData->data, &msgData->length );
}
return( ( status == CRYPT_ERROR ) ? CRYPT_ARGERROR_NUM1 : status );
}
static int setAttributeFunction( SESSION_INFO *sessionInfoPtr,
const void *data,
const CRYPT_ATTRIBUTE_TYPE type )
{
int status;
assert( type == CRYPT_SESSINFO_SSH_CHANNEL ||\
type == CRYPT_SESSINFO_SSH_CHANNEL_TYPE || \
type == CRYPT_SESSINFO_SSH_CHANNEL_ARG1 || \
type == CRYPT_SESSINFO_SSH_CHANNEL_ARG2 || \
type == CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE );
/* If we're selecting a channel and there's unwritten data from a
previous write still in the buffer, we can't change the write
channel */
if( type == CRYPT_SESSINFO_SSH_CHANNEL && sessionInfoPtr->partialWrite )
return( CRYPT_ERROR_INCOMPLETE );
/* If we're creating a new channel by setting the value to CRYPT_UNUSED,
create the new channel */
if( type == CRYPT_SESSINFO_SSH_CHANNEL && \
*( int * ) data == CRYPT_UNUSED )
{
/* If the session hasn't been activated yet, we can only create a
single channel during session activation, any subsequent ones
have to be handled later */
if( !( sessionInfoPtr->flags & SESSION_ISOPEN ) && \
getCurrentChannelNo( sessionInfoPtr, \
CHANNEL_READ ) != UNUSED_CHANNEL_NO )
return( CRYPT_ERROR_INITED );
return( createChannel( sessionInfoPtr ) );
}
/* If we 're setting the channel-active attribute, this implicitly
activates or deactivates the channel rather than setting any
attribute value */
if( type == CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE )
{
if( *( int * ) data )
return( sendChannelOpen( sessionInfoPtr ) );
return( closeChannel( sessionInfoPtr, FALSE ) );
}
if( type == CRYPT_SESSINFO_SSH_CHANNEL )
status = setChannelAttribute( sessionInfoPtr, type,
NULL, *( int * ) data );
else
{
const RESOURCE_DATA *msgData = data;
status = setChannelAttribute( sessionInfoPtr, type,
msgData->data, msgData->length );
}
return( ( status == CRYPT_ERROR ) ? CRYPT_ARGERROR_NUM1 : status );
}
static int checkAttributeFunction( SESSION_INFO *sessionInfoPtr,
const CRYPT_HANDLE cryptHandle,
const CRYPT_ATTRIBUTE_TYPE type )
{
HASHFUNCTION hashFunction;
STREAM stream;
BYTE buffer[ 128 + ( CRYPT_MAX_PKCSIZE * 4 ) ];
BYTE fingerPrint[ CRYPT_MAX_HASHSIZE ];
int length, hashSize, status;
if( type != CRYPT_SESSINFO_PRIVATEKEY )
return( CRYPT_OK );
/* Only the server key has a fingerprint */
if( !( sessionInfoPtr->flags & SESSION_ISSERVER ) )
return( CRYPT_OK );
getHashParameters( CRYPT_ALGO_MD5, &hashFunction, &hashSize );
/* The fingerprint is computed from the "key blob", which is different
from the server key. The server key is the full key, while the "key
blob" is only the raw key components (e, n for RSA, p, q, g, y for
DSA), so we have to skip the key header before we hash the key data.
Note that, as with the old PGP 2.x key hash mechanism, this allows
key spoofing (although it isn't quite as bad as the PGP 2.x key
fingerprint mechanism) since it doesn't hash an indication of the key
type or format */
sMemOpen( &stream, buffer, 128 + ( CRYPT_MAX_PKCSIZE * 4 ) );
status = exportAttributeToStream( &stream, cryptHandle,
CRYPT_IATTRIBUTE_KEY_SSH2,
CRYPT_USE_DEFAULT );
if( cryptStatusError( status ) )
return( status );
length = stell( &stream );
sseek( &stream, 0 );
readUint32( &stream ); /* Length */
status = readUniversal32( &stream ); /* Algorithm ID */
if( cryptStatusOK( status ) )
hashFunction( NULL, fingerPrint, sMemBufPtr( &stream ),
length - stell( &stream ), HASH_ALL );
sMemClose( &stream );
if( cryptStatusError( status ) )
return( status );
/* Add the fingerprint */
return( addSessionAttribute( &sessionInfoPtr->attributeList,
CRYPT_SESSINFO_SERVER_FINGERPRINT,
fingerPrint, hashSize ) );
}
/****************************************************************************
* *
* 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;
sessionInfoPtr->checkAttributeFunction = checkAttributeFunction;
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 + -