📄 ssh.c
字号:
/****************************************************************************
* *
* cryptlib SSH Test Routines *
* Copyright Peter Gutmann 1998-2005 *
* *
****************************************************************************/
#ifdef _MSC_VER
#include "../cryptlib.h"
#include "test.h"
#else
#include "cryptlib.h"
#include "test/test.h"
#endif /* Braindamaged MSC include handling */
#if defined( __MVS__ ) || defined( __VMCMS__ )
/* Suspend conversion of literals to ASCII. */
#pragma convlit( suspend )
#endif /* IBM big iron */
#if defined( __ILEC400__ )
#pragma convert( 0 )
#endif /* IBM medium iron */
/* Uncomment the following to ask the user for a password rather than using
a hardcoded password when testing against live accounts */
/* #define USER_SUPPLIED_PASSWORD */
#ifdef USER_SUPPLIED_PASSWORD
#undef SSH2_SERVER_NAME
#undef SSH_USER_NAME
#define SSH2_SERVER_NAME "testserver"
#define SSH_USER_NAME "testname"
#endif /* USER_SUPPLIED_PASSWORD */
/* We can run the SSH self-test with a large variety of options, rather than
use dozens of boolean option flags to control them all we define various
test classes that exercise each option type */
typedef enum {
SSH_TEST_NORMAL, /* Standard SSHv2 test */
SSH_TEST_SSH1, /* SSHv1 test */
SSH_TEST_CLIENTCERT, /* Use client public-key for auth */
SSH_TEST_SUBSYSTEM, /* Test SFTP subsystem */
SSH_TEST_PORTFORWARDING, /* Test port forwarding */
SSH_TEST_MULTICHANNEL, /* Test multi-channel handling */
SSH_TEST_FINGERPRINT, /* Test (invalid) key fingerprint */
SSH_TEST_CONFIRMAUTH /* Test manual server confirmation of auth.*/
} SSH_TEST_TYPE;
/****************************************************************************
* *
* Utility Functions *
* *
****************************************************************************/
/* Test the ability to parse URLs */
static const struct {
const C_STR url; /* Server URL */
const C_STR name; /* Parsed server name */
const int port; /* Parsed server port */
const C_STR userInfo; /* Parsed user info */
} urlParseInfo[] = {
/* IP address forms */
{ TEXT( "1.2.3.4" ), TEXT( "1.2.3.4" ), 0, NULL },
{ TEXT( "1.2.3.4:80" ), TEXT( "1.2.3.4" ), 80, NULL },
{ TEXT( "user@1.2.3.4" ), TEXT( "1.2.3.4" ), 0, TEXT( "user" ) },
{ TEXT( "[1:2:3:4]" ), TEXT( "1:2:3:4" ), 0, NULL },
{ TEXT( "[1:2:3:4]:80" ), TEXT( "1:2:3:4" ), 80, NULL },
{ TEXT( "user@[1:2:3:4]" ), TEXT( "1:2:3:4" ), 0, TEXT( "user" ) },
/* General URI forms */
{ TEXT( "www.server.com" ), TEXT( "www.server.com" ), 0, NULL },
{ TEXT( "www.server.com:80" ), TEXT( "www.server.com" ), 80, NULL },
{ TEXT( "http://www.server.com:80" ), TEXT( "www.server.com" ), 80, NULL },
{ TEXT( "http://user@www.server.com:80" ), TEXT( "www.server.com" ), 80, TEXT( "user" ) },
/* Spurious whitespace */
{ TEXT( " www.server.com : 80 " ), TEXT( "www.server.com" ), 80, NULL },
{ TEXT( "http:// user @ www.server.com : 80 " ), TEXT( "www.server.com" ), 80, TEXT( "user" ) },
{ NULL, NULL, 0, NULL }
};
int testSessionUrlParse( void )
{
CRYPT_SESSION cryptSession;
int i, status;
puts( "Testing session URL parsing..." );
/* Create a session of the most generic type */
status = cryptCreateSession( &cryptSession, CRYPT_UNUSED, CRYPT_SESSION_SSL );
if( status == CRYPT_ERROR_PARAM3 ) /* SSL session access not available */
return( CRYPT_ERROR_NOTAVAIL );
if( cryptStatusError( status ) )
{
printf( "cryptCreateSession() failed with error code %d, line %d.\n",
status, __LINE__ );
return( FALSE );
}
/* Set various URLs as the server name and retrieve the parsed form */
for( i = 0; urlParseInfo[ i ].url != NULL; i++ )
{
C_CHR nameBuffer[ 256 ], userInfoBuffer[ 256 ];
int lengthLength, userInfoLength, port;
/* Clear any leftover attributes from previous tests */
cryptDeleteAttribute( cryptSession, CRYPT_SESSINFO_SERVER_NAME );
cryptDeleteAttribute( cryptSession, CRYPT_SESSINFO_SERVER_PORT );
cryptDeleteAttribute( cryptSession, CRYPT_SESSINFO_USERNAME );
/* Set the URL */
status = cryptSetAttributeString( cryptSession,
CRYPT_SESSINFO_SERVER_NAME,
urlParseInfo[ i ].url,
paramStrlen( urlParseInfo[ i ].url ) );
if( cryptStatusError( status ) )
{
printf( "Couldn't set URL '%s', line %d.\n",
urlParseInfo[ i ].url, __LINE__ );
return( FALSE );
}
/* Make sure that the parsed form is OK */
status = cryptGetAttributeString( cryptSession,
CRYPT_SESSINFO_SERVER_NAME,
nameBuffer, &lengthLength );
if( cryptStatusOK( status ) && urlParseInfo[ i ].port )
status = cryptGetAttribute( cryptSession,
CRYPT_SESSINFO_SERVER_PORT, &port );
if( cryptStatusOK( status ) && urlParseInfo[ i ].userInfo != NULL )
status = cryptGetAttributeString( cryptSession,
CRYPT_SESSINFO_USERNAME,
userInfoBuffer,
&userInfoLength );
if( cryptStatusError( status ) )
{
printf( "Couldn't get parsed URL info for '%s', line %d.\n",
urlParseInfo[ i ].url, __LINE__ );
return( FALSE );
}
if( memcmp( nameBuffer, urlParseInfo[ i ].name, lengthLength ) || \
( urlParseInfo[ i ].port && port != urlParseInfo[ i ].port ) || \
( urlParseInfo[ i ].userInfo != NULL && \
memcmp( userInfoBuffer, urlParseInfo[ i ].userInfo,
userInfoLength ) ) )
{
printf( "Parsed URL info for '%s' is incorrect, line %d.\n",
urlParseInfo[ i ].url, __LINE__ );
return( FALSE );
}
}
/* Clean up */
status = cryptDestroySession( cryptSession );
if( cryptStatusError( status ) )
{
printf( "cryptDestroySession() failed with error code %d, line %d.\n",
status, __LINE__ );
return( FALSE );
}
puts( "Session URL parsing succeeded.\n" );
return( TRUE );
}
/* Test the ability to have multiple server threads waiting on a session.
Since this requries (OS-specific) threading, we just use two sample
systems, Win32 (Windows threads) and Linux (pthreads). Since Linux's
somewhat strange not-quite-a-thread/not-quite-a-process implementation
can be a bit buggy, we also use another sample pthreads implementation
(FreeBSD/NetBSD) as a sanity check */
#define NO_SERVER_THREADS 4
#if defined( WINDOWS_THREADS ) || defined( __linux ) || \
defined( __FreeBSD__ ) || defined( __NetBSD__ )
#if defined( __FreeBSD__ ) || defined( __NetBSD__ )
#include <pthread.h>
#endif /* FreeBSD || NetBSD */
#ifdef WINDOWS_THREADS
#define THREAD_HANDLE HANDLE
#define THREAD_EXIT() _endthreadex( 0 ); return( 0 )
#define THREAD_SELF() GetCurrentThreadId()
#else
#define THREAD_HANDLE pthread_t
#define THREAD_EXIT() pthread_exit( ( void * ) 0 ); return
#define THREAD_SELF() pthread_self()
#endif /* Windows vs. pthreads */
#ifdef WINDOWS_THREADS
unsigned __stdcall sshMultiServerThread( void *dummy )
#else
void *sshMultiServerThread( void *dummy )
#endif /* Windows vs. pthreads */
{
CRYPT_SESSION cryptSession;
CRYPT_CONTEXT privateKey;
int status;
printf( "Server thread %lX activated.\n", THREAD_SELF() );
/* Create the session and try to activate it. We don't do anything
beyond that point since this is a test of multi-thread handling
capability, not session handling */
status = cryptCreateSession( &cryptSession, CRYPT_UNUSED,
CRYPT_SESSION_SSH_SERVER );
if( cryptStatusError( status ) )
{
printf( "cryptCreateSession() failed with error code %d, line %d.\n",
status, __LINE__ );
THREAD_EXIT();
}
if( !setLocalConnect( cryptSession, 22 ) )
{
THREAD_EXIT();
}
status = getPrivateKey( &privateKey, SSH_PRIVKEY_FILE,
SSH_PRIVKEY_LABEL, TEST_PRIVKEY_PASSWORD );
if( cryptStatusOK( status ) )
{
status = cryptSetAttribute( cryptSession,
CRYPT_SESSINFO_PRIVATEKEY, privateKey );
cryptDestroyContext( privateKey );
}
if( cryptStatusError( status ) )
{
printf( "cryptSetAttribute/AttributeString() failed with error code "
"%d, line %d.\n", status, __LINE__ );
THREAD_EXIT();
}
printf( "Server for thread %lX activated.\n", THREAD_SELF() );
status = cryptSetAttribute( cryptSession, CRYPT_SESSINFO_ACTIVE, TRUE );
printConnectInfo( cryptSession );
if( cryptStatusError( status ) )
printExtError( cryptSession,
"Attempt to activate SSH server session", status,
__LINE__ );
cryptDestroySession( cryptSession );
printf( "Server for thread %lX has exited.\n", THREAD_SELF() );
THREAD_EXIT();
}
#ifdef WINDOWS_THREADS
unsigned __stdcall sshMultiClientThread( void *dummy )
#else
void *sshMultiClientThread( void *dummy )
#endif /* Windows vs. pthreads */
{
CRYPT_SESSION cryptSession;
int status;
printf( "Client thread %lX activated.\n", THREAD_SELF() );
/* Create the session and try to activate it. We don't do anything
beyond that point since this is a test of multi-thread handling
capability, not session handling */
status = cryptCreateSession( &cryptSession, CRYPT_UNUSED,
CRYPT_SESSION_SSH );
if( cryptStatusError( status ) )
{
printf( "cryptCreateSession() failed with error code %d, line %d.\n",
status, __LINE__ );
THREAD_EXIT();
}
if( !setLocalConnect( cryptSession, 22 ) )
{
THREAD_EXIT();
}
status = cryptSetAttribute( cryptSession,
CRYPT_OPTION_NET_CONNECTTIMEOUT, 10 );
if( cryptStatusOK( status ) )
status = cryptSetAttributeString( cryptSession,
CRYPT_SESSINFO_USERNAME,
SSH_USER_NAME,
paramStrlen( SSH_USER_NAME ) );
if( cryptStatusOK( status ) )
status = cryptSetAttributeString( cryptSession,
CRYPT_SESSINFO_PASSWORD,
SSH_PASSWORD,
paramStrlen( SSH_PASSWORD ) );
if( cryptStatusError( status ) )
{
printf( "cryptSetAttribute/AttributeString() failed with error code "
"%d, line %d.\n", status, __LINE__ );
THREAD_EXIT();
}
printf( "Client for thread %lX activated.\n", THREAD_SELF() );
status = cryptSetAttribute( cryptSession, CRYPT_SESSINFO_ACTIVE, TRUE );
printConnectInfo( cryptSession );
if( cryptStatusError( status ) )
printExtError( cryptSession,
"Attempt to activate SSH client session", status,
__LINE__ );
cryptDestroySession( cryptSession );
printf( "Client for thread %lX has exited.\n", THREAD_SELF() );
THREAD_EXIT();
}
int testSessionSSHMultiServer( void )
{
THREAD_HANDLE hClientThreads[ NO_SERVER_THREADS ];
THREAD_HANDLE hServerThreads[ NO_SERVER_THREADS ];
int i;
/* Start the sessions and wait for them initialise. We have to wait for
some time since the multiple private key reads can take awhile */
for( i = 0; i < NO_SERVER_THREADS; i++ )
{
#ifdef WINDOWS_THREADS
unsigned int threadID;
hServerThreads[ i ] = ( HANDLE ) \
_beginthreadex( NULL, 0, sshMultiServerThread,
NULL, 0, &threadID );
#else
pthread_t threadHandle;
hServerThreads[ i ] = 0;
if( pthread_create( &threadHandle, NULL, sshMultiServerThread,
NULL ) == 0 )
hServerThreads[ i ] = threadHandle;
#endif /* Windows vs. pthreads */
}
delayThread( 3 );
/* Connect to the local server */
for( i = 0; i < NO_SERVER_THREADS; i++ )
{
#ifdef WINDOWS_THREADS
unsigned int threadID;
hClientThreads[ i ] = ( HANDLE ) \
_beginthreadex( NULL, 0, sshMultiClientThread,
NULL, 0, &threadID );
#else
pthread_t threadHandle;
hServerThreads[ i ] = 0;
if( pthread_create( &threadHandle, NULL, sshMultiClientThread,
NULL ) == 0 )
hClientThreads[ i ] = threadHandle;
#endif /* Windows vs. pthreads */
}
#ifdef WINDOWS_THREADS
if( WaitForMultipleObjects( NO_SERVER_THREADS, hServerThreads, TRUE,
60000 ) == WAIT_TIMEOUT )
#else
/* Posix doesn't have an ability to wait for multiple threads for mostly
religious reasons ("That's not how we do things around here") so we
just wait for two token threads */
pthread_join( hServerThreads[ 0 ], NULL );
pthread_join( hClientThreads[ 0 ], NULL );
#endif /* Windows vs. pthreads */
{
puts( "Warning: Server threads are still active due to session "
"negotiation failure,\n this will cause an error "
"condition when cryptEnd() is called due\n to "
"resources remaining allocated. Press a key to continue." );
getchar();
}
#ifdef WINDOWS_THREADS
for( i = 0; i < NO_SERVER_THREADS; i++ )
if( hServerThreads[ i ] != 0 )
CloseHandle( hServerThreads[ i ] );
for( i = 0; i < NO_SERVER_THREADS; i++ )
if( hClientThreads[ i ] != 0 )
CloseHandle( hClientThreads[ i ] );
#endif /* Windows vs. pthreads */
return( TRUE );
}
#endif /* OS-specific threading functions */
/* Create an SSH channel */
static int createChannel( const CRYPT_SESSION cryptSession,
const C_STR type, const C_STR arg1 )
{
int status;
status = cryptSetAttribute( cryptSession, CRYPT_SESSINFO_SSH_CHANNEL,
CRYPT_UNUSED );
if( cryptStatusOK( status ) )
status = cryptSetAttributeString( cryptSession,
CRYPT_SESSINFO_SSH_CHANNEL_TYPE,
type, paramStrlen( type ) );
if( cryptStatusOK( status ) )
status = cryptSetAttributeString( cryptSession,
CRYPT_SESSINFO_SSH_CHANNEL_ARG1,
arg1, paramStrlen( arg1 ) );
return( status );
}
/* Print information on an SSH channel */
static int printChannelInfo( const CRYPT_SESSION cryptSession,
const SSH_TEST_TYPE testType,
const BOOLEAN isServer )
{
C_CHR stringBuffer[ CRYPT_MAX_TEXTSIZE + 1 ];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -