📄 ssh2_svr.c
字号:
/****************************************************************************
* *
* cryptlib SSHv2 Session Management *
* Copyright Peter Gutmann 1998-2003 *
* *
****************************************************************************/
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
#include "crypt.h"
#include "session.h"
#include "ssh.h"
#elif defined( INC_CHILD )
#include "../crypt.h"
#include "../session/session.h"
#include "../session/ssh.h"
#else
#include "crypt.h"
#include "session/session.h"
#include "session/ssh.h"
#endif /* Compiler-specific includes */
#ifdef USE_SSH2
/****************************************************************************
* *
* Utility Functions *
* *
****************************************************************************/
/* SSHv2 algorithm names sent to client, in preferred algorithm order.
Since we have a fixed algorithm for our public key (determined by the key
type), we only send a single value for this that's evaluated at runtime,
so there's no list for this defined.
Note that these tables must match the algoStringXXXTbl values in ssh2.c */
static const FAR_BSS CRYPT_ALGO_TYPE algoKeyexList[] = {
CRYPT_ALGO_DH, CRYPT_ALGO_NONE };
static const FAR_BSS char *algoStringCoprList = "none";
static const FAR_BSS CRYPT_ALGO_TYPE algoEncrList[] = {
/* We can't list AES as an option because the peer can pick up anything
it wants from the list as its preferred choice, which means that if
we're talking to any non-cryptlib implementation they always go for
AES even though it doesn't currently have the provenance of 3DES.
Once AES passes the five-year test this option can be enabled */
CRYPT_ALGO_3DES, /*CRYPT_ALGO_AES,*/ CRYPT_ALGO_BLOWFISH,
CRYPT_ALGO_CAST, CRYPT_ALGO_IDEA, CRYPT_ALGO_RC4, CRYPT_ALGO_NONE };
static const FAR_BSS CRYPT_ALGO_TYPE algoMACList[] = {
CRYPT_ALGO_HMAC_SHA, CRYPT_ALGO_HMAC_MD5, CRYPT_ALGO_NONE };
static const FAR_BSS char *algoStringUserauthentList = "password";
/* Encode a list of available algorithms */
static int putAlgoList( BYTE **bufPtrPtr, const CRYPT_ALGO_TYPE algoList[] )
{
static const FAR_BSS ALGO_STRING_INFO algoStringMapTbl[] = {
{ "ssh-rsa", CRYPT_ALGO_RSA },
{ "ssh-dss", CRYPT_ALGO_DSA },
{ "3des-cbc", CRYPT_ALGO_3DES },
{ "aes128-cbc", CRYPT_ALGO_AES },
{ "blowfish-cbc", CRYPT_ALGO_BLOWFISH },
{ "cast128-cbc", CRYPT_ALGO_CAST },
{ "idea-cbc", CRYPT_ALGO_IDEA },
{ "arcfour", CRYPT_ALGO_RC4 },
{ "diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1", CRYPT_ALGO_DH },
{ "diffie-hellman-group1-sha1", CRYPT_ALGO_DH },
{ "hmac-sha1", CRYPT_ALGO_HMAC_SHA },
{ "hmac-md5", CRYPT_ALGO_HMAC_MD5 },
{ "none", CRYPT_ALGO_NONE },
};
const char *availableAlgos[ 16 ];
int noAlgos = 0, length = 0, algoIndex;
/* Walk down the list of algorithms remembering the encoded name of each
one that's available for use */
for( algoIndex = 0; algoList[ algoIndex ] != CRYPT_ALGO_NONE; algoIndex++ )
if( algoAvailable( algoList[ algoIndex ] ) )
{
int i;
for( i = 0; algoStringMapTbl[ i ].algo != CRYPT_ALGO_NONE && \
algoStringMapTbl[ i ].algo != algoList[ algoIndex ]; i++ );
assert( algoStringMapTbl[ i ].algo != CRYPT_ALGO_NONE );
availableAlgos[ noAlgos++ ] = algoStringMapTbl[ i ].name;
length += strlen( algoStringMapTbl[ i ].name );
if( noAlgos > 1 )
length++; /* Room for comma delimiter */
}
/* Encode the list of available algorithms into a comma-separated string */
if( bufPtrPtr != NULL )
{
BYTE *bufPtr = *bufPtrPtr;
mputLong( bufPtr, length );
for( algoIndex = 0; algoIndex < noAlgos; algoIndex++ )
{
const int algoLen = strlen( availableAlgos[ algoIndex ] );
if( algoIndex > 0 )
*bufPtr++ = ','; /* Add comma delimiter */
memcpy( bufPtr, availableAlgos[ algoIndex ], algoLen );
bufPtr += algoLen;
}
*bufPtrPtr = bufPtr;
}
return( LENGTH_SIZE + length );
}
/* Process a channel open */
int getAddressAndPort( SESSION_INFO *sessionInfoPtr, const BYTE *data,
const int dataLength )
{
char portBuffer[ 16 ];
long port;
int length = dataLength, stringLength, portLength;
/* Get the host and port and convert it into string form for the user to
read:
string host
uint32 port */
stringLength = ( int ) mgetLong( data );
if( stringLength <= 0 || stringLength > CRYPT_MAX_TEXTSIZE - 4 || \
length < ( LENGTH_SIZE + stringLength ) + UINT_SIZE )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid port forwarding host name length %d",
stringLength );
memcpy( sessionInfoPtr->sshPortForward, data, stringLength );
data += stringLength;
port = mgetLong( data );
length -= ( LENGTH_SIZE + stringLength ) + UINT_SIZE;
portBuffer[ 0 ] = ':';
portLength = sprintf( portBuffer + 1, "%ld", port ) + 1;
if( stringLength + portLength <= CRYPT_MAX_TEXTSIZE )
{
memcpy( sessionInfoPtr->sshPortForward + stringLength,
portBuffer, portLength );
stringLength += portLength;
}
sessionInfoPtr->sshPortForwardLength = stringLength;
return( CRYPT_OK );
}
int processChannelOpen( SESSION_INFO *sessionInfoPtr, const BYTE *data,
const int dataLength )
{
BYTE *bufPtr;
BOOLEAN isPortForwarding = FALSE;
long maxPacketSize;
int length = dataLength, stringLength;
/* Read the channel open request. The ID byte has already been read by
the caller:
[ byte type = SSH2_MSG_CHANNEL_OPEN ]
string channel_type = "session" | "direct-tcpip"
uint32 sender_channel
uint32 initial_window_size
uint32 max_packet_size
[ string host_to_connect - For port-forwarding
uint32 port_to_connect
string originator_IP_address
uint32 originator_port ]
Some clients open a standard (non-forwarded) channel when they connect
for general comms and then later open a forwarded channel when client
-> server forwarding is being used and a forwarded connection arrives,
we interpret this to mean that the forwarded channel should supersede
the original non-forwarded one, performed by simply copying the new
channel info over the top of the existing info */
stringLength = ( int ) mgetLong( data );
if( stringLength <= 0 || \
length < ( LENGTH_SIZE + stringLength ) + \
UINT_SIZE + UINT_SIZE + UINT_SIZE )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid channel open packet length %d, string length %d",
length, stringLength );
if( stringLength != 7 || memcmp( data, "session", 7 ) )
{
/* It's not a normal channel open, see if the caller is trying to
do port forwarding */
if( stringLength != 12 || memcmp( data, "direct-tcpip", 12 ) )
{
char stringBuffer[ CRYPT_MAX_TEXTSIZE + 1 ];
/* It's something else, report it as an error */
stringLength = min( stringLength, CRYPT_MAX_TEXTSIZE );
memcpy( stringBuffer, data, stringLength );
stringBuffer[ stringLength ] = '\0';
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid channel open channel type '%'", stringBuffer );
}
isPortForwarding = TRUE;
}
data += stringLength;
length -= ( LENGTH_SIZE + stringLength );
sessionInfoPtr->sshChannel = mgetLong( data );
data += UINT_SIZE; /* Skip window size */
length -= UINT_SIZE + UINT_SIZE;
sessionInfoPtr->sshWindowCount = 0; /* New window, reset count */
maxPacketSize = mgetLong( data );
if( maxPacketSize < 16 || maxPacketSize > 0x100000L )
/* General sanity check to make sure that the packet size is in the
range 16 bytes ... 16MB */
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid maximum packet size %d", maxPacketSize );
length -= UINT_SIZE;
if( isPortForwarding )
{
int status;
/* Get the source and destination host information */
if( length < ( LENGTH_SIZE + 1 ) + UINT_SIZE + \
( LENGTH_SIZE + 1 ) + UINT_SIZE )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid port forwarding channel open data length %d",
length );
status = getAddressAndPort( sessionInfoPtr, data, length );
if( cryptStatusError( status ) )
return( status );
}
else
/* If it's a straight channel open, there shouldn't be any more
data */
if( length != 0 )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid additional %d data bytes in channel open",
length );
/* Send back the open confirmation:
byte type = SSH2_MSG_CHANNEL_OPEN_CONFIRMATION
uint32 recipient_channel = prev. sender_channel
uint32 sender_channel
uint32 initial_window_size = MAX_WINDOW_SIZE
uint32 max_packet_size = bufSize
The SSHv2 spec doesn't really explain the semantics of the server's
response to the channel open command, in particular whether the
returned data size parameters are merely a confirmation of the
client's requested values or whether the server is allowed to further
modify them to suit its own requirements (or perhaps one is for send
and the other for receive?). In the absence of any further guidance,
we try and comply with a client's request for smaller data
quantities, but also return a smaller-than-requested data size value
if they ask for too much data.
See the comments in the client-handshake code for the reason for the
window size */
bufPtr = sessionInfoPtr->sendBuffer + SSH2_HEADER_SIZE;
*bufPtr++ = SSH2_MSG_CHANNEL_OPEN_CONFIRMATION;
mputLong( bufPtr, sessionInfoPtr->sshChannel ); /* Recip.channel */
mputLong( bufPtr, sessionInfoPtr->sshChannel ); /* Sender channel */
mputLong( bufPtr, MAX_WINDOW_SIZE ); /* Window size */
maxPacketSize = min( maxPacketSize, \
sessionInfoPtr->receiveBufSize - EXTRA_PACKET_SIZE );
mputLong( bufPtr, maxPacketSize );
return( sendPacketSSH2( sessionInfoPtr,
bufPtr - ( sessionInfoPtr->sendBuffer + SSH2_HEADER_SIZE ),
FALSE ) );
}
/****************************************************************************
* *
* 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 }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -