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