📄 ssh2.c
字号:
status = krnlSendMessage( createInfo.cryptHandle,
IMESSAGE_SETATTRIBUTE,
( void * ) &requestedKeySize,
CRYPT_CTXINFO_KEYSIZE );
if( cryptStatusOK( status ) )
status = krnlSendMessage( createInfo.cryptHandle,
IMESSAGE_CTX_GENKEY, NULL,
FALSE );
however because the handshake is set up so that the client
(rather than the server) chooses the key size, we can't
actually perform the generation until we're in the middle of
the handshake. This means that the server will grind to a
halt during each handshake as it generates a new key of
whatever size takes the client's fancy (it also leads to a
nice potential DoS attack on the server). To avoid this
problem, we use fixed keys for common sizes, only generating
a key if it's absolutely necessary */
switch( actualKeySize )
{
case bitsToBytes( 1024 ):
setMessageData( &msgData, ( void * ) dh1024SPKI,
sizeof( dh1024SPKI ) );
keyType = CRYPT_IATTRIBUTE_KEY_SPKI;
break;
case bitsToBytes( 1536 ):
setMessageData( &msgData, ( void * ) dh1536SSH,
sizeof( dh1536SSH ) );
break;
case bitsToBytes( 2048 ):
setMessageData( &msgData, ( void * ) dh2048SSH,
sizeof( dh2048SSH ) );
break;
case bitsToBytes( 3072 ):
default: /* Hier ist der mast zu ende */
setMessageData( &msgData, ( void * ) dh3072SSH,
sizeof( dh3072SSH ) );
break;
}
}
status = krnlSendMessage( createInfo.cryptHandle,
IMESSAGE_SETATTRIBUTE_S, &msgData,
keyType );
}
if( cryptStatusOK( status ) )
status = krnlSendMessage( createInfo.cryptHandle,
IMESSAGE_GETATTRIBUTE, &length,
CRYPT_CTXINFO_KEYSIZE );
if( cryptStatusError( status ) )
{
assert( NOTREACHED );
krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
return( status );
}
*iCryptContext = createInfo.cryptHandle;
*keySize = length;
return( CRYPT_OK );
}
/* Convert an SSHv2 algorithm list to a cryptlib ID in preferred-algorithm
order. For some bizarre reason the algorithm information is communicated
as a comma-delimited list (in an otherwise binary protocol), so we have
to unpack and pack them into this cumbersome format alongside just
choosing which algorithm to use. In addition, the algorithm selection
mechanism differs depending on whether we're the client or server, and
what set of algorithms we're matching. Unlike SSL, which uses the
offered-suites/chosen-suites mechanism, in SSHv2 both sides offer a
selection of cipher suites and the server chooses the first one that
appears on both it and the client's list, with special-case handling for
the keyex and signature algorithms if the match isn't the first one on
the list. This means that the client can choose as it pleases from the
server's list if it waits for the server hello (see the comment in the
client/server hello handling code on the annoying nature of this portion
of the SSHv2 handshake), but the server has to perform a complex double-
match of its own vs.the client's list. The cases that we need to handle
are:
get the first matching algorithm, used by the server to match the client.
get the first matching algorithm and warn if it isn't the first one on
the list of possible algorithms, used by the server to match the
client for the keyex and public-key algorithms.
get the best matching algorithm (that is, the one corresponding to the
strongest crypto mechanism), used by the client to match the server.
This is a sufficiently complex and screwball function that we need to
define a composite structure to pass all of the control information in
and out */
typedef enum {
GETALGO_NONE, /* No match action */
GETALGO_FIRST_MATCH, /* Get first matching algorithm */
GETALGO_FIRST_MATCH_WARN,/* Get first matching algo, warn if not first */
GETALGO_BEST_MATCH, /* Get best matching algorithm */
GETALGO_LAST /* Last possible match action */
} GETALGO_TYPE;
typedef struct {
const ALGO_STRING_INFO *algoInfo;/* Algorithm selection info */
CRYPT_ALGO_TYPE preferredAlgo; /* Preferred algo for first-match */
GETALGO_TYPE getAlgoType; /* Type of match to perform */
CRYPT_ALGO_TYPE algo; /* Matched algorithm */
int algoStringLength; /* Length of algorithm string */
BOOLEAN prefAlgoMismatch; /* First match != preferredAlgo */
} ALGOID_INFO;
#define setAlgoIDInfo( algoIDInfo, algoStrInfo, prefAlgo, getType ) \
{ \
memset( ( algoIDInfo ), 0, sizeof( ALGOID_INFO ) ); \
( algoIDInfo )->algoInfo = ( algoStrInfo ); \
( algoIDInfo )->preferredAlgo = ( prefAlgo ); \
( algoIDInfo )->getAlgoType = ( getType ); \
}
static int getAlgoIDEx( ALGOID_INFO *algoIDInfo, const BYTE *string,
const int maxLength, void *errorInfo )
{
int stringPos = 0, stringLen, algoIndex = 999;
assert( isWritePtr( algoIDInfo, sizeof( ALGOID_INFO ) ) );
assert( isReadPtr( algoIDInfo->algoInfo, sizeof( ALGO_STRING_INFO ) ) );
assert( isReadPtr( string, maxLength ) );
assert( maxLength > LENGTH_SIZE );
assert( ( algoIDInfo->getAlgoType == GETALGO_BEST_MATCH && \
algoIDInfo->preferredAlgo == CRYPT_ALGO_NONE ) || \
( algoIDInfo->getAlgoType == GETALGO_FIRST_MATCH ) ||
( algoIDInfo->getAlgoType == GETALGO_FIRST_MATCH_WARN && \
( algoIDInfo->preferredAlgo > CRYPT_ALGO_NONE && \
algoIDInfo->preferredAlgo < CRYPT_ALGO_LAST ) ) );
/* Get the string length and make sure that it's valid */
if( maxLength < LENGTH_SIZE + SSH2_MIN_ALGOID_SIZE )
retExt( errorInfo, CRYPT_ERROR_BADDATA,
"Invalid minimum algorithm ID size %d", maxLength );
stringLen = mgetLong( string );
if( stringLen <= 0 || stringLen > maxLength - LENGTH_SIZE )
retExt( errorInfo, CRYPT_ERROR_BADDATA,
"Invalid algorithm ID string size %d", stringLen );
/* Walk down the string looking for a recognised algorithm. Since our
preference may not match the other side's preferences, we have to walk
down the entire list to find our preferred choice */
while( stringPos < stringLen )
{
int len, i;
/* Find the length of the next algorithm name */
for( len = stringPos; len < stringLen && string[ len ] != ','; len++ );
len -= stringPos;
if( len < SSH2_MIN_ALGOID_SIZE )
{
/* Empty or too-short algorithm name, continue */
stringPos += len + 1;
continue;
}
/* Check whether it's something that we can handle */
for( i = 0; algoIDInfo->algoInfo[ i ].name != NULL; i++ )
if( len == strlen( algoIDInfo->algoInfo[ i ].name ) && \
!memcmp( algoIDInfo->algoInfo[ i ].name,
string + stringPos, len ) )
break;
if( algoIDInfo->algoInfo[ i ].name == NULL || \
!algoAvailable( algoIDInfo->algoInfo[ i ].algo ) )
{
/* No match or the matched algorithm isn't available in this
build, if we have to match the first algorithm on the list
remember to warn the caller, then move on to the next name */
if( algoIDInfo->getAlgoType == GETALGO_FIRST_MATCH_WARN )
algoIDInfo->prefAlgoMismatch = TRUE;
stringPos += len + 1;
continue;
}
switch( algoIDInfo->getAlgoType )
{
case GETALGO_BEST_MATCH:
/* If we're looking for the best (highest-ranked algorithm)
match, see whether the current match ranks higher than
the existing one */
if( i < algoIndex )
{
algoIndex = i;
if( algoIndex <= 0 )
/* Gruener werd's net */
stringPos = stringLen; /* Force outer loop exit */
}
break;
case GETALGO_FIRST_MATCH:
/* If we've found an acceptable algorithm, remember it and
exit */
if( algoIDInfo->preferredAlgo == CRYPT_ALGO_NONE || \
algoIDInfo->preferredAlgo == algoIDInfo->algoInfo[ i ].algo )
{
algoIndex = i;
stringPos = stringLen; /* Force outer loop exit */
}
break;
case GETALGO_FIRST_MATCH_WARN:
/* If we found the algorithm that we're after, remember it
and exit */
if( algoIDInfo->preferredAlgo != algoIDInfo->algoInfo[ i ].algo )
/* We didn't match the first algorithm on the list, warn
the caller */
algoIDInfo->prefAlgoMismatch = TRUE;
algoIndex = i;
stringPos = stringLen; /* Force outer loop exit */
break;
default:
assert( NOTREACHED );
}
/* Check the next name */
stringPos += len + 1;
}
if( algoIndex == 999 )
/* We couldn't find anything to use */
retExt( errorInfo, CRYPT_ERROR_NOTAVAIL,
"No crypto algorithm compatible with the remote system was "
"found" );
/* We found a more-preferred algorithm than the default, go with that */
algoIDInfo->algo = algoIDInfo->algoInfo[ algoIndex ].algo;
algoIDInfo->algoStringLength = LENGTH_SIZE + stringLen;
return( CRYPT_OK );
}
int getAlgoID( const ALGO_STRING_INFO *algoInfo, CRYPT_ALGO_TYPE *algo,
const CRYPT_ALGO_TYPE preferredAlgo, const BYTE *string,
const int maxLength, void *errorInfo )
{
ALGOID_INFO algoIDInfo;
int status;
setAlgoIDInfo( &algoIDInfo, algoInfo, preferredAlgo,
GETALGO_FIRST_MATCH );
status = getAlgoIDEx( &algoIDInfo, string, maxLength, errorInfo );
if( cryptStatusOK( status ) && algo != NULL )
*algo = algoIDInfo.algo;
return( cryptStatusError( status ) ? \
status : algoIDInfo.algoStringLength );
}
/* Algorithms used to protect data packets are used in pairs, one for
incoming and the other for outgoing data. To keep things simple we
always force these to be the same, first reading the algorithm for one
direction and then making sure that the one for the other direction
matches this. All implementations seem to do this anyway, many aren't
even capable of supporting asymmetric algorithm choices */
static int getAlgoIDpair( const ALGO_STRING_INFO *algoInfo,
CRYPT_ALGO_TYPE *algo, const BYTE *string,
const int maxLength, const BOOLEAN isServer,
void *errorInfo )
{
CRYPT_ALGO_TYPE pairPreferredAlgo;
ALGOID_INFO algoIDInfo;
int length, status;
assert( isReadPtr( algoInfo, sizeof( ALGO_STRING_INFO ) ) );
assert( isReadPtr( string, maxLength ) );
/* Clear return value */
if( algo != NULL )
*algo = CRYPT_ALGO_NONE;
/* Make sure the input parameters are in order */
if( maxLength < ( LENGTH_SIZE + SSH2_MIN_ALGOID_SIZE ) * 2 )
retExt( errorInfo, CRYPT_ERROR_BADDATA,
"Invalid minimum algorithm ID pair size %d", maxLength );
/* Get the first algorithm */
setAlgoIDInfo( &algoIDInfo, algoInfo, CRYPT_ALGO_NONE,
isServer ? GETALGO_FIRST_MATCH : GETALGO_BEST_MATCH );
status = getAlgoIDEx( &algoIDInfo, string, maxLength, errorInfo );
if( cryptStatusError( status ) )
return( status );
pairPreferredAlgo = algoIDInfo.algo;
length = algoIDInfo.algoStringLength;
/* Get the matched seconed algorithm */
setAlgoIDInfo( &algoIDInfo, algoInfo, pairPreferredAlgo,
GETALGO_FIRST_MATCH );
status = getAlgoIDEx( &algoIDInfo, string + length, maxLength - length,
errorInfo );
if( pairPreferredAlgo != algoIDInfo.algo )
retExt( errorInfo, CRYPT_ERROR_BADDATA,
"Client algorithm %d doesn't match server algorithm %d in "
"algorithm pair", pairPreferredAlgo, algoIDInfo.algo );
if( algo != NULL )
*algo = algoIDInfo.algo;
return( cryptStatusError( status ) ? \
status : length + algoIDInfo.algoStringLength );
}
/* Convert a cryptlib algorithm ID to an SSHv2 algorithm name */
int putAlgoID( BYTE **bufPtrPtr, const CRYPT_ALGO_TYPE algo )
{
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-group1-sha1", CRYPT_ALGO_DH },
{ "hmac-sha1", CRYPT_ALGO_HMAC_SHA },
{ "hmac-md5", CRYPT_ALGO_HMAC_MD5 },
{ "none", CRYPT_ALGO_NONE },
{ "none", CRYPT_ALGO_LAST } /* Catch-all */
};
int length, i;
/* Locate the name for this algorithm and encode it as an SSH string */
for( i = 0; algoStringMapTbl[ i ].algo != CRYPT_ALGO_LAST && \
algoStringMapTbl[ i ].algo != algo; i++ );
assert( algoStringMapTbl[ i ].algo != CRYPT_ALGO_LAST );
length = strlen( algoStringMapTbl[ i ].name );
if( bufPtrPtr != NULL )
{
BYTE *bufPtr = *bufPtrPtr;
mputLong( bufPtr, length );
memcpy( bufPtr, algoStringMapTbl[ i ].name, length );
*bufPtrPtr += LENGTH_SIZE + length;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -