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