1 /****************************************************************************
2 *																			*
3 *							cryptlib SSH Test Routines						*
4 *						Copyright Peter Gutmann 1998-2005					*
5 *																			*
6 ****************************************************************************/
7 
8 #include "cryptlib.h"
9 #include "test/test.h"
10 
11 /* Various features can be disabled by configuration options, in order to
12    handle this we need to include the cryptlib config file so that we can
13    selectively disable some tests */
14 
15 #ifdef __WINDOWS__
16   /* For checking for debug-only capabilities */
17   #define _OSSPEC_DEFINED
18   #define VC_16BIT( version )		( version <= 800 )
19   #define VC_LE_VC6( version )		( version <= 1200 )
20   #define VC_LT_2005( version )		( version < 1400 )
21   #define VC_GE_2005( version )		( version >= 1400 )
22   #define VC_GE_2010( version )		( version >= 1600 )
23 #else
24   #define VC_16BIT( version )		0
25   #define VC_LE_VC6( version )		0
26   #define VC_LT_2005( version )		0
27   #define VC_GE_2005( version )		0
28   #define VC_GE_2010( version )		0
29 #endif /* __WINDOWS__ */
30 #include "misc/config.h"
31 #ifndef NDEBUG
32   #include "misc/analyse.h"		/* Needed for fault.h */
33   #include "misc/fault.h"
34 #endif /* !NDEBUG */
35 
36 #if defined( __MVS__ ) || defined( __VMCMS__ )
37   /* Suspend conversion of literals to ASCII. */
38   #pragma convlit( suspend )
39 #endif /* IBM big iron */
40 #if defined( __ILEC400__ )
41   #pragma convert( 0 )
42 #endif /* IBM medium iron */
43 
44 /* Uncomment the following to ask the user for a password rather than using
45    a hardcoded password when testing against live accounts */
46 
47 /* #define USER_SUPPLIED_PASSWORD */
48 #ifdef USER_SUPPLIED_PASSWORD
49   #undef SSH2_SERVER_NAME
50   #undef SSH_USER_NAME
51   #define SSH2_SERVER_NAME	"testserver"
52   #define SSH_USER_NAME		"testname"
53 #endif /* USER_SUPPLIED_PASSWORD */
54 
55 /* We can run the SSH self-test with a large variety of options, rather than
56    using dozens of boolean option flags to control them all we define
57    various test classes that exercise each option type */
58 
59 typedef enum {
60 	SSH_TEST_NORMAL,			/* Standard SSHv2 test */
61 	SSH_TEST_DSAKEY,			/* DSA server key instead of RSA */
62 	SSH_TEST_ECCKEY,			/* ECDSA server key instead of RSA */
63 	SSH_TEST_CLIENTCERT,		/* Use client public-key for auth */
64 	SSH_TEST_SUBSYSTEM,			/* Test SFTP subsystem */
65 	SSH_TEST_PORTFORWARDING,	/* Test port forwarding */
66 	SSH_TEST_EXEC,				/* Test rexec rather than rsh functionality */
67 	SSH_TEST_MULTICHANNEL,		/* Test multi-channel handling */
68 	SSH_TEST_FINGERPRINT,		/* Test (invalid) key fingerprint */
69 	SSH_TEST_CONFIRMAUTH,		/* Test manual server confirmation of auth.*/
70 	SSH_TEST_DUALTHREAD,
71 	SSH_TEST_DUALTHREAD2,		/* Two-phase connect via different threads */
72 	SSH_TEST_CORRUPT_HANDSHAKE,	/* Detect corruption of handshake data */
73 	SSH_TEST_CORRUPT_DATA,		/* Detect corruption of payload data */
74 	SSH_TEST_WRONGCERT,			/* Detect wrong key for server */
75 	SSH_TEST_BADSIG_HASH,		/* Detect corruption of signed DH params */
76 	SSH_TEST_BADSIG_DATA		/* Detect corruption of signed DH params */
77 	} SSH_TEST_TYPE;
78 
79 #if defined( TEST_SESSION ) || defined( TEST_SESSION_LOOPBACK )
80 
81 /****************************************************************************
82 *																			*
83 *								Utility Functions							*
84 *																			*
85 ****************************************************************************/
86 
87 /* Test the ability to parse URLs */
88 
89 typedef struct {
90 	const C_STR url;			/* Server URL */
91 	const C_STR name;			/* Parsed server name */
92 	const int port;				/* Parsed server port */
93 	const C_STR userInfo;		/* Parsed user info */
94 	} URL_PARSE_INFO;
95 
96 static const FAR_BSS URL_PARSE_INFO urlParseInfo[] = {
97 	/* IP address forms */
98 	{ TEXT( "1.2.3.4" ), TEXT( "1.2.3.4" ), 0, NULL },
99 	{ TEXT( "1.2.3.4:80" ), TEXT( "1.2.3.4" ), 80, NULL },
100 	{ TEXT( "user@1.2.3.4" ), TEXT( "1.2.3.4" ), 0, TEXT( "user" ) },
101 	{ TEXT( "[1:2:3:4]" ), TEXT( "[1:2:3:4]" ), 0, NULL },
102 	{ TEXT( "[1:2:3:4]:80" ), TEXT( "[1:2:3:4]" ), 80, NULL },
103 	{ TEXT( "user@[1:2:3:4]" ), TEXT( "[1:2:3:4]" ), 0, TEXT( "user" ) },
104 	{ TEXT( "[::1]" ), TEXT( "[::1]" ), 0, NULL },
105 
106 	/* General URI forms */
107 	{ TEXT( "www.server.com" ), TEXT( "www.server.com" ), 0, NULL },
108 	{ TEXT( "www.server.com:80" ), TEXT( "www.server.com" ), 80, NULL },
109 	{ TEXT( "http://www.server.com/" ), TEXT( "www.server.com" ), 0, NULL },
110 	{ TEXT( "http://www.server.com:80" ), TEXT( "www.server.com" ), 80, NULL },
111 	{ TEXT( "http://user@www.server.com:80" ), TEXT( "www.server.com" ), 80, TEXT( "user" ) },
112 	{ TEXT( "http://www.server.com/location.php" ), TEXT( "www.server.com" ), 0, NULL },
113 	{ TEXT( "http://www.server.com:80/location.php" ), TEXT( "www.server.com" ), 80, NULL },
114 	{ TEXT( "http://www.server.com/location1/location2/location.php" ), TEXT( "www.server.com" ), 0, NULL },
115 
116 	/* Spurious whitespace */
117 	{ TEXT( "  www.server.com  :   80 " ), TEXT( "www.server.com" ), 80, NULL },
118 	{ TEXT( " user  @  www.server.com  :   80 " ), TEXT( "www.server.com" ), 80, NULL },
119 	{ TEXT( "http:// user  @ www.server.com  :   80 " ), TEXT( "www.server.com" ), 80, TEXT( "user" ) },
120 	{ TEXT( "www.server.com  :   80 /location.php" ), TEXT( "www.server.com" ), 80, NULL },
121 
122 	{ NULL, NULL, 0, NULL }
123 	};
124 
125 static const FAR_BSS URL_PARSE_INFO invalidUrlParseInfo[] = {
126 	/* Bad port */
127 	{ TEXT( "www.server.com:2" ), NULL, 0, NULL },
128 	{ TEXT( "www.server.com:80abcd" ), NULL, 0, NULL },
129 	{ TEXT( "www.server.com:abcd" ), NULL, 0, NULL },
130 
131 	/* Bad general URI */
132 	{ TEXT( "http://" ), NULL, 0, NULL },
133 	{ TEXT( "http://xy" ), NULL, 0, NULL },
134 	{ TEXT( "@www.server.com" ), NULL, 0, NULL },
135 	{ TEXT( "   @www.server.com" ), NULL, 0, NULL },
136 
137 	{ NULL, NULL, 0, NULL }
138 	};
139 
testSessionUrlParse(void)140 int testSessionUrlParse( void )
141 	{
142 	CRYPT_SESSION cryptSession;
143 	int i, status;
144 
145 	puts( "Testing session URL parsing..." );
146 
147 	/* Create a session of the most generic type */
148 	status = cryptCreateSession( &cryptSession, CRYPT_UNUSED, CRYPT_SESSION_SSL );
149 	if( status == CRYPT_ERROR_PARAM3 )	/* SSL session access not available */
150 		return( CRYPT_ERROR_NOTAVAIL );
151 	if( cryptStatusError( status ) )
152 		{
153 		printf( "cryptCreateSession() failed with error code %d, line %d.\n",
154 				status, __LINE__ );
155 		return( FALSE );
156 		}
157 
158 	/* Set various URLs as the server name and retrieve the parsed form */
159 	for( i = 0; urlParseInfo[ i ].url != NULL; i++ )
160 		{
161 		C_CHR nameBuffer[ 256 ], userInfoBuffer[ 256 ];
162 		int nameLength, userInfoLength DUMMY_INIT, port DUMMY_INIT;
163 
164 		/* Clear any leftover attributes from previous tests */
165 		memset( nameBuffer, 0, 16 );
166 		memset( userInfoBuffer, 0, 16 );
167 		cryptDeleteAttribute( cryptSession, CRYPT_SESSINFO_SERVER_NAME );
168 		cryptDeleteAttribute( cryptSession, CRYPT_SESSINFO_SERVER_PORT );
169 		cryptDeleteAttribute( cryptSession, CRYPT_SESSINFO_USERNAME );
170 
171 		/* Set the URL */
172 		status = cryptSetAttributeString( cryptSession,
173 										  CRYPT_SESSINFO_SERVER_NAME,
174 										  urlParseInfo[ i ].url,
175 										  paramStrlen( urlParseInfo[ i ].url ) );
176 		if( cryptStatusError( status ) )
177 			{
178 			printf( "Couldn't set URL '%s', status %d, line %d.\n",
179 					urlParseInfo[ i ].url, status, __LINE__ );
180 			return( FALSE );
181 			}
182 
183 		/* Make sure that the parsed form is OK */
184 		status = cryptGetAttributeString( cryptSession,
185 										  CRYPT_SESSINFO_SERVER_NAME,
186 										  nameBuffer, &nameLength );
187 		if( cryptStatusOK( status ) && urlParseInfo[ i ].port )
188 			status = cryptGetAttribute( cryptSession,
189 										CRYPT_SESSINFO_SERVER_PORT, &port );
190 		if( cryptStatusOK( status ) && urlParseInfo[ i ].userInfo != NULL )
191 			{
192 			status = cryptGetAttributeString( cryptSession,
193 											  CRYPT_SESSINFO_USERNAME,
194 											  userInfoBuffer,
195 											  &userInfoLength );
196 			}
197 		if( cryptStatusError( status ) )
198 			{
199 			printf( "Couldn't get parsed URL info for '%s', status %d, "
200 					"line %d.\n", urlParseInfo[ i ].url, status, __LINE__ );
201 			return( FALSE );
202 			}
203 		if( paramStrlen( urlParseInfo[ i ].name ) != ( size_t ) nameLength || \
204 			memcmp( nameBuffer, urlParseInfo[ i ].name, nameLength ) || \
205 			( urlParseInfo[ i ].port && port != urlParseInfo[ i ].port ) || \
206 			( urlParseInfo[ i ].userInfo != NULL && \
207 			  memcmp( userInfoBuffer, urlParseInfo[ i ].userInfo,
208 			  userInfoLength ) ) )
209 			{
210 			printf( "Parsed URL info for '%s' is incorrect, line %d.\n",
211 					urlParseInfo[ i ].url, __LINE__ );
212 			return( FALSE );
213 			}
214 		}
215 
216 	/* Now try it with invalid URLs */
217 	for( i = 0; invalidUrlParseInfo[ i ].url != NULL; i++ )
218 		{
219 		/* Clear any leftover attributes from previous tests */
220 		cryptDeleteAttribute( cryptSession, CRYPT_SESSINFO_SERVER_NAME );
221 		cryptDeleteAttribute( cryptSession, CRYPT_SESSINFO_SERVER_PORT );
222 		cryptDeleteAttribute( cryptSession, CRYPT_SESSINFO_USERNAME );
223 
224 		/* Set the URL */
225 		status = cryptSetAttributeString( cryptSession,
226 										  CRYPT_SESSINFO_SERVER_NAME,
227 										  invalidUrlParseInfo[ i ].url,
228 										  paramStrlen( invalidUrlParseInfo[ i ].url ) );
229 		if( cryptStatusOK( status ) )
230 			{
231 			printf( "Invalid URL '%s' was accepted as valid, line %d.\n",
232 					invalidUrlParseInfo[ i ].url, __LINE__ );
233 			return( FALSE );
234 			}
235 		}
236 
237 	/* Clean up */
238 	status = cryptDestroySession( cryptSession );
239 	if( cryptStatusError( status ) )
240 		{
241 		printf( "cryptDestroySession() failed with error code %d, line %d.\n",
242 				status, __LINE__ );
243 		return( FALSE );
244 		}
245 	puts( "Session URL parsing succeeded.\n" );
246 	return( TRUE );
247 	}
248 
249 /* Test session attribute handling */
250 
testSessionAttributes(void)251 int testSessionAttributes( void )
252 	{
253 	CRYPT_SESSION cryptSession;
254 	int status;
255 
256 	puts( "Testing session attribute handling..." );
257 
258 	/* Create a server session of the most generic type */
259 	status = cryptCreateSession( &cryptSession, CRYPT_UNUSED,
260 								 CRYPT_SESSION_SSL_SERVER );
261 	if( status == CRYPT_ERROR_PARAM3 )	/* SSL server session access not avail.*/
262 		return( CRYPT_ERROR_NOTAVAIL );
263 	if( cryptStatusError( status ) )
264 		{
265 		printf( "cryptCreateSession() failed with error code %d, line %d.\n",
266 				status, __LINE__ );
267 		return( FALSE );
268 		}
269 
270 	/* Add an initial attribute */
271 	status = cryptSetAttributeString( cryptSession,
272 								CRYPT_SESSINFO_SERVER_NAME, TEXT( "servername" ),
273 								paramStrlen( TEXT( "servername" ) ) );
274 	if( cryptStatusError( status ) )
275 		{
276 		printf( "cryptSetAttributeString() failed with error code %d, "
277 				"line %d.\n", status, __LINE__ );
278 		return( FALSE );
279 		}
280 
281 	/* Add several username/password pairs */
282 	status = cryptSetAttributeString( cryptSession,
283 								CRYPT_SESSINFO_USERNAME, TEXT( "test1" ),
284 								paramStrlen( TEXT( "test1" ) ) );
285 	if( cryptStatusOK( status ) )
286 		status = cryptSetAttributeString( cryptSession,
287 								CRYPT_SESSINFO_PASSWORD, TEXT( "test1" ),
288 								paramStrlen( TEXT( "test1" ) ) );
289 	if( cryptStatusOK( status ) )
290 		status = cryptSetAttributeString( cryptSession,
291 								CRYPT_SESSINFO_USERNAME, TEXT( "test2" ),
292 								paramStrlen( TEXT( "test2" ) ) );
293 	if( cryptStatusOK( status ) )
294 		status = cryptSetAttributeString( cryptSession,
295 								CRYPT_SESSINFO_PASSWORD, TEXT( "test2" ),
296 								paramStrlen( TEXT( "test2" ) ) );
297 	if( cryptStatusOK( status ) )
298 		status = cryptSetAttributeString( cryptSession,
299 								CRYPT_SESSINFO_USERNAME, TEXT( "test3" ),
300 								paramStrlen( TEXT( "test3" ) ) );
301 	if( cryptStatusOK( status ) )
302 		status = cryptSetAttributeString( cryptSession,
303 								CRYPT_SESSINFO_PASSWORD, TEXT( "test3" ),
304 								paramStrlen( TEXT( "test3" ) ) );
305 	if( cryptStatusError( status ) )
306 		{
307 		printf( "cryptSetAttributeString() for username/password pairs "
308 				"failed with error code %d, line %d.\n", status, __LINE__ );
309 		return( FALSE );
310 		}
311 
312 	/* Add a duplicate entry and make sure that it's detected */
313 	status = cryptSetAttributeString( cryptSession,
314 								CRYPT_SESSINFO_USERNAME, TEXT( "test2" ),
315 								paramStrlen( TEXT( "test2" ) ) );
316 	if( status != CRYPT_ERROR_DUPLICATE )
317 		{
318 		printf( "Addition of duplicate user/password entry wasn't detected, "
319 				"line %d.\n", __LINE__ );
320 		return( FALSE );
321 		}
322 
323 	/* Add a password without a preceding username and make sure that it's
324 	   detected */
325 	status = cryptSetAttributeString( cryptSession,
326 								CRYPT_SESSINFO_PASSWORD, TEXT( "invalid_pw" ),
327 								paramStrlen( TEXT( "invalid_pw" ) ) );
328 	if( status != CRYPT_ERROR_NOTINITED )
329 		{
330 		printf( "Addition of password without username wasn't detected, "
331 				"line %d.\n", __LINE__ );
332 		return( FALSE );
333 		}
334 
335 	/* Add a username without a password and make sure that it's detected */
336 	status = cryptSetAttributeString( cryptSession,
337 								CRYPT_SESSINFO_USERNAME, TEXT( "valid_name" ),
338 								paramStrlen( TEXT( "valid_name" ) ) );
339 	if( cryptStatusOK( status ) )
340 		status = cryptSetAttributeString( cryptSession,
341 								CRYPT_SESSINFO_USERNAME, TEXT( "invalid_name" ),
342 								paramStrlen( TEXT( "invalid_name" ) ) );
343 	if( status != CRYPT_ERROR_INITED )
344 		{
345 		printf( "Addition of username without password wasn't detected, "
346 				"line %d.\n", __LINE__ );
347 		return( FALSE );
348 		}
349 
350 	/* Clean up */
351 	status = cryptDestroySession( cryptSession );
352 	if( cryptStatusError( status ) )
353 		{
354 		printf( "cryptDestroySession() failed with error code %d, line %d.\n",
355 				status, __LINE__ );
356 		return( FALSE );
357 		}
358 	puts( "Session attribute handling succeeded.\n" );
359 	return( TRUE );
360 	}
361 
362 /****************************************************************************
363 *																			*
364 *							SSH Utility Functions							*
365 *																			*
366 ****************************************************************************/
367 
368 /* There are various servers running that we can use for testing, the
369    following remapping allows us to switch between them.  Notes:
370 
371 	Server 1: Local loopback.
372 	Server 2: Sends extraneous lines of text before the SSH ID string
373 			  (technically allowed by the RFC, but probably not in the way
374 			  that it's being used here).
375 	Server 3: Reference ssh.com implementation.
376 	Server 4: Reference OpenSSH implementation.
377 	Server 5: OpenSSH with ECC support.  There are two aliases for the same
378 			  server, anoncvs is a somewhat nonstandard config that only
379 			  allows access via the 'anoncvs' account and is rather abrupt
380 			  about disconnecting clients, and natsu, which is a more
381 			  standard config that behaves more normally.
382 	Server 6: Sends PAM auth request consisting of 16 bytes of zeroes,
383 			  technically this is valid (since the spec is so vague) but it
384 			  doesn't make any sense to send it.
385 	Server 7: Test server for SHA-256 signing support.
386 
387    To test local -> remote/remote -> local forwarding:
388 
389 	ssh localhost -v -l test -pw test -L 110:pop3.test.com:110
390 	ssh localhost -v -l test -pw test -R 110:pop3.test.com:110
391 
392   For test purposes we connect to the OpenSSH server because this is the
393   most frequently-used one around, so maintaining compatibility with it
394   whenever it changes is important.  Using it for test connects is slightly
395   antisocial but in practice few people seem to run the self-test and we
396   never get past the initial handshake phase so it shouldn't be a big deal */
397 
398 static const struct {
399 	const C_STR name;
400 	const C_STR userName;
401 	const C_STR password;
402 	} FAR_BSS sshInfo[] = {
403 	{ NULL, NULL, NULL },
404 	/*  1 */ { TEXT( "localhost" ), SSH_USER_NAME, SSH_PASSWORD },
405 	/*  2 */ { TEXT( "sorrel.humboldt.edu:222" ), TEXT( "user" ), TEXT( "password" ) },
406 	/*  3 */ { TEXT( "www.ssh.com" ), TEXT( "user" ), TEXT( "password" ) },
407 	/*  4 */ { TEXT( "www.openssh.com" ), TEXT( "user" ), TEXT( "password" ) },
408 	/*  5 */ /* { TEXT( "anoncvs.mindrot.org" ), TEXT( "user" ), TEXT( "password" ) },	See comment above */
409 	/*  5 */ { TEXT( "natsu.mindrot.org" ), TEXT( "user" ), TEXT( "password" ) },
410 	/*  6 */ { TEXT( "home.bbsdev.net" ), TEXT( "user" ), TEXT( "password" ) },
411 	/*  7 */ { TEXT( "experiment.bitvise.com:10712" ), TEXT( "user" ), TEXT( "password" ) },
412 	{ NULL, NULL, NULL }
413 	};
414 
415 #define SSH2_SERVER_NO	4
416 
417 #if defined( WINDOWS_THREADS ) || defined( UNIX_THREADS )
418 
419 /* Test the ability to have multiple server threads waiting on a session.
420    Since this requries (OS-specific) threading, we just use two sample
421    systems, Win32 (Windows threads) and Linux (pthreads).  Since Linux's
422    somewhat strange not-quite-a-thread/not-quite-a-process implementation
423    can be a bit buggy, we also use another sample pthreads implementation
424    (FreeBSD/NetBSD) as a sanity check */
425 
426 #ifdef WINDOWS_THREADS
sshServerMultiThread(void * dummy)427   unsigned __stdcall sshServerMultiThread( void *dummy )
428 #else
429   void *sshServerMultiThread( void *dummy )
430 #endif /* Windows vs. pthreads */
431 	{
432 	CRYPT_SESSION cryptSession;
433 	CRYPT_CONTEXT privateKey;
434 	BYTE filenameBuffer[ FILENAME_BUFFER_SIZE ];
435 #ifdef UNICODE_STRINGS
436 	wchar_t wcBuffer[ FILENAME_BUFFER_SIZE ];
437 #endif /* UNICODE_STRINGS */
438 	void *fileNamePtr = filenameBuffer;
439 	int status;
440 
441 	printf( "Server thread %lX activated.\n",
442 			( unsigned long ) THREAD_SELF() );
443 	fflush( stdout );
444 
445 	/* Create the session and try to activate it.  We don't do anything
446 	   beyond that point since this is a test of multi-thread handling
447 	   capability, not session handling */
448 	status = cryptCreateSession( &cryptSession, CRYPT_UNUSED,
449 								 CRYPT_SESSION_SSH_SERVER );
450 	if( cryptStatusError( status ) )
451 		{
452 		printf( "cryptCreateSession() failed with error code %d, line %d.\n",
453 				status, __LINE__ );
454 		THREAD_EXIT();
455 		}
456 	if( !setLocalConnect( cryptSession, 22 ) )
457 		{
458 		THREAD_EXIT();
459 		}
460 	filenameFromTemplate( filenameBuffer, SSH_PRIVKEY_FILE_TEMPLATE, 1 );
461 #ifdef UNICODE_STRINGS
462 	mbstowcs( wcBuffer, filenameBuffer, strlen( filenameBuffer ) + 1 );
463 	fileNamePtr = wcBuffer;
464 #endif /* UNICODE_STRINGS */
465 	status = getPrivateKey( &privateKey, fileNamePtr, USER_PRIVKEY_LABEL,
466 							TEST_PRIVKEY_PASSWORD );
467 	if( cryptStatusOK( status ) )
468 		{
469 		status = cryptSetAttribute( cryptSession,
470 									CRYPT_SESSINFO_PRIVATEKEY, privateKey );
471 		cryptDestroyContext( privateKey );
472 		}
473 	if( cryptStatusOK( status ) )
474 		status = cryptSetAttribute( cryptSession, CRYPT_SESSINFO_AUTHRESPONSE,
475 									TRUE );
476 	if( cryptStatusError( status ) )
477 		{
478 		printf( "Private key read/set failed with error code %d, line %d.\n",
479 				status, __LINE__ );
480 		THREAD_EXIT();
481 		}
482 	printf( "Server for thread %lX activated.\n",
483 			( unsigned long ) THREAD_SELF() );
484 	status = cryptSetAttribute( cryptSession, CRYPT_SESSINFO_ACTIVE, TRUE );
485 	printConnectInfo( cryptSession );
486 	if( cryptStatusError( status ) )
487 		printExtError( cryptSession,
488 					   "Attempt to activate SSH server session", status,
489 					   __LINE__ );
490 	cryptDestroySession( cryptSession );
491 	printf( "Server for thread %lX has exited.\n",
492 			( unsigned long ) THREAD_SELF() );
493 	fflush( stdout );
494 
495 	THREAD_EXIT();
496 	}
497 
498 #ifdef WINDOWS_THREADS
sshClientMultiThread(void * dummy)499   unsigned __stdcall sshClientMultiThread( void *dummy )
500 #else
501   void *sshClientMultiThread( void *dummy )
502 #endif /* Windows vs. pthreads */
503 	{
504 	CRYPT_SESSION cryptSession;
505 	int status;
506 
507 	printf( "Client thread %lX activated.\n",
508 			( unsigned long ) THREAD_SELF() );
509 	fflush( stdout );
510 
511 	/* Create the session and try to activate it.  We don't do anything
512 	   beyond that point since this is a test of multi-thread handling
513 	   capability, not session handling */
514 	status = cryptCreateSession( &cryptSession, CRYPT_UNUSED,
515 								 CRYPT_SESSION_SSH );
516 	if( cryptStatusError( status ) )
517 		{
518 		printf( "cryptCreateSession() failed with error code %d, line %d.\n",
519 				status, __LINE__ );
520 		THREAD_EXIT();
521 		}
522 	if( !setLocalConnect( cryptSession, 22 ) )
523 		{
524 		THREAD_EXIT();
525 		}
526 	status = cryptSetAttribute( cryptSession,
527 								CRYPT_OPTION_NET_CONNECTTIMEOUT, 10 );
528 	if( cryptStatusOK( status ) )
529 		{
530 		status = cryptSetAttributeString( cryptSession,
531 										  CRYPT_SESSINFO_USERNAME,
532 										  sshInfo[ SSH2_SERVER_NO ].userName,
533 										  paramStrlen( sshInfo[ SSH2_SERVER_NO ].userName ) );
534 		}
535 	if( cryptStatusOK( status ) )
536 		{
537 		status = cryptSetAttributeString( cryptSession,
538 										  CRYPT_SESSINFO_PASSWORD,
539 										  sshInfo[ SSH2_SERVER_NO ].password,
540 										  paramStrlen( sshInfo[ SSH2_SERVER_NO ].password ) );
541 		}
542 	if( cryptStatusError( status ) )
543 		{
544 		printf( "cryptSetAttribute/AttributeString() failed with error code "
545 				"%d, line %d.\n", status, __LINE__ );
546 		THREAD_EXIT();
547 		}
548 	printf( "Client for thread %lX activated.\n",
549 			( unsigned long ) THREAD_SELF() );
550 	status = cryptSetAttribute( cryptSession, CRYPT_SESSINFO_ACTIVE, TRUE );
551 	printConnectInfo( cryptSession );
552 	if( cryptStatusError( status ) )
553 		{
554 		printExtError( cryptSession,
555 					   "Attempt to activate SSH client session", status,
556 					   __LINE__ );
557 		}
558 	cryptDestroySession( cryptSession );
559 	printf( "Client for thread %lX has exited.\n",
560 			( unsigned long ) THREAD_SELF() );
561 	fflush( stdout );
562 
563 	THREAD_EXIT();
564 	}
565 
testSessionSSHClientServerMultiThread(void)566 int testSessionSSHClientServerMultiThread( void )
567 	{
568 	return( multiThreadDispatch( sshClientMultiThread,
569 								 sshServerMultiThread, MAX_NO_THREADS ) );
570 	}
571 #endif /* OS-specific threading functions */
572 
573 #ifdef USE_SSH_EXTENDED
574 
575 /* Create an SSH channel */
576 
createChannel(const CRYPT_SESSION cryptSession,const C_STR type,const C_STR arg1)577 static int createChannel( const CRYPT_SESSION cryptSession,
578 						  const C_STR type, const C_STR arg1 )
579 	{
580 	int status;
581 
582 	status = cryptSetAttribute( cryptSession, CRYPT_SESSINFO_SSH_CHANNEL,
583 								CRYPT_UNUSED );
584 	if( cryptStatusOK( status ) )
585 		status = cryptSetAttributeString( cryptSession,
586 										  CRYPT_SESSINFO_SSH_CHANNEL_TYPE,
587 										  type, paramStrlen( type ) );
588 	if( cryptStatusOK( status ) )
589 		status = cryptSetAttributeString( cryptSession,
590 										  CRYPT_SESSINFO_SSH_CHANNEL_ARG1,
591 										  arg1, paramStrlen( arg1 ) );
592 	return( status );
593 	}
594 
595 /* Print information on an SSH channel */
596 
printChannelInfo(const CRYPT_SESSION cryptSession,const SSH_TEST_TYPE testType,const BOOLEAN isServer)597 static BOOLEAN printChannelInfo( const CRYPT_SESSION cryptSession,
598 								 const SSH_TEST_TYPE testType,
599 								 const BOOLEAN isServer )
600 	{
601 	C_CHR stringBuffer[ CRYPT_MAX_TEXTSIZE + 1 ];
602 	C_CHR argBuffer[ CRYPT_MAX_TEXTSIZE + 1 ];
603 	int channel, stringLength, argLength = 0, status;
604 
605 	status = cryptGetAttribute( cryptSession, CRYPT_SESSINFO_SSH_CHANNEL,
606 								&channel );
607 	if( cryptStatusOK( status ) )
608 		{
609 		status = cryptGetAttributeString( cryptSession,
610 										  CRYPT_SESSINFO_SSH_CHANNEL_TYPE,
611 										  stringBuffer, &stringLength );
612 		}
613 	if( cryptStatusError( status ) )
614 		{
615 		printf( "%sCouldn't query channel ID/type, status %d, line %d.\n",
616 				isServer ? "SVR: " : "", status, __LINE__ );
617 		return( FALSE );
618 		}
619 #ifdef UNICODE_STRINGS
620 	stringBuffer[ stringLength / sizeof( wchar_t ) ] = TEXT( '\0' );
621 #else
622 	stringBuffer[ stringLength ] = '\0';
623 #endif /* UNICODE_STRINGS */
624 	if( !paramStrcmp( stringBuffer, TEXT( "subsystem" ) ) || \
625 		!paramStrcmp( stringBuffer, TEXT( "direct-tcpip" ) ) )
626 		{
627 		status = cryptGetAttributeString( cryptSession,
628 										  CRYPT_SESSINFO_SSH_CHANNEL_ARG1,
629 										  argBuffer, &argLength );
630 		if( cryptStatusError( status ) )
631 			{
632 			printf( "%sCouldn't query channel arg, status %d, line %d.\n",
633 					isServer ? "SVR: " : "", status, __LINE__ );
634 			return( FALSE );
635 			}
636 #ifdef UNICODE_STRINGS
637 		argBuffer[ argLength / sizeof( wchar_t ) ] = TEXT( '\0' );
638 		printf( "SVR: Client opened channel #%d, type '%S', arg '%S'.\n",
639 				channel, stringBuffer, argBuffer );
640 #else
641 		argBuffer[ argLength ] = '\0';
642 		printf( "SVR: Client opened channel #%d, type '%s', arg '%s'.\n",
643 				channel, stringBuffer, argBuffer );
644 #endif /* UNICODE_STRINGS */
645 		fflush( stdout );
646 
647 		return( TRUE );
648 		}
649 
650 	if( testType == SSH_TEST_SUBSYSTEM )
651 		{
652 		printf( "SVR: Client requested subsystem but server reported "
653 				"request as '%s', line %d.\n", stringBuffer, __LINE__ );
654 		return( FALSE );
655 		}
656 
657 #ifdef UNICODE_STRINGS
658 	printf( "SVR: Client opened channel #%d, type '%S'.\n",
659 			channel, stringBuffer );
660 #else
661 	printf( "SVR: Client opened channel #%d, type '%s'.\n",
662 			channel, stringBuffer );
663 #endif /* UNICODE_STRINGS */
664 	fflush( stdout );
665 	return( TRUE );
666 	}
667 #endif /* USE_SSH_EXTENDED */
668 
669 /* Print information on data sent over an SSH channel */
670 
printDataInfo(CRYPT_SESSION cryptSession,char * buffer,int * bytesCopied,const BOOLEAN isServer,const BOOLEAN isErrorTest)671 static int printDataInfo( CRYPT_SESSION cryptSession,
672 						  char *buffer, int *bytesCopied,
673 						  const BOOLEAN isServer,
674 						  const BOOLEAN isErrorTest )
675 	{
676 	int channel = 0, status;
677 
678 #ifdef USE_SSH_EXTENDED
679 	status = cryptGetAttribute( cryptSession, CRYPT_SESSINFO_SSH_CHANNEL,
680 								&channel );
681 	if( cryptStatusError( status ) )
682 		{
683 		printExtError( cryptSession,
684 					   isServer ? "SVR: Couldn't get data channel number" : \
685 								  "Couldn't get data channel number",
686 					   status, __LINE__ );
687 		return( status );
688 		}
689 #endif /* USE_SSH_EXTENDED */
690 	status = cryptPopData( cryptSession, buffer, BUFFER_SIZE, bytesCopied );
691 	if( cryptStatusError( status ) )
692 		{
693 		printExtError( cryptSession,
694 					   isServer ? "SVR: Client data read failed" : \
695 								  "Server data read failed",
696 					   status, __LINE__ );
697 #ifndef NDEBUG
698 		if( isErrorTest )
699 			{
700 			if( !isServer && status != CRYPT_ERROR_SIGNATURE )
701 					{
702 					printf( "Test returned status %d, should have been %d.\n",
703 							status, CRYPT_ERROR_SIGNATURE );
704 					return( status );
705 					}
706 
707 			/* These tests are supposed to fail, so if this happens then the
708 			   overall test has succeeded */
709 			puts( "  (This test checks error handling, so the failure "
710 				  "response is correct).\n" );
711 			return( SENTINEL );
712 			}
713 #endif /* !NDEBUG */
714 		return( status );
715 		}
716 	buffer[ *bytesCopied ] = '\0';
717 	printf( "%s---- %s sent %d bytes on channel #%d ----\n",
718 			isServer ? "SVR: " : "", isServer ? "Client" : "Server",
719 			*bytesCopied, channel );
720 	if( isServer )
721 		printf( "SVR: " );
722 	puts( buffer );
723 	printf( "%s---- End of output ----\n", isServer ? "SVR: " : "" );
724 	fflush( stdout );
725 
726 	return( CRYPT_OK );
727 	}
728 
729 /* Print information on SSH authorisation info */
730 
printAuthInfo(CRYPT_SESSION cryptSession)731 static BOOLEAN printAuthInfo( CRYPT_SESSION cryptSession )
732 	{
733 	C_CHR stringBuffer[ CRYPT_MAX_TEXTSIZE + 1 ];
734 	int length, status;
735 
736 	status = cryptGetAttributeString( cryptSession, CRYPT_SESSINFO_USERNAME,
737 									  stringBuffer, &length );
738 	if( cryptStatusOK( status ) )
739 		{
740 #ifdef UNICODE_STRINGS
741 		stringBuffer[ length / sizeof( wchar_t ) ] = TEXT( '\0' );
742 		printf( "SVR: User name = '%S', ", stringBuffer );
743 #else
744 		stringBuffer[ length ] = '\0';
745 		printf( "SVR: User name = '%s', ", stringBuffer );
746 #endif /* UNICODE_STRINGS */
747 		}
748 	if( cryptStatusOK( status ) )
749 		status = cryptGetAttributeString( cryptSession, CRYPT_SESSINFO_PASSWORD,
750 										  stringBuffer, &length );
751 	if( cryptStatusOK( status ) )
752 		{
753 #ifdef UNICODE_STRINGS
754 		stringBuffer[ length / sizeof( wchar_t ) ] = TEXT( '\0' );
755 		printf( "password = '%S'.\n", stringBuffer );
756 #else
757 		stringBuffer[ length ] = '\0';
758 		printf( "password = '%s'.\n", stringBuffer );
759 #endif /* UNICODE_STRINGS */
760 		}
761 	if( cryptStatusError( status ) )
762 		{
763 		printf( "SVR: Couldn't read client authentication details, "
764 				"status = %d, line %d.\n", status, __LINE__ );
765 		return( FALSE );
766 		}
767 	fflush( stdout );
768 
769 	return( TRUE );
770 	}
771 
772 /****************************************************************************
773 *																			*
774 *								SSH Routines Test							*
775 *																			*
776 ****************************************************************************/
777 
778 /* If we're testing dual-thread handling of sessions, we need to provide a
779    forward declaration of the threading function since it's called from
780    within the SSH connect code */
781 
782 #ifdef WINDOWS_THREADS
783   unsigned __stdcall ssh2ServerThread( void *arg );
784 #endif /* WINDOWS_THREADS */
785 
786 /* Establish an SSH session */
787 
connectSSH(const CRYPT_SESSION_TYPE sessionType,const SSH_TEST_TYPE testType,const BOOLEAN localSession)788 static int connectSSH( const CRYPT_SESSION_TYPE sessionType,
789 					   const SSH_TEST_TYPE testType,
790 					   const BOOLEAN localSession )
791 	{
792 	CRYPT_SESSION cryptSession;
793 #ifdef SSH2_SERVER_NAME
794 	const C_STR serverName = SSH2_SERVER_NAME;
795 #else
796 	const C_STR serverName = localSession ? TEXT( "localhost" ) : \
797 											sshInfo[ SSH2_SERVER_NO ].name;
798 #endif /* SSH2_SERVER_NAME */
799 	const BOOLEAN isServer = ( sessionType == CRYPT_SESSION_SSH_SERVER ) ? \
800 							   TRUE : FALSE;
801 	const BOOLEAN isErrorTest = ( testType >= SSH_TEST_CORRUPT_HANDSHAKE && \
802 								  testType <= SSH_TEST_BADSIG_DATA ) ? \
803 								  TRUE : FALSE;
804 	char buffer[ BUFFER_SIZE ];
805 #ifdef USE_SSH_EXTENDED
806 	int channel;
807 #endif /* USE_SSH_EXTENDED */
808 	int bytesCopied, status;
809 
810 	/* If this is a local session, synchronise the client and server */
811 	if( localSession )
812 		{
813 		if( isServer )
814 			{
815 			/* Acquire the init mutex */
816 			acquireMutex();
817 			}
818 		else
819 			{
820 			/* We're the client Wait for the server to finish initialising */
821 			if( waitMutex() == CRYPT_ERROR_TIMEOUT )
822 				{
823 				printf( "Timed out waiting for server to initialise, "
824 						"line %d.\n", __LINE__ );
825 				return( FALSE );
826 				}
827 			}
828 		}
829 
830 	/* If this is the dual-thread server test and we're the second server
831 	   thread, skip the portions that have already been handled by the first
832 	   thread */
833 #ifdef WINDOWS_THREADS
834 	if( isServer && testType == SSH_TEST_DUALTHREAD2 )
835 		goto dualThreadContinue;
836 #endif /* WINDOWS_THREADS */
837 
838 	printf( "%sTesting %sSSH%s session...\n",
839 			isServer ? "SVR: " : "",
840 			localSession ? "local " : "",
841 			( testType == SSH_TEST_DSAKEY ) ? " with DSA server key" : \
842 			( testType == SSH_TEST_ECCKEY ) ? " with ECDSA server key" : \
843 			( testType == SSH_TEST_SUBSYSTEM ) ? " SFTP" : \
844 			( testType == SSH_TEST_PORTFORWARDING ) ? " port-forwarding" : \
845 			( testType == SSH_TEST_EXEC ) ? " remote exec" : \
846 			( testType == SSH_TEST_MULTICHANNEL ) ? " multi-channel" : \
847 			( testType == SSH_TEST_CLIENTCERT ) ? " pubkey-auth" : \
848 			isErrorTest ? " with checking for error handling" : "" );
849 	if( !isServer && !localSession )
850 		{
851 #ifdef UNICODE_STRINGS
852 		printf( "  Remote host: %S.\n", serverName );
853 #else
854 		printf( "  Remote host: %s.\n", serverName );
855 #endif /* UNICODE_STRINGS */
856 		}
857 	fflush( stdout );
858 
859 	/* Create the session */
860 	status = cryptCreateSession( &cryptSession, CRYPT_UNUSED, sessionType );
861 	if( status == CRYPT_ERROR_PARAM3 )	/* SSH session access not available */
862 		return( CRYPT_ERROR_NOTAVAIL );
863 	if( cryptStatusError( status ) )
864 		{
865 		printf( "cryptCreateSession() failed with error code %d, line %d.\n",
866 				status, __LINE__ );
867 		return( FALSE );
868 		}
869 #ifndef NDEBUG
870 	if( isServer && isErrorTest )
871 		{
872 		cryptSetFaultType( ( testType == SSH_TEST_CORRUPT_HANDSHAKE ) ? \
873 							 FAULT_SESSION_CORRUPT_HANDSHAKE : \
874 						   ( testType == SSH_TEST_CORRUPT_DATA ) ? \
875 							 FAULT_SESSION_CORRUPT_DATA : \
876 						   ( testType == SSH_TEST_WRONGCERT ) ? \
877 							 FAULT_SESSION_WRONGCERT : \
878 						   ( testType == SSH_TEST_BADSIG_HASH ) ? \
879 							 FAULT_SESSION_BADSIG_HASH : \
880 						   ( testType == SSH_TEST_BADSIG_DATA ) ? \
881 							 FAULT_SESSION_BADSIG_DATA : FAULT_NONE );
882 		}
883 #endif /* !NDEBUG */
884 
885 	/* Set up the server and user information and activate the session */
886 	if( isServer )
887 		{
888 		CRYPT_CONTEXT privateKey;
889 		BYTE filenameBuffer[ FILENAME_BUFFER_SIZE ];
890 #ifdef UNICODE_STRINGS
891 		wchar_t wcBuffer[ FILENAME_BUFFER_SIZE ];
892 #endif /* UNICODE_STRINGS */
893 		void *fileNamePtr = filenameBuffer;
894 
895 		if( !setLocalConnect( cryptSession, 22 ) )
896 			return( FALSE );
897 		filenameFromTemplate( filenameBuffer, SSH_PRIVKEY_FILE_TEMPLATE,
898 							  ( testType == SSH_TEST_ECCKEY ) ? 3 : \
899 							  ( testType == SSH_TEST_DSAKEY ) ? 2 : 1 );
900 #ifdef UNICODE_STRINGS
901 		mbstowcs( wcBuffer, filenameBuffer, strlen( filenameBuffer ) + 1 );
902 		fileNamePtr = wcBuffer;
903 #endif /* UNICODE_STRINGS */
904 		status = getPrivateKey( &privateKey, fileNamePtr, USER_PRIVKEY_LABEL,
905 								TEST_PRIVKEY_PASSWORD );
906 		if( cryptStatusOK( status ) )
907 			{
908 			status = cryptSetAttribute( cryptSession,
909 										CRYPT_SESSINFO_PRIVATEKEY, privateKey );
910 			cryptDestroyContext( privateKey );
911 			}
912 		}
913 	else
914 		{
915 		if( localSession )
916 			{
917 			if( !setLocalConnect( cryptSession, 22 ) )
918 				return( FALSE );
919 			}
920 		else
921 			{
922 			status = cryptSetAttributeString( cryptSession,
923 							CRYPT_SESSINFO_SERVER_NAME, serverName,
924 							paramStrlen( serverName ) );
925 			}
926 		if( cryptStatusOK( status ) )
927 			{
928 			status = cryptSetAttributeString( cryptSession,
929 							CRYPT_SESSINFO_USERNAME,
930 							sshInfo[ SSH2_SERVER_NO ].userName,
931 							paramStrlen( sshInfo[ SSH2_SERVER_NO ].userName ) );
932 			}
933 		if( cryptStatusOK( status ) )
934 			{
935 			if( testType == SSH_TEST_CLIENTCERT )
936 				{
937 				CRYPT_CONTEXT privateKey;
938 
939 				status = getPrivateKey( &privateKey, USER_PRIVKEY_FILE,
940 							USER_PRIVKEY_LABEL, TEST_PRIVKEY_PASSWORD );
941 				if( cryptStatusOK( status ) )
942 					{
943 					status = cryptSetAttribute( cryptSession,
944 									CRYPT_SESSINFO_PRIVATEKEY, privateKey );
945 					cryptDestroyContext( privateKey );
946 					}
947 				}
948 			else
949 				{
950 #ifdef USER_SUPPLIED_PASSWORD
951 				char password[ 256 ];
952 
953 				printf( "Enter SSHv2 server password: " );
954 				fgets( password, 255, stdin );
955 				password[ strlen( password ) - 1 ] = '\0';
956 				status = cryptSetAttributeString( cryptSession,
957 							CRYPT_SESSINFO_PASSWORD, password,
958 							strlen( password ) );
959 #else
960 				status = cryptSetAttributeString( cryptSession,
961 							CRYPT_SESSINFO_PASSWORD,
962 							sshInfo[ SSH2_SERVER_NO ].password,
963 							paramStrlen( sshInfo[ SSH2_SERVER_NO ].password ) );
964 #endif /* User-supplied password */
965 				}
966 			}
967 #ifdef USE_SSH_EXTENDED
968 		if( cryptStatusOK( status ) && \
969 			( testType == SSH_TEST_SUBSYSTEM ) )
970 			{
971 			status = createChannel( cryptSession, TEXT( "subsystem" ),
972 									TEXT( "sftp" ) );
973 			}
974 		if( cryptStatusOK( status ) && \
975 			( testType == SSH_TEST_PORTFORWARDING || \
976 			  testType == SSH_TEST_MULTICHANNEL ) )
977 			{
978 			status = createChannel( cryptSession, TEXT( "direct-tcpip" ),
979 									TEXT( "localhost:1234" ) );
980 			}
981 #endif /* USE_SSH_EXTENDED */
982 		if( cryptStatusOK( status ) && \
983 			( testType == SSH_TEST_FINGERPRINT ) )
984 			{
985 			BYTE fingerPrint[ CRYPT_MAX_HASHSIZE ];
986 
987 			/* Set a dummy (all-zero) fingerprint to force the connect to
988 			   fail */
989 			memset( fingerPrint, 0, CRYPT_MAX_HASHSIZE );
990 			status = cryptSetAttributeString( cryptSession,
991 											  CRYPT_SESSINFO_SERVER_FINGERPRINT_SHA1,
992 											  fingerPrint, 20 );
993 			}
994 #ifdef USE_SSH_EXTENDED
995 		if( cryptStatusOK( status ) && \
996 			( testType == SSH_TEST_EXEC ) )
997 			{
998 			status = createChannel( cryptSession, TEXT( "exec" ),
999 									TEXT( "/bin/netstat" ) );
1000 			}
1001 #endif /* USE_SSH_EXTENDED */
1002 		}
1003 	if( cryptStatusOK( status ) && isServer && \
1004 		( testType != SSH_TEST_CONFIRMAUTH && \
1005 		  testType != SSH_TEST_DUALTHREAD ) )
1006 		{
1007 		/* If we're not testing manual confirmation of client auth, have
1008 		   cryptlib automatically confirm the auth */
1009 		status = cryptSetAttribute( cryptSession, CRYPT_SESSINFO_AUTHRESPONSE,
1010 									TRUE );
1011 		}
1012 	if( cryptStatusError( status ) )
1013 		{
1014 		printf( "%scryptSetAttribute/AttributeString() failed with error "
1015 				"code %d, line %d.\n", isServer ? "SVR: " : "", status,
1016 				__LINE__ );
1017 		return( FALSE );
1018 		}
1019 
1020 	/* Activate the session.  Since we need to be able to process out-of-
1021 	   band signalling such as channel control messages, we set a non-zero
1022 	   timeout for reads */
1023 	cryptSetAttribute( cryptSession, CRYPT_OPTION_NET_READTIMEOUT, 5 );
1024 	if( localSession )
1025 		{
1026 		/* For the loopback test we also increase the connection timeout to
1027 		   a higher-than-normal level, since this gives us more time for
1028 		   tracing through the code when debugging */
1029 		cryptSetAttribute( cryptSession, CRYPT_OPTION_NET_CONNECTTIMEOUT,
1030 						   120 );
1031 		}
1032 	if( localSession && isServer )
1033 		{
1034 		/* Tell the client that we're ready to go */
1035 		releaseMutex();
1036 		}
1037 	status = cryptSetAttribute( cryptSession, CRYPT_SESSINFO_ACTIVE, TRUE );
1038 	if( isServer )
1039 		{
1040 #ifdef WINDOWS_THREADS
1041 		if( isServer && testType == SSH_TEST_DUALTHREAD && \
1042 			status == CRYPT_ENVELOPE_RESOURCE )
1043 			{
1044 			static CRYPT_SESSION localCryptSession = 0;
1045 			int arg = SSH_TEST_DUALTHREAD2;
1046 			unsigned threadID;
1047 
1048 			/* Start a second thread to complete the handshake and exit */
1049 			localCryptSession = cryptSession;
1050 			_beginthreadex( NULL, 0, ssh2ServerThread, &arg, 0, &threadID );
1051 			return( TRUE );
1052 
1053 			/* The second thread continues from here */
1054 dualThreadContinue:
1055 			assert( localSession > 0 );
1056 			cryptSession = localCryptSession;
1057 
1058 			/* Allow the auth.and complete the handshake */
1059 			puts( "SVR: Confirming authentication to client..." );
1060 			status = cryptSetAttribute( cryptSession,
1061 										CRYPT_SESSINFO_AUTHRESPONSE, TRUE );
1062 			if( cryptStatusOK( status ) )
1063 				status = cryptSetAttribute( cryptSession,
1064 											CRYPT_SESSINFO_ACTIVE, TRUE );
1065 			}
1066 #endif /* WINDOWS_THREADS */
1067 		if( status == CRYPT_ENVELOPE_RESOURCE )
1068 			{
1069 			/* The client has tried to authenticate themselves, print the
1070 			   info */
1071 			if( !printAuthInfo( cryptSession ) )
1072 				return( FALSE );
1073 
1074 			/* Deny the auth.and force them to retry, unless it's a loopback
1075 			   test which is non-interactive and for which the client can't
1076 			   perform an interactive re-auth */
1077 			if( !localSession )
1078 				{
1079 				puts( "SVR: Denying authentication to client, who should "
1080 					  "reauth..." );
1081 				status = cryptSetAttribute( cryptSession,
1082 											CRYPT_SESSINFO_AUTHRESPONSE,
1083 											FALSE );
1084 				if( cryptStatusOK( status ) )
1085 					status = cryptSetAttribute( cryptSession,
1086 												CRYPT_SESSINFO_ACTIVE, TRUE );
1087 				if( status != CRYPT_ENVELOPE_RESOURCE )
1088 					{
1089 					printExtError( cryptSession,
1090 								   "SVR: Attempt to deny auth.to client",
1091 								   status, __LINE__ );
1092 					return( FALSE );
1093 					}
1094 				if( !printAuthInfo( cryptSession ) )
1095 					return( FALSE );
1096 				}
1097 
1098 			/* Allow the auth.and complete the handshake */
1099 			puts( "SVR: Confirming authentication to client..." );
1100 			status = cryptSetAttribute( cryptSession,
1101 										CRYPT_SESSINFO_AUTHRESPONSE, TRUE );
1102 			if( cryptStatusOK( status ) )
1103 				status = cryptSetAttribute( cryptSession,
1104 											CRYPT_SESSINFO_ACTIVE, TRUE );
1105 			}
1106 
1107 		/* Now that the handshake is complete, display the connection info */
1108 		if( cryptStatusOK( status ) && !printConnectInfo( cryptSession ) )
1109 			return( FALSE );
1110 		}
1111 	if( cryptStatusError( status ) )
1112 		{
1113 		if( testType == SSH_TEST_FINGERPRINT )
1114 			{
1115 			/* We've forced the connect to fail by using a dummy fingerprint,
1116 			   everything is OK */
1117 			if( isServer )
1118 				printf( "SVR: " );
1119 			puts( "SSH client rejected key with invalid fingerprint." );
1120 			cryptDestroySession( cryptSession );
1121 			puts( isServer ? "SVR: SSH server session succeeded.\n" : \
1122 							 "SSH client session succeeded.\n" );
1123 			fflush( stdout );
1124 			return( TRUE );
1125 			}
1126 		printExtError( cryptSession, isServer ? \
1127 					   "SVR: Attempt to activate SSH server session" : \
1128 					   "Attempt to activate SSH client session", status,
1129 					   __LINE__ );
1130 		if( isErrorTest )
1131 			{
1132 			/* These tests are supposed to fail, so if this happens then the
1133 			   overall test has succeeded */
1134 			cryptDestroySession( cryptSession );
1135 			if( !isServer && status != CRYPT_ERROR_SIGNATURE )
1136 				{
1137 				printf( "Test returned status %d, should have been "
1138 						"%d.\n", status, CRYPT_ERROR_SIGNATURE );
1139 				return( FALSE );
1140 				}
1141 			puts( "  (This test checks error handling, so the failure "
1142 				  "response is correct).\n" );
1143 			return( TRUE );
1144 			}
1145 		if( localSession )
1146 			{
1147 			/* If it's a local session then none of the following soft-
1148 			   failure conditions are valid */
1149 			return( FALSE );
1150 			}
1151 		if( !isServer && isServerDown( cryptSession, status ) )
1152 			{
1153 			puts( "  (Server could be down, faking it and continuing...)\n" );
1154 			cryptDestroySession( cryptSession );
1155 			return( CRYPT_ERROR_FAILED );
1156 			}
1157 		cryptDestroySession( cryptSession );
1158 		if( status == CRYPT_ERROR_WRONGKEY )
1159 			{
1160 			/* This is another possible soft error condition, the default
1161 			   username and password shouldn't be able to get into many
1162 			   machines */
1163 			puts( "  (Incorrect username/password, continuing...)\n" );
1164 			return( TRUE );
1165 			}
1166 		if( status == CRYPT_ERROR_NOSECURE )
1167 			{
1168 			/* Another soft error condition, the server can't handle the
1169 			   security level we want (usually occurs when trying to perform
1170 			   an SSHv2 connect to an SSHv1 server) */
1171 			puts( "  (Insufficiently secure protocol parameters, "
1172 				  "continuing...)\n" );
1173 			return( TRUE );
1174 			}
1175 		return( FALSE );
1176 		}
1177 	if( testType == SSH_TEST_FINGERPRINT )
1178 		{
1179 		printf( "Attempt to connect with invalid key fingerprint succeeded "
1180 				"when it should\nhave failed, line %d.\n", __LINE__ );
1181 		return( FALSE );
1182 		}
1183 
1184 	/* The error tests should cause handshake failures, so getting to this
1185 	   point is an error */
1186 	if( isErrorTest && testType != SSH_TEST_CORRUPT_DATA )
1187 		{
1188 		cryptDestroySession( cryptSession );
1189 		puts( "  (This test should have led to a handshake failure but "
1190 			  "didn't, test has\n   failed).\n" );
1191 		return( FALSE );
1192 		}
1193 
1194 	/* Report the session security info.  In standard SSH usage
1195 	   channel == session so we only try and report channel details if the
1196 	   SSH extended capabilities are enabled */
1197 	if( !printSecurityInfo( cryptSession, isServer, TRUE, FALSE, FALSE ) )
1198 		return( FALSE );
1199 #ifdef USE_SSH_EXTENDED
1200 	status = cryptGetAttribute( cryptSession, CRYPT_SESSINFO_SSH_CHANNEL,
1201 								&channel );
1202 	if( cryptStatusError( status ) )
1203 		{
1204 		printf( "cryptGetAttributeString() failed with error code "
1205 				"%d, line %d.\n", status, __LINE__ );
1206 		return( FALSE );
1207 		}
1208 	printf( "%sCurrent channel is #%d.\n", isServer ? "SVR: " : "",
1209 			channel );
1210 #endif /* USE_SSH_EXTENDED */
1211 	fflush( stdout );
1212 
1213 	/* Report additional channel-specific information */
1214 	if( isServer )
1215 		{
1216 		/* Display info on any channels that the client has opened.  As with
1217 		   the earlier channel display, we can only do this if SSH extended
1218 		   capabilities are enabled */
1219 #ifdef USE_SSH_EXTENDED
1220 		if( !printChannelInfo( cryptSession, testType, TRUE ) )
1221 			return( FALSE );
1222 #endif /* USE_SSH_EXTENDED */
1223 
1224 		/* Process any additional information that the client may throw
1225 		   at us after the user-auth has completed */
1226 		status = cryptPopData( cryptSession, buffer, BUFFER_SIZE,
1227 							   &bytesCopied );
1228 		if( cryptStatusOK( status ) && bytesCopied > 0 )
1229 			{
1230 			printf( "SVR: Client sent additional %d bytes post-"
1231 					"handshake data.\n", bytesCopied );
1232 			fflush( stdout );
1233 			}
1234 #ifdef USE_SSH_EXTENDED
1235 		else
1236 			{
1237 			if( status == CRYPT_ENVELOPE_RESOURCE )
1238 				{
1239 				/* The client performed additional control actions that were
1240 				   handled inline as part of the data-pop, report the
1241 				   details */
1242 				if( !printChannelInfo( cryptSession, testType, TRUE ) )
1243 					return( FALSE );
1244 				}
1245 			}
1246 #endif /* USE_SSH_EXTENDED */
1247 		}
1248 
1249 	/* If we're using the SFTP subsystem as a server, use the special-case
1250 	   routines for this */
1251 #if defined( WINDOWS_THREADS ) && 0
1252 	if( testType == SSH_TEST_SUBSYSTEM )
1253 		{
1254 		if( isServer )
1255 			{
1256 			int sftpServer( const CRYPT_SESSION cryptSession );
1257 
1258 			status = sftpServer( cryptSession );
1259 			if( cryptStatusError( status ) )
1260 				{
1261 				printf( "SVR: Couldn't receive SFTP data from client, status %d, "
1262 						"line %d.\n", status, __LINE__ );
1263 				return( FALSE );
1264 				}
1265 			cryptDestroySession( cryptSession );
1266 			puts( "SVR: SFTP server session succeeded.\n" );
1267 			fflush( stdout );
1268 			return( TRUE );
1269 			}
1270 		else
1271 			{
1272 			int sftpClient( const CRYPT_SESSION cryptSession );
1273 
1274 			status = sftpClient( cryptSession );
1275 			if( cryptStatusError( status ) )
1276 				{
1277 				printf( "Couldn't send SFTP data to server, status %d, line "
1278 						"%d.\n", status, __LINE__ );
1279 				return( FALSE );
1280 				}
1281 			cryptDestroySession( cryptSession );
1282 			puts( "SFTP client session succeeded.\n" );
1283 			fflush( stdout );
1284 			return( TRUE );
1285 			}
1286 		}
1287 #endif /* WINDOWS_THREADS && 0 */
1288 
1289 #ifdef USE_SSH_EXTENDED
1290 	/* If we're performing a multi-channel test, open a second channel (the
1291 	   server handles this as part of its general connect-handling) */
1292 	if( testType == SSH_TEST_MULTICHANNEL && !isServer )
1293 		{
1294 		status = createChannel( cryptSession, TEXT( "direct-tcpip" ),
1295 								TEXT( "localhost:5678" ) );
1296 		if( cryptStatusOK( status ) )
1297 			status = cryptGetAttribute( cryptSession,
1298 										CRYPT_SESSINFO_SSH_CHANNEL,
1299 										&channel );
1300 		if( cryptStatusOK( status ) )
1301 			status = cryptSetAttribute( cryptSession,
1302 										CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE,
1303 										TRUE );
1304 		if( cryptStatusError( status ) )
1305 			{
1306 			printf( "Couldn't open second SSH chanel, status %d, line "
1307 					"%d.\n", status, __LINE__ );
1308 			return( FALSE );
1309 			}
1310 		printf( "Opened additional channel #%d to server.\n", channel );
1311 		fflush( stdout );
1312 		}
1313 #endif /* USE_SSH_EXTENDED */
1314 
1315 	/* Send data over the SSH link */
1316 	if( isServer )
1317 		{
1318 		/* Send a status message to the client */
1319 		status = cryptPushData( cryptSession, "Welcome to cryptlib, now go "
1320 								"away.\r\n", 35, &bytesCopied );
1321 		if( cryptStatusOK( status ) )
1322 			status = cryptFlushData( cryptSession );
1323 		if( cryptStatusError( status ) || bytesCopied != 35 )
1324 			{
1325 			printf( "SVR: Couldn't send data to client, status %d, line "
1326 					"%d.\n", status, __LINE__ );
1327 			return( FALSE );
1328 			}
1329 		}
1330 
1331 	/* Wait a bit while data arrives */
1332 	delayThread( 2 );
1333 
1334 	/* Print the first lot of output from the other side */
1335 	status = printDataInfo( cryptSession, buffer, &bytesCopied, isServer,
1336 							isErrorTest );
1337 	if( cryptStatusError( status ) )
1338 		{
1339 		/* Some tests are meant to fail because they check failure
1340 		   conditions, if we encounter one of these then we exit cleanly */
1341 		if( status == SENTINEL )
1342 			{
1343 			cryptDestroySession( cryptSession );
1344 			return( TRUE );
1345 			}
1346 
1347 		return( FALSE );
1348 		}
1349 
1350 	/* If we're the server, echo the command to the client */
1351 	if( isServer )
1352 		{
1353 		const int clientBytesCopied = bytesCopied;
1354 		int dummy, i;
1355 
1356 		/* If it's a multi-channel test, send the response back on a
1357 		   different channel.  The currently-selected channel will be the
1358 		   last one that the client opened (#2), so we can hardcode in
1359 		   #1 for testing purposes */
1360 		if( testType == SSH_TEST_MULTICHANNEL )
1361 			{
1362 			status = cryptSetAttribute( cryptSession,
1363 										CRYPT_SESSINFO_SSH_CHANNEL, 1 );
1364 			if( cryptStatusError( status ) )
1365 				{
1366 				printf( "SVR: Couldn't select channel #1 to return data to "
1367 						"client, status %d, line %d.\n", status, __LINE__ );
1368 				return( FALSE );
1369 				}
1370 			}
1371 		for( i = 0; i < clientBytesCopied; i++ )
1372 			{
1373 			if( buffer[ i ] < ' ' || buffer[ i ] >= 0x7F )
1374 				buffer[ i ] = '.';
1375 			}
1376 		status = cryptPushData( cryptSession, "Input was [", 11, &dummy );
1377 		if( cryptStatusOK( status ) && clientBytesCopied > 0 )
1378 			status = cryptPushData( cryptSession, buffer, clientBytesCopied,
1379 									&bytesCopied );
1380 		if( cryptStatusOK( status ) )
1381 			status = cryptPushData( cryptSession, "]\r\n", 3, &dummy );
1382 		if( cryptStatusOK( status ) )
1383 			status = cryptFlushData( cryptSession );
1384 		if( cryptStatusError( status ) || bytesCopied != clientBytesCopied )
1385 			{
1386 			printf( "SVR: Couldn't send data to client, status %d, line "
1387 					"%d.\n", status, __LINE__ );
1388 			return( FALSE );
1389 			}
1390 		}
1391 	else
1392 		{
1393 		/* We're the client, if it's a session to a Unix ssh server, send a
1394 		   sample command and display the output */
1395 		if( !localSession )
1396 			{
1397 			/* Send a command to the server and get the results */
1398 			status = cryptPushData( cryptSession, "ls -l | head -25\n", 18,
1399 									&bytesCopied );
1400 			if( cryptStatusOK( status ) )
1401 				status = cryptFlushData( cryptSession );
1402 			if( cryptStatusError( status ) || bytesCopied != 18 )
1403 				{
1404 				printf( "Couldn't send data to server, status %d, line "
1405 						"%d.\n", status, __LINE__ );
1406 				return( FALSE );
1407 				}
1408 			puts( "Sent 'ls -l | head -25'" );
1409 			delayThread( 3 );
1410 			status = printDataInfo( cryptSession, buffer, &bytesCopied,
1411 									isServer, isErrorTest );
1412 			if( cryptStatusError( status ) )
1413 				return( FALSE );
1414 			}
1415 		else
1416 			{
1417 			/* It's a local session, just send a simple text string for
1418 			   testing */
1419 			status = cryptPushData( cryptSession, "Some test data", 14,
1420 									&bytesCopied );
1421 			if( cryptStatusOK( status ) )
1422 				status = cryptFlushData( cryptSession );
1423 			if( cryptStatusError( status ) || bytesCopied != 14 )
1424 				{
1425 				printf( "Couldn't send data to server, status %d, line "
1426 						"%d.\n", status, __LINE__ );
1427 				return( FALSE );
1428 				}
1429 
1430 			/* Make sure that we stay around long enough to get the
1431 			   server's response */
1432 			delayThread( 1 );
1433 
1434 			/* Print the server's response */
1435 			status = printDataInfo( cryptSession, buffer, &bytesCopied,
1436 									isServer, isErrorTest );
1437 			if( cryptStatusError( status ) )
1438 				return( FALSE );
1439 			}
1440 		}
1441 
1442 	/* If we're performing a multi-channel test, close the second channel */
1443 	if( testType == SSH_TEST_MULTICHANNEL )
1444 		{
1445 		if( isServer )
1446 			{
1447 			/* Perform a dummy pop to process the channel close */
1448 			( void ) cryptPopData( cryptSession, buffer, BUFFER_SIZE,
1449 								   &bytesCopied );
1450 			}
1451 		else
1452 			{
1453 			/* Close the current channel */
1454 			status = cryptSetAttribute( cryptSession,
1455 										CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE,
1456 										FALSE );
1457 			if( cryptStatusError( status ) )
1458 				{
1459 				printf( "Couldn't close second SSH chanel, status %d, line "
1460 						"%d.\n", status, __LINE__ );
1461 				return( FALSE );
1462 				}
1463 			printf( "Closed second channel to server.\n" );
1464 			fflush( stdout );
1465 			}
1466 		}
1467 
1468 	/* Clean up */
1469 	status = cryptDestroySession( cryptSession );
1470 	if( cryptStatusError( status ) )
1471 		{
1472 		printf( "cryptDestroySession() failed with error code %d, line %d.\n",
1473 				status, __LINE__ );
1474 		return( FALSE );
1475 		}
1476 
1477 	puts( isServer ? "SVR: SSH server session succeeded.\n" : \
1478 					 "SSH client session succeeded.\n" );
1479 	fflush( stdout );
1480 	return( TRUE );
1481 	}
1482 
testSessionSSH(void)1483 int testSessionSSH( void )
1484 	{
1485 	return( connectSSH( CRYPT_SESSION_SSH, SSH_TEST_NORMAL, FALSE ) );
1486 	}
testSessionSSHClientCert(void)1487 int testSessionSSHClientCert( void )
1488 	{
1489 	return( connectSSH( CRYPT_SESSION_SSH, SSH_TEST_CLIENTCERT, FALSE ) );
1490 	}
testSessionSSHPortforward(void)1491 int testSessionSSHPortforward( void )
1492 	{
1493 #ifdef USE_SSH_EXTENDED
1494 	return( connectSSH( CRYPT_SESSION_SSH, SSH_TEST_PORTFORWARDING, FALSE ) );
1495 #else
1496 	return( TRUE );
1497 #endif /* USE_SSH_EXTENDED */
1498 	}
testSessionSSHExec(void)1499 int testSessionSSHExec( void )
1500 	{
1501 #ifdef USE_SSH_EXTENDED
1502 	return( connectSSH( CRYPT_SESSION_SSH, SSH_TEST_EXEC, FALSE ) );
1503 #else
1504 	return( TRUE );
1505 #endif /* USE_SSH_EXTENDED */
1506 	}
testSessionSSH_SFTP(void)1507 int testSessionSSH_SFTP( void )
1508 	{
1509 #ifdef USE_SSH_EXTENDED
1510 	return( connectSSH( CRYPT_SESSION_SSH, SSH_TEST_SUBSYSTEM, FALSE ) );
1511 #else
1512 	return( TRUE );
1513 #endif /* USE_SSH_EXTENDED */
1514 	}
testSessionSSHServer(void)1515 int testSessionSSHServer( void )
1516 	{
1517 	int status;
1518 
1519 	createMutex();
1520 	status = connectSSH( CRYPT_SESSION_SSH_SERVER, SSH_TEST_CONFIRMAUTH, FALSE );
1521 	destroyMutex();
1522 
1523 	return( status );
1524 	}
testSessionSSH_SFTPServer(void)1525 int testSessionSSH_SFTPServer( void )
1526 	{
1527 #ifdef USE_SSH_EXTENDED
1528 	int status;
1529 
1530 	createMutex();
1531 	status = connectSSH( CRYPT_SESSION_SSH_SERVER, SSH_TEST_SUBSYSTEM, FALSE );
1532 	destroyMutex();
1533 
1534 	return( status );
1535 #else
1536 	return( TRUE );
1537 #endif /* USE_SSH_EXTENDED */
1538 	}
1539 
1540 /* Perform a client/server loopback test */
1541 
1542 #ifdef WINDOWS_THREADS
1543 
ssh2ServerThread(void * arg)1544 unsigned __stdcall ssh2ServerThread( void *arg )
1545 	{
1546 	const int argValue = *( ( int * ) arg );
1547 
1548 	connectSSH( CRYPT_SESSION_SSH_SERVER, argValue, TRUE );
1549 	_endthreadex( 0 );
1550 	return( 0 );
1551 	}
sshClientServer(const SSH_TEST_TYPE testType)1552 static int sshClientServer( const SSH_TEST_TYPE testType )
1553 	{
1554 	HANDLE hThread;
1555 	unsigned threadID;
1556 	int arg = testType, status;
1557 
1558 	/* Start the server */
1559 	createMutex();
1560 	hThread = ( HANDLE ) _beginthreadex( NULL, 0, ssh2ServerThread, &arg, 0,
1561 										 &threadID );
1562 	Sleep( 1000 );
1563 
1564 	/* Connect to the local server */
1565 	status = connectSSH( CRYPT_SESSION_SSH, testType, TRUE );
1566 	waitForThread( hThread );
1567 	destroyMutex();
1568 	return( status );
1569 	}
1570 
testSessionSSHClientServer(void)1571 int testSessionSSHClientServer( void )
1572 	{
1573 	return( sshClientServer( SSH_TEST_NORMAL ) );
1574 	}
testSessionSSHClientServerDsaKey(void)1575 int testSessionSSHClientServerDsaKey( void )
1576 	{
1577 	return( sshClientServer( SSH_TEST_DSAKEY ) );
1578 	}
testSessionSSHClientServerEccKey(void)1579 int testSessionSSHClientServerEccKey( void )
1580 	{
1581 	/* ECC algorithms may not be available so we only run this test if
1582 	   they've been enabled */
1583 	if( cryptQueryCapability( CRYPT_ALGO_ECDSA, \
1584 							  NULL ) == CRYPT_ERROR_NOTAVAIL )
1585 		return( TRUE );
1586 
1587 	return( sshClientServer( SSH_TEST_ECCKEY ) );
1588 	}
testSessionSSHClientServerFingerprint(void)1589 int testSessionSSHClientServerFingerprint( void )
1590 	{
1591 	/* Note that this test tests the correct functioning of a refused
1592 	   connection when an incorrect key fingerprint is used, so it's
1593 	   supposed to fail */
1594 	return( sshClientServer( SSH_TEST_FINGERPRINT ) );
1595 	}
testSessionSSHClientServerSFTP(void)1596 int testSessionSSHClientServerSFTP( void )
1597 	{
1598 	return( sshClientServer( SSH_TEST_SUBSYSTEM ) );
1599 	}
testSessionSSHClientServerPortForward(void)1600 int testSessionSSHClientServerPortForward( void )
1601 	{
1602 #ifdef USE_SSH_EXTENDED
1603 	return( sshClientServer( SSH_TEST_PORTFORWARDING ) );
1604 #else
1605 	return( TRUE );
1606 #endif /* USE_SSH_EXTENDED */
1607 	}
testSessionSSHClientServerExec(void)1608 int testSessionSSHClientServerExec( void )
1609 	{
1610 #ifdef USE_SSH_EXTENDED
1611 	return( sshClientServer( SSH_TEST_EXEC ) );
1612 #else
1613 	return( TRUE );
1614 #endif /* USE_SSH_EXTENDED */
1615 	}
testSessionSSHClientServerMultichannel(void)1616 int testSessionSSHClientServerMultichannel( void )
1617 	{
1618 #ifdef USE_SSH_EXTENDED
1619 	return( sshClientServer( SSH_TEST_MULTICHANNEL ) );
1620 #else
1621 	return( TRUE );
1622 #endif /* USE_SSH_EXTENDED */
1623 	}
testSessionSSHClientServerDualThread(void)1624 int testSessionSSHClientServerDualThread( void )
1625 	{
1626 	return( sshClientServer( SSH_TEST_DUALTHREAD ) );
1627 	}
testSessionSSHClientServerDebugCheck(void)1628 int testSessionSSHClientServerDebugCheck( void )
1629 	{
1630 #ifndef NDEBUG
1631 	if( !sshClientServer( SSH_TEST_CORRUPT_HANDSHAKE ) )
1632 		return( FALSE );	/* Detect corruption of handshake data */
1633 	if( !sshClientServer( SSH_TEST_CORRUPT_DATA ) )
1634 		return( FALSE );	/* Detect corruption of payload data */
1635 	if( !sshClientServer( SSH_TEST_WRONGCERT ) )
1636 		return( FALSE );	/* Detect wrong key for server */
1637 	if( !sshClientServer( SSH_TEST_BADSIG_HASH ) )
1638 		return( FALSE );	/* Detect corruption of signed DH params */
1639 	if( !sshClientServer( SSH_TEST_BADSIG_DATA ) )
1640 		return( FALSE );	/* Detect corruption of signed DH params */
1641 	cryptSetFaultType( FAULT_NONE );
1642 #endif /* !NDEBUG */
1643 	return( TRUE );
1644 	}
1645 #endif /* WINDOWS_THREADS */
1646 
1647 /****************************************************************************
1648 *																			*
1649 *							SFTP Routines for SSH							*
1650 *																			*
1651 ****************************************************************************/
1652 
1653 /* The following code re-uses internal parts of cryptlib, so it provides its
1654    own dummy functions as stubs for cryptlib-internal ones.  Since this would
1655    produce link errors when cryptlib is statically linked with the test
1656    app, we only enable it for the threaded Windows (i.e. DLL) self-test */
1657 
1658 #if defined( WINDOWS_THREADS ) && 0
1659 
1660 /* The following code is a bare-bones SFTP implementation created purely for
1661    interop/performance testing of cryptlib's SSH implementation.  It does
1662    the bare minimum needed to set up an SFTP transfer, and shouldn't be used
1663    for anything other than testing.
1664 
1665    Rather than creating our own versions of code already present in cryptlib,
1666    we pull in the cryptlib code wholesale here unless we've built cryptlib as
1667    a static lib, in which case it'll already be present.  This is a pretty
1668    ugly hack, but saves having to copy over a pile of cryptlib code.
1669 
1670    Because cryptlib has an internal BYTE type, we need to no-op it out before
1671    we pull in any cryptlib code */
1672 
1673 #undef BYTE
1674 #define BYTE	_BYTE_DUMMY
1675 #ifdef BOOLEAN
1676   #undef BOOLEAN	/* May be a typedef or a #define */
1677 #endif /* BOOLEAN */
1678 #ifndef STATIC_LIB
1679   #include "enc_dec/misc_rw.c"
1680 #endif /* Non-static lib cryptlib */
1681 #undef BYTE
1682 #define BYTE	unsigned char
1683 
1684 /* Replacements for cryptlib stream routines */
1685 
1686 #define sMemDisconnect(	stream )
1687 #define sMemConnect					sMemOpen
1688 #define stell( stream )				( ( stream )->bufPos )
1689 
sSetError(STREAM * stream,const int status)1690 int sSetError( STREAM *stream, const int status )
1691 	{
1692 	stream->status = status;
1693 	return( status );
1694 	}
1695 
sMemOpen(STREAM * stream,void * buffer,const int bufSize)1696 int sMemOpen( STREAM *stream, void *buffer, const int bufSize )
1697 	{
1698 	memset( stream, 0, sizeof( STREAM ) );
1699 	stream->buffer = ( void * ) buffer;
1700 	stream->bufEnd = bufSize;
1701 	return( CRYPT_OK );
1702 	}
1703 
sread(STREAM * stream,void * buffer,const int count)1704 int sread( STREAM *stream, void *buffer, const int count )
1705 	{
1706 	if( stream->bufPos + count > stream->bufEnd )
1707 		{
1708 		sSetError( stream, CRYPT_ERROR_UNDERFLOW );
1709 		return( CRYPT_ERROR_UNDERFLOW );
1710 		}
1711 	memcpy( buffer, stream->buffer + stream->bufPos, count );
1712 	stream->bufPos += count;
1713 	return( CRYPT_OK );
1714 	}
1715 
swrite(STREAM * stream,const void * buffer,const int count)1716 int swrite( STREAM *stream, const void *buffer, const int count )
1717 	{
1718 	if( stream->buffer != NULL )
1719 		{
1720 		if( stream->bufPos + count > stream->bufEnd )
1721 			{
1722 			sSetError( stream, CRYPT_ERROR_OVERFLOW );
1723 			return( CRYPT_ERROR_OVERFLOW );
1724 			}
1725 		memcpy( stream->buffer + stream->bufPos, buffer, count );
1726 		}
1727 	stream->bufPos += count;
1728 	return( CRYPT_OK );
1729 	}
1730 
sgetc(STREAM * stream)1731 int sgetc( STREAM *stream )
1732 	{
1733 	int ch;
1734 
1735 	if( stream->bufPos + 1 > stream->bufEnd )
1736 		{
1737 		sSetError( stream, CRYPT_ERROR_UNDERFLOW );
1738 		return( CRYPT_ERROR_UNDERFLOW );
1739 		}
1740 	ch = stream->buffer[ stream->bufPos ];
1741 	stream->bufPos++;
1742 	return( ch );
1743 	}
1744 
sputc(STREAM * stream,const int data)1745 int sputc( STREAM *stream, const int data )
1746 	{
1747 	if( stream->buffer != NULL )
1748 		{
1749 		if( stream->bufPos + 1 > stream->bufEnd )
1750 			{
1751 			sSetError( stream, CRYPT_ERROR_OVERFLOW );
1752 			return( CRYPT_ERROR_OVERFLOW );
1753 			}
1754 		stream->buffer[ stream->bufPos++ ] = data;
1755 		}
1756 	else
1757 		stream->bufPos++;
1758 	return( CRYPT_OK );
1759 	}
1760 
sseek(STREAM * stream,const long position)1761 int sseek( STREAM *stream, const long position )
1762 	{
1763 	return( 0 );
1764 	}
1765 
sPeek(STREAM * stream)1766 int sPeek( STREAM *stream )
1767 	{
1768 	return( 0 );
1769 	}
1770 
sSkip(STREAM * stream,const long offset)1771 int sSkip( STREAM *stream, const long offset )
1772 	{
1773 	return( 0 );
1774 	}
1775 
sMemDataLeft(const STREAM * stream)1776 int sMemDataLeft( const STREAM *stream )
1777 	{
1778 	return( stream->bufSize - stream->bufPos );
1779 	}
1780 
1781 /* Dummy routines needed in misc_rw.c */
1782 
BN_num_bits(const BIGNUM * a)1783 int BN_num_bits( const BIGNUM *a ) { return 0; }
BN_high_bit(BIGNUM * a)1784 int BN_high_bit( BIGNUM *a ) { return 0; }
BN_bin2bn(const unsigned char * s,int len,BIGNUM * ret)1785 BIGNUM *BN_bin2bn( const unsigned char *s, int len, BIGNUM *ret ) { return NULL; }
BN_bn2bin(const BIGNUM * a,unsigned char * to)1786 int	BN_bn2bin( const BIGNUM *a, unsigned char *to ) { return 0; }
importBignum(BIGNUM * bn,const void * buffer,const int length,const int minLength,const int maxLength,const BIGNUM * maxRange,const BOOLEAN checkKeysize)1787 int importBignum( BIGNUM *bn, const void *buffer, const int length,
1788 				  const int minLength, const int maxLength,
1789 				  const BIGNUM *maxRange, const BOOLEAN checkKeysize ) { return -1; }
exportBignum(void * data,const int dataMaxLength,int * dataLength,const void * bignumPtr)1790 int exportBignum( void *data, const int dataMaxLength, int *dataLength,
1791 				  const void *bignumPtr ) { return -1; }
1792 
1793 /* SFTP command types */
1794 
1795 #define SSH_FXP_INIT			1
1796 #define SSH_FXP_VERSION			2
1797 #define SSH_FXP_OPEN			3
1798 #define SSH_FXP_CLOSE			4
1799 #define SSH_FXP_READ			5
1800 #define SSH_FXP_WRITE			6
1801 #define SSH_FXP_LSTAT			7
1802 #define SSH_FXP_FSTAT			8
1803 #define SSH_FXP_SETSTAT			9
1804 #define SSH_FXP_FSETSTAT		10
1805 #define SSH_FXP_OPENDIR			11
1806 #define SSH_FXP_READDIR			12
1807 #define SSH_FXP_REMOVE			13
1808 #define SSH_FXP_MKDIR			14
1809 #define SSH_FXP_RMDIR			15
1810 #define SSH_FXP_REALPATH		16
1811 #define SSH_FXP_STAT			17
1812 #define SSH_FXP_RENAME			18
1813 #define SSH_FXP_READLINK		19
1814 #define SSH_FXP_SYMLINK			20
1815 #define SSH_FXP_STATUS			101
1816 #define SSH_FXP_HANDLE			102
1817 #define SSH_FXP_DATA			103
1818 #define SSH_FXP_NAME			104
1819 #define SSH_FXP_ATTRS			105
1820 
1821 /* SFTP attribute presence flags.  When these flags are set, the
1822    corresponding file attribute value is present */
1823 
1824 #define SSH_FILEXFER_ATTR_SIZE			0x01
1825 #define SSH_FILEXFER_ATTR_UIDGID		0x02
1826 #define SSH_FILEXFER_ATTR_PERMISSIONSv3	0x04
1827 #define SSH_FILEXFER_ATTR_ACMODTIME		0x08
1828 #define SSH_FILEXFER_ATTR_ACCESSTIME	0x08
1829 #define SSH_FILEXFER_ATTR_CREATETIME	0x10
1830 #define SSH_FILEXFER_ATTR_MODIFYTIME	0x20
1831 #define SSH_FILEXFER_ATTR_PERMISSIONSv4	0x40
1832 #define SSH_FILEXFER_ATTR_ACL			0x40
1833 #define SSH_FILEXFER_ATTR_OWNERGROUP	0x80
1834 #define SSH_FILEXFER_ATTR_SUBSECOND_TIMES 0x100
1835 #define SSH_FILEXFER_ATTR_EXTENDED		0x80000000
1836 
1837 /* SFTP file open/create flags */
1838 
1839 #define SSH_FXF_READ			0x01
1840 #define SSH_FXF_WRITE			0x02
1841 #define SSH_FXF_APPEND			0x04
1842 #define SSH_FXF_CREAT			0x08
1843 #define SSH_FXF_TRUNC			0x10
1844 #define SSH_FXF_EXCL			0x20
1845 #define SSH_FXF_TEXT			0x40
1846 
1847 /* SFTP file types */
1848 
1849 #define SSH_FILETYPE_REGULAR	1
1850 #define SSH_FILETYPE_DIRECTORY	2
1851 #define SSH_FILETYPE_SYMLINK	3
1852 #define SSH_FILETYPE_SPECIAL	4
1853 #define SSH_FILETYPE_UNKNOWN	5
1854 
1855 /* SFTP status codes */
1856 
1857 #define SSH_FX_OK				0
1858 #define SSH_FX_EOF				1
1859 #define SSH_FX_NO_SUCH_FILE		2
1860 #define SSH_FX_PERMISSION_DENIED 3
1861 #define SSH_FX_FAILURE			4
1862 #define SSH_FX_BAD_MESSAGE		5
1863 #define SSH_FX_NO_CONNECTION	6
1864 #define SSH_FX_CONNECTION_LOST	7
1865 #define SSH_FX_OP_UNSUPPORTED	8
1866 #define SSH_FX_INVALID_HANDLE	9
1867 #define SSH_FX_NO_SUCH_PATH		10
1868 #define SSH_FX_FILE_ALREADY_EXISTS 11
1869 #define SSH_FX_WRITE_PROTECT	12
1870 #define SSH_FX_NO_MEDIA			13
1871 
1872 /* A structure to contain SFTP file attributes */
1873 
1874 typedef struct {
1875 	BOOLEAN isDirectory;		/* Whether directory or normal file */
1876 	long size;					/* File size */
1877 	int permissions;			/* File permissions */
1878 	time_t ctime, atime, mtime;	/* File create, access, mod times */
1879 	} SFTP_ATTRS;
1880 
1881 /* A structure to contain SFTP session information */
1882 
1883 #define MAX_HANDLE_SIZE		16
1884 
1885 typedef struct {
1886 	int version;				/* SFTP protocol version */
1887 	long id;					/* Session ID */
1888 	BYTE handle[ MAX_HANDLE_SIZE ];	/* File handle */
1889 	int handleSize;
1890 	} SFTP_INFO;
1891 
1892 /* Read/write SFTP attributes.  This changed completely from v3 to v4, so we
1893    have to treat them as special-cases:
1894 
1895 	uint32		flags
1896 	byte		file_type
1897 	uint64		size (present if ATTR_SIZE)
1898 	string		owner (present if ATTR_OWNERGROUP)
1899 	string		group (present if ATTR_OWNERGROUP)
1900 	uint32		permissions (present if ATTR_PERMISSIONS)
1901 	uint64		atime (present if ATTR_ACCESSTIME)
1902 	uint32		atime_nseconds (present if ATTR_SUBSECOND_TIMES)
1903 	uint64		createtime (present if ATTR_CREATETIME)
1904 	uint32		createtime_nseconds (present if ATTR_SUBSECOND_TIMES)
1905 	uint64		mtime (present if ATTR_MODIFYTIME)
1906 	uint32		mtime_nseconds (present if ATTR_SUBSECOND_TIMES)
1907 	string		acl (present if ATTR_ACL)
1908 	uint32		extended_count (present if ATTR_EXTENDED)
1909 		string	extended_type
1910 		string	extended_value
1911    		[ extended_count type/value pairs ] */
1912 
sizeofAttributes(SFTP_ATTRS * attributes,const int version)1913 static int sizeofAttributes( SFTP_ATTRS *attributes, const int version )
1914 	{
1915 	int size = UINT32_SIZE;	/* Flags */
1916 
1917 	if( version < 4 )
1918 		{
1919 		if( attributes->size != CRYPT_UNUSED )
1920 			size += UINT64_SIZE;
1921 		if( attributes->permissions != CRYPT_UNUSED )
1922 			size += UINT32_SIZE;
1923 		if( attributes->atime )
1924 			size += UINT32_SIZE;
1925 		if( attributes->mtime )
1926 			size += UINT32_SIZE;
1927 		}
1928 	else
1929 		{
1930 		size++;
1931 		if( attributes->size != CRYPT_UNUSED )
1932 			size += UINT64_SIZE;
1933 		if( attributes->permissions != CRYPT_UNUSED )
1934 			size += UINT32_SIZE;
1935 		if( attributes->ctime )
1936 			size += UINT64_SIZE;
1937 		if( attributes->atime )
1938 			size += UINT64_SIZE;
1939 		if( attributes->mtime )
1940 			size += UINT64_SIZE;
1941 		}
1942 
1943 	return( size );
1944 	}
1945 
readAttributes(STREAM * stream,SFTP_ATTRS * attributes,const int version)1946 static int readAttributes( STREAM *stream, SFTP_ATTRS *attributes, const int version )
1947 	{
1948 	long flags;
1949 
1950 	memset( attributes, 0, sizeof( SFTP_ATTRS ) );
1951 	attributes->permissions = CRYPT_UNUSED;
1952 	attributes->size = CRYPT_UNUSED;
1953 
1954 	/* Read basic attribute information: File size, and owner, and
1955 	   permissions */
1956 	flags = readUint32( stream );
1957 	if( cryptStatusError( flags ) )
1958 		return( flags );
1959 	if( version < 4 )
1960 		{
1961 		if( flags & SSH_FILEXFER_ATTR_SIZE )
1962 			readUint64( stream, &attributes->size );
1963 		if( flags & SSH_FILEXFER_ATTR_UIDGID )
1964 			{
1965 			readUint32( stream );
1966 			readUint32( stream );
1967 			}
1968 		if( flags & SSH_FILEXFER_ATTR_PERMISSIONSv3 )
1969 			attributes->permissions = readUint32( stream );
1970 
1971 		/* Read file access and modify times */
1972 		if( flags & SSH_FILEXFER_ATTR_ACMODTIME )
1973 			{
1974 			readUint32Time( stream, &attributes->atime );
1975 			readUint32Time( stream, &attributes->mtime );
1976 			}
1977 		}
1978 	else
1979 		{
1980 		if( flags & SSH_FILEXFER_ATTR_SIZE )
1981 			readUint64( stream, &attributes->size );
1982 		if( flags & SSH_FILEXFER_ATTR_OWNERGROUP )
1983 			{
1984 			readString32( stream, NULL, 0, NULL );
1985 			readString32( stream, NULL, 0, NULL );
1986 			}
1987 		if( flags & SSH_FILEXFER_ATTR_PERMISSIONSv4 )
1988 			attributes->permissions = readUint32( stream );
1989 
1990 		/* Read file create, access, and modify times */
1991 		if( flags & SSH_FILEXFER_ATTR_ACCESSTIME )
1992 			{
1993 			readUint64Time( stream, &attributes->atime );
1994 			if( flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES )
1995 				readUint32( stream );
1996 			}
1997 		if( flags & SSH_FILEXFER_ATTR_CREATETIME )
1998 			{
1999 			readUint64Time( stream, &attributes->ctime );
2000 			if( flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES )
2001 				readUint32( stream );
2002 			}
2003 		if( flags & SSH_FILEXFER_ATTR_MODIFYTIME )
2004 			{
2005 			readUint64Time( stream, &attributes->mtime );
2006 			if( flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES )
2007 				readUint32( stream );
2008 			}
2009 		}
2010 
2011 	/* Read ACLs and extended attribute type/value pairs, the one thing that
2012 	   stayed the same from v3 to v4 */
2013 	if( flags & SSH_FILEXFER_ATTR_ACL )
2014 		readString32( stream, NULL, 0, NULL );
2015 	if( flags & SSH_FILEXFER_ATTR_EXTENDED )
2016 		{
2017 		int extAttrCount = readUint32( stream );
2018 
2019 		if( cryptStatusError( extAttrCount ) )
2020 			return( extAttrCount );
2021 		while( extAttrCount > 0 )
2022 			{
2023 			readString32( stream, NULL, 0, NULL );
2024 			readString32( stream, NULL, 0, NULL );
2025 			extAttrCount--;
2026 			}
2027 		}
2028 
2029 	return( sGetStatus( stream ) );
2030 	}
2031 
writeAttributes(STREAM * stream,SFTP_ATTRS * attributes,const int version)2032 static int writeAttributes( STREAM *stream, SFTP_ATTRS *attributes, const int version )
2033 	{
2034 	int flags = 0;
2035 
2036 	if( version < 4 )
2037 		{
2038 		/* Indicate which attribute values we're going to write */
2039 		if( attributes->size != CRYPT_UNUSED )
2040 			flags |= SSH_FILEXFER_ATTR_SIZE;
2041 		if( attributes->permissions != CRYPT_UNUSED )
2042 			flags |= SSH_FILEXFER_ATTR_PERMISSIONSv3;
2043 		if( attributes->atime )
2044 			flags |= SSH_FILEXFER_ATTR_ACMODTIME;
2045 		writeUint32( stream, flags );
2046 
2047 		/* Write the optional attributes */
2048 		if( attributes->size != CRYPT_UNUSED )
2049 			writeUint64( stream, attributes->size );
2050 		if( attributes->permissions != CRYPT_UNUSED )
2051 			writeUint32( stream, attributes->permissions );
2052 		if( attributes->atime )
2053 			{
2054 			writeUint32Time( stream, attributes->atime );
2055 			writeUint32Time( stream, attributes->mtime );
2056 			}
2057 		}
2058 	else
2059 		{
2060 		/* Indicate which attribute values we're going to write */
2061 		if( attributes->size != CRYPT_UNUSED )
2062 			flags |= SSH_FILEXFER_ATTR_SIZE;
2063 		if( attributes->permissions != CRYPT_UNUSED )
2064 			flags |= SSH_FILEXFER_ATTR_PERMISSIONSv4;
2065 		if( attributes->ctime )
2066 			flags |= SSH_FILEXFER_ATTR_CREATETIME;
2067 		if( attributes->atime )
2068 			flags |= SSH_FILEXFER_ATTR_ACCESSTIME;
2069 		if( attributes->mtime )
2070 			flags |= SSH_FILEXFER_ATTR_MODIFYTIME;
2071 		writeUint32( stream, flags );
2072 		sputc( stream, attributes->isDirectory ? \
2073 					   SSH_FILETYPE_DIRECTORY : SSH_FILETYPE_REGULAR );
2074 
2075 		/* Write the optional attributes */
2076 		if( attributes->size != CRYPT_UNUSED )
2077 			writeUint64( stream, attributes->size );
2078 		if( attributes->permissions != CRYPT_UNUSED )
2079 			writeUint32( stream, attributes->permissions );
2080 		if( attributes->ctime )
2081 			writeUint64Time( stream, attributes->ctime );
2082 		if( attributes->atime )
2083 			writeUint64Time( stream, attributes->atime );
2084 		if( attributes->mtime )
2085 			writeUint64Time( stream, attributes->mtime );
2086 		}
2087 
2088 	return( sGetStatus( stream ) );
2089 	}
2090 
2091 /* Read/write SFTP status:
2092 
2093 	uint32		id
2094 	uint32		error/status code
2095 	string		error message (ISO-10646 UTF-8 [RFC-2279])
2096 	string		language tag (as defined in [RFC-1766]) */
2097 
sizeofStatus(const char * sshStatusString)2098 static int sizeofStatus( const char *sshStatusString )
2099 	{
2100 	return( UINT32_SIZE + UINT32_SIZE + \
2101 			( UINT32_SIZE + strlen( sshStatusString ) ) + \
2102 			UINT32_SIZE );
2103 	}
2104 
readStatus(STREAM * stream,SFTP_INFO * info)2105 static int readStatus( STREAM *stream, SFTP_INFO *info )
2106 	{
2107 	static const struct {
2108 		const int sftpStatus, cryptlibStatus;
2109 		} sftpStatusMap[] = {
2110 		{ SSH_FX_OK, CRYPT_OK },
2111 		{ SSH_FX_EOF, CRYPT_ERROR_COMPLETE },
2112 		{ SSH_FX_NO_SUCH_FILE, CRYPT_ERROR_NOTFOUND },
2113 		{ SSH_FX_PERMISSION_DENIED, CRYPT_ERROR_PERMISSION },
2114 		{ SSH_FX_FAILURE, CRYPT_ERROR_FAILED },
2115 		{ SSH_FX_BAD_MESSAGE, CRYPT_ERROR_BADDATA },
2116 		{ SSH_FX_NO_CONNECTION, CRYPT_ERROR_FAILED },
2117 		{ SSH_FX_CONNECTION_LOST, CRYPT_ERROR_FAILED },
2118 		{ SSH_FX_OP_UNSUPPORTED, CRYPT_ERROR_NOTAVAIL },
2119 		{ SSH_FX_INVALID_HANDLE, CRYPT_ERROR_BADDATA },
2120 		{ SSH_FX_NO_SUCH_PATH, CRYPT_ERROR_NOTFOUND },
2121 		{ SSH_FX_FILE_ALREADY_EXISTS, CRYPT_ERROR_DUPLICATE },
2122 		{ SSH_FX_WRITE_PROTECT, CRYPT_ERROR_PERMISSION },
2123 		{ SSH_FX_NO_MEDIA, CRYPT_ERROR_FAILED },
2124 		{ CRYPT_ERROR, CRYPT_ERROR_FAILED }
2125 		};
2126 	int value, i, status;
2127 
2128 	/* Read the status info and make sure that it's valid */
2129 	value = readUint32( stream );
2130 	status = readUint32( stream );
2131 	if( cryptStatusError( status ) )
2132 		return( status );
2133 	if( value != info->id )
2134 		return( CRYPT_ERROR_BADDATA );
2135 
2136 	/* Translate the SFTP status into a cryptlib status */
2137 	for( i = 0; sftpStatusMap[ i ].sftpStatus != CRYPT_ERROR && \
2138 				sftpStatusMap[ i ].sftpStatus != status; i++ );
2139 	status = sftpStatusMap[ i ].cryptlibStatus;
2140 
2141 	return( status );
2142 	}
2143 
writeStatus(STREAM * stream,SFTP_INFO * info,const int sshStatus,const char * sshStatusString)2144 static int writeStatus( STREAM *stream, SFTP_INFO *info, const int sshStatus,
2145 						const char *sshStatusString )
2146 	{
2147 	writeUint32( stream, info->id );
2148 	writeUint32( stream, sshStatus );
2149 	writeString32( stream, sshStatusString, strlen( sshStatusString ) );
2150 	return( writeString32( stream, "", 0 ) );
2151 	}
2152 
readSftpPacket(const CRYPT_SESSION cryptSession,void * buffer,const int bufSize)2153 static int readSftpPacket( const CRYPT_SESSION cryptSession, void *buffer,
2154 						   const int bufSize )
2155 	{
2156 	int bytesCopied, status;
2157 
2158 	status = cryptPopData( cryptSession, buffer, BUFFER_SIZE, &bytesCopied );
2159 	if( cryptStatusError( status ) )
2160 		{
2161 		printf( "SVR: Couldn't read data from SFTP client, status %d, line "
2162 				"%d.\n", status, __LINE__ );
2163 		return( status );
2164 		}
2165 	return( bytesCopied > 0 ? bytesCopied : CRYPT_ERROR_UNDERFLOW );
2166 	}
2167 
writeSftpPacket(const CRYPT_SESSION cryptSession,const void * data,const int length)2168 static int writeSftpPacket( const CRYPT_SESSION cryptSession, const void *data,
2169 							const int length )
2170 	{
2171 	int bytesCopied, status;
2172 
2173 	status = cryptPushData( cryptSession, data, length, &bytesCopied );
2174 	if( cryptStatusOK( status ) )
2175 		status = cryptFlushData( cryptSession );
2176 	if( cryptStatusError( status ) )
2177 		{
2178 		printf( "SVR: Couldn't write data to SFTP client, status %d, line "
2179 				"%d.\n", status, __LINE__ );
2180 		return( status );
2181 		}
2182 	if( bytesCopied < length )
2183 		{
2184 		printf( "SVR: Only wrote %d of %d bytes of SFTP data, line %d.\n",
2185 				bytesCopied, length, __LINE__ );
2186 		return( status );
2187 		}
2188 	return( CRYPT_OK );
2189 	}
2190 
sendAck(const CRYPT_SESSION cryptSession,SFTP_INFO * sftpInfo)2191 static int sendAck( const CRYPT_SESSION cryptSession, SFTP_INFO *sftpInfo )
2192 	{
2193 	STREAM stream;
2194 	BYTE buffer[ 128 ];
2195 	int length;
2196 
2197 	/* Ack an SFTP packet */
2198 	sMemOpen( &stream, buffer, 128 );
2199 	writeUint32( &stream, 1 + sizeofStatus( "" ) );
2200 	sputc( &stream, SSH_FXP_STATUS );
2201 	writeStatus( &stream, sftpInfo, SSH_FX_OK, "" );
2202 	length = stell( &stream );
2203 	sMemDisconnect( &stream );
2204 	return( writeSftpPacket( cryptSession, buffer, length ) );
2205 	}
2206 
sftpServer(const CRYPT_SESSION cryptSession)2207 int sftpServer( const CRYPT_SESSION cryptSession )
2208 	{
2209 	STREAM stream;
2210 	SFTP_ATTRS sftpAttrs;
2211 	SFTP_INFO sftpInfo;
2212 	BYTE buffer[ BUFFER_SIZE ], nameBuffer[ 128 ];
2213 	time_t xferTime;
2214 	long xferCount = 0, dataLength;
2215 	int length, value, status;
2216 
2217 	cryptSetAttribute( cryptSession, CRYPT_OPTION_NET_READTIMEOUT, 30 );
2218 
2219 	memset( &sftpInfo, 0, sizeof( SFTP_INFO ) );
2220 
2221 	/* Read the client's FXP_INIT and send our response */
2222 	status = readSftpPacket( cryptSession, buffer, BUFFER_SIZE );
2223 	if( cryptStatusError( status ) )
2224 		return( status );
2225 	sMemConnect( &stream, buffer, status );
2226 	length = readUint32( &stream );
2227 	value = sgetc( &stream );
2228 	if( ( length != 1 + 4 ) || ( value != SSH_FXP_INIT ) )
2229 		return( CRYPT_ERROR_BADDATA );
2230 	sftpInfo.version = readUint32( &stream );
2231 	sMemDisconnect( &stream );
2232 	printf( "SVR: Client supports SFTP version %d.\n", sftpInfo.version );
2233 	sMemOpen( &stream, buffer, BUFFER_SIZE );
2234 	writeUint32( &stream, 1 + 4 );
2235 	sputc( &stream, SSH_FXP_VERSION );
2236 	writeUint32( &stream, 3 );
2237 	length = stell( &stream );
2238 	sMemDisconnect( &stream );
2239 	status = writeSftpPacket( cryptSession, buffer, length );
2240 	if( cryptStatusError( status ) )
2241 		return( status );
2242 
2243 	/* Read the client's FXP_OPEN and send our response */
2244 	status = readSftpPacket( cryptSession, buffer, BUFFER_SIZE );
2245 	if( cryptStatusError( status ) )
2246 		{
2247 		printExtError( cryptSession, "SVR: Attempt to read data from "
2248 					   "client", status, __LINE__ );
2249 		return( status );
2250 		}
2251 	sMemConnect( &stream, buffer, status );
2252 	length = readUint32( &stream );
2253 	value = sgetc( &stream );
2254 	if( value == SSH_FXP_STAT )
2255 		{
2256 		/* See what the client is after */
2257 		sftpInfo.id = readUint32( &stream );
2258 		length = readUint32( &stream );
2259 		sread( &stream, nameBuffer, length );
2260 		sMemDisconnect( &stream );
2261 		nameBuffer[ length ] = '\0';
2262 		printf( "SVR: Client tried to stat file '%s'.\n", nameBuffer );
2263 		if( strcmp( nameBuffer, "." ) )
2264 			{
2265 			puts( "SVR: Don't know how to respond to stat request for this "
2266 				  "file." );
2267 			return( CRYPT_ERROR_NOTAVAIL );
2268 			}
2269 
2270 		/* Send back a dummy response */
2271 		memset( &sftpAttrs, 0, sizeof( SFTP_ATTRS ) );
2272 		sftpAttrs.isDirectory = TRUE;
2273 		sftpAttrs.permissions = 0777;
2274 		sftpAttrs.size = CRYPT_UNUSED;
2275 		sftpAttrs.atime = sftpAttrs.ctime = sftpAttrs.mtime = time( NULL );
2276 		length = sizeofAttributes( &sftpAttrs, sftpInfo.version );
2277 		sMemOpen( &stream, buffer, BUFFER_SIZE );
2278 		writeUint32( &stream, 1 + UINT32_SIZE + length );
2279 		sputc( &stream, SSH_FXP_ATTRS );
2280 		writeUint32( &stream, sftpInfo.id );
2281 		writeAttributes( &stream, &sftpAttrs, sftpInfo.version );
2282 		length = stell( &stream );
2283 		sMemDisconnect( &stream );
2284 		status = writeSftpPacket( cryptSession, buffer, length );
2285 		if( cryptStatusError( status ) )
2286 			return( status );
2287 
2288 		/* See what they want next */
2289 		status = readSftpPacket( cryptSession, buffer, BUFFER_SIZE );
2290 		if( cryptStatusError( status ) )
2291 			{
2292 			printExtError( cryptSession, "SVR: Attempt to read data from "
2293 						   "client", status, __LINE__ );
2294 			return( status );
2295 			}
2296 		sMemConnect( &stream, buffer, status );
2297 		length = readUint32( &stream );
2298 		value = sgetc( &stream );
2299 		}
2300 	if( value == SSH_FXP_OPEN )
2301 		{
2302 		/* See what the client is after */
2303 		sftpInfo.id = readUint32( &stream );
2304 		length = readUint32( &stream );
2305 		sread( &stream, nameBuffer, length );
2306 		value = readUint32( &stream );
2307 		readAttributes( &stream, &sftpAttrs, sftpInfo.version );
2308 		sMemDisconnect( &stream );
2309 		nameBuffer[ length ] = '\0';
2310 		printf( "Client tried to open file '%s', mode %02X, length %d.\n",
2311 				nameBuffer, value, sftpAttrs.size );
2312 
2313 		/* Putty for some reason tries to open the current directory for
2314 		   create (rather than the filename), and bails out when it gets a
2315 		   permission-denied.  So I guess we tell it to go ahead... */
2316 		sMemOpen( &stream, buffer, BUFFER_SIZE );
2317 		writeUint32( &stream, 1 + UINT32_SIZE + ( UINT32_SIZE + 1 ) );
2318 		sputc( &stream, SSH_FXP_HANDLE );
2319 		writeUint32( &stream, sftpInfo.id );
2320 		writeUint32( &stream, 1 );
2321 		sputc( &stream, 1 );
2322 		length = stell( &stream );
2323 		sMemDisconnect( &stream );
2324 		status = writeSftpPacket( cryptSession, buffer, length );
2325 		if( cryptStatusError( status ) )
2326 			return( status );
2327 		}
2328 
2329 	/* Now we're in the write loop... */
2330 	xferTime = time( NULL );
2331 	dataLength = 0;
2332 	while( TRUE )
2333 		{
2334 		/* See what they want next */
2335 		status = readSftpPacket( cryptSession, buffer, BUFFER_SIZE );
2336 		if( cryptStatusError( status ) )
2337 			{
2338 			printExtError( cryptSession, "SVR: Attempt to read data from "
2339 						   "client", status, __LINE__ );
2340 			return( status );
2341 			}
2342 		if( status < 1 )
2343 			{
2344 			printf( "SVR: Read 0 bytes from client.\n" );
2345 			return( CRYPT_ERROR_UNDERFLOW );
2346 			}
2347 		if( dataLength > 0 )
2348 			{
2349 			xferCount += status;
2350 			dataLength -= status;
2351 			printf( "SRV: -------- : %d.\r", xferCount );
2352 			if( dataLength <= 0 )
2353 				break;
2354 			continue;
2355 			}
2356 		sMemConnect( &stream, buffer, status );
2357 		length = readUint32( &stream );
2358 		if( status < BUFFER_SIZE && ( length != status - UINT32_SIZE ) )
2359 			{
2360 			printf( "Didn't read complete packet, length = %d, byte count = "
2361 					"%d.\n", length, status - UINT32_SIZE );
2362 			}
2363 		value = sgetc( &stream );
2364 		if( value != SSH_FXP_WRITE )
2365 			break;
2366 		sftpInfo.id = readUint32( &stream );
2367 		readString32( &stream, nameBuffer, 128, &length );
2368 		readUint64( &stream, &value );
2369 		dataLength = readUint32( &stream );
2370 		printf( "SRV: %8d : %d.\r", value, length );
2371 		xferCount += status - stell( &stream );
2372 		dataLength -= status - stell( &stream );
2373 		sMemDisconnect( &stream );
2374 
2375 		/* Ack the write */
2376 		if( dataLength <= 0 )
2377 			{
2378 			status = sendAck( cryptSession, &sftpInfo );
2379 			if( cryptStatusError( status ) )
2380 				return( status );
2381 			}
2382 		}
2383 	xferTime = time( NULL ) - xferTime;
2384 	printf( "Transfer time = %d seconds, %ld bytes, %d bytes/sec.\n",
2385 			xferTime, xferCount, xferCount / xferTime );
2386 
2387 	/* Clean up */
2388 	if( value != SSH_FXP_CLOSE )
2389 		{
2390 		printf( "SVR: Client sent unexpected packet %d.\n", value );
2391 		return( CRYPT_ERROR_BADDATA );
2392 		}
2393 	sftpInfo.id = readUint32( &stream );
2394 	status = sendAck( cryptSession, &sftpInfo );
2395 	if( cryptStatusError( status ) )
2396 		return( status );
2397 	status = readSftpPacket( cryptSession, buffer, BUFFER_SIZE );
2398 	if( status == CRYPT_ERROR_COMPLETE )
2399 		{
2400 		puts( "SVR: Client has closed the channel." );
2401 		return( CRYPT_OK );
2402 		}
2403 	if( cryptStatusError( status ) )
2404 		return( status );
2405 	sMemConnect( &stream, buffer, status );
2406 	length = readUint32( &stream );
2407 	value = sgetc( &stream );
2408 
2409 	return( CRYPT_OK );
2410 	}
2411 
2412 #define SFTP_DATA_AMOUNT	( 1024 * 1024 )
2413 
sftpClient(const CRYPT_SESSION cryptSession)2414 int sftpClient( const CRYPT_SESSION cryptSession )
2415 	{
2416 	STREAM stream;
2417 	SFTP_ATTRS sftpAttrs;
2418 	SFTP_INFO sftpInfo;
2419 	BYTE buffer[ BUFFER_SIZE ];
2420 	long totalLength = SFTP_DATA_AMOUNT;
2421 	int length, value, status;
2422 
2423 	cryptSetAttribute( cryptSession, CRYPT_OPTION_NET_READTIMEOUT, 30 );
2424 
2425 	memset( &sftpInfo, 0, sizeof( SFTP_INFO ) );
2426 
2427 	/* Send our FXP_INIT and read back the response */
2428 	sMemOpen( &stream, buffer, BUFFER_SIZE );
2429 	writeUint32( &stream, 1 + 4 );
2430 	sputc( &stream, SSH_FXP_INIT );
2431 	writeUint32( &stream, 3 );
2432 	length = stell( &stream );
2433 	sMemDisconnect( &stream );
2434 	status = writeSftpPacket( cryptSession, buffer, length );
2435 	if( cryptStatusError( status ) )
2436 		return( status );
2437 	status = readSftpPacket( cryptSession, buffer, BUFFER_SIZE );
2438 	if( cryptStatusError( status ) )
2439 		return( status );
2440 	sMemConnect( &stream, buffer, status );
2441 	length = readUint32( &stream );
2442 	value = sgetc( &stream );
2443 	if( ( length != 1 + 4 ) || ( value != SSH_FXP_VERSION ) )
2444 		return( CRYPT_ERROR_BADDATA );
2445 	sftpInfo.version = readUint32( &stream );
2446 	sMemDisconnect( &stream );
2447 	printf( "Server supports SFTP version %d.\n", sftpInfo.version );
2448 
2449 	/* Open the file to transfer */
2450 	memset( &sftpAttrs, 0, sizeof( SFTP_ATTRS ) );
2451 	sftpAttrs.permissions = 0777;
2452 	sftpAttrs.size = CRYPT_UNUSED;
2453 	sftpAttrs.atime = sftpAttrs.ctime = sftpAttrs.mtime = time( NULL );
2454 	length = sizeofAttributes( &sftpAttrs, sftpInfo.version );
2455 	sMemOpen( &stream, buffer, BUFFER_SIZE );
2456 	writeUint32( &stream, 1 + UINT32_SIZE + ( UINT32_SIZE + 8 ) + UINT32_SIZE + length );
2457 	sputc( &stream, SSH_FXP_OPEN );
2458 	writeUint32( &stream, 1 );
2459 	writeString32( &stream, "test.dat", 8 );
2460 	writeUint32( &stream, SSH_FXF_CREAT | SSH_FXF_WRITE	);
2461 	writeAttributes( &stream, &sftpAttrs, sftpInfo.version );
2462 	length = stell( &stream );
2463 	sMemDisconnect( &stream );
2464 	status = writeSftpPacket( cryptSession, buffer, length );
2465 	if( cryptStatusError( status ) )
2466 		return( status );
2467 	status = readSftpPacket( cryptSession, buffer, BUFFER_SIZE );
2468 	if( cryptStatusError( status ) )
2469 		{
2470 		printExtError( cryptSession, "Attempt to read data from server",
2471 					   status, __LINE__ );
2472 		return( status );
2473 		}
2474 	sMemConnect( &stream, buffer, status );
2475 	length = readUint32( &stream );
2476 	value = sgetc( &stream );
2477 	readUint32( &stream );
2478 	readString32( &stream, sftpInfo.handle, MAX_HANDLE_SIZE,
2479 				  &sftpInfo.handleSize );
2480 	sMemDisconnect( &stream );
2481 	if( value != SSH_FXP_HANDLE )
2482 		{
2483 		printf( "Server sent packet %d, expected file handle.\n", value );
2484 		return( CRYPT_ERROR_BADDATA );
2485 		}
2486 
2487 	/* Send the file (just 1MB of test data) */
2488 	sMemOpen( &stream, buffer, BUFFER_SIZE );
2489 	writeUint32( &stream, 1 + UINT32_SIZE + \
2490 						  ( UINT32_SIZE + sftpInfo.handleSize ) + \
2491 						  UINT64_SIZE + ( UINT32_SIZE + SFTP_DATA_AMOUNT ) );
2492 	sputc( &stream, SSH_FXP_WRITE );
2493 	writeUint32( &stream, sftpInfo.id );
2494 	writeString32( &stream, sftpInfo.handle, sftpInfo.handleSize );
2495 	writeUint64( &stream, 0 );
2496 	writeUint32( &stream, SFTP_DATA_AMOUNT );
2497 	length = stell( &stream );
2498 	memset( buffer + length, '*', BUFFER_SIZE - length );
2499 	status = writeSftpPacket( cryptSession, buffer, BUFFER_SIZE );
2500 	if( cryptStatusError( status ) )
2501 		return( status );
2502 	totalLength -= BUFFER_SIZE - length;
2503 	while( totalLength > 0 )
2504 		{
2505 		memset( buffer, '*', BUFFER_SIZE );
2506 		status = writeSftpPacket( cryptSession, buffer,
2507 								  min( totalLength, BUFFER_SIZE ) );
2508 		if( cryptStatusError( status ) )
2509 			return( status );
2510 		totalLength -= min( totalLength, BUFFER_SIZE );
2511 		}
2512 
2513 	/* Wait for the ack */
2514 	status = readSftpPacket( cryptSession, buffer, BUFFER_SIZE );
2515 	if( cryptStatusError( status ) )
2516 		{
2517 		printExtError( cryptSession, "Attempt to read data from server",
2518 					   status, __LINE__ );
2519 		return( status );
2520 		}
2521 
2522 	return( CRYPT_OK );
2523 	}
2524 #endif /* WINDOWS_THREADS && 0 */
2525 
2526 #endif /* TEST_SESSION || TEST_SESSION_LOOPBACK */
2527