cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
ssh2_svr.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib SSHv2 Server 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 /****************************************************************************
23 * *
24 * Utility Functions *
25 * *
26 ****************************************************************************/
27 
28 /* SSH algorithm names sent to the client, in preferred algorithm order.
29  Since we have a fixed algorithm for our public key (determined by the key
30  type) we only send a single value for this that's evaluated at runtime,
31  so there's no list for public-key algorithms. In addition if the server's
32  key isn't an ECC key we probably shouldn't be advertising any ECC keyex
33  algorithms, so we vary what we send based on the server key type.
34 
35  Note that the values in these lists must be present in the algorithm-name
36  mapping tables in ssh2.c */
37 
38 static const CRYPT_ALGO_TYPE FAR_BSS algoKeyexEccList[] = {
39 #ifdef PREFER_ECC_SUITES
41 #endif /* PREFER_ECC_SUITES */
43 #if !defined( PREFER_ECC_SUITES )
45 #endif /* !PREFER_ECC_SUITES */
46  CRYPT_ALGO_NONE, CRYPT_ALGO_NONE };
47 static const CRYPT_ALGO_TYPE FAR_BSS algoKeyexList[] = {
49  CRYPT_ALGO_NONE, CRYPT_ALGO_NONE };
50 
51 static const CRYPT_ALGO_TYPE FAR_BSS algoEncrList[] = {
53  CRYPT_ALGO_NONE, CRYPT_ALGO_NONE };
54 
55 static const CRYPT_ALGO_TYPE FAR_BSS algoMACList[] = {
57  CRYPT_ALGO_NONE, CRYPT_ALGO_NONE };
58 
59 /* Encode a list of available algorithms */
60 
61 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
62 static int writeAlgoList( INOUT STREAM *stream,
63  IN_ARRAY( algoListLength ) \
64  const CRYPT_ALGO_TYPE *algoList,
65  IN_RANGE( 1, 16 ) const int algoListLength )
66  {
67  const ALGO_STRING_INFO FAR_BSS *algoStringInfoTbl;
68  int availAlgoIndex[ 16 + 8 ];
69  int noAlgos = 0, length = 0, algoIndex, noAlgoStringInfoEntries, status;
70 
71  assert( isWritePtr( stream, sizeof( STREAM ) ) );
72  assert( isReadPtr( algoList, sizeof( CRYPT_ALGO_TYPE ) * \
73  algoListLength ) );
74 
75  REQUIRES( algoListLength >= 1 && algoListLength <= 16 );
76 
77  /* Get the list of SSH algorithms and names */
78  status = getAlgoStringInfo( &algoStringInfoTbl, &noAlgoStringInfoEntries );
79  ENSURES( cryptStatusOK( status ) );
80 
81  /* Walk down the list of algorithms remembering the encoded name of each
82  one that's available for use */
83  for( algoIndex = 0; \
84  algoIndex < algoListLength && \
85  algoList[ algoIndex ] != CRYPT_ALGO_NONE && \
86  algoIndex < FAILSAFE_ITERATIONS_SMALL;
87  algoIndex++ )
88  {
89  const ALGO_STRING_INFO *algoStringInfo;
90  const CRYPT_ALGO_TYPE cryptAlgo = algoList[ algoIndex ];
91  int i;
92 
93  /* Make sure that this algorithm is available for use. If it's a
94  composite cipher suite then we can't perform the check until
95  after we've mapped it to the corresponding cryptlib algorithm
96  values */
97  if( !isPseudoAlgo( cryptAlgo ) && !algoAvailable( cryptAlgo ) )
98  continue;
99 
100  /* Find the mapping entry for this algorithm or cipher suite */
101  for( i = 0;
102  algoStringInfoTbl[ i ].algo != CRYPT_ALGO_NONE && \
103  algoStringInfoTbl[ i ].algo != cryptAlgo && \
104  i < noAlgoStringInfoEntries; i++ );
105  ENSURES( i < noAlgoStringInfoEntries );
106  ENSURES( algoStringInfoTbl[ i ].algo != CRYPT_ALGO_NONE && \
107  noAlgos >= 0 && noAlgos < 16 );
108  algoStringInfo = &algoStringInfoTbl[ i ];
109 
110  /* If it's a cipher suite, make sure that the algorithms that it's
111  made up of are available */
112  if( isPseudoAlgo( cryptAlgo ) )
113  {
114  if( algoStringInfo->checkCryptAlgo != CRYPT_ALGO_NONE && \
115  !algoAvailable( algoStringInfo->checkCryptAlgo ) )
116  continue;
117  if( algoStringInfo->checkHashAlgo != CRYPT_ALGO_NONE && \
118  !algoAvailable( algoStringInfo->checkHashAlgo ) )
119  continue;
120  }
121 
122  /* Remember the algorithm details */
123  availAlgoIndex[ noAlgos++ ] = i;
124  length += algoStringInfo->nameLen;
125  if( noAlgos > 1 )
126  length++; /* Room for comma delimiter */
127  }
128  ENSURES( algoIndex < FAILSAFE_ITERATIONS_SMALL );
129 
130  /* Encode the list of available algorithms into a comma-separated string */
131  status = writeUint32( stream, length );
132  for( algoIndex = 0; cryptStatusOK( status ) && algoIndex < noAlgos;
133  algoIndex++ )
134  {
135  const ALGO_STRING_INFO *algoStringInfo = \
136  &algoStringInfoTbl[ availAlgoIndex[ algoIndex ] ];
137 
138  if( algoIndex > 0 )
139  sputc( stream, ',' ); /* Add comma delimiter */
140  status = swrite( stream, algoStringInfo->name,
141  algoStringInfo->nameLen );
142  }
143  return( status );
144  }
145 
146 /* Set up the public-key algorithm that we'll be advertising to the client
147  based on the server key */
148 
149 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
150 static int initPubkeyAlgo( INOUT SESSION_INFO *sessionInfoPtr,
152  {
153  static const ALGO_STRING_INFO FAR_BSS algoStringPubkeyRSATbl[] = {
154  { "ssh-rsa", 7, CRYPT_ALGO_RSA },
155  { NULL, CRYPT_ALGO_NONE }, { NULL, CRYPT_ALGO_NONE }
156  };
157  static const ALGO_STRING_INFO FAR_BSS algoStringPubkeyDSATbl[] = {
158  { "ssh-dss", 7, CRYPT_ALGO_DSA },
159  { NULL, CRYPT_ALGO_NONE }, { NULL, CRYPT_ALGO_NONE }
160  };
161  static const ALGO_STRING_INFO FAR_BSS algoStringPubkeyECDSATbl[] = {
162  { "ecdsa-sha2-nistp256", 19, CRYPT_ALGO_ECDSA },
163  { NULL, CRYPT_ALGO_NONE }, { NULL, CRYPT_ALGO_NONE }
164  };
165  static const ALGO_STRING_INFO FAR_BSS algoStringPubkeyECDSA384Tbl[] = {
166  { "ecdsa-sha2-nistp384", 19, CRYPT_ALGO_ECDSA },
167  { NULL, CRYPT_ALGO_NONE }, { NULL, CRYPT_ALGO_NONE }
168  };
169  static const ALGO_STRING_INFO FAR_BSS algoStringPubkeyECDSA521Tbl[] = {
170  { "ecdsa-sha2-nistp521", 19, CRYPT_ALGO_ECDSA },
171  { NULL, CRYPT_ALGO_NONE }, { NULL, CRYPT_ALGO_NONE }
172  };
173  int pubKeyAlgo, keySize, status;
174 
175  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
176  assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
177 
178  /* Find out which algorithm the server key is using */
179  status = krnlSendMessage( sessionInfoPtr->privateKey,
180  IMESSAGE_GETATTRIBUTE, &pubKeyAlgo,
182  if( cryptStatusError( status ) )
183  return( status );
184  handshakeInfo->pubkeyAlgo = pubKeyAlgo; /* int vs.enum */
185 
186  /* If it's a standard public-key algorithm, return the algorithm
187  information directly */
188  if( handshakeInfo->pubkeyAlgo == CRYPT_ALGO_RSA )
189  {
190  handshakeInfo->algoStringPubkeyTbl = algoStringPubkeyRSATbl;
191  handshakeInfo->algoStringPubkeyTblNoEntries = \
192  FAILSAFE_ARRAYSIZE( algoStringPubkeyRSATbl, ALGO_STRING_INFO );
193  return( CRYPT_OK );
194  }
195  if( handshakeInfo->pubkeyAlgo == CRYPT_ALGO_DSA )
196  {
197  handshakeInfo->algoStringPubkeyTbl = algoStringPubkeyDSATbl;
198  handshakeInfo->algoStringPubkeyTblNoEntries = \
199  FAILSAFE_ARRAYSIZE( algoStringPubkeyDSATbl, ALGO_STRING_INFO );
200  return( CRYPT_OK );
201  }
202  ENSURES( handshakeInfo->pubkeyAlgo == CRYPT_ALGO_ECDSA );
203 
204  /* ECDSA gets more complicated because there are multiple fixed key
205  sizes possible so we have to vary the algrithm table based on our key
206  size */
207  status = krnlSendMessage( sessionInfoPtr->privateKey,
208  IMESSAGE_GETATTRIBUTE, &keySize,
210  if( cryptStatusError( status ) )
211  return( status );
212  switch( keySize )
213  {
214  case bitsToBytes( 256 ):
215  handshakeInfo->algoStringPubkeyTbl = algoStringPubkeyECDSATbl;
216  handshakeInfo->algoStringPubkeyTblNoEntries = \
217  FAILSAFE_ARRAYSIZE( algoStringPubkeyECDSATbl, ALGO_STRING_INFO );
218  break;
219 
220  case bitsToBytes( 384 ):
221  handshakeInfo->algoStringPubkeyTbl = algoStringPubkeyECDSA384Tbl;
222  handshakeInfo->algoStringPubkeyTblNoEntries = \
223  FAILSAFE_ARRAYSIZE( algoStringPubkeyECDSA384Tbl, ALGO_STRING_INFO );
224  break;
225 
226  case bitsToBytes( 521 ):
227  handshakeInfo->algoStringPubkeyTbl = algoStringPubkeyECDSA521Tbl;
228  handshakeInfo->algoStringPubkeyTblNoEntries = \
229  FAILSAFE_ARRAYSIZE( algoStringPubkeyECDSA521Tbl, ALGO_STRING_INFO );
230  break;
231 
232  default:
233  retIntError();
234  }
235 
236  return( CRYPT_OK );
237  }
238 
239 /* Handle an ephemeral DH key exchange */
240 
241 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
242 static int processDHE( INOUT SESSION_INFO *sessionInfoPtr,
243  INOUT SSH_HANDSHAKE_INFO *handshakeInfo )
244  {
246  STREAM stream;
247  BYTE keyData[ ( CRYPT_MAX_PKCSIZE * 2 ) + 16 + 8 ];
248  void *keyexInfoPtr = DUMMY_INIT_PTR;
249  int keyexInfoLength, keyDataStart, keyDataLength, length;
250  int keySize, status;
251 
252  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
253  assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
254 
255  /* Get the keyex key request from the client:
256 
257  byte type = SSH_MSG_KEXDH_GEX_REQUEST_OLD
258  uint32 n (bits)
259 
260  or:
261 
262  byte type = SSH_MSG_KEXDH_GEX_REQUEST_NEW
263  uint32 min (bits)
264  uint32 n (bits)
265  uint32 max (bits)
266 
267  Portions of the the request information are hashed later as part of
268  the exchange hash so we have to save a copy for then. We save the
269  original encoded form because some clients send non-integral lengths
270  that don't survive the conversion from bits to bytes */
271  status = length = \
272  readHSPacketSSH2( sessionInfoPtr, SSH_MSG_KEXDH_GEX_REQUEST_OLD,
273  ID_SIZE + UINT32_SIZE );
274  if( cryptStatusError( status ) )
275  return( status );
276  sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
277  streamBookmarkSet( &stream, keyexInfoLength );
278  if( sessionInfoPtr->sessionSSH->packetType == SSH_MSG_KEXDH_GEX_REQUEST_NEW )
279  {
280  /* It's a { min_length, length, max_length } sequence, save a copy
281  and get the length value */
282  readUint32( &stream );
283  keySize = readUint32( &stream );
284  status = readUint32( &stream );
285  }
286  else
287  {
288  /* It's a straight length, save a copy and get the length value */
289  status = keySize = readUint32( &stream );
290  }
291  if( !cryptStatusError( status ) )
292  status = streamBookmarkComplete( &stream, &keyexInfoPtr,
293  &keyexInfoLength, keyexInfoLength );
294  sMemDisconnect( &stream );
295  if( cryptStatusError( status ) )
296  {
297  retExt( status,
298  ( status, SESSION_ERRINFO,
299  "Invalid ephemeral DH key data request packet" ) );
300  }
301  ANALYSER_HINT( keyexInfoPtr != NULL );
302  if( keySize < bytesToBits( MIN_PKCSIZE ) || \
303  keySize > bytesToBits( CRYPT_MAX_PKCSIZE ) )
304  {
307  "Client requested invalid ephemeral DH key size %d bits, "
308  "should be %d...%d", keySize,
311  }
312  ENSURES( rangeCheckZ( 0, keyexInfoLength, MAX_ENCODED_KEYEXSIZE ) );
313  memcpy( handshakeInfo->encodedReqKeySizes, keyexInfoPtr,
314  keyexInfoLength );
315  handshakeInfo->encodedReqKeySizesLength = keyexInfoLength;
316  handshakeInfo->requestedServerKeySize = bitsToBytes( keySize );
317 
318  /* If the requested key size differs too much from the built-in default
319  one, destroy the existing default DH key and load a new one of the
320  appropriate size. Things get quite confusing here because the spec
321  is a schizophrenic mix of two different documents, one that specifies
322  the behaviour for the original message format which uses a single
323  length value and a second one that specifies the behaviour for the
324  { min, n, max } combination (multi sunt, qui ad id, quod non
325  proposuerant scribere, alicuius verbi placentis decore vocentur).
326 
327  The range option was added as an attempted fix for implementations
328  that couldn't handle the single size option but the real problem is
329  that the server knows what key sizes are appropriate but the client
330  has to make the choice, without any knowledge of what the server can
331  actually handle. Because of this the spec (in its n-only mindset,
332  which also applies to the min/n/max version since it's the same
333  document) contains assorted weasel-words that allow the server to
334  choose any key size it feels like if the client sends a range
335  indication that's inappropriate. Although the spec ends up saying
336  that the server can do anything it feels like ("The server should
337  return the smallest group it knows that is larger than the size the
338  client requested. If the server does not know a group that is
339  larger than the client request, then it SHOULD return the largest
340  group it knows"), we use a least-upper-bound interpretation of the
341  above, mostly because we store a range of fixed keys of different
342  sizes and can always find something reasonably close to any
343  (sensible) requested length */
344  if( handshakeInfo->requestedServerKeySize < \
345  SSH2_DEFAULT_KEYSIZE - 16 || \
346  handshakeInfo->requestedServerKeySize > \
347  SSH2_DEFAULT_KEYSIZE + 16 )
348  {
349  krnlSendNotifier( handshakeInfo->iServerCryptContext,
351  handshakeInfo->iServerCryptContext = CRYPT_ERROR;
352  status = initDHcontextSSH( &handshakeInfo->iServerCryptContext,
353  &handshakeInfo->serverKeySize, NULL, 0,
354  handshakeInfo->requestedServerKeySize );
355  if( cryptStatusError( status ) )
356  return( status );
357  }
358 
359  /* Send the DH key values to the client:
360 
361  byte type = SSH_MSG_KEXDH_GEX_GROUP
362  mpint p
363  mpint g
364 
365  Since this phase of the key negotiation exchanges raw key components
366  rather than the standard SSH public-key format we have to rewrite
367  the public key before we can send it to the client. What this
368  involves is stripping the:
369 
370  uint32 length
371  string "ssh-dh"
372 
373  header from the start of the datab and then writing what's left to the
374  packet. First we export the key data and figure out the location of
375  the payload that we need to send */
376  setMessageData( &msgData, keyData, ( CRYPT_MAX_PKCSIZE * 2 ) + 16 );
377  status = krnlSendMessage( handshakeInfo->iServerCryptContext,
378  IMESSAGE_GETATTRIBUTE_S, &msgData,
379  CRYPT_IATTRIBUTE_KEY_SSH );
380  if( cryptStatusError( status ) )
381  return( status );
382  sMemConnect( &stream, keyData, msgData.length );
383  readUint32( &stream );
384  status = readUniversal32( &stream );
385  ENSURES( cryptStatusOK( status ) );
386  keyDataStart = stell( &stream );
387  keyDataLength = sMemDataLeft( &stream );
388  sMemDisconnect( &stream );
389 
390  /* Then we create and send the SSH packet using as the payload the key
391  data content of the SSH public key */
392  status = openPacketStreamSSH( &stream, sessionInfoPtr,
394  if( cryptStatusError( status ) )
395  return( status );
396  status = swrite( &stream, keyData + keyDataStart, keyDataLength );
397  if( cryptStatusOK( status ) )
398  status = sendPacketSSH2( sessionInfoPtr, &stream, FALSE );
399  sMemDisconnect( &stream );
400  return( status );
401  }
402 
403 /****************************************************************************
404 * *
405 * Server-side Connect Functions *
406 * *
407 ****************************************************************************/
408 
409 /* Perform the initial part of the handshake with the client */
410 
411 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
412 static int beginServerHandshake( INOUT SESSION_INFO *sessionInfoPtr,
413  INOUT SSH_HANDSHAKE_INFO *handshakeInfo )
414  {
415  STREAM stream;
416  BOOLEAN skipGuessedKeyex = FALSE;
417  void *serverHelloPtr = DUMMY_INIT_PTR;
418  int length, serverHelloLength, clientHelloLength, status;
419 
420  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
421  assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
422 
423  /* Get the public-key algorithm that we'll be advertising to the client
424  and set the algorithm table used for processing the client hello to
425  match the one that we're offering */
426  status = initPubkeyAlgo( sessionInfoPtr, handshakeInfo );
427  if( cryptStatusError( status ) )
428  return( status );
429 
430  /* SSH hashes parts of the handshake messages for integrity-protection
431  purposes so before we start we hash the ID strings (first the client
432  string that we read previously, then our server string) encoded as SSH
433  string values. In addition since the handshake can retroactively
434  switch to a different hash algorithm mid-exchange we have to
435  speculatively hash the messages with alternative algorithms in case
436  the other side decides to switch */
437  status = hashAsString( handshakeInfo->iExchangeHashContext,
438  sessionInfoPtr->receiveBuffer,
439  strlen( sessionInfoPtr->receiveBuffer ) );
440  if( cryptStatusOK( status ) )
441  status = hashAsString( handshakeInfo->iExchangeHashContext,
443  if( cryptStatusOK( status ) && \
444  handshakeInfo->iExchangeHashAltContext != CRYPT_ERROR )
445  {
446  status = hashAsString( handshakeInfo->iExchangeHashAltContext,
447  sessionInfoPtr->receiveBuffer,
448  strlen( sessionInfoPtr->receiveBuffer ) );
449  if( cryptStatusOK( status ) )
450  status = hashAsString( handshakeInfo->iExchangeHashAltContext,
452  }
453  if( cryptStatusError( status ) )
454  return( status );
455 
456  /* Send the server hello packet:
457 
458  byte type = SSH_MSG_KEXINIT
459  byte[16] cookie
460  string keyex algorithms
461  string pubkey algorithms
462  string client_crypto algorithms
463  string server_crypto algorithms
464  string client_mac algorithms
465  string server_mac algorithms
466  string client_compression algorithms = "none"
467  string server_compression algorithms = "none"
468  string client_language = ""
469  string server_language = ""
470  boolean first_keyex_packet_follows = FALSE
471  uint32 reserved = 0
472 
473  The SSH spec leaves the order in which things happen ambiguous, in
474  order to save a while round trip it has provisions for both sides
475  shouting at each other and then a complex interlock process where
476  bits of the initial exchange can be discarded and retried if necessary.
477  This is ugly and error-prone. The client code solves this by waiting
478  for the server hello, choosing known-good algorithms, and then sending
479  the client hello immediately followed by the client key exchange data.
480  Since it waits for the server to speak first it can choose parameters
481  that are accepted the first time.
482 
483  Unfortunately this doesn't work if we're the server since we'd end up
484  waiting for the client to speak first while it waits for us to speak
485  first, so we have to send the server hello in order to prevent
486  deadlock. This works fine with most clients, which take the same
487  approach and wait for the server to speak first. The message flow is
488  then:
489 
490  server hello;
491  client hello;
492  client keyex;
493  server keyex;
494 
495  There are one or two exceptions to this, the worst of which is the
496  F-Secure client, which has the client speak first choosing as its
497  preference the incompletely specified "x509v3-sign-dss" format (see
498  the comment in exchangeServerKeys() below) that we can't use since no-
499  one's quite sure what the format is (this was fixed in mid-2004 when
500  the x509v3-* schemes were removed from the spec since no-one could
501  figure out what they were. F-Secure still specifies them, but has
502  moved them down so that they follow the standard ssh-* schemes). In
503  this case the message flow is:
504 
505  server hello;
506  client hello;
507  client keyex1;
508  client keyex2;
509  server keyex;
510 
511  This is handled by having the code that reads the client hello return
512  OK_SPECIAL to indicate that the next packet should be skipped. An
513  alternative (and simpler) strategy would be to always throw away the
514  client's first keyex sent by older versions of the F-Secure client
515  since they're using an algorithm choice that's impossible to use but
516  that implementation-specific approach doesn't generalise well to
517  other versions or other clients */
518  status = openPacketStreamSSH( &stream, sessionInfoPtr, SSH_MSG_KEXINIT );
519  if( cryptStatusError( status ) )
520  return( status );
521  streamBookmarkSetFullPacket( &stream, serverHelloLength );
522  status = exportVarsizeAttributeToStream( &stream, SYSTEM_OBJECT_HANDLE,
523  CRYPT_IATTRIBUTE_RANDOM_NONCE,
525  if( cryptStatusOK( status ) )
526  {
527  int pkcAlgo;
528 
529  /* If the server key is a non-ECC key then it can't be used with an
530  ECC keyex so we have to explicitly disable it (technically it is
531  possible to mix ECDH with RSA but this is more likely an error
532  than anything deliberate) */
533  status = krnlSendMessage( sessionInfoPtr->privateKey,
534  IMESSAGE_GETATTRIBUTE, &pkcAlgo,
536  if( cryptStatusOK( status ) && isEccAlgo( pkcAlgo ) )
537  {
538  status = writeAlgoList( &stream, algoKeyexEccList,
539  FAILSAFE_ARRAYSIZE( algoKeyexEccList, \
540  CRYPT_ALGO_TYPE ) );
541  }
542  else
543  {
544  status = writeAlgoList( &stream, algoKeyexList,
545  FAILSAFE_ARRAYSIZE( algoKeyexList, \
546  CRYPT_ALGO_TYPE ) );
547  }
548  }
549  if( cryptStatusOK( status ) )
550  status = writeAlgoString( &stream, handshakeInfo->pubkeyAlgo );
551  if( cryptStatusOK( status ) )
552  status = writeAlgoList( &stream, algoEncrList,
553  FAILSAFE_ARRAYSIZE( algoEncrList, \
554  CRYPT_ALGO_TYPE ) );
555  if( cryptStatusOK( status ) )
556  status = writeAlgoList( &stream, algoEncrList,
557  FAILSAFE_ARRAYSIZE( algoEncrList, \
558  CRYPT_ALGO_TYPE ) );
559  if( cryptStatusOK( status ) )
560  status = writeAlgoList( &stream, algoMACList,
561  FAILSAFE_ARRAYSIZE( algoMACList, \
562  CRYPT_ALGO_TYPE ) );
563  if( cryptStatusOK( status ) )
564  status = writeAlgoList( &stream, algoMACList,
565  FAILSAFE_ARRAYSIZE( algoMACList, \
566  CRYPT_ALGO_TYPE ) );
567  if( cryptStatusOK( status ) )
568  status = writeAlgoString( &stream, CRYPT_PSEUDOALGO_COPR );
569  if( cryptStatusOK( status ) )
570  status = writeAlgoString( &stream, CRYPT_PSEUDOALGO_COPR );
571  if( cryptStatusOK( status ) )
572  {
573  writeUint32( &stream, 0 ); /* No language tag */
574  writeUint32( &stream, 0 );
575  sputc( &stream, 0 ); /* Don't try and guess the keyex */
576  status = writeUint32( &stream, 0 ); /* Reserved */
577  }
578  if( cryptStatusOK( status ) )
579  {
580  status = streamBookmarkComplete( &stream, &serverHelloPtr,
581  &serverHelloLength,
582  serverHelloLength );
583  }
584  if( cryptStatusOK( status ) )
585  status = sendPacketSSH2( sessionInfoPtr, &stream, FALSE );
586  sMemDisconnect( &stream );
587  if( cryptStatusError( status ) )
588  return( status );
589  ANALYSER_HINT( serverHelloPtr != NULL );
590 
591  /* While we wait for the client to digest our hello and send back its
592  response, create the context with the DH key */
593  status = initDHcontextSSH( &handshakeInfo->iServerCryptContext,
594  &handshakeInfo->serverKeySize, NULL, 0,
596  if( cryptStatusError( status ) )
597  return( status );
598 
599  /* Process the client hello packet and hash the client and server
600  hello. Since the entire encoded packet (including the type value)
601  is hashed we have to reconstruct this at the start of the client
602  hello packet */
603  status = processHelloSSH( sessionInfoPtr, handshakeInfo,
604  &clientHelloLength, TRUE );
605  if( cryptStatusError( status ) )
606  {
607  if( status != OK_SPECIAL )
608  return( status );
609 
610  /* There's a guessed keyex following the client hello that we have
611  to skip later (we can't process it at this point because we still
612  need to hash the data sitting in the receive buffer) */
613  skipGuessedKeyex = TRUE;
614  }
615  REQUIRES( rangeCheck( 1, clientHelloLength,
616  sessionInfoPtr->receiveBufSize ) );
617  memmove( sessionInfoPtr->receiveBuffer + 1,
618  sessionInfoPtr->receiveBuffer, clientHelloLength );
619  sessionInfoPtr->receiveBuffer[ 0 ] = SSH_MSG_KEXINIT;
620  status = hashAsString( handshakeInfo->iExchangeHashContext,
621  sessionInfoPtr->receiveBuffer,
622  clientHelloLength + 1 );
623  if( cryptStatusOK( status ) && skipGuessedKeyex )
624  {
625  /* There's an incorrectly-guessed keyex following the client hello,
626  skip it */
627  status = readHSPacketSSH2( sessionInfoPtr,
628  ( handshakeInfo->requestedServerKeySize > 0 ) ? \
631  }
632  if( !cryptStatusError( status ) ) /* readHSPSSH2() returns a length */
633  status = hashAsString( handshakeInfo->iExchangeHashContext,
634  serverHelloPtr, serverHelloLength );
635  if( cryptStatusError( status ) )
636  return( status );
637 
638  /* If we're using a nonstandard DH key value, negotiate a new key with
639  the client */
640  if( handshakeInfo->requestedServerKeySize > 0 )
641  {
642  status = processDHE( sessionInfoPtr, handshakeInfo );
643  if( cryptStatusError( status ) )
644  return( status );
645  }
646 
647 #ifdef USE_ECDH
648  /* If we're using ECDH rather than DH we have to switch from DH contexts
649  and a DH exchange to the equivalent ECDH contexts and values */
650  if( handshakeInfo->isECDH )
651  {
652  krnlSendNotifier( handshakeInfo->iServerCryptContext,
654  handshakeInfo->iServerCryptContext = CRYPT_ERROR;
655  status = initECDHcontextSSH( &handshakeInfo->iServerCryptContext,
656  &handshakeInfo->serverKeySize,
657  handshakeInfo->keyexAlgo );
658  if( cryptStatusError( status ) )
659  {
660  sMemDisconnect( &stream );
661  return( status );
662  }
663  }
664 #endif /* USE_ECDH */
665 
666  /* Process the client keyex:
667 
668  DH:
669  byte type = SSH_MSG_KEXDH_INIT / SSH_MSG_KEXDH_GEX_INIT
670  mpint y
671  ECDH:
672  byte type = SSH_MSG_KEX_ECDH_INIT
673  string q_c */
674  status = length = \
675  readHSPacketSSH2( sessionInfoPtr,
676  ( handshakeInfo->requestedServerKeySize > 0 ) ? \
678  ID_SIZE + ( handshakeInfo->isECDH ? \
680  sizeofString32( "", MIN_PKCSIZE ) ) );
681  if( cryptStatusError( status ) )
682  return( status );
683  sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
684  status = readRawObject32( &stream, handshakeInfo->clientKeyexValue,
686  &handshakeInfo->clientKeyexValueLength );
687  sMemDisconnect( &stream );
688  if( cryptStatusOK( status ) )
689  {
690  if( handshakeInfo->isECDH )
691  {
692  if( !isValidECDHsize( handshakeInfo->clientKeyexValueLength,
693  handshakeInfo->serverKeySize, LENGTH_SIZE ) )
694  status = CRYPT_ERROR_BADDATA;
695  }
696  else
697  {
698  if( !isValidDHsize( handshakeInfo->clientKeyexValueLength,
699  handshakeInfo->serverKeySize, LENGTH_SIZE ) )
700  status = CRYPT_ERROR_BADDATA;
701  }
702  }
703  if( cryptStatusError( status ) )
704  {
707  "Invalid %s phase 1 keyex value",
708  handshakeInfo->isECDH ? "ECDH" : "DH" ) );
709  }
710  return( CRYPT_OK );
711  }
712 
713 /* Exchange keys with the client */
714 
715 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
716 static int exchangeServerKeys( INOUT SESSION_INFO *sessionInfoPtr,
717  INOUT SSH_HANDSHAKE_INFO *handshakeInfo )
718  {
719  KEYAGREE_PARAMS keyAgreeParams;
720  STREAM stream;
721  void *keyPtr = DUMMY_INIT_PTR, *dataPtr;
722  int keyLength, dataLength, sigLength = DUMMY_INIT, packetOffset, status;
723 
724  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
725  assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
726 
727  /* Create the server DH/ECDH value */
728  memset( &keyAgreeParams, 0, sizeof( KEYAGREE_PARAMS ) );
729  status = krnlSendMessage( handshakeInfo->iServerCryptContext,
730  IMESSAGE_CTX_ENCRYPT, &keyAgreeParams,
731  sizeof( KEYAGREE_PARAMS ) );
732  if( cryptStatusError( status ) )
733  return( status );
734  sMemOpen( &stream, handshakeInfo->serverKeyexValue,
736  if( handshakeInfo->isECDH )
737  {
738  status = writeString32( &stream, keyAgreeParams.publicValue,
739  keyAgreeParams.publicValueLen );
740  }
741  else
742  {
743  status = writeInteger32( &stream, keyAgreeParams.publicValue,
744  keyAgreeParams.publicValueLen );
745  }
746  if( cryptStatusOK( status ) )
747  handshakeInfo->serverKeyexValueLength = stell( &stream );
748  sMemDisconnect( &stream );
749  if( cryptStatusError( status ) )
750  return( status );
751 
752  /* Build the DH phase 2 keyex packet:
753 
754  DH + RSA/DSA
755  byte type = SSH_MSG_KEXDH_REPLY / SSH_MSG_KEXDH_GEX_REPLY
756  string [ server key/certificate ]
757  string "ssh-rsa" "ssh-dss"
758  mpint e p
759  mpint n q
760  mpint g
761  mpint y
762  mpint y'
763  string [ signature of handshake data ]
764  string "ssh-rsa" "ssh-dss"
765  string signature signature
766  ...
767 
768  ECDH + ECDSA
769  byte SSH_MSG_KEX_ECDH_REPLY
770  string [ server key/certificate ]
771  string "ecdsa-sha2-*"
772  string "*" -- The "*" portion from the above field
773  string Q
774  string q_s
775  string [ signature of handshake data ]
776  string "ecdsa-sha2-*"
777  string signature
778  ...
779 
780  The specification also makes provision for using X.509 and PGP keys,
781  but only so far as to say that keys and signatures are in "X.509 DER"
782  and "PGP" formats, neither of which actually explain what it is
783  that's sent or signed (and no-one on the SSH list can agree on what
784  they're supposed to look like) so we can't use either of them */
785  status = openPacketStreamSSH( &stream, sessionInfoPtr,
786  handshakeInfo->requestedServerKeySize ? \
789  if( cryptStatusError( status ) )
790  return( status );
791  streamBookmarkSet( &stream, keyLength );
792  status = exportAttributeToStream( &stream, sessionInfoPtr->privateKey,
793  CRYPT_IATTRIBUTE_KEY_SSH );
794  if( cryptStatusOK( status ) )
795  status = streamBookmarkComplete( &stream, &keyPtr, &keyLength,
796  keyLength );
797  if( cryptStatusOK( status ) )
798  status = krnlSendMessage( handshakeInfo->iExchangeHashContext,
799  IMESSAGE_CTX_HASH, keyPtr, keyLength );
800  if( cryptStatusError( status ) )
801  {
802  sMemDisconnect( &stream );
803  return( status );
804  }
805  swrite( &stream, handshakeInfo->serverKeyexValue,
806  handshakeInfo->serverKeyexValueLength );
807 
808  /* Complete phase 2 of the DH key agreement process to obtain the shared
809  secret value */
810  status = completeKeyex( sessionInfoPtr, handshakeInfo, TRUE );
811  if( cryptStatusError( status ) )
812  return( status );
813 
814  /* Sign the hash. The reason for the min() part of the expression is
815  that iCryptCreateSignature() gets suspicious of very large buffer
816  sizes, for example when the user has specified the use of a 1MB send
817  buffer */
818  status = sMemGetDataBlockRemaining( &stream, &dataPtr, &dataLength );
819  if( cryptStatusOK( status ) )
820  {
821  status = iCryptCreateSignature( dataPtr,
822  min( dataLength, MAX_INTLENGTH_SHORT - 1 ),
823  &sigLength, CRYPT_IFORMAT_SSH,
824  sessionInfoPtr->privateKey,
825  handshakeInfo->iExchangeHashContext, NULL );
826  }
827  krnlSendNotifier( handshakeInfo->iExchangeHashContext,
829  handshakeInfo->iExchangeHashContext = CRYPT_ERROR;
830  if( handshakeInfo->iExchangeHashAltContext != CRYPT_ERROR )
831  {
832  krnlSendNotifier( handshakeInfo->iExchangeHashAltContext,
834  handshakeInfo->iExchangeHashAltContext = CRYPT_ERROR;
835  }
836  if( cryptStatusOK( status ) )
837  status = sSkip( &stream, sigLength );
838  if( cryptStatusOK( status ) )
839  status = wrapPacketSSH2( sessionInfoPtr, &stream, 0, FALSE, TRUE );
840  if( cryptStatusError( status ) )
841  {
842  sMemDisconnect( &stream );
843  return( status );
844  }
845 
846  /* Set up the security information required for the session. We have to
847  do this before sending the change cipherspec (rather than during the
848  pause while we're waiting for the other side's response) because we
849  can only tell the other side to switch to secure mode if we're sure
850  that we're already in that state ourselves */
851  status = initSecurityInfo( sessionInfoPtr, handshakeInfo );
852  if( cryptStatusError( status ) )
853  return( status );
854 
855  /* Build our change cipherspec message and send the whole mess through
856  to the client:
857  ...
858  byte type = SSH_MSG_NEWKEYS
859 
860  After this point the write channel is in the secure state */
861  status = continuePacketStreamSSH( &stream, SSH_MSG_NEWKEYS,
862  &packetOffset );
863  if( cryptStatusOK( status ) )
864  status = wrapPacketSSH2( sessionInfoPtr, &stream, packetOffset,
865  FALSE, TRUE );
866  if( cryptStatusOK( status ) )
867  status = sendPacketSSH2( sessionInfoPtr, &stream, TRUE );
868  sMemDisconnect( &stream );
869  if( cryptStatusError( status ) )
870  return( status );
871  sessionInfoPtr->flags |= SESSION_ISSECURE_WRITE;
872  return( CRYPT_OK );
873  }
874 
875 /* Complete the handshake with the client */
876 
877 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
878 static int completeServerHandshake( INOUT SESSION_INFO *sessionInfoPtr,
879  INOUT SSH_HANDSHAKE_INFO *handshakeInfo )
880  {
881  STREAM stream;
883  int length, status;
884 
885  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
886  assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
887 
888  /* If this is the first time through, set up the security information
889  and wait for the client's pre-authentication */
890  if( !( sessionInfoPtr->flags & SESSION_PARTIALOPEN ) )
891  {
892  BYTE stringBuffer[ CRYPT_MAX_TEXTSIZE + 8 ];
893  int stringLength;
894 
895  /* If the caller has supplied user information to match against we
896  require a match against the fixed caller-supplied information
897  rather than accepting what the client sends us and passing it
898  back to the caller to check */
899  if( findSessionInfo( sessionInfoPtr->attributeList,
900  CRYPT_SESSINFO_USERNAME ) != NULL )
901  userInfoPresent = TRUE;
902 
903  /* Wait for the client's change cipherspec message. From this point
904  on the read channel is in the secure state */
905  status = readHSPacketSSH2( sessionInfoPtr, SSH_MSG_NEWKEYS,
906  ID_SIZE );
907  if( cryptStatusError( status ) )
908  return( status );
909  sessionInfoPtr->flags |= SESSION_ISSECURE_READ;
910 
911  /* Wait for the client's pre-authentication packets, which aren't
912  used for any authentication but which are required anyway by the
913  protocol. For some reason SSH requires the use of two messages
914  where one would do, first an "I'm about to authenticate" packet
915  and then an "I'm authenticating" packet after that. Since the
916  former isn't useful for anything we clear it to get it out of the
917  way so that we can perform the actual authentication:
918 
919  byte type = SSH_MSG_SERVICE_REQUEST
920  string service_name = "ssh-userauth"
921 
922  byte type = SSH_MSG_SERVICE_ACCEPT
923  string service_name = "ssh-userauth" */
924  status = length = \
925  readHSPacketSSH2( sessionInfoPtr, SSH_MSG_SERVICE_REQUEST,
926  ID_SIZE + sizeofString32( "", 8 ) );
927  if( cryptStatusError( status ) )
928  return( status );
929  sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
930  status = readString32( &stream, stringBuffer, CRYPT_MAX_TEXTSIZE,
931  &stringLength );
932  sMemDisconnect( &stream );
933  if( cryptStatusError( status ) || \
934  stringLength != 12 || memcmp( stringBuffer, "ssh-userauth", 12 ) )
935  {
938  "Invalid service request packet" ) );
939  }
940  status = openPacketStreamSSH( &stream, sessionInfoPtr,
942  if( cryptStatusError( status ) )
943  return( status );
944  status = writeString32( &stream, "ssh-userauth", 12 );
945  if( cryptStatusOK( status ) )
946  status = sendPacketSSH2( sessionInfoPtr, &stream, FALSE );
947  sMemDisconnect( &stream );
948  if( cryptStatusError( status ) )
949  return( status );
950  }
951 
952  /* Process the client's authentication */
953  status = processServerAuth( sessionInfoPtr, userInfoPresent );
954  if( cryptStatusError( status ) )
955  return( status );
956 
957  /* Handle the channel open */
958  status = length = \
959  readHSPacketSSH2( sessionInfoPtr, SSH_MSG_CHANNEL_OPEN,
960  ID_SIZE + sizeofString32( "", 4 ) + \
962  if( cryptStatusError( status ) )
963  return( status );
964  sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
965  status = processChannelOpen( sessionInfoPtr, &stream );
966  sMemDisconnect( &stream );
967 #if 1
968  return( status );
969 #else /* If we handle the following inline as part of the general read code
970  it requires that the user try and read some data (with a non-zero
971  timeout) right after the connect completes. Because it's awkward
972  to have to rely on this we provide optional code to explicitly
973  clear the pipe here. This code stops as soon as the first data
974  channel-opening request is received, with further requests being
975  handled inline as part of the standard data-read handling. The
976  reason why this isn't enabled by default is that it's possible to
977  encounter a client that doesn't send anything beyond the initial
978  channel open, which means that we'd hang around waiting for a
979  control message until we time out */
980  if( cryptStatusError( status ) )
981  return( status );
982 
983  /* Process any further junk that the caller may throw at us until we get
984  a request that we can handle, indicated by an OK_SPECIAL response */
985  do
986  {
987  status = length = \
988  readHSPacketSSH2( sessionInfoPtr, SSH_MSG_SPECIAL_REQUEST, 8 );
989  if( !cryptStatusError( status ) )
990  {
991  sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
992  status = processChannelControlMessage( sessionInfoPtr, &stream );
993  sMemDisconnect( &stream );
994  }
995  }
996  while( cryptStatusOK( status ) );
997  return( ( status == OK_SPECIAL ) ? CRYPT_OK : status );
998 #endif /* 1 */
999  }
1000 
1001 /****************************************************************************
1002 * *
1003 * Session Access Routines *
1004 * *
1005 ****************************************************************************/
1006 
1007 STDC_NONNULL_ARG( ( 1, 2 ) ) \
1008 void initSSH2serverProcessing( STDC_UNUSED SESSION_INFO *sessionInfoPtr,
1009  INOUT SSH_HANDSHAKE_INFO *handshakeInfo )
1010  {
1011  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
1012  assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
1013 
1014  handshakeInfo->beginHandshake = beginServerHandshake;
1015  handshakeInfo->exchangeKeys = exchangeServerKeys;
1016  handshakeInfo->completeHandshake = completeServerHandshake;
1017  }
1018 #endif /* USE_SSH */