cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
ssh2.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib SSHv2 Session Management *
4 * Copyright Peter Gutmann 1998-2008 *
5 * *
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9  #include "crypt.h"
10  #include "misc_rw.h"
11  #include "session.h"
12  #include "ssh.h"
13 #else
14  #include "crypt.h"
15  #include "enc_dec/misc_rw.h"
16  #include "session/session.h"
17  #include "session/ssh.h"
18 #endif /* Compiler-specific includes */
19 
20 #ifdef USE_SSH
21 
22 /* Tables mapping SSHv2 algorithm names to cryptlib algorithm IDs, in
23  preferred algorithm order */
24 
25 static const ALGO_STRING_INFO FAR_BSS algoStringKeyexTbl[] = {
26 #ifdef PREFER_ECC_SUITES
27  { "ecdh-sha2-nistp256", 18, CRYPT_ALGO_ECDH,
29 #endif /* PREFER_ECC_SUITES */
30  { "diffie-hellman-group-exchange-sha256", 36, CRYPT_PSEUDOALGO_DHE_ALT,
32  { "diffie-hellman-group-exchange-sha1", 34, CRYPT_PSEUDOALGO_DHE,
33  CRYPT_ALGO_DH },
34  { "diffie-hellman-group1-sha1", 26, CRYPT_ALGO_DH },
35 #if !defined( PREFER_ECC_SUITES )
36  { "ecdh-sha2-nistp256", 18, CRYPT_ALGO_ECDH,
38 #endif /* !PREFER_ECC_SUITES */
39  { NULL, 0, CRYPT_ALGO_NONE }, { NULL, 0, CRYPT_ALGO_NONE }
40  };
41 
42 static const ALGO_STRING_INFO FAR_BSS algoStringCoprTbl[] = {
43  { "none", 4, CRYPT_PSEUDOALGO_COPR },
44  { NULL, 0, CRYPT_ALGO_NONE }, { NULL, 0, CRYPT_ALGO_NONE }
45  };
46 
48 #ifdef PREFER_ECC_SUITES
49  { "ecdsa-sha2-nistp256", 19, CRYPT_ALGO_ECDSA,
51 #endif /* PREFER_ECC_SUITES */
52  { "ssh-rsa", 7, CRYPT_ALGO_RSA },
53  { "ssh-dss", 7, CRYPT_ALGO_DSA },
54 #if !defined( PREFER_ECC_SUITES )
55  { "ecdsa-sha2-nistp256", 19, CRYPT_ALGO_ECDSA,
57 #endif /* !PREFER_ECC_SUITES */
58  { NULL, 0, CRYPT_ALGO_NONE }, { NULL, 0, CRYPT_ALGO_NONE }
59  };
60 
61 static const ALGO_STRING_INFO FAR_BSS algoStringEncrTbl[] = {
62  { "3des-cbc", 8, CRYPT_ALGO_3DES },
63  { "aes128-cbc", 10, CRYPT_ALGO_AES },
64  { "blowfish-cbc", 12, CRYPT_ALGO_BLOWFISH },
65  { NULL, 0, CRYPT_ALGO_NONE }, { NULL, 0, CRYPT_ALGO_NONE }
66  };
67 
68 static const ALGO_STRING_INFO FAR_BSS algoStringMACTbl[] = {
69  { "hmac-sha2-256", 13, CRYPT_ALGO_HMAC_SHA2 },
70  { "hmac-sha1", 9, CRYPT_ALGO_HMAC_SHA1 },
71  { "hmac-md5", 8, CRYPT_ALGO_HMAC_MD5 },
72  { NULL, 0, CRYPT_ALGO_NONE }, { NULL, 0, CRYPT_ALGO_NONE }
73  };
74 
75 /* A grand unified version of the above */
76 
77 static const ALGO_STRING_INFO FAR_BSS algoStringMapTbl[] = {
78  /* Signature algorithms */
79  { "ssh-rsa", 7, CRYPT_ALGO_RSA },
80  { "ssh-dss", 7, CRYPT_ALGO_DSA },
81  { "ecdsa-sha2-nistp256", 19, CRYPT_ALGO_ECDSA,
83 
84  /* Encryption algorithms */
85  { "3des-cbc", 8, CRYPT_ALGO_3DES },
86  { "aes128-cbc", 10, CRYPT_ALGO_AES },
87  { "blowfish-cbc", 12, CRYPT_ALGO_BLOWFISH },
88 
89  /* Keyex algorithms */
90  { "diffie-hellman-group-exchange-sha256", 36, CRYPT_PSEUDOALGO_DHE_ALT,
92  { "diffie-hellman-group-exchange-sha1", 34, CRYPT_PSEUDOALGO_DHE,
93  CRYPT_ALGO_DH },
94  { "diffie-hellman-group1-sha1", 26, CRYPT_ALGO_DH },
95  { "ecdh-sha2-nistp256", 18, CRYPT_ALGO_ECDH,
97 
98  /* MAC algorithms */
99  { "hmac-sha2-256", 13, CRYPT_ALGO_HMAC_SHA2 },
100  { "hmac-sha1", 9, CRYPT_ALGO_HMAC_SHA1 },
101  { "hmac-md5", 8, CRYPT_ALGO_HMAC_MD5 },
102  { "password", 8, CRYPT_PSEUDOALGO_PASSWORD },
103 
104  /* Miscellaneous */
105  { "none", 4, CRYPT_PSEUDOALGO_COPR },
106  { "none", 4, CRYPT_ALGO_LAST }, /* Catch-all */
107 
108  { NULL, 0, CRYPT_ALGO_NONE }, { NULL, 0, CRYPT_ALGO_NONE }
109  };
110 
111 CHECK_RETVAL \
112 int getAlgoStringInfo( OUT const ALGO_STRING_INFO **algoStringInfoPtrPtr,
113  OUT_INT_Z int *noInfoEntries )
114  {
115  assert( isReadPtr( algoStringInfoPtrPtr, \
116  sizeof( ALGO_STRING_INFO * ) ) );
117  assert( isWritePtr( noInfoEntries, sizeof( int ) ) );
118 
119  *algoStringInfoPtrPtr = algoStringMapTbl;
120  *noInfoEntries = FAILSAFE_ARRAYSIZE( algoStringMapTbl, ALGO_STRING_INFO );
121 
122  return( CRYPT_OK );
123  }
124 
125 /****************************************************************************
126 * *
127 * Utility Functions *
128 * *
129 ****************************************************************************/
130 
131 /* Convert an SSH algorithm list to a cryptlib ID in preferred-algorithm
132  order. For some bizarre reason the algorithm information is communicated
133  as a comma-delimited list (in an otherwise binary protocol) so we have to
134  unpack and pack them into this cumbersome format alongside just choosing
135  which algorithm to use. In addition, the algorithm selection mechanism
136  differs depending on whether we're the client or server, and what set of
137  algorithms we're matching. Unlike SSL, which uses the offered-suites/
138  chosen-suites mechanism, in SSH both sides offer a selection of cipher
139  suites and the server chooses the first one that appears on both it and
140  the client's list, with special-case handling for the keyex and signature
141  algorithms if the match isn't the first one on the list. This means that
142  the client can choose as it pleases from the server's list if it waits
143  for the server hello (see the comment in the client/server hello handling
144  code on the annoying nature of this portion of the SSH handshake) but the
145  server has to perform a complex double-match of its own vs.the client's
146  list. The cases that we need to handle are:
147 
148  BEST_MATCH: Get the best matching algorithm (that is, the one
149  corresponding to the strongest crypto mechanism), used by the client
150  to match the server.
151 
152  FIRST_MATCH: Get the first matching algorithm, used by the server to
153  match the client.
154 
155  FIRST_MATCH_WARN: Get the first matching algorithm and warn if it isn't
156  the first one on the list of possible algorithms, used by the server
157  to match the client for the keyex and public-key algorithms.
158 
159  This is a sufficiently complex and screwball function that we need to
160  define a composite structure to pass all of the control information in
161  and out */
162 
163 typedef enum {
164  GETALGO_NONE, /* No match action */
165  GETALGO_FIRST_MATCH, /* Get first matching algorithm */
166  GETALGO_FIRST_MATCH_WARN,/* Get first matching algo, warn if not first */
167  GETALGO_BEST_MATCH, /* Get best matching algorithm */
168  GETALGO_LAST /* Last possible match action */
169  } GETALGO_TYPE;
170 
171 typedef struct {
172  /* Match information passed in by the caller */
173  ARRAY_FIXED( noAlgoInfoEntries ) \
174  const ALGO_STRING_INFO *algoInfo;/* Algorithm selection information */
175  int noAlgoInfoEntries;
176  CRYPT_ALGO_TYPE preferredAlgo; /* Preferred algo for first-match */
177  GETALGO_TYPE getAlgoType; /* Type of match to perform */
178  BOOLEAN allowECC; /* Whether to allow ECC algos */
179 
180  /* Information returned by the read-algorithm function */
181  CRYPT_ALGO_TYPE algo; /* Matched algorithm */
182  BOOLEAN prefAlgoMismatch; /* First match != preferredAlgo */
183  } ALGOID_INFO;
184 
185 #define setAlgoIDInfo( algoIDInfo, algoStrInfo, algoStrInfoEntries, prefAlgo, getType ) \
186  { \
187  memset( ( algoIDInfo ), 0, sizeof( ALGOID_INFO ) ); \
188  ( algoIDInfo )->algoInfo = ( algoStrInfo ); \
189  ( algoIDInfo )->noAlgoInfoEntries = ( algoStrInfoEntries ); \
190  ( algoIDInfo )->preferredAlgo = ( prefAlgo ); \
191  ( algoIDInfo )->getAlgoType = ( getType ); \
192  ( algoIDInfo )->allowECC = TRUE; \
193  }
194 
195 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
196 static int readAlgoStringEx( INOUT STREAM *stream,
197  INOUT ALGOID_INFO *algoIDInfo,
199  {
200  BOOLEAN foundMatch = FALSE;
201  void *string = DUMMY_INIT_PTR;
202  int stringPos, stringLen, substringLen, algoIndex = 999;
203  int iterationCount, status;
204 
205  assert( isWritePtr( stream, sizeof( STREAM ) ) );
206  assert( isWritePtr( algoIDInfo, sizeof( ALGOID_INFO ) ) );
207  assert( isReadPtr( algoIDInfo->algoInfo, \
208  sizeof( ALGO_STRING_INFO ) * \
209  algoIDInfo->noAlgoInfoEntries ) );
210 
211  ENSURES( ( algoIDInfo->getAlgoType == GETALGO_BEST_MATCH && \
212  algoIDInfo->preferredAlgo == CRYPT_ALGO_NONE ) || \
213  ( algoIDInfo->getAlgoType == GETALGO_FIRST_MATCH ) ||
214  ( algoIDInfo->getAlgoType == GETALGO_FIRST_MATCH_WARN && \
215  ( algoIDInfo->preferredAlgo > CRYPT_ALGO_NONE && \
216  algoIDInfo->preferredAlgo < CRYPT_ALGO_LAST_EXTERNAL ) ) );
217  /* FIRST_MATCH uses CRYPT_ALGO_NONE on the first match of an
218  algorithm pair and the first algorithm chosen on the second
219  match */
220 
221  /* Get the string length and data and make sure that it's valid */
222  status = stringLen = readUint32( stream );
223  if( !cryptStatusError( status ) && stringLen < SSH2_MIN_ALGOID_SIZE )
224  status = CRYPT_ERROR_BADDATA; /* Quick-rej.for too-short strings */
225  if( !cryptStatusError( status ) )
226  status = sMemGetDataBlock( stream, &string, stringLen );
227  if( cryptStatusOK( status ) )
228  status = sSkip( stream, stringLen );
229  if( cryptStatusError( status ) )
230  {
232  ( CRYPT_ERROR_BADDATA, errorInfo,
233  "Invalid algorithm ID string" ) );
234  }
235 
236  /* Walk down the string looking for a recognised algorithm. Since our
237  preference may not match the other side's preferences we have to walk
238  down the entire list to find our preferred choice:
239 
240  stringPos stringLen
241  | |
242  v v
243  "algo1,algo2,algo3,algoN"
244  ^
245  |
246  substrLen */
247  for( stringPos = 0, iterationCount = 0;
248  stringPos < stringLen && !foundMatch && \
249  iterationCount < FAILSAFE_ITERATIONS_LARGE;
250  stringPos += substringLen + 1, iterationCount++ )
251  {
252  const ALGO_STRING_INFO *matchedAlgoInfo = NULL;
253  const BYTE *stringPtr = string;
254  BOOLEAN algoMatched = TRUE;
255  int currentAlgoIndex;
256 
257  /* Find the length of the next algorithm name */
258  for( substringLen = stringPos; \
259  substringLen < stringLen && stringPtr[ substringLen ] != ','; \
260  substringLen++ );
261  substringLen -= stringPos;
262  if( substringLen < SSH2_MIN_ALGOID_SIZE || \
263  substringLen > CRYPT_MAX_TEXTSIZE )
264  {
265  /* Empty or too-short algorithm name (or excessively long one),
266  continue. Note that even with an (invalid) zero-length
267  substring we'll still progress down the string since the loop
268  increment is the substring length plus one */
269  continue;
270  }
271 
272  /* Check whether it's something that we can handle */
273  for( currentAlgoIndex = 0;
274  currentAlgoIndex < algoIDInfo->noAlgoInfoEntries && \
275  algoIDInfo->algoInfo[ currentAlgoIndex ].name != NULL && \
276  currentAlgoIndex < FAILSAFE_ITERATIONS_MED;
277  currentAlgoIndex++ )
278  {
279  if( substringLen == algoIDInfo->algoInfo[ currentAlgoIndex ].nameLen && \
280  !memcmp( algoIDInfo->algoInfo[ currentAlgoIndex ].name,
281  stringPtr + stringPos, substringLen ) )
282  {
283  matchedAlgoInfo = &algoIDInfo->algoInfo[ currentAlgoIndex ];
284  break;
285  }
286  }
287  ENSURES( currentAlgoIndex < FAILSAFE_ITERATIONS_MED );
288  ENSURES( currentAlgoIndex < algoIDInfo->noAlgoInfoEntries );
289  if( matchedAlgoInfo == NULL )
290  {
291  /* Unrecognised algorithm name, remember to warn the caller if
292  we have to match the first algorithm on the list, then move
293  on to the next name */
294  if( algoIDInfo->getAlgoType == GETALGO_FIRST_MATCH_WARN )
295  algoIDInfo->prefAlgoMismatch = TRUE;
296  continue;
297  }
298 
299  /* If it's a cipher suite, make sure that the algorithms that it's
300  made up of are available */
301  if( isPseudoAlgo( matchedAlgoInfo->algo ) )
302  {
303  if( matchedAlgoInfo->checkCryptAlgo != CRYPT_ALGO_NONE && \
304  !algoAvailable( matchedAlgoInfo->checkCryptAlgo ) )
305  algoMatched = FALSE;
306  if( matchedAlgoInfo->checkHashAlgo != CRYPT_ALGO_NONE && \
307  !algoAvailable( matchedAlgoInfo->checkHashAlgo ) )
308  algoMatched = FALSE;
309  }
310  else
311  {
312  /* It's a straight algorithm, make sure that it's available */
313  if( !algoAvailable( matchedAlgoInfo->algo ) )
314  algoMatched = FALSE;
315  }
316 
317  /* If this is an ECC algorithm and the use of ECC algorithms has
318  been prevented by external conditions such as the server key
319  not being an ECC key, we can't use it even if ECC algorithms in
320  general are available */
321  if( ( isEccAlgo( matchedAlgoInfo->algo ) || \
322  ( matchedAlgoInfo->checkCryptAlgo != CRYPT_ALGO_NONE && \
323  isEccAlgo( matchedAlgoInfo->checkCryptAlgo ) ) ) && \
324  !algoIDInfo->allowECC )
325  algoMatched = FALSE;
326 
327  /* If the matched algorithm isn't available, remember to warn the
328  caller if we have to match the first algorithm on the list, then
329  move on to the next name */
330  if( !algoMatched )
331  {
332  if( algoIDInfo->getAlgoType == GETALGO_FIRST_MATCH_WARN )
333  algoIDInfo->prefAlgoMismatch = TRUE;
334  continue;
335  }
336 
337  switch( algoIDInfo->getAlgoType )
338  {
339  case GETALGO_BEST_MATCH:
340  /* If we're looking for the best (highest-ranked algorithm)
341  match, see whether the current match ranks higher than
342  the existing one */
343  if( currentAlgoIndex < algoIndex )
344  {
345  algoIndex = currentAlgoIndex;
346  if( algoIndex <= 0 )
347  foundMatch = TRUE; /* Gruener werd's net */
348  }
349  break;
350 
351  case GETALGO_FIRST_MATCH:
352  /* If we've found an acceptable algorithm, remember it and
353  exit */
354  if( algoIDInfo->preferredAlgo == CRYPT_ALGO_NONE || \
355  algoIDInfo->preferredAlgo == matchedAlgoInfo->algo )
356  {
357  algoIndex = currentAlgoIndex;
358  foundMatch = TRUE;
359  }
360  break;
361 
362  case GETALGO_FIRST_MATCH_WARN:
363  /* If we found the algorithm that we're after, remember it
364  and exit */
365  if( algoIDInfo->preferredAlgo != matchedAlgoInfo->algo )
366  {
367  /* We didn't match the first algorithm on the list, warn
368  the caller */
369  algoIDInfo->prefAlgoMismatch = TRUE;
370  }
371  algoIndex = currentAlgoIndex;
372  foundMatch = TRUE;
373  break;
374 
375  default:
376  retIntError();
377  }
378  }
379  ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE );
380  if( algoIndex > 50 )
381  {
382  char algoString[ 256 + 8 ];
383  const int algoStringLen = min( stringLen, \
384  min( MAX_ERRMSG_SIZE - 80, 255 ) );
385 
386  /* We couldn't find anything to use, tell the caller what was
387  available */
388  if( algoStringLen > 0 )
389  memcpy( algoString, string, algoStringLen );
391  ( CRYPT_ERROR_NOTAVAIL, errorInfo,
392  "No algorithm compatible with the remote system's "
393  "selection was found: '%s'",
394  sanitiseString( algoString, 256, stringLen ) ) );
395  }
396 
397  /* We found a more-preferred algorithm than the default, go with that */
398  algoIDInfo->algo = algoIDInfo->algoInfo[ algoIndex ].algo;
399  return( CRYPT_OK );
400  }
401 
402 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4, 6 ) ) \
403 int readAlgoString( INOUT STREAM *stream,
404  IN_ARRAY( noAlgoStringEntries ) \
405  const ALGO_STRING_INFO *algoInfo,
406  IN_RANGE( 1, 100 ) const int noAlgoStringEntries,
408  const BOOLEAN useFirstMatch,
409  INOUT ERROR_INFO *errorInfo )
410  {
411  ALGOID_INFO algoIDInfo;
412  int status;
413 
414  assert( isWritePtr( stream, sizeof( STREAM ) ) );
415  assert( isReadPtr( algoInfo, sizeof( ALGO_STRING_INFO ) * \
416  noAlgoStringEntries ) );
417  assert( isWritePtr( algo, sizeof( CRYPT_ALGO_TYPE ) ) );
418 
419  REQUIRES( noAlgoStringEntries > 0 && noAlgoStringEntries <= 100 );
420 
421  /* Clear return value */
422  *algo = CRYPT_ALGO_NONE;
423 
424  setAlgoIDInfo( &algoIDInfo, algoInfo, noAlgoStringEntries,
425  CRYPT_ALGO_NONE, useFirstMatch ? GETALGO_FIRST_MATCH : \
426  GETALGO_BEST_MATCH );
427  status = readAlgoStringEx( stream, &algoIDInfo, errorInfo );
428  if( cryptStatusOK( status ) )
429  *algo = algoIDInfo.algo;
430  return( status );
431  }
432 
433 /* Algorithms used to protect data packets are used in pairs, one for
434  incoming and the other for outgoing data. To keep things simple we
435  always force these to be the same, first reading the algorithm for one
436  direction and then making sure that the one for the other direction
437  matches this. All implementations seem to do this anyway, many aren't
438  even capable of supporting asymmetric algorithm choices */
439 
440 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4, 7 ) ) \
441 static int readAlgoStringPair( INOUT STREAM *stream,
442  IN_ARRAY( noAlgoStringEntries ) \
443  const ALGO_STRING_INFO *algoInfo,
444  IN_RANGE( 1, 100 ) const int noAlgoStringEntries,
446  const BOOLEAN isServer,
447  const BOOLEAN allowAsymmetricAlgos,
448  INOUT ERROR_INFO *errorInfo )
449  {
450  CRYPT_ALGO_TYPE pairPreferredAlgo;
451  ALGOID_INFO algoIDInfo;
452  int status;
453 
454  assert( isWritePtr( stream, sizeof( STREAM ) ) );
455  assert( isReadPtr( algoInfo, sizeof( ALGO_STRING_INFO ) * \
456  noAlgoStringEntries ) );
457  assert( isWritePtr( algo, sizeof( CRYPT_ALGO_TYPE ) ) );
458 
459  REQUIRES( noAlgoStringEntries > 0 && noAlgoStringEntries <= 100 );
460 
461  /* Clear return value */
462  *algo = CRYPT_ALGO_NONE;
463 
464  /* Get the first algorithm */
465  setAlgoIDInfo( &algoIDInfo, algoInfo, noAlgoStringEntries,
466  CRYPT_ALGO_NONE, isServer ? GETALGO_FIRST_MATCH : \
467  GETALGO_BEST_MATCH );
468  status = readAlgoStringEx( stream, &algoIDInfo, errorInfo );
469  if( cryptStatusError( status ) )
470  return( status );
471  pairPreferredAlgo = algoIDInfo.algo;
472 
473  /* Get the matched second algorithm. Some buggy implementations request
474  mismatched algorithms (at the moment this is only for compression
475  algorithms) but have no problems in accepting the same algorithm in
476  both directions, so if we're talking to one of these then we ignore
477  an algorithm mismatch */
478  setAlgoIDInfo( &algoIDInfo, algoInfo, noAlgoStringEntries,
479  pairPreferredAlgo, GETALGO_FIRST_MATCH );
480  status = readAlgoStringEx( stream, &algoIDInfo, errorInfo );
481  if( cryptStatusError( status ) )
482  return( status );
483  if( pairPreferredAlgo != algoIDInfo.algo && !allowAsymmetricAlgos )
484  {
486  ( CRYPT_ERROR_BADDATA, errorInfo,
487  "Client algorithm %d doesn't match server algorithm %d "
488  "in algorithm pair", pairPreferredAlgo,
489  algoIDInfo.algo ) );
490  }
491  *algo = algoIDInfo.algo;
492 
493  return( status );
494  }
495 
496 /* Convert a cryptlib algorithm ID to an SSH algorithm name */
497 
499 int writeAlgoString( INOUT STREAM *stream,
500  IN_ALGO const CRYPT_ALGO_TYPE algo )
501  {
502  int i;
503 
504  assert( isWritePtr( stream, sizeof( STREAM ) ) );
505 
506  REQUIRES( algo >= CRYPT_ALGO_NONE && algo < CRYPT_ALGO_LAST_EXTERNAL );
507 
508  /* Locate the name for this algorithm and encode it as an SSH string */
509  for( i = 0; algoStringMapTbl[ i ].algo != CRYPT_ALGO_LAST && \
510  algoStringMapTbl[ i ].algo != algo && \
511  i < FAILSAFE_ARRAYSIZE( algoStringMapTbl, ALGO_STRING_INFO );
512  i++ );
513  ENSURES( i < FAILSAFE_ARRAYSIZE( algoStringMapTbl, ALGO_STRING_INFO ) );
514  ENSURES( algoStringMapTbl[ i ].algo != CRYPT_ALGO_LAST );
515  return( writeString32( stream, algoStringMapTbl[ i ].name,
516  algoStringMapTbl[ i ].nameLen ) );
517  }
518 
519 /****************************************************************************
520 * *
521 * Miscellaneous Functions *
522 * *
523 ****************************************************************************/
524 
525 /* Process a client/server hello packet */
526 
527 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
528 int processHelloSSH( INOUT SESSION_INFO *sessionInfoPtr,
531  const BOOLEAN isServer )
532  {
533  CRYPT_ALGO_TYPE dummyAlgo;
534  STREAM stream;
535  ALGOID_INFO algoIDInfo;
536  BOOLEAN preferredAlgoMismatch = FALSE, guessedKeyex = FALSE;
537  int length, status;
538 
539  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
540  assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
541  assert( isWritePtr( keyexLength, sizeof( int ) ) );
542 
543  /* Clear return value */
544  *keyexLength = 0;
545 
546  /* Process the client/server hello:
547 
548  byte type = SSH_MSG_KEXINIT
549  byte[16] cookie
550  string keyex algorithms
551  string pubkey algorithms
552  string client_crypto algorithms
553  string server_crypto algorithms
554  string client_mac algorithms
555  string server_mac algorithms
556  string client_compression algorithms
557  string server_compression algorithms
558  string client_language
559  string server_language
560  boolean first_keyex_packet_follows
561  uint32 reserved
562 
563  The cookie isn't explicitly processed as with SSHv1 since SSHv2
564  hashes the entire hello message */
565  status = length = \
566  readHSPacketSSH2( sessionInfoPtr, SSH_MSG_KEXINIT, 128 );
567  if( cryptStatusError( status ) )
568  return( status );
569  *keyexLength = length;
570  sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
571  sSkip( &stream, SSH2_COOKIE_SIZE );
572 
573  /* Read the keyex algorithm information */
574  if( isServer )
575  {
576  int pkcAlgo;
577 
578  setAlgoIDInfo( &algoIDInfo, algoStringKeyexTbl,
579  FAILSAFE_ARRAYSIZE( algoStringKeyexTbl, \
581  CRYPT_PSEUDOALGO_DHE, GETALGO_FIRST_MATCH_WARN );
582 
583  /* By default the use of ECC algorithms is enabled is support for
584  them is present, however if the server key is a non-ECC key then
585  it can't be used with an ECC keyex so we have to explicitly
586  disable it (technically it is possible to mix ECDH with RSA but
587  this is more likely an error than anything deliberate) */
588  status = krnlSendMessage( sessionInfoPtr->privateKey,
589  IMESSAGE_GETATTRIBUTE, &pkcAlgo,
591  if( cryptStatusError( status ) || !isEccAlgo( pkcAlgo ) )
592  algoIDInfo.allowECC = FALSE;
593  }
594  else
595  {
596  setAlgoIDInfo( &algoIDInfo, algoStringKeyexTbl,
597  FAILSAFE_ARRAYSIZE( algoStringKeyexTbl, \
599  CRYPT_ALGO_NONE, GETALGO_BEST_MATCH );
600  }
601  status = readAlgoStringEx( &stream, &algoIDInfo, SESSION_ERRINFO );
602  if( cryptStatusError( status ) )
603  {
604  sMemDisconnect( &stream );
605  return( status );
606  }
607  handshakeInfo->keyexAlgo = algoIDInfo.algo;
608  if( algoIDInfo.prefAlgoMismatch )
609  {
610  /* We didn't get a match for our first choice, remember that we have
611  to discard any guessed keyex that may follow */
612  preferredAlgoMismatch = TRUE;
613  }
614  if( algoIDInfo.algo == CRYPT_PSEUDOALGO_DHE || \
615  algoIDInfo.algo == CRYPT_PSEUDOALGO_DHE_ALT )
616  {
617  /* If we're using the non-default exchange hash mechanism, switch to
618  the alternative algorithm */
619  if( algoIDInfo.algo == CRYPT_PSEUDOALGO_DHE_ALT )
620  handshakeInfo->exchangeHashAlgo = CRYPT_ALGO_SHA2;
621 
622  /* If we're using ephemeral rather than static DH keys we need to
623  negotiate the keyex key before we can perform the exchange */
624  handshakeInfo->requestedServerKeySize = SSH2_DEFAULT_KEYSIZE;
625  }
626  if( algoIDInfo.algo == CRYPT_ALGO_ECDH || \
627  algoIDInfo.algo == CRYPT_PSEUDOALGO_ECDH_P384 || \
628  algoIDInfo.algo == CRYPT_PSEUDOALGO_ECDH_P521 )
629  {
630  /* If we're using an ECDH cipher suite we need to use SHA2 for the
631  keyex hashing */
632  handshakeInfo->isECDH = TRUE;
633  handshakeInfo->exchangeHashAlgo = CRYPT_ALGO_SHA2;
634  }
635 
636  /* Read the pubkey (signature) algorithm information */
637  if( isServer )
638  {
639  setAlgoIDInfo( &algoIDInfo, handshakeInfo->algoStringPubkeyTbl,
640  handshakeInfo->algoStringPubkeyTblNoEntries,
641  handshakeInfo->pubkeyAlgo, GETALGO_FIRST_MATCH_WARN );
642  }
643  else
644  {
645  setAlgoIDInfo( &algoIDInfo, handshakeInfo->algoStringPubkeyTbl,
646  handshakeInfo->algoStringPubkeyTblNoEntries,
647  CRYPT_ALGO_NONE, GETALGO_BEST_MATCH );
648  }
649  status = readAlgoStringEx( &stream, &algoIDInfo, SESSION_ERRINFO );
650  if( cryptStatusError( status ) )
651  {
652  sMemDisconnect( &stream );
653  return( status );
654  }
655  if( !isServer )
656  handshakeInfo->pubkeyAlgo = algoIDInfo.algo;
657  if( algoIDInfo.prefAlgoMismatch )
658  {
659  /* We didn't get a match for our first choice, remember that we have
660  to discard any guessed keyex that may follow */
661  preferredAlgoMismatch = TRUE;
662  }
663 
664  /* Read the encryption and MAC algorithm information */
665  status = readAlgoStringPair( &stream, algoStringEncrTbl,
666  FAILSAFE_ARRAYSIZE( algoStringEncrTbl, \
668  &sessionInfoPtr->cryptAlgo, isServer, FALSE,
669  SESSION_ERRINFO );
670  if( cryptStatusOK( status ) )
671  {
672  status = readAlgoStringPair( &stream, algoStringMACTbl,
673  FAILSAFE_ARRAYSIZE( algoStringMACTbl, \
675  &sessionInfoPtr->integrityAlgo,
676  isServer, FALSE, SESSION_ERRINFO );
677  }
678  if( cryptStatusError( status ) )
679  {
680  sMemDisconnect( &stream );
681  return( status );
682  }
683 
684  /* Read the remaining algorithm information. The final reserved value
685  should always be zero but we don't specifically check for this since
686  at some point in the future it may become non-zero */
687  status = readAlgoStringPair( &stream, algoStringCoprTbl,
688  FAILSAFE_ARRAYSIZE( algoStringCoprTbl, \
690  &dummyAlgo, isServer,
691  ( sessionInfoPtr->protocolFlags & SSH_PFLAG_ASYMMCOPR ) ? \
693  if( cryptStatusError( status ) )
694  {
695  sMemDisconnect( &stream );
696  return( status );
697  }
698  status = readUniversal32( &stream );
699  if( cryptStatusOK( status ) )
700  status = readUniversal32( &stream ); /* Language string pair */
701  if( cryptStatusOK( status ) )
702  {
703  if( sgetc( &stream ) )
704  guessedKeyex = TRUE;
705  status = readUint32( &stream ); /* Reserved value */
706  }
707  sMemDisconnect( &stream );
708  if( cryptStatusError( status ) )
709  {
710  retExt( status,
711  ( status, SESSION_ERRINFO,
712  "Invalid hello packet language string/trailer data" ) );
713  }
714 
715  /* If we're using an alternative exchange hash algorithm, switch the
716  contexts around to using the alternative one for hashing from now
717  on */
718  if( handshakeInfo->exchangeHashAlgo == CRYPT_ALGO_SHA2 )
719  {
720  const CRYPT_CONTEXT tempContext = handshakeInfo->iExchangeHashContext;
721 
722  handshakeInfo->iExchangeHashContext = \
723  handshakeInfo->iExchangeHashAltContext;
724  handshakeInfo->iExchangeHashAltContext = tempContext;
725  }
726 
727  /* If there's a guessed keyex following this packet and we didn't match
728  the first-choice keyex/pubkey algorithm, tell the caller to skip it */
729  if( guessedKeyex && preferredAlgoMismatch )
730  return( OK_SPECIAL );
731 
732  return( CRYPT_OK );
733  }
734 
735 /****************************************************************************
736 * *
737 * Get/Put Data Functions *
738 * *
739 ****************************************************************************/
740 
741 /* Process a control message received during the processBodyFunction() */
742 
743 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
744 static int processControlMessage( INOUT SESSION_INFO *sessionInfoPtr,
746  IN_LENGTH_Z const int payloadLength )
747  {
748  SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
749  BYTE *bufPtr = sessionInfoPtr->receiveBuffer + \
750  sessionInfoPtr->receiveBufPos;
751  STREAM stream;
752  int localPayloadLength = payloadLength, status;
753 
754  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
755  assert( isWritePtr( readInfo, sizeof( READSTATE_INFO ) ) );
756 
757  REQUIRES( payloadLength >= 0 && payloadLength < MAX_INTLENGTH );
758 
759  /* Putty 0.59 erroneously sent zero-length SSH_MSG_IGNORE packets, if
760  we find one of these we convert it into a valid packet. Writing into
761  the buffer at this position is safe because we've got padding and at
762  least sessionInfoPtr->authBlocksize bytes of MAC following the
763  current position. We can also modify the localPayloadLength value
764  for the same reason */
765  if( ( sessionInfoPtr->protocolFlags & SSH_PFLAG_ZEROLENIGNORE ) && \
766  sshInfo->packetType == SSH_MSG_IGNORE && localPayloadLength == 0 )
767  {
768  memset( bufPtr, 0, UINT32_SIZE );
769  localPayloadLength = UINT32_SIZE;
770  }
771 
772  /* Make sure that the message length is valid. This will be caught
773  anyway when we try and process the channel control message (and the
774  excessive-length check has already been performed by the packet-read
775  code) but checking it here avoids an assertion in the debug build
776  when we connect the stream, as well as just being good programming
777  practice */
778  if( localPayloadLength <= 0 || \
779  localPayloadLength > sessionInfoPtr->receiveBufEnd - \
780  sessionInfoPtr->receiveBufPos )
781  {
784  "Invalid session control message payload length %d for "
785  "packet type %d", localPayloadLength,
786  sshInfo->packetType ) );
787  }
788 
789  /* Process the control message and reset the receive buffer indicators
790  to clear it */
791  ENSURES( rangeCheckZ( sessionInfoPtr->receiveBufPos, localPayloadLength,
792  sessionInfoPtr->receiveBufEnd ) );
793  sMemConnect( &stream, bufPtr, localPayloadLength );
794  status = processChannelControlMessage( sessionInfoPtr, &stream );
795  sMemDisconnect( &stream );
796  sessionInfoPtr->receiveBufEnd = sessionInfoPtr->receiveBufPos;
797  sessionInfoPtr->pendingPacketLength = 0;
798 
799  return( status );
800  }
801 
802 /* Read data over the SSH link */
803 
805 static int readHeaderFunction( INOUT SESSION_INFO *sessionInfoPtr,
806  INOUT READSTATE_INFO *readInfo )
807  {
808  SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
809  BYTE *bufPtr = sessionInfoPtr->receiveBuffer + \
810  sessionInfoPtr->receiveBufPos;
811  long length;
812  int extraLength, removedDataLength = ( ID_SIZE + PADLENGTH_SIZE );
813  int status;
814 
815  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
816  assert( isWritePtr( readInfo, sizeof( READSTATE_INFO ) ) );
817 
818  /* Clear return value */
819  *readInfo = READINFO_NONE;
820 
821  /* Make sure that there's room left to handle the speculative read */
822  if( sessionInfoPtr->receiveBufPos >= \
823  sessionInfoPtr->receiveBufSize - 128 )
824  return( 0 );
825 
826  /* Try and read the header data from the remote system */
827  REQUIRES( sessionInfoPtr->receiveBufPos == sessionInfoPtr->receiveBufEnd );
828  status = readPacketHeaderSSH2( sessionInfoPtr, SSH_MSG_CHANNEL_DATA,
829  &length, &extraLength, sshInfo,
830  readInfo );
831  if( cryptStatusError( status ) )
832  {
833  /* OK_SPECIAL means that we got a soft timeout before the entire
834  header was read, so we return zero bytes read to tell the
835  calling code that there's nothing more to do */
836  return( ( status == OK_SPECIAL ) ? 0 : status );
837  }
839  length <= sessionInfoPtr->receiveBufSize - \
840  sessionInfoPtr->receiveBufPos );
841  status = checkMacSSHIncremental( sessionInfoPtr->iAuthInContext,
842  sshInfo->readSeqNo, bufPtr,
845  length, MAC_START,
846  sessionInfoPtr->authBlocksize );
847  if( cryptStatusError( status ) )
848  {
849  /* We don't return an extended status at this point because we
850  haven't completed the message MAC calculation/check yet so
851  any errors will be cryptlib-internal ones */
852  return( status );
853  }
854 
855  /* If it's channel data, strip the encapsulation, which allows us to
856  process the payload directly without having to move it around in
857  the buffer */
858  if( sshInfo->packetType == SSH_MSG_CHANNEL_DATA )
859  {
860  STREAM stream;
861  long payloadLength;
862 
863  /* Skip the type, padding length, and channel number and make sure
864  that the payload length matches the packet length and, if
865  everything's OK, process the channel data header (this is
866  required in order to handle window size updates) */
867  sMemConnect( &stream, bufPtr, SSH2_HEADER_REMAINDER_SIZE );
868  sSkip( &stream, ID_SIZE + PADLENGTH_SIZE + UINT32_SIZE );
869  status = payloadLength = readUint32( &stream );
870  if( !cryptStatusError( status ) )
871  removedDataLength = stell( &stream );
872  if( cryptStatusError( status ) || \
873  payloadLength != length - ( removedDataLength + \
874  sshInfo->padLength ) )
875  {
876  sMemDisconnect( &stream );
879  "Invalid data packet payload length %ld, should be "
880  "%ld", cryptStatusError( status ) ? 0 : payloadLength,
881  length - ( removedDataLength + sshInfo->padLength ) ) );
882  }
883  sseek( &stream, ID_SIZE + PADLENGTH_SIZE );
884  status = processChannelControlMessage( sessionInfoPtr, &stream );
885  sMemDisconnect( &stream );
886  if( cryptStatusError( status ) )
887  return( status );
888  }
889 
890  /* Move the remainder down to the start of the buffer. The general idea
891  is to remove all of the header data so that only the payload remains
892  in the buffer, avoiding the need to move it down afterwards. This is
893  complicated by the fact that (unlike SSL) all of the data (including
894  the header) is encrypted and MAC'd so we can't just read that
895  separately but have to process it as part of the payload, remove it,
896  and remember anything that's left for later */
897  REQUIRES( SSH2_HEADER_REMAINDER_SIZE - removedDataLength > 0 );
898  memmove( bufPtr, bufPtr + removedDataLength,
899  SSH2_HEADER_REMAINDER_SIZE - removedDataLength );
900 
901  /* Determine how much data we'll be expecting, adjusted for the fixed
902  information that we've removed and the (implicitly present) MAC data */
903  sessionInfoPtr->pendingPacketLength = \
904  sessionInfoPtr->pendingPacketRemaining = \
905  ( length + extraLength ) - removedDataLength;
907  removedDataLength;
908 
909  /* Indicate that we got some payload as part of the header */
910  *readInfo = READINFO_HEADERPAYLOAD;
911  return( SSH2_HEADER_REMAINDER_SIZE - removedDataLength );
912  }
913 
915 static int processBodyFunction( INOUT SESSION_INFO *sessionInfoPtr,
916  INOUT READSTATE_INFO *readInfo )
917  {
918  SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
919  BYTE *dataRemainingPtr = sessionInfoPtr->receiveBuffer + \
920  sessionInfoPtr->receiveBufPos + \
921  sshInfo->partialPacketDataLength;
922  const int dataRemainingSize = sessionInfoPtr->pendingPacketLength - \
923  sshInfo->partialPacketDataLength;
924  const int dataLength = dataRemainingSize - sessionInfoPtr->authBlocksize;
925  int payloadLength, status;
926 
927  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
928  assert( isWritePtr( readInfo, sizeof( READSTATE_INFO ) ) );
929 
930  REQUIRES( dataRemainingSize >= sessionInfoPtr->authBlocksize && \
931  dataRemainingSize <= sessionInfoPtr->receiveBufEnd - \
932  ( sessionInfoPtr->receiveBufPos + \
933  sshInfo->partialPacketDataLength ) );
934  REQUIRES( dataLength >= 0 && dataLength < dataRemainingSize );
935 
936  /* All errors processing the payload are fatal */
937  *readInfo = READINFO_FATAL;
938 
939  /* Decrypt the packet in the buffer and MAC the payload. The length may
940  be zero if the entire message fits into the already-processed fixed-
941  length header portion, e.g. for channel-close messages that only
942  contain a channel number:
943 
944  Processed in header read +--+
945  rBufPos | | | Processed
946  |<----v----- pendingPacketLength ---------->| +--+
947  v<- pPPL -->| | +--+
948  ----+-----------+-----------------------+-------+-- |//| Encrypted
949  | |///////////////////////|\\\\\\\| +--+
950  ----+-----------+-----------------------+-------+-- +--+
951  |<---- dataLength ----->| | |\\| MAC
952  |<------- dataRemaining ------->| +--+ */
953  if( dataLength > 0 )
954  {
955 
956  status = krnlSendMessage( sessionInfoPtr->iCryptInContext,
957  IMESSAGE_CTX_DECRYPT, dataRemainingPtr,
958  dataLength );
959  if( cryptStatusError( status ) )
960  return( status );
961  status = checkMacSSHIncremental( sessionInfoPtr->iAuthInContext, 0,
962  dataRemainingPtr, dataRemainingSize,
963  dataLength, 0, MAC_END,
964  sessionInfoPtr->authBlocksize );
965  }
966  else
967  {
968  status = checkMacSSHIncremental( sessionInfoPtr->iAuthInContext, 0,
969  dataRemainingPtr, dataRemainingSize,
970  0, 0, MAC_END,
971  sessionInfoPtr->authBlocksize );
972  }
973  if( cryptStatusError( status ) )
974  {
977  "Bad message MAC for packet type %d, length %d",
978  sshInfo->packetType,
979  sshInfo->partialPacketDataLength + dataLength ) );
980  }
981 
982  /* Strip the padding and MAC and update the state information */
983  payloadLength = sessionInfoPtr->pendingPacketLength - \
984  ( sshInfo->padLength + sessionInfoPtr->authBlocksize );
985  sshInfo->readSeqNo++;
986  ENSURES( payloadLength >= 0 && \
987  payloadLength < sessionInfoPtr->pendingPacketLength + dataLength );
988  /* Must be '<' rather than '<=' because of the stripped padding */
989  DEBUG_PRINT(( "Read packet type %d, length %d.\n",
990  sshInfo->packetType, payloadLength ));
991  DEBUG_DUMP_DATA( sessionInfoPtr->receiveBuffer + \
992  sessionInfoPtr->receiveBufPos, payloadLength );
993 
994  /* If it's not plain data (which was handled at the readHeaderFunction()
995  stage), handle it as a control message */
996  if( sshInfo->packetType != SSH_MSG_CHANNEL_DATA )
997  {
998  status = processControlMessage( sessionInfoPtr, readInfo,
999  payloadLength );
1000  if( cryptStatusError( status ) )
1001  {
1002  /* If we got an OK_SPECIAL status then the packet was handled
1003  internally and we can try again. If it was a message that
1004  the user has to respond to it's also not a fatal error
1005  condition and they can continue afterwards */
1006  if( status == OK_SPECIAL || status == CRYPT_ENVELOPE_RESOURCE )
1007  *readInfo = READINFO_NOOP;
1008  return( status );
1009  }
1010  }
1011  sshInfo->partialPacketDataLength = 0;
1012 
1013  *readInfo = READINFO_NONE;
1014  return( payloadLength );
1015  }
1016 
1017 /* Write data over the SSH link */
1018 
1020 static int preparePacketFunction( INOUT SESSION_INFO *sessionInfoPtr )
1021  {
1022  SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
1023  STREAM stream;
1024  const int dataLength = sessionInfoPtr->sendBufPos - \
1026  int length = DUMMY_INIT, status;
1027 
1028  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
1029 
1030  REQUIRES( !( sessionInfoPtr->flags & SESSION_SENDCLOSED ) );
1031  REQUIRES( dataLength > 0 && dataLength < sessionInfoPtr->sendBufPos && \
1032  dataLength < MAX_INTLENGTH );
1033 
1034  /* Wrap up the payload ready for sending:
1035 
1036  byte SSH_MSG_CHANNEL_DATA
1037  uint32 channel_no
1038  string data
1039 
1040  Since this is wrapping in-place data, we first open a write stream to
1041  add the header, then open a read stream covering the full buffer in
1042  preparation for wrapping the packet */
1043  status = openPacketStreamSSHEx( &stream, sessionInfoPtr,
1046  if( cryptStatusError( status ) )
1047  return( status );
1048  writeUint32( &stream, getCurrentChannelNo( sessionInfoPtr, \
1049  CHANNEL_WRITE ) );
1050  status = writeUint32( &stream, dataLength );
1051  sMemDisconnect( &stream );
1052  ENSURES( cryptStatusOK( status ) );
1053  sMemConnect( &stream, sessionInfoPtr->sendBuffer,
1054  sessionInfoPtr->sendBufSize );
1055  status = sSkip( &stream, SSH2_HEADER_SIZE + SSH2_PAYLOAD_HEADER_SIZE + \
1056  dataLength );
1057  if( cryptStatusOK( status ) )
1058  status = wrapPacketSSH2( sessionInfoPtr, &stream, 0, FALSE, FALSE );
1059  if( cryptStatusOK( status ) )
1060  length = stell( &stream );
1061  sMemDisconnect( &stream );
1062  if( cryptStatusError( status ) )
1063  return( status );
1064 
1065  /* If there's control data enqueued to be written, try and append it to
1066  the existing data to be sent. This may or may not append it
1067  (depending on whether there's room in the send buffer) so we may end
1068  up here more than once */
1069  if( sshInfo->response.type > 0 )
1070  {
1071  int length2;
1072 
1073  length2 = appendChannelData( sessionInfoPtr, length );
1074  if( !cryptStatusError( length2 ) )
1075  length += length2;
1076  }
1077 
1078  return( length );
1079  }
1080 
1081 /* Close a previously-opened SSH session */
1082 
1083 STDC_NONNULL_ARG( ( 1 ) ) \
1084 static void shutdownFunction( INOUT SESSION_INFO *sessionInfoPtr )
1085  {
1086  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
1087 
1088  /* If we haven't entered the secure state yet (i.e. we're still in the
1089  middle of the handshake) this is an abnormal termination, send a
1090  disconnect indication:
1091 
1092  byte SSH_MSG_DISCONNECT
1093  uint32 reason_code = SSH_DISCONNECT_PROTOCOL_ERROR
1094  string description = "Handshake failed"
1095  string language_tag = "" */
1096  if( !( sessionInfoPtr->flags & SESSION_ISSECURE_WRITE ) )
1097  {
1098  STREAM stream;
1099  int status;
1100 
1101  status = openPacketStreamSSH( &stream, sessionInfoPtr,
1103  if( cryptStatusOK( status ) )
1104  {
1105  writeUint32( &stream, SSH_DISCONNECT_PROTOCOL_ERROR );
1106  writeString32( &stream, "Handshake failed", 16 );
1107  status = writeUint32( &stream, 0 ); /* No language tag */
1108  }
1109  if( cryptStatusOK( status ) )
1110  status = wrapPacketSSH2( sessionInfoPtr, &stream, 0,
1111  FALSE, TRUE );
1112  if( cryptStatusOK( status ) )
1113  {
1114  const int length = stell( &stream );
1115  void *dataPtr;
1116 
1117  /* Since there's nothing much that we can do at this point in
1118  response to an error except continue and close the network
1119  session, we don't check for errors */
1120  status = sMemGetDataBlockAbs( &stream, 0, &dataPtr, length );
1121  if( cryptStatusOK( status ) )
1122  ( void ) sendCloseNotification( sessionInfoPtr, dataPtr,
1123  length );
1124  }
1125  sMemDisconnect( &stream );
1126  sNetDisconnect( &sessionInfoPtr->stream );
1127  return;
1128  }
1129 
1130  /* Close all remaining channels. Since this is just a cleanup of a
1131  network session that's about to be closed anyway we ignore any errors
1132  that we encounter at this point (a typical error would be the link
1133  going down, in which case the only useful response is to take down
1134  the network session anyway) */
1135  ( void ) closeChannel( sessionInfoPtr, TRUE );
1136  }
1137 
1138 /****************************************************************************
1139 * *
1140 * Session Access Routines *
1141 * *
1142 ****************************************************************************/
1143 
1144 /* Set up access to the SSH session processing. This function can be called
1145  twice, initially with handshakeInfo == NULL to set the default SSH
1146  session processing to SSHv2 and a second time once the SSH handshake is
1147  in progress to initialise the handshake information (if SSHv1 is detected
1148  in the peer and enabled then the second call is to initSSH1processing()
1149  instead) */
1150 
1151 STDC_NONNULL_ARG( ( 1 ) ) \
1152 void initSSH2processing( INOUT SESSION_INFO *sessionInfoPtr,
1153  INOUT_OPT SSH_HANDSHAKE_INFO *handshakeInfo,
1154  const BOOLEAN isServer )
1155  {
1156  static const PROTOCOL_INFO protocolInfo = {
1157  /* General session information */
1158  FALSE, /* Request-response protocol */
1159  SESSION_NONE, /* Flags */
1160  SSH_PORT, /* SSH port */
1161  SESSION_NEEDS_USERID | /* Client attributes */
1163  SESSION_NEEDS_KEYORPASSWORD | \
1164  SESSION_NEEDS_PRIVKEYSIGN,
1165  /* The client private key is optional, but if present it has
1166  to be signature-capable */
1167  SESSION_NEEDS_PRIVATEKEY | /* Server attributes */
1169 #ifdef USE_SSH1
1170  2, 1, 2, /* Version 2 */
1171 #else
1172  2, 2, 2, /* Version 2 */
1173 #endif /* USE_SSH1 */
1174 
1175  /* Protocol-specific information */
1176  EXTRA_PACKET_SIZE + \
1177  DEFAULT_PACKET_SIZE, /* Send/receive buffer size */
1178  SSH2_HEADER_SIZE + \
1179  SSH2_PAYLOAD_HEADER_SIZE,/* Payload data start */
1180  DEFAULT_PACKET_SIZE /* (Default) maximum packet size */
1181  };
1182 
1183  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
1184  assert( ( handshakeInfo == NULL ) || \
1185  isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
1186 
1187  sessionInfoPtr->protocolInfo = &protocolInfo;
1188  sessionInfoPtr->readHeaderFunction = readHeaderFunction;
1189  sessionInfoPtr->processBodyFunction = processBodyFunction;
1190  sessionInfoPtr->preparePacketFunction = preparePacketFunction;
1191  sessionInfoPtr->shutdownFunction = shutdownFunction;
1192  if( handshakeInfo != NULL )
1193  {
1194  if( isServer )
1195  initSSH2serverProcessing( sessionInfoPtr, handshakeInfo );
1196  else
1197  initSSH2clientProcessing( sessionInfoPtr, handshakeInfo );
1198 
1199  handshakeInfo->algoStringPubkeyTbl = algoStringPubkeyTbl;
1200  handshakeInfo->algoStringPubkeyTblNoEntries = \
1201  FAILSAFE_ARRAYSIZE( algoStringPubkeyTbl, ALGO_STRING_INFO );
1202  }
1203  }
1204 #endif /* USE_SSH */