cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
ssh2_cli.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib SSHv2 Client 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 /* Generate/check an SSH key fingerprint. This is simply an MD5 hash of the
29  server's key/certificate data */
30 
31 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
32 static int processKeyFingerprint( INOUT SESSION_INFO *sessionInfoPtr,
33  IN_BUFFER( keyDataLength ) const void *keyData,
34  IN_LENGTH_SHORT const int keyDataLength )
35  {
36  HASHFUNCTION_ATOMIC hashFunctionAtomic;
38  findSessionInfo( sessionInfoPtr->attributeList,
40  BYTE fingerPrint[ CRYPT_MAX_HASHSIZE + 8 ];
41  int hashSize;
42 
43  assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
44  assert( isReadPtr( keyData, keyDataLength ) );
45 
46  REQUIRES( keyDataLength > 0 && keyDataLength < MAX_INTLENGTH_SHORT );
47 
48  getHashAtomicParameters( CRYPT_ALGO_MD5, 0, &hashFunctionAtomic,
49  &hashSize );
51  keyData, keyDataLength );
52  if( attributeListPtr == NULL )
53  {
54  /* Remember the value for the caller */
55  return( addSessionInfoS( &sessionInfoPtr->attributeList,
57  fingerPrint, hashSize ) );
58  }
59 
60  /* In the unlikely event that the user has passed us a SHA-1 fingerprint
61  (which isn't allowed by the spec, but no doubt someone out there's
62  using it based on the fact that the SSH architecture draft suggested
63  a SHA-1 fingerprint while the SSH fingerprint draft required an MD5
64  fingerprint), calculate that instead */
65  if( attributeListPtr->valueLength == 20 )
66  {
67  getHashAtomicParameters( CRYPT_ALGO_SHA1, 0, &hashFunctionAtomic,
68  &hashSize );
70  keyData, keyDataLength );
71  }
72 
73  /* There's an existing fingerprint value, make sure that it matches what
74  we just calculated */
75  if( attributeListPtr->valueLength != hashSize || \
76  memcmp( attributeListPtr->value, fingerPrint, hashSize ) )
77  {
78  /* If there's enough fingerprint data present we can be a bit more
79  specific in our error message */
80  if( attributeListPtr->valueLength >= 8 )
81  {
82  const BYTE *reqFingerPrint = attributeListPtr->value;
83  const int reqFingerPrintLength = attributeListPtr->valueLength;
84 
87  "Server key fingerprint %02X %02X %02X %02X...%02X %02X "
88  "doesn't match requested fingerprint "
89  "%02X %02X %02X %02X...%02X %02X",
90  fingerPrint[ 0 ], fingerPrint[ 1 ],
91  fingerPrint[ 2 ], fingerPrint[ 3 ],
92  fingerPrint[ hashSize - 2 ], fingerPrint[ hashSize - 1 ],
93  reqFingerPrint[ 0 ], reqFingerPrint[ 1 ],
94  reqFingerPrint[ 2 ], reqFingerPrint[ 3 ],
95  reqFingerPrint[ reqFingerPrintLength - 2 ],
96  reqFingerPrint[ reqFingerPrintLength - 1 ] ) );
97  }
100  "Server key fingerprint doesn't match requested "
101  "fingerprint" ) );
102  }
103 
104  return( CRYPT_OK );
105  }
106 
107 /* Handle an ephemeral DH key exchange */
108 
109 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3, 4 ) ) \
110 static int processDHE( INOUT SESSION_INFO *sessionInfoPtr,
112  INOUT STREAM *stream,
113  INOUT KEYAGREE_PARAMS *keyAgreeParams )
114  {
115  const int keyDataHdrSize = LENGTH_SIZE + sizeofString32( "ssh-dh", 6 );
116  void *keyexInfoPtr = DUMMY_INIT_PTR;
117  int keyexInfoLength = DUMMY_INIT, length, packetOffset, dummy, status;
118 
119  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
120  assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
121  assert( isWritePtr( stream, sizeof( STREAM ) ) );
122  assert( isWritePtr( keyAgreeParams, sizeof( KEYAGREE_PARAMS ) ) );
123 
124  /* ...
125  byte type = SSH_MSG_KEXDH_GEX_REQUEST_OLD
126  uint32 n = 1024 bits
127 
128  There's an alternative format that allows the client to specify a
129  range of key sizes:
130 
131  byte type = SSH_MSG_KEXDH_GEX_REQUEST_NEW
132  uint32 min = 1024 bits
133  uint32 n = SSH_DEFAULT_KEYSIZE (as bits)
134  uint32 max = CRYPT_MAX_PKCSIZE (as bits)
135 
136  but a number of implementations never really got around to supporting
137  this properly, with some servers just dropping the connection without
138  any error response if they encounter the newer packet type */
139 #if 1
140  status = continuePacketStreamSSH( stream, SSH_MSG_KEXDH_GEX_REQUEST_OLD,
141  &packetOffset );
142  if( cryptStatusOK( status ) )
143  {
144  streamBookmarkSet( stream, keyexInfoLength );
145  status = writeUint32( stream, bytesToBits( SSH2_DEFAULT_KEYSIZE ) );
146  }
147 #else
148  status = continuePacketStreamSSH( stream, SSH_MSG_KEXDH_GEX_REQUEST_NEW,
149  &packetOffset );
150  if( cryptStatusOK( status ) )
151  {
152  streamBookmarkSet( stream, keyexInfoLength );
153  writeUint32( stream, 1024 );
154  writeUint32( stream, bytesToBits( SSH2_DEFAULT_KEYSIZE ) );
155  status = writeUint32( stream, bytesToBits( CRYPT_MAX_PKCSIZE ) );
156  }
157 #endif /* 1 */
158  if( cryptStatusOK( status ) )
159  status = streamBookmarkComplete( stream, &keyexInfoPtr,
160  &keyexInfoLength, keyexInfoLength );
161  if( cryptStatusOK( status ) )
162  status = wrapPacketSSH2( sessionInfoPtr, stream, packetOffset,
163  FALSE, TRUE );
164  if( cryptStatusOK( status ) )
165  status = sendPacketSSH2( sessionInfoPtr, stream, TRUE );
166  sMemDisconnect( stream );
167  if( cryptStatusError( status ) )
168  return( status );
169  ANALYSER_HINT( keyexInfoPtr != NULL );
170 
171  /* Remember the encoded key size information for later when we generate
172  the exchange hash */
173  ENSURES( rangeCheckZ( 0, keyexInfoLength, ENCODED_REQKEYSIZE ) );
174  memcpy( handshakeInfo->encodedReqKeySizes, keyexInfoPtr,
175  keyexInfoLength );
176  handshakeInfo->encodedReqKeySizesLength = keyexInfoLength;
177 
178  /* Process the ephemeral DH key:
179 
180  byte type = SSH_MSG_KEXDH_GEX_GROUP
181  mpint p
182  mpint g */
183  status = length = \
184  readHSPacketSSH2( sessionInfoPtr, SSH_MSG_KEXDH_GEX_GROUP,
185  ID_SIZE + sizeofString32( "", MIN_PKCSIZE ) + \
186  sizeofString32( "", 1 ) );
187  if( cryptStatusError( status ) )
188  return( status );
189  sMemConnect( stream, sessionInfoPtr->receiveBuffer, length );
190  streamBookmarkSet( stream, keyexInfoLength );
191  status = readInteger32Checked( stream, NULL, &dummy, MIN_PKCSIZE,
193  if( cryptStatusOK( status ) )
194  status = readInteger32( stream, NULL, &dummy, 1,
196  if( cryptStatusOK( status ) )
197  {
198  status = streamBookmarkComplete( stream, &keyexInfoPtr,
199  &keyexInfoLength,
200  keyexInfoLength );
201  }
202  sMemDisconnect( stream );
203  if( cryptStatusError( status ) )
204  {
205  /* Some misconfigured servers may use very short keys, we perform
206  a special-case check for these and return a more specific message
207  than the generic bad-data */
208  if( status == CRYPT_ERROR_NOSECURE )
209  {
212  "Insecure DH key used in key exchange" ) );
213  }
214 
217  "Invalid DH ephemeral key data packet" ) );
218  }
219  ANALYSER_HINT( keyexInfoPtr != NULL );
220 
221  /* Since this phase of the key negotiation exchanges raw key components
222  rather than the standard SSH public-key format we have to rewrite the
223  raw key components into a standard SSH key so that we can import it:
224 
225  From: To:
226  string [ key/certificate ]
227  string "ssh-dh"
228  mpint p mpint p
229  mpint g mpint g */
230  REQUIRES( rangeCheck( keyDataHdrSize, keyexInfoLength,
231  sessionInfoPtr->receiveBufSize ) );
232  memmove( ( BYTE * ) keyexInfoPtr + keyDataHdrSize, keyexInfoPtr,
233  keyexInfoLength );
234  sMemOpen( stream, keyexInfoPtr, keyDataHdrSize );
235  writeUint32( stream, sizeofString32( "ssh-dh", 6 ) + keyexInfoLength );
236  status = writeString32( stream, "ssh-dh", 6 );
237  sMemDisconnect( stream );
238  ENSURES( cryptStatusOK( status ) );
239 
240  /* Destroy the existing static DH key, load the new one, and re-perform
241  phase 1 of the DH key agreement process */
242  krnlSendNotifier( handshakeInfo->iServerCryptContext,
244  handshakeInfo->iServerCryptContext = CRYPT_ERROR;
245  status = initDHcontextSSH( &handshakeInfo->iServerCryptContext,
246  &handshakeInfo->serverKeySize, keyexInfoPtr,
247  keyDataHdrSize + keyexInfoLength,
248  CRYPT_UNUSED );
249  if( cryptStatusOK( status ) )
250  {
251  memset( keyAgreeParams, 0, sizeof( KEYAGREE_PARAMS ) );
252  status = krnlSendMessage( handshakeInfo->iServerCryptContext,
253  IMESSAGE_CTX_ENCRYPT, keyAgreeParams,
254  sizeof( KEYAGREE_PARAMS ) );
255  }
256  if( cryptStatusError( status ) )
257  {
258  retExt( status,
259  ( status, SESSION_ERRINFO,
260  "Invalid DH ephemeral key data" ) );
261  }
262 
263  return( CRYPT_OK );
264  }
265 
266 #ifdef USE_ECDH
267 
268 /* Switch from using DH contexts and a DH exchange to the equivalent ECDH
269  contexts and values */
270 
271 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
272 static int switchToECDH( INOUT SESSION_INFO *sessionInfoPtr,
273  INOUT SSH_HANDSHAKE_INFO *handshakeInfo,
274  INOUT KEYAGREE_PARAMS *keyAgreeParams )
275  {
276  int status;
277 
278  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
279  assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
280  assert( isWritePtr( keyAgreeParams, sizeof( KEYAGREE_PARAMS ) ) );
281 
282  /* Destroy the existing DH context, replace it with an ECDH one, and
283  re-perform phase 1 of the ECDH key agreement process */
284  krnlSendNotifier( handshakeInfo->iServerCryptContext,
286  handshakeInfo->iServerCryptContext = CRYPT_ERROR;
287  status = initECDHcontextSSH( &handshakeInfo->iServerCryptContext,
288  &handshakeInfo->serverKeySize,
289  handshakeInfo->keyexAlgo );
290  if( cryptStatusError( status ) )
291  return( status );
292  memset( keyAgreeParams, 0, sizeof( KEYAGREE_PARAMS ) );
293  return( krnlSendMessage( handshakeInfo->iServerCryptContext,
294  IMESSAGE_CTX_ENCRYPT, keyAgreeParams,
295  sizeof( KEYAGREE_PARAMS ) ) );
296  }
297 #endif /* USE_ECDH */
298 
299 /****************************************************************************
300 * *
301 * Client-side Connect Functions *
302 * *
303 ****************************************************************************/
304 
305 /* Perform the initial part of the handshake with the server */
306 
307 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
308 static int beginClientHandshake( INOUT SESSION_INFO *sessionInfoPtr,
309  INOUT SSH_HANDSHAKE_INFO *handshakeInfo )
310  {
311  MESSAGE_CREATEOBJECT_INFO createInfo;
312  KEYAGREE_PARAMS keyAgreeParams;
313  STREAM stream;
314  void *clientHelloPtr = DUMMY_INIT_PTR, *keyexPtr = DUMMY_INIT_PTR;
315  int serverHelloLength, clientHelloLength, keyexLength = DUMMY_INIT;
316  int packetOffset = 0, status;
317 
318  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
319  assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
320 
321  /* The higher-level code has already read the server version information,
322  send back our own version information */
323  status = swrite( &sessionInfoPtr->stream, SSH2_ID_STRING "\r\n",
324  SSH_ID_STRING_SIZE + 2 );
325  if( cryptStatusError( status ) )
326  {
327  sNetGetErrorInfo( &sessionInfoPtr->stream,
328  &sessionInfoPtr->errorInfo );
329  return( status );
330  }
331 
332  /* SSH hashes parts of the handshake messages for integrity-protection
333  purposes so we hash the ID strings (first our client string, then the
334  server string that we read previously) encoded as SSH string values
335  and without the CRLF terminator that we sent above, which isn't part
336  of the hashed data. In addition since the handshake can
337  retroactively switch to a different hash algorithm mid-exchange we
338  have to speculatively hash the messages with alternative algorithms
339  in case the other side decides to switch */
340  status = hashAsString( handshakeInfo->iExchangeHashContext,
342  if( cryptStatusOK( status ) )
343  status = hashAsString( handshakeInfo->iExchangeHashContext,
344  sessionInfoPtr->receiveBuffer,
345  strlen( sessionInfoPtr->receiveBuffer ) );
346  if( cryptStatusOK( status ) && \
347  handshakeInfo->iExchangeHashAltContext != CRYPT_ERROR )
348  {
349  status = hashAsString( handshakeInfo->iExchangeHashAltContext,
351  if( cryptStatusOK( status ) )
352  status = hashAsString( handshakeInfo->iExchangeHashAltContext,
353  sessionInfoPtr->receiveBuffer,
354  strlen( sessionInfoPtr->receiveBuffer ) );
355  }
356  if( cryptStatusError( status ) )
357  return( status );
358 
359  /* While we wait for the server to digest our version information and
360  send back its response we can create the context with the DH key and
361  perform phase 1 of the DH key agreement process */
362  status = initDHcontextSSH( &handshakeInfo->iServerCryptContext,
363  &handshakeInfo->serverKeySize, NULL, 0,
365  if( cryptStatusError( status ) )
366  return( status );
367  memset( &keyAgreeParams, 0, sizeof( KEYAGREE_PARAMS ) );
368  status = krnlSendMessage( handshakeInfo->iServerCryptContext,
369  IMESSAGE_CTX_ENCRYPT, &keyAgreeParams,
370  sizeof( KEYAGREE_PARAMS ) );
371  if( cryptStatusError( status ) )
372  return( status );
373 
374  /* Process the server hello */
375  status = processHelloSSH( sessionInfoPtr, handshakeInfo,
376  &serverHelloLength, FALSE );
377  if( cryptStatusError( status ) )
378  return( status );
379 
380  /* Build the client hello and DH/ECDH phase 1 keyex packet:
381 
382  byte type = SSH_MSG_KEXINIT
383  byte[16] cookie
384  string keyex algorithms = DH/DHE/ECDH
385  string pubkey algorithms
386  string client_crypto algorithms
387  string server_crypto algorithms
388  string client_mac algorithms
389  string server_mac algorithms
390  string client_compression algorithms = "none"
391  string server_compression algorithms = "none"
392  string client_language = ""
393  string server_language = ""
394  boolean first_keyex_packet_follows = FALSE
395  uint32 reserved = 0
396  ...
397 
398  The SSH spec leaves the order in which things happen ambiguous, in
399  order to save a whole round trip it has provisions for both sides
400  shouting at each other and then a complex interlock process where
401  bits of the initial exchange can be discarded and retried if necessary.
402  This is ugly and error-prone so what we do is wait for the server
403  hello (already done earlier), choose known-good algorithms, and then
404  send the client hello immediately followed by the client keyex.
405  Since we wait for the server to speak first we can choose parameters
406  that are accepted the first time. In theory this means that we can
407  set keyex_follows to true (since a correct keyex packet always
408  follows the hello), however because of the nondeterministic initial
409  exchange the spec requires that a (guessed) keyex be discarded by the
410  server if the hello doesn't match (even if the keyex does):
411 
412  svr: hello
413  client: matched hello, keyex
414  svr: (discard keyex)
415 
416  To avoid this problem we set keyex_follows to false to make it clear
417  to the server that the keyex is the real thing and shouldn't be
418  discarded */
419  status = openPacketStreamSSH( &stream, sessionInfoPtr, SSH_MSG_KEXINIT );
420  if( cryptStatusError( status ) )
421  return( status );
422  streamBookmarkSetFullPacket( &stream, clientHelloLength );
423  status = exportVarsizeAttributeToStream( &stream, SYSTEM_OBJECT_HANDLE,
424  CRYPT_IATTRIBUTE_RANDOM_NONCE,
426  if( cryptStatusError( status ) )
427  {
428  sMemDisconnect( &stream );
429  return( status );
430  }
431  status = writeAlgoString( &stream, handshakeInfo->keyexAlgo );
432  if( cryptStatusOK( status ) )
433  status = writeAlgoString( &stream, handshakeInfo->pubkeyAlgo );
434  if( cryptStatusOK( status ) )
435  status = writeAlgoString( &stream, sessionInfoPtr->cryptAlgo );
436  if( cryptStatusOK( status ) )
437  status = writeAlgoString( &stream, sessionInfoPtr->cryptAlgo );
438  if( cryptStatusOK( status ) )
439  status = writeAlgoString( &stream, sessionInfoPtr->integrityAlgo );
440  if( cryptStatusOK( status ) )
441  status = writeAlgoString( &stream, sessionInfoPtr->integrityAlgo );
442  if( cryptStatusOK( status ) )
443  status = writeAlgoString( &stream, CRYPT_PSEUDOALGO_COPR );
444  if( cryptStatusOK( status ) )
445  status = writeAlgoString( &stream, CRYPT_PSEUDOALGO_COPR );
446  if( cryptStatusError( status ) )
447  return( status );
448  writeUint32( &stream, 0 ); /* No language tag */
449  writeUint32( &stream, 0 );
450  sputc( &stream, 0 ); /* Tell the server not to discard the packet */
451  status = writeUint32( &stream, 0 ); /* Reserved */
452  if( cryptStatusOK( status ) )
453  {
454  status = streamBookmarkComplete( &stream, &clientHelloPtr,
455  &clientHelloLength,
456  clientHelloLength );
457  }
458  if( cryptStatusOK( status ) )
459  status = wrapPacketSSH2( sessionInfoPtr, &stream, 0, FALSE, TRUE );
460  if( cryptStatusError( status ) )
461  {
462  sMemDisconnect( &stream );
463  return( status );
464  }
465  ANALYSER_HINT( clientHelloPtr != NULL );
466 
467  /* Hash the client and server hello messages. We have to do this now
468  (rather than deferring it until we're waiting on network traffic from
469  the server) because they may get overwritten by the keyex negotiation
470  data if we're using a non-builtin DH key value. In addition since the
471  entire encoded packet (including the type value) is hashed we have to
472  reconstruct this at the start of the packet */
473  status = hashAsString( handshakeInfo->iExchangeHashContext,
474  clientHelloPtr, clientHelloLength );
475  if( cryptStatusOK( status ) )
476  {
477  REQUIRES( rangeCheck( 1, serverHelloLength,
478  sessionInfoPtr->receiveBufSize ) );
479  memmove( sessionInfoPtr->receiveBuffer + 1,
480  sessionInfoPtr->receiveBuffer, serverHelloLength );
481  sessionInfoPtr->receiveBuffer[ 0 ] = SSH_MSG_KEXINIT;
482  status = hashAsString( handshakeInfo->iExchangeHashContext,
483  sessionInfoPtr->receiveBuffer,
484  serverHelloLength + 1 );
485  }
486  if( cryptStatusError( status ) )
487  {
488  sMemDisconnect( &stream );
489  return( status );
490  }
491 
492  /* If we're using a non-builtin DH key value, request the keyex key from
493  the server. This additional negotiation requires disconnecting and
494  re-connecting the data packet stream since it exchanges further data
495  with the server, so if there's an error return we don't disconnect
496  the stream before we exit */
497  if( handshakeInfo->requestedServerKeySize > 0 )
498  {
499  status = processDHE( sessionInfoPtr, handshakeInfo, &stream,
500  &keyAgreeParams );
501  if( cryptStatusError( status ) )
502  {
503  /* processDHE() has already disconnected the stream */
504  return( status );
505  }
506  }
507 
508 #ifdef USE_ECDH
509  /* If we're using ECDH rather than DH we have to switch from DH contexts
510  and a DH exchange to the equivalent ECDH contexts and values */
511  if( handshakeInfo->isECDH )
512  {
513  status = switchToECDH( sessionInfoPtr, handshakeInfo,
514  &keyAgreeParams );
515  if( cryptStatusError( status ) )
516  {
517  sMemDisconnect( &stream );
518  return( status );
519  }
520  }
521 #endif /* USE_ECDH */
522 
523  /* ...
524  DH:
525  byte type = SSH_MSG_KEXDH_INIT / SSH_MSG_KEXDH_GEX_INIT
526  mpint y
527  ECDH:
528  byte type = SSH_MSG_KEX_ECDH_INIT
529  string q_c */
530  if( handshakeInfo->requestedServerKeySize > 0 )
531  {
532  /* processDHE() has disconnected the stream as part of the ephemeral
533  DH packet exchange so we need to create a new stream */
534  status = openPacketStreamSSH( &stream, sessionInfoPtr,
536  }
537  else
538  {
539  /* It's a DH/ECDH exchange with static keys, we specify the
540  SSH_MSG_KEXDH_INIT packet type which has the same value
541  as SSH_MSG_KEX_ECDH_INIT */
542  status = continuePacketStreamSSH( &stream, SSH_MSG_KEXDH_INIT,
543  &packetOffset );
544  }
545  if( cryptStatusOK( status ) )
546  {
547  streamBookmarkSet( &stream, keyexLength );
548  if( handshakeInfo->isECDH )
549  status = writeString32( &stream, keyAgreeParams.publicValue,
550  keyAgreeParams.publicValueLen );
551  else
552  status = writeInteger32( &stream, keyAgreeParams.publicValue,
553  keyAgreeParams.publicValueLen );
554  }
555  if( cryptStatusOK( status ) )
556  status = streamBookmarkComplete( &stream, &keyexPtr, &keyexLength,
557  keyexLength );
558  if( cryptStatusOK( status ) )
559  status = wrapPacketSSH2( sessionInfoPtr, &stream, packetOffset,
560  FALSE, TRUE );
561  if( cryptStatusOK( status ) )
562  {
563  /* Send the whole mess to the server. Since SSH, unlike SSL,
564  requires that each packet in a multi-packet group be individually
565  gift-wrapped we have to first assemble the packets via
566  wrapPacket() and then send them in a group via sendPacket() with
567  the send-only flag set */
568  status = sendPacketSSH2( sessionInfoPtr, &stream, TRUE );
569  }
570  sMemDisconnect( &stream );
571  if( cryptStatusError( status ) )
572  return( status );
573  ANALYSER_HINT( keyexPtr != NULL );
574 
575  /* Save the MPI-encoded client DH keyex value/octet string-encoded client
576  ECDH keyex value for later, when we need to hash it */
577  ENSURES( rangeCheckZ( 0, keyexLength, MAX_ENCODED_KEYEXSIZE ) );
578  memcpy( handshakeInfo->clientKeyexValue, keyexPtr, keyexLength );
579  handshakeInfo->clientKeyexValueLength = keyexLength;
580 
581  /* Set up PKC information while we wait for the server to process our
582  response */
583  setMessageCreateObjectInfo( &createInfo, handshakeInfo->pubkeyAlgo );
585  IMESSAGE_DEV_CREATEOBJECT, &createInfo,
587  if( cryptStatusOK( status ) )
588  sessionInfoPtr->iKeyexAuthContext = createInfo.cryptHandle;
589  return( status );
590  }
591 
592 /* Exchange keys with the server */
593 
594 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
595 static int exchangeClientKeys( INOUT SESSION_INFO *sessionInfoPtr,
596  INOUT SSH_HANDSHAKE_INFO *handshakeInfo )
597  {
598  CRYPT_ALGO_TYPE pubkeyAlgo = DUMMY_INIT;
599  STREAM stream;
601  void *keyPtr = DUMMY_INIT_PTR, *keyBlobPtr = DUMMY_INIT_PTR;
602  void *sigPtr = DUMMY_INIT_PTR;
603  int keyLength = DUMMY_INIT, keyBlobLength, sigLength, length;
604  int dummy, status;
605 
606  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
607  assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
608 
609  /* Process the DH/ECDH phase 2 keyex packet:
610 
611  DH + RSA/DSA
612  byte type = SSH_MSG_KEXDH_REPLY / SSH_MSG_KEXDH_GEX_REPLY
613  string [ server key/certificate ]
614  string "ssh-rsa" "ssh-dss"
615  mpint e p
616  mpint n q
617  mpint g
618  mpint y
619  mpint y'
620  string [ signature of handshake data ]
621  string "ssh-rsa" "ssh-dss"
622  string signature signature
623 
624  ECDH + ECDSA
625  byte SSH_MSG_KEX_ECDH_REPLY
626  string [ server key/certificate ]
627  string "ecdsa-sha2-*"
628  string "*" -- The "*" portion from the above field
629  string Q
630  string q_s
631  string [ signature of handshake data ]
632  string "ecdsa-sha2-*"
633  string signature
634 
635  First, we read and hash the server key/certificate. Since this is
636  already encoded as an SSH string we can hash it directly.
637 
638  We set the minimum key size to 512 bits instead of MIN_PKCSIZE in
639  order to provide better diagnostics if the server is using weak keys
640  since otherwise the data will be rejected in the packet read long
641  before it gets to the keysize check */
642  status = length = \
643  readHSPacketSSH2( sessionInfoPtr,
644  ( handshakeInfo->requestedServerKeySize > 0 ) ? \
646  ID_SIZE + LENGTH_SIZE + sizeofString32( "", 6 ) + \
647  sizeofString32( "", 1 ) + \
648  sizeofString32( "", bitsToBytes( 512 ) - 4 ) + \
649  sizeofString32( "", bitsToBytes( 512 ) - 4 ) + \
650  LENGTH_SIZE + sizeofString32( "", 6 ) + 40 );
651  if( cryptStatusError( status ) )
652  return( status );
653  sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
654  streamBookmarkSet( &stream, keyLength );
655  status = readUint32( &stream ); /* Server key data size */
656  if( !cryptStatusError( status ) )
657  {
658  status = readAlgoString( &stream, handshakeInfo->algoStringPubkeyTbl,
659  handshakeInfo->algoStringPubkeyTblNoEntries,
660  &pubkeyAlgo, TRUE, SESSION_ERRINFO );
661  }
662  if( cryptStatusError( status ) )
663  {
664  sMemDisconnect( &stream );
665  return( status );
666  }
667  if( pubkeyAlgo != handshakeInfo->pubkeyAlgo )
668  {
669  sMemDisconnect( &stream );
672  "Invalid %s phase 2 public key algorithm %d, expected %d",
673  handshakeInfo->isECDH ? "ECDH" : "DH", pubkeyAlgo,
674  handshakeInfo->pubkeyAlgo ) );
675  }
676  streamBookmarkSet( &stream, keyBlobLength );
677  switch( pubkeyAlgo )
678  {
679  case CRYPT_ALGO_RSA:
680  /* RSA e, n */
681  readInteger32( &stream, NULL, &dummy, 1, CRYPT_MAX_PKCSIZE );
682  status = readInteger32Checked( &stream, NULL, &dummy,
684  break;
685 
686  case CRYPT_ALGO_DSA:
687  /* DSA p, q, g, y */
688  status = readInteger32Checked( &stream, NULL, &dummy,
690  if( cryptStatusError( status ) )
691  break;
692  readInteger32( &stream, NULL, &dummy, 1, CRYPT_MAX_PKCSIZE );
693  readInteger32( &stream, NULL, &dummy, 1, CRYPT_MAX_PKCSIZE );
694  status = readInteger32Checked( &stream, NULL, &dummy,
696  break;
697 
698  case CRYPT_ALGO_ECDSA:
699  readUniversal32( &stream ); /* Skip field size */
700  status = readInteger32Checked( &stream, NULL, &dummy,
703  break;
704 
705  default:
706  retIntError();
707  }
708  if( cryptStatusOK( status ) )
709  status = streamBookmarkComplete( &stream, &keyBlobPtr,
710  &keyBlobLength, keyBlobLength );
711  if( cryptStatusOK( status ) )
712  status = streamBookmarkComplete( &stream, &keyPtr, &keyLength,
713  keyLength );
714  if( cryptStatusError( status ) )
715  {
716  sMemDisconnect( &stream );
717 
718  /* Some misconfigured servers may use very short keys, we perform
719  a special-case check for these and return a more specific message
720  than the generic bad-data response */
721  if( status == CRYPT_ERROR_NOSECURE )
722  {
725  "Insecure server public key used in key exchange" ) );
726  }
727 
730  "Invalid %s phase 2 server public key data",
731  handshakeInfo->isECDH ? "ECDH" : "DH" ) );
732  }
733  setMessageData( &msgData, keyPtr, keyLength );
734  status = krnlSendMessage( sessionInfoPtr->iKeyexAuthContext,
735  IMESSAGE_SETATTRIBUTE_S, &msgData,
736  CRYPT_IATTRIBUTE_KEY_SSH );
737  if( cryptStatusError( status ) )
738  {
739  sMemDisconnect( &stream );
740  retExt( cryptArgError( status ) ? \
741  CRYPT_ERROR_BADDATA : status,
742  ( cryptArgError( status ) ? \
744  "Invalid %s phase 2 server public key value",
745  handshakeInfo->isECDH ? "ECDH" : "DH" ) );
746  }
747  ANALYSER_HINT( keyBlobPtr != NULL );
748  status = krnlSendMessage( handshakeInfo->iExchangeHashContext,
749  IMESSAGE_CTX_HASH, keyPtr, keyLength );
750  if( cryptStatusOK( status ) )
751  {
752  /* The fingerprint is computed from the "key blob" which is
753  different from the server key. The server key is the full key
754  while the "key blob" is only the raw key components (e, n for
755  RSA, p, q, g, y for DSA). Note that, as with the old PGP 2.x key
756  hash mechanism, this allows key spoofing (although it isn't quite
757  as bad as the PGP 2.x key fingerprint mechanism) since it doesn't
758  hash an indication of the key type or format */
759  status = processKeyFingerprint( sessionInfoPtr,
760  keyBlobPtr, keyBlobLength );
761  }
762  if( cryptStatusError( status ) )
763  {
764  sMemDisconnect( &stream );
765  return( status );
766  }
767 
768  /* Read the server DH/ECDH keyex value and complete the DH/ECDH key
769  agreement */
770  status = readRawObject32( &stream, handshakeInfo->serverKeyexValue,
772  &handshakeInfo->serverKeyexValueLength );
773  if( cryptStatusOK( status ) )
774  {
775  if( handshakeInfo->isECDH )
776  {
777  if( !isValidECDHsize( handshakeInfo->clientKeyexValueLength,
778  handshakeInfo->serverKeySize,
779  LENGTH_SIZE ) )
780  status = CRYPT_ERROR_BADDATA;
781  }
782  else
783  {
784  if( !isValidDHsize( handshakeInfo->clientKeyexValueLength,
785  handshakeInfo->serverKeySize,
786  LENGTH_SIZE ) )
787  status = CRYPT_ERROR_BADDATA;
788  }
789  }
790  if( cryptStatusError( status ) )
791  {
792  sMemDisconnect( &stream );
795  "Invalid %s phase 2 keyex value",
796  handshakeInfo->isECDH ? "ECDH" : "DH" ) );
797  }
798  status = completeKeyex( sessionInfoPtr, handshakeInfo, FALSE );
799  if( cryptStatusError( status ) )
800  {
801  sMemDisconnect( &stream );
802  return( status );
803  }
804 
805  /* Prepare to process the handshake packet signature */
806  streamBookmarkSet( &stream, sigLength );
807  status = length = readUint32( &stream );
808  if( !cryptStatusError( status ) )
809  status = sSkip( &stream, length );
810  if( cryptStatusOK( status ) )
811  status = streamBookmarkComplete( &stream, &sigPtr, &sigLength,
812  sigLength );
813  sMemDisconnect( &stream );
814  if( cryptStatusError( status ) )
815  {
818  "Invalid %s phase 2 packet signature data",
819  handshakeInfo->isECDH ? "ECDH" : "DH" ) );
820  }
821  ANALYSER_HINT( sigPtr != NULL );
822 
823  /* Some implementations incorrectly format the signature packet,
824  omitting the algorithm name and signature blob length for DSA sigs
825  (that is, they just encode two 20-byte values instead of a properly-
826  formatted signature):
827 
828  Right Wrong
829  string [ signature data ] string [ nothing ]
830  string "ssh-dss"
831  string signature signature
832 
833  If we're talking to one of these versions we check to see whether the
834  packet is correctly formatted (that is, that it has the algorithm-
835  type string present as required) and if it isn't present rewrite it
836  into the correct form so that we can verify the signature. This
837  check requires that the signature format be one of the SSH standard
838  types but since we can't (by definition) handle proprietary formats
839  this isn't a problem. What we're specifically checking for here is
840  the *absence* of any known algorithm string, if any known string
841  (even one for a format that we can't handle) is present then the
842  signature is in the correct format, it's only if we don't find a
843  match for any known string that we have to rewrite the signature */
844  if( ( sessionInfoPtr->protocolFlags & SSH_PFLAG_SIGFORMAT ) && \
845  ( pubkeyAlgo == CRYPT_ALGO_DSA ) && \
846  ( memcmp( ( BYTE * ) sigPtr + LENGTH_SIZE + LENGTH_SIZE,
847  "ssh-dss", 7 ) && \
848  memcmp( ( BYTE * ) sigPtr + LENGTH_SIZE + LENGTH_SIZE,
849  "x509v3-sign-dss", 15 ) && \
850  memcmp( ( BYTE * ) sigPtr + LENGTH_SIZE + LENGTH_SIZE,
851  "spki-sign-dss", 13 ) && \
852  memcmp( ( BYTE * ) sigPtr + LENGTH_SIZE + LENGTH_SIZE,
853  "pgp-sign-dss", 12 ) ) )
854  {
855  int fixedSigLength = DUMMY_INIT;
856 
857  /* Rewrite the signature to fix up the overall length at the start
858  and insert the algorithm name and signature length. We can
859  safely reuse the receive buffer for this because the start
860  contains the complete server key/certificate and DH keyex value
861  which is far longer than the 12 bytes of header plus signature
862  that we'll be writing there */
863  sMemOpen( &stream, sessionInfoPtr->receiveBuffer,
864  LENGTH_SIZE + sizeofString32( "", 7 ) + sigLength );
865  writeUint32( &stream, sizeofString32( "", 7 ) + sigLength );
866  writeString32( &stream, "ssh-dss", 7 );
867  status = swrite( &stream, sigPtr, sigLength );
868  if( cryptStatusOK( status ) )
869  fixedSigLength = stell( &stream );
870  sMemDisconnect( &stream );
871  if( cryptStatusError( status ) )
872  return( status );
873 
874  /* The rewritten signature is now at the start of the buffer, update
875  the signature pointer and size to accomodate the added header */
876  sigPtr = sessionInfoPtr->receiveBuffer;
877  sigLength = fixedSigLength;
878  }
879 
880  /* Finally, verify the server's signature on the exchange hash */
881  status = iCryptCheckSignature( sigPtr, sigLength, CRYPT_IFORMAT_SSH,
882  sessionInfoPtr->iKeyexAuthContext,
883  handshakeInfo->iExchangeHashContext,
884  CRYPT_UNUSED, NULL );
885  if( cryptStatusError( status ) )
886  {
887  retExt( status,
888  ( status, SESSION_ERRINFO,
889  "Invalid handshake data signature" ) );
890  }
891 
892  /* We don't need the exchange hash contexts any more, get rid of them */
893  krnlSendNotifier( handshakeInfo->iExchangeHashContext,
895  handshakeInfo->iExchangeHashContext = CRYPT_ERROR;
896  if( handshakeInfo->iExchangeHashAltContext != CRYPT_ERROR )
897  {
898  krnlSendNotifier( handshakeInfo->iExchangeHashAltContext,
900  handshakeInfo->iExchangeHashAltContext = CRYPT_ERROR;
901  }
902 
903  return( CRYPT_OK );
904  }
905 
906 /* Complete the handshake with the server */
907 
908 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
909 static int completeClientHandshake( INOUT SESSION_INFO *sessionInfoPtr,
910  INOUT SSH_HANDSHAKE_INFO *handshakeInfo )
911  {
912  STREAM stream;
913  BYTE stringBuffer[ CRYPT_MAX_TEXTSIZE + 8 ];
915 
916  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
917  assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
918 
919  /* Set up the security information required for the session */
920  status = initSecurityInfo( sessionInfoPtr, handshakeInfo );
921  if( cryptStatusError( status ) )
922  return( status );
923 
924  /* Build our change cipherspec message and request authentication with
925  the server:
926 
927  byte type = SSH_MSG_NEWKEYS
928  ...
929 
930  After this point the write channel is in the secure state */
931  status = openPacketStreamSSH( &stream, sessionInfoPtr, SSH_MSG_NEWKEYS );
932  if( cryptStatusOK( status ) )
933  status = wrapPacketSSH2( sessionInfoPtr, &stream, 0, FALSE, TRUE );
934  if( cryptStatusError( status ) )
935  {
936  sMemDisconnect( &stream );
937  return( status );
938  }
939  sessionInfoPtr->flags |= SESSION_ISSECURE_WRITE;
940 
941  /* ...
942  byte type = SSH_MSG_SERVICE_REQUEST
943  string service_name = "ssh-userauth".
944 
945  For some reason SSH requires the use of two authentication messages,
946  an "I'm about to authenticate" packet and an "I'm authenticating"
947  packet, so we have to perform the authentication in two parts (dum
948  loquimur, fugerit invida aetas) */
949  status = continuePacketStreamSSH( &stream, SSH_MSG_SERVICE_REQUEST,
950  &packetOffset );
951  if( cryptStatusOK( status ) )
952  status = writeString32( &stream, "ssh-userauth", 12 );
953  if( cryptStatusOK( status ) )
954  status = wrapPacketSSH2( sessionInfoPtr, &stream, packetOffset,
955  FALSE, TRUE );
956  if( cryptStatusError( status ) )
957  {
958  sMemDisconnect( &stream );
959  return( status );
960  }
961 
962  /* Send the whole mess to the server. This is yet another place where
963  the SSH spec's vagueness over message ordering causes problems. SSL
964  at this point uses a Finished message in which the client and server
965  do a mutual proof-of-possession of encryption and MAC keys via a
966  pipeline-stalling message that prevents any further (sensitive) data
967  from being exchanged until the PoP has concluded (the SSL Finished
968  also authenticates the handshake messages) but SSH doesn't have any
969  such requirements. The signed exchange hash from the server proves
970  to the client that the server knows the master secret but not
971  necessarily that the client and server share encryption and MAC keys.
972  Without this mutual PoP the client could potentially end up sending
973  passwords to the server using an incorrect (and potentially weak) key
974  if it's messed up and derived the key incorrectly. Although mutual
975  PoP isn't a design goal of the SSH handshake we do it anyway (as far
976  as we can without a proper Finished message), although this
977  introduces a pipeline stall at this point.
978 
979  In addition because of the aforementioned ambiguity over message
980  ordering we have to send our change cipherspec first because some
981  implementations will stop and wait before they send their one, so if
982  they don't see our one first they lock up. To make this even more
983  entertaining these are typically older ssh.com implementations with a
984  whole smorgasbord of handshaking and crypto bugs, because of the lack
985  of PoP and the fact that we have to send the first encrypted/MACd
986  message, encountering any of these bugs results in garbage from the
987  server followed by a closed connection with no ability to diagnose
988  the problem.
989 
990  The spec in fact says that after a key exchange with implicit server
991  authentication the client has to wait for the server to send a
992  service-accept packet before continuing, however it never explains
993  what implicit (and, by extension, explicit) server authentication
994  actually are. This text is a leftover from an extremely early SSH
995  draft in which the only keyex mechanism was "double-encrypting-sha",
996  a mechanism that required a pipeline stall at this point because the
997  client wasn't able to authenticate the server until it received the
998  first encrypted/MAC'ed message from it. To extricate ourselves from
999  the confusion due to the missing definition we could define "implicit
1000  authentication" to be "Something completely different from what we're
1001  doing here" which means that we could send the two packets together
1002  without having to wait for the server, but it's probably better to
1003  use SSL-tyle Finished semantics at this point even if it adds an
1004  extra RTT delay */
1005  status = sendPacketSSH2( sessionInfoPtr, &stream, TRUE );
1006  sMemDisconnect( &stream );
1007  if( cryptStatusError( status ) )
1008  return( status );
1009 
1010  /* Wait for the server's change cipherspec message. From this point
1011  on the read channel is also in the secure state */
1012  status = readHSPacketSSH2( sessionInfoPtr, SSH_MSG_NEWKEYS, ID_SIZE );
1013  if( cryptStatusError( status ) )
1014  return( status );
1015  sessionInfoPtr->flags |= SESSION_ISSECURE_READ;
1016 
1017  /* Wait for the server's service-accept message that should follow in
1018  response to our change cipherspec. Some buggy versions send an empty
1019  service-accept packet so we only check the contents if it's a
1020  correctly-formatted packet */
1021  if( sessionInfoPtr->protocolFlags & SSH_PFLAG_EMPTYSVCACCEPT )
1022  {
1023  /* It's a buggy implementation, just check for the presence of a
1024  packet without looking at the contents */
1025  status = readHSPacketSSH2( sessionInfoPtr, SSH_MSG_SERVICE_ACCEPT,
1026  ID_SIZE );
1027  if( cryptStatusError( status ) )
1028  return( status );
1029  }
1030  else
1031  {
1032  int length;
1033 
1034  /* Check the service-accept packet:
1035 
1036  byte type = SSH_MSG_SERVICE_ACCEPT
1037  string service_name = "ssh-userauth" */
1038  status = length = \
1039  readHSPacketSSH2( sessionInfoPtr, SSH_MSG_SERVICE_ACCEPT,
1040  ID_SIZE + sizeofString32( "", 8 ) );
1041  if( cryptStatusError( status ) )
1042  return( status );
1043  sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
1044  status = readString32( &stream, stringBuffer, CRYPT_MAX_TEXTSIZE,
1045  &stringLength );
1046  sMemDisconnect( &stream );
1047  if( cryptStatusError( status ) || \
1048  stringLength != 12 || \
1049  memcmp( stringBuffer, "ssh-userauth", 12 ) )
1050  {
1051  /* More of a sanity check than anything else, the MAC should
1052  have caught any keying problems */
1055  "Invalid service accept packet" ) );
1056  }
1057  }
1058 
1059  /* Try and authenticate ourselves to the server */
1060  status = processClientAuth( sessionInfoPtr, handshakeInfo );
1061  if( cryptStatusError( status ) )
1062  return( status );
1063 
1064  /* We've finally made it through all of the formalities (post proelia
1065  praemia), create (if necessary) and open a channel */
1066  if( getCurrentChannelNo( sessionInfoPtr, \
1068  {
1069  /* The user hasn't specified any channel details, create a
1070  channel of the default type */
1071  status = createChannel( sessionInfoPtr );
1072  if( cryptStatusError( status ) )
1073  return( status );
1074  }
1075 #if 1
1076  return( sendChannelOpen( sessionInfoPtr ) );
1077 #else /* Test handling of OpenSSH "[email protected]" */
1078  status = sendChannelOpen( sessionInfoPtr );
1079  if( cryptStatusError( status ) )
1080  return( status );
1081 
1082  /* byte type = SSH_MSG_GLOBAL_REQUEST
1083  string request_name = "[email protected]"
1084  boolean want_reply = FALSE */
1085  status = openPacketStreamSSH( &stream, sessionInfoPtr, CRYPT_USE_DEFAULT,
1087  writeString32( &stream, "[email protected]", 29 );
1088  sputc( &stream, 0 );
1089  status = wrapPacketSSH2( sessionInfoPtr, &stream, 0, TRUE, TRUE );
1090  if( cryptStatusOK( status ) )
1091  status = sendPacketSSH2( sessionInfoPtr, &stream, TRUE );
1092  sMemDisconnect( &stream );
1093  return( status );
1094 #endif /* 0 */
1095  }
1096 
1097 /****************************************************************************
1098 * *
1099 * Session Access Routines *
1100 * *
1101 ****************************************************************************/
1102 
1103 STDC_NONNULL_ARG( ( 1, 2 ) ) \
1104 void initSSH2clientProcessing( STDC_UNUSED SESSION_INFO *sessionInfoPtr,
1105  INOUT SSH_HANDSHAKE_INFO *handshakeInfo )
1106  {
1107  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
1108  assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
1109 
1110  handshakeInfo->beginHandshake = beginClientHandshake;
1111  handshakeInfo->exchangeKeys = exchangeClientKeys;
1112  handshakeInfo->completeHandshake = completeClientHandshake;
1113  }
1114 #endif /* USE_SSH */