1 /****************************************************************************
2 * *
3 * cryptlib SSHv2 Session Management *
4 * Copyright Peter Gutmann 1998-2014 *
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. The algorithm may not be a real cryptlib
24 algorithm type but an SSH-specific pseudo-algorithm in the
25 CRYPT_PSEUDOALGO_xxx range, for example CRYPT_PSEUDOALGO_PASSWORD.
26 To deal with this we have to map the pseudo-algorithm value to a
27 CRYPT_ALGO_TYPE via the MK_ALGO() macro.
28
29 Note that ECC support is very hit-and-miss. If we were to advertise ECC
30 only (which we never do), some servers will respond with RSA/DSA keys
31 (even though they're not specified as being supported), and others will
32 respond with an empty host key.
33
34 In addition the algorithms aren't just algorithm values but a combination
35 of the algorithm, the key size, and the hash algorithm, with
36 CRYPT_ALGO_ECDH/CRYPT_ALGO_ECDSA being the default P256 curve with
37 SHA-256. Because the curve types are tied to the oddball SHA-2 hash
38 variants (we can't just use SHA-256 for every curve), we don't support
39 P384 and P512 because we'd have to support an entirely new (and 64-bit-
40 only) hash algorithm for each of the curves */
41
42 static const ALGO_STRING_INFO FAR_BSS algoStringKeyexTbl[] = {
43 #if defined( USE_ECDH ) && defined( PREFER_ECC )
44 { "ecdh-sha2-nistp256", 18, CRYPT_ALGO_ECDH, CRYPT_ALGO_SHA2 },
45 #endif /* USE_ECDH && PREFER_ECC */
46 { "diffie-hellman-group-exchange-sha256", 36, CRYPT_ALGO_DH, CRYPT_ALGO_SHA2 },
47 { "diffie-hellman-group-exchange-sha1", 34, CRYPT_ALGO_DH, CRYPT_ALGO_SHA1 },
48 { "diffie-hellman-group1-sha1", 26, CRYPT_ALGO_DH, CRYPT_ALGO_SHA1 },
49 #if defined( USE_ECDH ) && !defined( PREFER_ECC )
50 { "ecdh-sha2-nistp256", 18, CRYPT_ALGO_ECDH, CRYPT_ALGO_SHA2 },
51 #endif /* USE_ECDH && !PREFER_ECC */
52 { NULL, 0, CRYPT_ALGO_NONE, CRYPT_ALGO_NONE },
53 { NULL, 0, CRYPT_ALGO_NONE, CRYPT_ALGO_NONE }
54 };
55 static const ALGO_STRING_INFO FAR_BSS algoStringKeyexNoECCTbl[] = {
56 { "diffie-hellman-group-exchange-sha256", 36, CRYPT_ALGO_DH, CRYPT_ALGO_SHA2 },
57 { "diffie-hellman-group-exchange-sha1", 34, CRYPT_ALGO_DH, CRYPT_ALGO_SHA1 },
58 { "diffie-hellman-group1-sha1", 26, CRYPT_ALGO_DH, CRYPT_ALGO_SHA1 },
59 { NULL, 0, CRYPT_ALGO_NONE, CRYPT_ALGO_NONE },
60 { NULL, 0, CRYPT_ALGO_NONE, CRYPT_ALGO_NONE }
61 };
62
63 static const ALGO_STRING_INFO FAR_BSS algoStringPubkeyTbl[] = {
64 #ifdef PREFER_ECC
65 { "ecdsa-sha2-nistp256", 19, CRYPT_ALGO_ECDSA, CRYPT_ALGO_SHA2 },
66 #endif /* PREFER_ECC */
67 { "rsa-sha2-256", 12, CRYPT_ALGO_RSA, CRYPT_ALGO_SHA2 },
68 { "ssh-rsa", 7, CRYPT_ALGO_RSA, CRYPT_ALGO_SHA1 },
69 { "ssh-dss", 7, CRYPT_ALGO_DSA, CRYPT_ALGO_SHA1 },
70 #if !defined( PREFER_ECC )
71 { "ecdsa-sha2-nistp256", 19, CRYPT_ALGO_ECDSA, CRYPT_ALGO_SHA2 },
72 #endif /* !PREFER_ECC */
73 { NULL, 0, CRYPT_ALGO_NONE, CRYPT_ALGO_NONE },
74 { NULL, 0, CRYPT_ALGO_NONE, CRYPT_ALGO_NONE }
75 };
76
77 static const ALGO_STRING_INFO FAR_BSS algoStringEncrTbl[] = {
78 { "aes128-cbc", 10, CRYPT_ALGO_AES },
79 { "3des-cbc", 8, CRYPT_ALGO_3DES },
80 { NULL, 0, CRYPT_ALGO_NONE }, { NULL, 0, CRYPT_ALGO_NONE }
81 };
82
83 static const ALGO_STRING_INFO FAR_BSS algoStringMACTbl[] = {
84 { "hmac-sha2-256", 13, CRYPT_ALGO_HMAC_SHA2 },
85 { "hmac-sha1", 9, CRYPT_ALGO_HMAC_SHA1 },
86 { NULL, 0, CRYPT_ALGO_NONE }, { NULL, 0, CRYPT_ALGO_NONE }
87 };
88
89 static const ALGO_STRING_INFO FAR_BSS algoStringCoprTbl[] = {
90 { "none", 4, CRYPT_ALGO_AES /* Always-valid placeholder */ },
91 { NULL, 0, CRYPT_ALGO_NONE }, { NULL, 0, CRYPT_ALGO_NONE }
92 };
93
94 /* A grand unified version of the above */
95
96 static const ALGO_STRING_INFO FAR_BSS algoStringMapTbl[] = {
97 /* Keyex algorithms */
98 { "diffie-hellman-group-exchange-sha256", 36, CRYPT_ALGO_DH, CRYPT_ALGO_SHA2 },
99 { "diffie-hellman-group-exchange-sha1", 34, CRYPT_ALGO_DH, CRYPT_ALGO_SHA1 },
100 { "diffie-hellman-group1-sha1", 26, CRYPT_ALGO_DH, CRYPT_ALGO_SHA1 },
101 #ifdef USE_ECDH
102 { "ecdh-sha2-nistp256", 18, CRYPT_ALGO_ECDH, CRYPT_ALGO_SHA2 },
103 #endif /* USE_ECDH */
104
105 /* Signature algorithms */
106 { "rsa-sha2-256", 12, CRYPT_ALGO_RSA, CRYPT_ALGO_SHA2 },
107 { "ssh-rsa", 7, CRYPT_ALGO_RSA, CRYPT_ALGO_SHA1 },
108 { "ssh-dss", 7, CRYPT_ALGO_DSA, CRYPT_ALGO_SHA1 },
109 #ifdef USE_ECDSA
110 { "ecdsa-sha2-nistp256", 19, CRYPT_ALGO_ECDSA, CRYPT_ALGO_SHA2 },
111 #endif /* USE_ECDSA */
112
113 /* Encryption algorithms */
114 { "aes128-cbc", 10, CRYPT_ALGO_AES },
115 { "3des-cbc", 8, CRYPT_ALGO_3DES },
116
117 /* MAC algorithms */
118 { "hmac-sha2-256", 13, CRYPT_ALGO_HMAC_SHA2 },
119 { "hmac-sha1", 9, CRYPT_ALGO_HMAC_SHA1 },
120
121 /* Miscellaneous */
122 { "password", 8, MK_ALGO( PSEUDOALGO_PASSWORD ) },
123 { "none", 4, CRYPT_ALGO_LAST }, /* Catch-all */
124
125 { NULL, 0, CRYPT_ALGO_NONE }, { NULL, 0, CRYPT_ALGO_NONE }
126 };
127
128 /****************************************************************************
129 * *
130 * Utility Functions *
131 * *
132 ****************************************************************************/
133
134 /* Initialise crypto-related handshake information */
135
136 STDC_NONNULL_ARG( ( 1 ) ) \
initHandshakeCrypt(INOUT SSH_HANDSHAKE_INFO * handshakeInfo)137 void initHandshakeCrypt( INOUT SSH_HANDSHAKE_INFO *handshakeInfo )
138 {
139 assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
140
141 /* Set the initial hash algorithm used to authenticate the handshake */
142 handshakeInfo->exchangeHashAlgo = CRYPT_ALGO_SHA1;
143
144 /* Most of the SSH <-> cryptlib mapping tables are fixed, however the
145 pubkey table table is pointed to by the handshakeInfo and may
146 later be changed dynamically on the server depending on the server's
147 key type */
148 handshakeInfo->algoStringPubkeyTbl = algoStringPubkeyTbl;
149 handshakeInfo->algoStringPubkeyTblNoEntries = \
150 FAILSAFE_ARRAYSIZE( algoStringPubkeyTbl, ALGO_STRING_INFO );
151 }
152
153 /* Hash the SSH ID strings that are exchanged as pre-handshake out-of-band
154 data */
155
156 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
hashHandshakeStrings(INOUT SSH_HANDSHAKE_INFO * handshakeInfo,IN_BUFFER (clientStringLength)const void * clientString,IN_LENGTH_SHORT const int clientStringLength,IN_BUFFER (serverStringLength)const void * serverString,IN_LENGTH_SHORT const int serverStringLength)157 int hashHandshakeStrings( INOUT SSH_HANDSHAKE_INFO *handshakeInfo,
158 IN_BUFFER( clientStringLength ) \
159 const void *clientString,
160 IN_LENGTH_SHORT const int clientStringLength,
161 IN_BUFFER( serverStringLength ) \
162 const void *serverString,
163 IN_LENGTH_SHORT const int serverStringLength )
164 {
165 int status;
166
167 assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
168 assert( isReadPtr( clientString, clientStringLength ) );
169 assert( isReadPtr( serverString, serverStringLength ) );
170
171 REQUIRES( clientStringLength > 0 && \
172 clientStringLength < MAX_INTLENGTH_SHORT );
173 REQUIRES( serverStringLength > 0 && \
174 serverStringLength < MAX_INTLENGTH_SHORT );
175
176 /* SSH hashes the handshake ID strings for integrity-protection purposes,
177 first the client string and then the server string, encoded as SSH
178 string values. In addition since the handshake can retroactively
179 switch to a different hash algorithm mid-exchange we have to
180 speculatively hash the messages with alternative algorithms in case
181 the other side decides to switch */
182 status = hashAsString( handshakeInfo->iExchangeHashContext,
183 clientString, clientStringLength );
184 if( cryptStatusOK( status ) )
185 status = hashAsString( handshakeInfo->iExchangeHashContext,
186 serverString, serverStringLength );
187 if( handshakeInfo->iExchangeHashAltContext == CRYPT_ERROR )
188 return( status );
189 status = hashAsString( handshakeInfo->iExchangeHashAltContext,
190 clientString, clientStringLength );
191 if( cryptStatusOK( status ) )
192 status = hashAsString( handshakeInfo->iExchangeHashAltContext,
193 serverString, serverStringLength );
194 return( status );
195 }
196
197 /****************************************************************************
198 * *
199 * Read/Write Algorithm Info *
200 * *
201 ****************************************************************************/
202
203 /* Convert an SSH algorithm list to a cryptlib ID in preferred-algorithm
204 order. For some bizarre reason the algorithm information is communicated
205 as a comma-delimited list (in an otherwise binary protocol) so we have to
206 unpack and pack them into this cumbersome format alongside just choosing
207 which algorithm to use. In addition the algorithm selection mechanism
208 differs depending on whether we're the client or the server, and what set
209 of algorithms we're matching. Unlike SSL, which uses the offered-suites/
210 chosen-suites mechanism, in SSH both sides offer a selection of cipher
211 suites and then the server chooses the first one that appears on both it
212 and the client's list, with special-case handling for the keyex and
213 signature algorithms if the match isn't the first one on the list. This
214 means that the client can choose as it pleases from the server's list if
215 it waits for the server hello (see the comment in the client/server hello
216 handling code on the annoying nature of this portion of the SSH handshake)
217 but the server has to perform a complex double-match of its own vs.the
218 client's list. The cases that we need to handle are:
219
220 BEST_MATCH: Get the best matching algorithm (that is, the one
221 corresponding to the strongest crypto mechanism), used by the client
222 to match the server.
223
224 FIRST_MATCH: Get the first matching algorithm, used by the server to
225 match the client.
226
227 FIRST_MATCH_WARN: Get the first matching algorithm and warn if it isn't
228 the first one on the list of possible algorithms, used by the server
229 to match the client for the keyex and public-key algorithms.
230
231 This is a sufficiently complex and screwball function that we need to
232 define a composite structure to pass all of the control information in
233 and out */
234
235 typedef enum {
236 GETALGO_NONE, /* No match action */
237 GETALGO_FIRST_MATCH, /* Get first matching algorithm */
238 GETALGO_FIRST_MATCH_WARN,/* Get first matching algo, warn if not first */
239 GETALGO_BEST_MATCH, /* Get best matching algorithm */
240 GETALGO_LAST /* Last possible match action */
241 } GETALGO_TYPE;
242
243 typedef struct {
244 /* Match information passed in by the caller */
245 ARRAY_FIXED( noAlgoInfoEntries ) \
246 const ALGO_STRING_INFO *algoInfo;/* Algorithm selection information */
247 int noAlgoInfoEntries;
248 CRYPT_ALGO_TYPE preferredAlgo; /* Preferred algo for first-match */
249 GETALGO_TYPE getAlgoType; /* Type of match to perform */
250 BOOLEAN allowECC; /* Whether to allow ECC algos */
251
252 /* Information returned by the read-algorithm function */
253 CRYPT_ALGO_TYPE algo; /* Matched algorithm */
254 CRYPT_ALGO_TYPE subAlgo; /* Sub-algorithm (e.g. hash for keyex) */
255 BOOLEAN prefAlgoMismatch; /* First match != preferredAlgo */
256 } ALGOID_INFO;
257
258 #if defined( USE_ECDH ) || defined( USE_ECDSA )
259 #define ALLOW_ECC TRUE
260 #else
261 #define ALLOW_ECC FALSE
262 #endif /* USE_ECDH || USE_ECDSA */
263
264 #define setAlgoIDInfo( algoIDInfo, algoStrInfo, algoStrInfoEntries, prefAlgo, getType ) \
265 { \
266 memset( ( algoIDInfo ), 0, sizeof( ALGOID_INFO ) ); \
267 ( algoIDInfo )->algoInfo = ( algoStrInfo ); \
268 ( algoIDInfo )->noAlgoInfoEntries = ( algoStrInfoEntries ); \
269 ( algoIDInfo )->preferredAlgo = ( prefAlgo ); \
270 ( algoIDInfo )->getAlgoType = ( getType ); \
271 ( algoIDInfo )->allowECC = ALLOW_ECC; \
272 }
273
274 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
readAlgoStringEx(INOUT STREAM * stream,INOUT ALGOID_INFO * algoIDInfo,INOUT ERROR_INFO * errorInfo)275 static int readAlgoStringEx( INOUT STREAM *stream,
276 INOUT ALGOID_INFO *algoIDInfo,
277 INOUT ERROR_INFO *errorInfo )
278 {
279 BOOLEAN foundMatch = FALSE;
280 void *string DUMMY_INIT_PTR;
281 int stringPos, stringLen, substringLen, algoIndex = 999;
282 int noStrings, status;
283
284 assert( isWritePtr( stream, sizeof( STREAM ) ) );
285 assert( isWritePtr( algoIDInfo, sizeof( ALGOID_INFO ) ) );
286 assert( isReadPtr( algoIDInfo->algoInfo, \
287 sizeof( ALGO_STRING_INFO ) * \
288 algoIDInfo->noAlgoInfoEntries ) );
289
290 REQUIRES( ( algoIDInfo->getAlgoType == GETALGO_BEST_MATCH && \
291 algoIDInfo->preferredAlgo == CRYPT_ALGO_NONE ) || \
292 ( algoIDInfo->getAlgoType == GETALGO_FIRST_MATCH ) ||
293 ( algoIDInfo->getAlgoType == GETALGO_FIRST_MATCH_WARN && \
294 ( algoIDInfo->preferredAlgo > CRYPT_ALGO_NONE && \
295 algoIDInfo->preferredAlgo < CRYPT_ALGO_LAST_EXTERNAL ) ) );
296 /* FIRST_MATCH uses CRYPT_ALGO_NONE on the first match of an
297 algorithm pair and the first algorithm chosen on the second
298 match */
299 REQUIRES( algoIDInfo->noAlgoInfoEntries > 0 && \
300 algoIDInfo->noAlgoInfoEntries < MAX_INTLENGTH_SHORT );
301
302 /* Get the string length and data and make sure that it's valid */
303 status = stringLen = readUint32( stream );
304 if( !cryptStatusError( status ) && \
305 ( stringLen < SSH2_MIN_ALGOID_SIZE || \
306 stringLen >= MAX_INTLENGTH_SHORT ) )
307 {
308 /* Quick-reject check for an obviously-invalid string */
309 status = CRYPT_ERROR_BADDATA;
310 }
311 if( !cryptStatusError( status ) )
312 status = sMemGetDataBlock( stream, &string, stringLen );
313 if( cryptStatusOK( status ) )
314 status = sSkip( stream, stringLen, MAX_INTLENGTH_SHORT );
315 if( cryptStatusError( status ) )
316 {
317 retExt( CRYPT_ERROR_BADDATA,
318 ( CRYPT_ERROR_BADDATA, errorInfo,
319 "Invalid algorithm ID string" ) );
320 }
321 ENSURES( stringLen >= SSH2_MIN_ALGOID_SIZE && \
322 stringLen < MAX_INTLENGTH_SHORT );
323 ANALYSER_HINT( string != NULL );
324
325 /* Walk down the string looking for a recognised algorithm. Since our
326 preference may not match the other side's preferences we have to walk
327 down the entire list to find our preferred choice:
328
329 stringPos stringLen
330 | |
331 v v
332 "algo1,algo2,algo3,algoN"
333 ^
334 |
335 substrLen
336
337 This works by walking an index stringPos down the string, with each
338 substring delimited by { stringPos, subStringLen }, which is checked
339 against the table of algorithm names */
340 for( stringPos = 0, noStrings = 0;
341 stringPos < stringLen && !foundMatch && \
342 noStrings < FAILSAFE_ITERATIONS_MED;
343 stringPos += substringLen + 1, noStrings++ )
344 {
345 const ALGO_STRING_INFO *matchedAlgoInfo = NULL;
346 const BYTE *substringPtr = ( BYTE * ) string + stringPos;
347 const int substringMaxLen = stringLen - stringPos;
348 BOOLEAN algoMatched = TRUE;
349 int currentAlgoIndex;
350
351 /* Find the length of the next algorithm name */
352 for( substringLen = 0;
353 substringLen < substringMaxLen && \
354 substringPtr[ substringLen ] != ',' && \
355 substringLen < FAILSAFE_ITERATIONS_LARGE; \
356 substringLen++ );
357 if( substringLen >= FAILSAFE_ITERATIONS_LARGE )
358 {
359 retExt( CRYPT_ERROR_OVERFLOW,
360 ( CRYPT_ERROR_OVERFLOW, errorInfo,
361 "Excessively long (more than %d characters) SSH "
362 "algorithm string encountered", substringLen ) );
363 }
364 if( substringLen < SSH2_MIN_ALGOID_SIZE || \
365 substringLen > CRYPT_MAX_TEXTSIZE )
366 {
367 /* Empty or too-short algorithm name (or excessively long one),
368 continue. Note that even with an (invalid) zero-length
369 substring we'll still progress down the string since the loop
370 increment is the substring length plus one */
371 continue;
372 }
373
374 /* Check whether it's something that we can handle */
375 for( currentAlgoIndex = 0;
376 currentAlgoIndex < algoIDInfo->noAlgoInfoEntries && \
377 algoIDInfo->algoInfo[ currentAlgoIndex ].name != NULL && \
378 currentAlgoIndex < FAILSAFE_ITERATIONS_MED;
379 currentAlgoIndex++ )
380 {
381 const ALGO_STRING_INFO *algoIDInfoPtr = \
382 &algoIDInfo->algoInfo[ currentAlgoIndex ];
383
384 if( algoIDInfoPtr->nameLen == substringLen && \
385 !memcmp( algoIDInfoPtr->name, substringPtr, substringLen ) )
386 {
387 matchedAlgoInfo = algoIDInfoPtr;
388 break;
389 }
390 }
391 ENSURES( currentAlgoIndex < FAILSAFE_ITERATIONS_MED );
392 ENSURES( currentAlgoIndex < algoIDInfo->noAlgoInfoEntries );
393 if( matchedAlgoInfo == NULL )
394 {
395 /* Unrecognised algorithm name, remember to warn the caller if
396 we have to match the first algorithm on the list, then move
397 on to the next name */
398 if( algoIDInfo->getAlgoType == GETALGO_FIRST_MATCH_WARN )
399 algoIDInfo->prefAlgoMismatch = TRUE;
400 continue;
401 }
402 DEBUG_PRINT(( "Offered suite: %s.\n", matchedAlgoInfo->name ));
403
404 /* Make sure that the required algorithms are available */
405 if( !isPseudoAlgo( matchedAlgoInfo->algo ) && \
406 !algoAvailable( matchedAlgoInfo->algo ) )
407 algoMatched = FALSE;
408 if( matchedAlgoInfo->subAlgo != CRYPT_ALGO_NONE && \
409 !algoAvailable( matchedAlgoInfo->subAlgo ) )
410 algoMatched = FALSE;
411
412 /* If this is an ECC algorithm and the use of ECC algorithms has
413 been prevented by external conditions such as the server key
414 not being an ECC key, we can't use it even if ECC algorithms in
415 general are available */
416 if( algoMatched && !algoIDInfo->allowECC && \
417 isEccAlgo( matchedAlgoInfo->algo ) )
418 algoMatched = FALSE;
419
420 /* If the matched algorithm isn't available, remember to warn the
421 caller if we have to match the first algorithm on the list, then
422 move on to the next name */
423 if( !algoMatched )
424 {
425 if( algoIDInfo->getAlgoType == GETALGO_FIRST_MATCH_WARN )
426 algoIDInfo->prefAlgoMismatch = TRUE;
427 continue;
428 }
429
430 switch( algoIDInfo->getAlgoType )
431 {
432 case GETALGO_BEST_MATCH:
433 /* If we're looking for the best (highest-ranked algorithm)
434 match, see whether the current match ranks higher than
435 the existing one */
436 if( currentAlgoIndex < algoIndex )
437 {
438 algoIndex = currentAlgoIndex;
439 if( algoIndex <= 0 )
440 foundMatch = TRUE; /* Gruener werd's net */
441 DEBUG_PRINT(( "Accepted suite: %s.\n",
442 matchedAlgoInfo->name ));
443 }
444 break;
445
446 case GETALGO_FIRST_MATCH:
447 /* If we've found an acceptable algorithm, remember it and
448 exit */
449 if( algoIDInfo->preferredAlgo == CRYPT_ALGO_NONE || \
450 algoIDInfo->preferredAlgo == matchedAlgoInfo->algo )
451 {
452 algoIndex = currentAlgoIndex;
453 foundMatch = TRUE;
454 DEBUG_PRINT(( "Accepted suite: %s.\n",
455 matchedAlgoInfo->name ));
456 }
457 break;
458
459 case GETALGO_FIRST_MATCH_WARN:
460 /* If we found the algorithm that we're after, remember it
461 and exit */
462 if( algoIDInfo->preferredAlgo != matchedAlgoInfo->algo )
463 {
464 /* We didn't match the first algorithm on the list, warn
465 the caller */
466 algoIDInfo->prefAlgoMismatch = TRUE;
467 DEBUG_PRINT(( "Accepted suite: %s.\n",
468 matchedAlgoInfo->name ));
469 }
470 algoIndex = currentAlgoIndex;
471 foundMatch = TRUE;
472 break;
473
474 default:
475 retIntError();
476 }
477 }
478 if( noStrings >= FAILSAFE_ITERATIONS_MED )
479 {
480 retExt( CRYPT_ERROR_OVERFLOW,
481 ( CRYPT_ERROR_OVERFLOW, errorInfo,
482 "Excessive number (more than %d) of SSH algorithm "
483 "strings encountered", noStrings ) );
484 }
485 if( algoIndex > 50 )
486 {
487 char algoString[ 256 + 8 ];
488 const int algoStringLen = min( stringLen, \
489 min( MAX_ERRMSG_SIZE - 80, 255 ) );
490
491 REQUIRES( algoStringLen > 0 && \
492 algoStringLen <= min( MAX_ERRMSG_SIZE - 80, 255 ) );
493
494 /* We couldn't find anything to use, tell the caller what was
495 available */
496 memcpy( algoString, string, algoStringLen );
497 retExt( CRYPT_ERROR_NOTAVAIL,
498 ( CRYPT_ERROR_NOTAVAIL, errorInfo,
499 "No algorithm compatible with the remote system's "
500 "selection was found: '%s'",
501 sanitiseString( algoString, 256, stringLen ) ) );
502 }
503
504 /* We found a more-preferred algorithm than the default, go with that */
505 algoIDInfo->algo = algoIDInfo->algoInfo[ algoIndex ].algo;
506 algoIDInfo->subAlgo = algoIDInfo->algoInfo[ algoIndex ].subAlgo;
507 DEBUG_PRINT(( "Final accepted suite: %s.\n",
508 algoIDInfo->algoInfo[ algoIndex ].name ));
509 return( CRYPT_OK );
510 }
511
512 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4, 6 ) ) \
513 int readAlgoString( INOUT STREAM *stream,
514 IN_ARRAY( noAlgoStringEntries ) \
515 const ALGO_STRING_INFO *algoInfo,
516 IN_RANGE( 1, 100 ) const int noAlgoStringEntries,
517 OUT_ALGO_Z CRYPT_ALGO_TYPE *algo,
518 const BOOLEAN useFirstMatch,
519 INOUT ERROR_INFO *errorInfo )
520 {
521 ALGOID_INFO algoIDInfo;
522 int status;
523
524 assert( isWritePtr( stream, sizeof( STREAM ) ) );
525 assert( isReadPtr( algoInfo, sizeof( ALGO_STRING_INFO ) * \
526 noAlgoStringEntries ) );
527 assert( isWritePtr( algo, sizeof( CRYPT_ALGO_TYPE ) ) );
528
529 REQUIRES( noAlgoStringEntries > 0 && noAlgoStringEntries <= 100 );
530
531 /* Clear return value */
532 *algo = CRYPT_ALGO_NONE;
533
534 setAlgoIDInfo( &algoIDInfo, algoInfo, noAlgoStringEntries,
535 CRYPT_ALGO_NONE, useFirstMatch ? GETALGO_FIRST_MATCH : \
536 GETALGO_BEST_MATCH );
537 status = readAlgoStringEx( stream, &algoIDInfo, errorInfo );
538 if( cryptStatusOK( status ) )
539 *algo = algoIDInfo.algo;
540 return( status );
541 }
542
543 /* Algorithms used to protect data packets are used in pairs, one for
544 incoming and the other for outgoing data. To keep things simple we
545 always force these to be the same, first reading the algorithm for one
546 direction and then making sure that the one for the other direction
547 matches this. All implementations seem to do this anyway, many aren't
548 even capable of supporting asymmetric algorithm choices */
549
550 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4, 7 ) ) \
551 static int readAlgoStringPair( INOUT STREAM *stream,
552 IN_ARRAY( noAlgoStringEntries ) \
553 const ALGO_STRING_INFO *algoInfo,
554 IN_RANGE( 1, 100 ) const int noAlgoStringEntries,
555 OUT_ALGO_Z CRYPT_ALGO_TYPE *algo,
556 const BOOLEAN isServer,
557 const BOOLEAN allowAsymmetricAlgos,
558 INOUT ERROR_INFO *errorInfo )
559 {
560 CRYPT_ALGO_TYPE pairPreferredAlgo;
561 ALGOID_INFO algoIDInfo;
562 int status;
563
564 assert( isWritePtr( stream, sizeof( STREAM ) ) );
565 assert( isReadPtr( algoInfo, sizeof( ALGO_STRING_INFO ) * \
566 noAlgoStringEntries ) );
567 assert( isWritePtr( algo, sizeof( CRYPT_ALGO_TYPE ) ) );
568
569 REQUIRES( noAlgoStringEntries > 0 && noAlgoStringEntries <= 100 );
570
571 /* Clear return value */
572 *algo = CRYPT_ALGO_NONE;
573
574 /* Get the first algorithm */
575 setAlgoIDInfo( &algoIDInfo, algoInfo, noAlgoStringEntries,
576 CRYPT_ALGO_NONE, isServer ? GETALGO_FIRST_MATCH : \
577 GETALGO_BEST_MATCH );
578 status = readAlgoStringEx( stream, &algoIDInfo, errorInfo );
579 if( cryptStatusError( status ) )
580 return( status );
581 pairPreferredAlgo = algoIDInfo.algo;
582
583 /* Get the matched second algorithm. Some buggy implementations request
584 mismatched algorithms (at the moment this is only for compression
585 algorithms) but have no problems in accepting the same algorithm in
586 both directions, so if we're talking to one of these then we ignore
587 an algorithm mismatch */
588 setAlgoIDInfo( &algoIDInfo, algoInfo, noAlgoStringEntries,
589 pairPreferredAlgo, GETALGO_FIRST_MATCH );
590 status = readAlgoStringEx( stream, &algoIDInfo, errorInfo );
591 if( cryptStatusError( status ) )
592 return( status );
593 if( pairPreferredAlgo != algoIDInfo.algo && !allowAsymmetricAlgos )
594 {
595 retExt( CRYPT_ERROR_BADDATA,
596 ( CRYPT_ERROR_BADDATA, errorInfo,
597 "Client algorithm %d doesn't match server algorithm %d "
598 "in algorithm pair", pairPreferredAlgo,
599 algoIDInfo.algo ) );
600 }
601 *algo = algoIDInfo.algo;
602
603 return( status );
604 }
605
606 /* Write a cryptlib algorithm ID as an SSH algorithm name */
607
608 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
writeAlgoStringEx(INOUT STREAM * stream,IN_ALGO const CRYPT_ALGO_TYPE algo,IN_ALGO_OPT const CRYPT_ALGO_TYPE subAlgo)609 int writeAlgoStringEx( INOUT STREAM *stream,
610 IN_ALGO const CRYPT_ALGO_TYPE algo,
611 IN_ALGO_OPT const CRYPT_ALGO_TYPE subAlgo )
612 {
613 int algoIndex;
614
615 assert( isWritePtr( stream, sizeof( STREAM ) ) );
616
617 REQUIRES( algo > CRYPT_ALGO_NONE && algo < CRYPT_ALGO_LAST_EXTERNAL );
618 REQUIRES( subAlgo >= CRYPT_ALGO_NONE && subAlgo < CRYPT_ALGO_LAST_EXTERNAL );
619
620 /* Locate the name for this algorithm and optional sub-algoritihm and
621 encode it as an SSH string */
622 for( algoIndex = 0;
623 algoStringMapTbl[ algoIndex ].algo != CRYPT_ALGO_NONE && \
624 algoStringMapTbl[ algoIndex ].algo != algo && \
625 algoIndex < FAILSAFE_ARRAYSIZE( algoStringMapTbl, ALGO_STRING_INFO );
626 algoIndex++ );
627 ENSURES( algoIndex < FAILSAFE_ARRAYSIZE( algoStringMapTbl, \
628 ALGO_STRING_INFO ) );
629 ENSURES( algoStringMapTbl[ algoIndex ].algo != CRYPT_ALGO_NONE );
630
631 /* If there's a sub-algorithm, find the entry for that */
632 if( subAlgo != CRYPT_ALGO_NONE )
633 {
634 for( ; algoStringMapTbl[ algoIndex ].algo != CRYPT_ALGO_NONE && \
635 algoStringMapTbl[ algoIndex ].algo == algo && \
636 algoStringMapTbl[ algoIndex ].subAlgo != subAlgo && \
637 algoIndex < FAILSAFE_ARRAYSIZE( algoStringMapTbl, \
638 ALGO_STRING_INFO );
639 algoIndex++ );
640 ENSURES( algoIndex < FAILSAFE_ARRAYSIZE( algoStringMapTbl, \
641 ALGO_STRING_INFO ) );
642 ENSURES( algoStringMapTbl[ algoIndex ].algo != CRYPT_ALGO_NONE );
643 }
644
645 return( writeString32( stream, algoStringMapTbl[ algoIndex ].name,
646 algoStringMapTbl[ algoIndex ].nameLen ) );
647 }
648
649 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
writeAlgoString(INOUT STREAM * stream,IN_ALGO const CRYPT_ALGO_TYPE algo)650 int writeAlgoString( INOUT STREAM *stream,
651 IN_ALGO const CRYPT_ALGO_TYPE algo )
652 {
653 assert( isWritePtr( stream, sizeof( STREAM ) ) );
654
655 REQUIRES( algo > CRYPT_ALGO_NONE && algo < CRYPT_ALGO_LAST_EXTERNAL );
656
657 return( writeAlgoStringEx( stream, algo, CRYPT_ALGO_NONE ) );
658 }
659
660 /* Write a list of algorithms */
661
662 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
663 int writeAlgoList( INOUT STREAM *stream,
664 IN_ARRAY( noAlgoStringInfoEntries ) \
665 const ALGO_STRING_INFO *algoStringInfoTbl,
666 IN_RANGE( 1, 10 ) const int noAlgoStringInfoEntries )
667 {
668 int availAlgoIndex[ 16 + 8 ];
669 int noAlgos = 0, length = 0, algoIndex, status;
670
671 assert( isWritePtr( stream, sizeof( STREAM ) ) );
672 assert( isReadPtr( algoStringInfoTbl, sizeof( ALGO_STRING_INFO ) * \
673 noAlgoStringInfoEntries ) );
674
675 REQUIRES( noAlgoStringInfoEntries > 0 && noAlgoStringInfoEntries <= 10 );
676
677 /* Walk down the list of algorithms remembering the encoded name of each
678 one that's available for use */
679 for( algoIndex = 0; \
680 algoIndex < noAlgoStringInfoEntries && \
681 algoStringInfoTbl[ algoIndex ].algo != CRYPT_ALGO_NONE && \
682 algoIndex < FAILSAFE_ITERATIONS_SMALL;
683 algoIndex++ )
684 {
685 const ALGO_STRING_INFO *algoStringInfo = &algoStringInfoTbl[ algoIndex ];
686
687 /* Make sure that this algorithm is available for use */
688 if( !isPseudoAlgo( algoStringInfo->algo ) && \
689 !algoAvailable( algoStringInfo->algo ) )
690 continue;
691
692 /* Make sure that any required sub-algorithms are available */
693 if( algoStringInfo->subAlgo != CRYPT_ALGO_NONE && \
694 !algoAvailable( algoStringInfo->subAlgo ) )
695 continue;
696
697 /* Remember the algorithm details */
698 REQUIRES( noAlgos >= 0 && noAlgos < 16 );
699 availAlgoIndex[ noAlgos++ ] = algoIndex;
700 length += algoStringInfo->nameLen;
701 if( noAlgos > 1 )
702 length++; /* Room for comma delimiter */
703 }
704 ENSURES( algoIndex < FAILSAFE_ITERATIONS_SMALL );
705
706 /* Encode the list of available algorithms into a comma-separated string */
707 status = writeUint32( stream, length );
708 for( algoIndex = 0; cryptStatusOK( status ) && algoIndex < noAlgos && \
709 algoIndex < FAILSAFE_ITERATIONS_MED;
710 algoIndex++ )
711 {
712 const ALGO_STRING_INFO *algoStringInfo = \
713 &algoStringInfoTbl[ availAlgoIndex[ algoIndex ] ];
714
715 if( algoIndex > 0 )
716 sputc( stream, ',' ); /* Add comma delimiter */
717 status = swrite( stream, algoStringInfo->name,
718 algoStringInfo->nameLen );
719 }
720 ENSURES( algoIndex < FAILSAFE_ITERATIONS_MED );
721 return( status );
722 }
723
724 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
writeAlgoClassList(INOUT STREAM * stream,IN_ENUM (SSH_ALGOCLASS)const SSH_ALGOCLASS_TYPE algoClass)725 int writeAlgoClassList( INOUT STREAM *stream,
726 IN_ENUM( SSH_ALGOCLASS ) \
727 const SSH_ALGOCLASS_TYPE algoClass )
728 {
729 assert( isWritePtr( stream, sizeof( STREAM ) ) );
730
731 REQUIRES( algoClass > SSH_ALGOCLASS_NONE && \
732 algoClass < SSH_ALGOCLASS_LAST );
733
734 /* Write the appropriate algorithm list for this algorithm class */
735 switch( algoClass )
736 {
737 case SSH_ALGOCLASS_KEYEX:
738 return( writeAlgoList( stream, algoStringKeyexTbl,
739 FAILSAFE_ARRAYSIZE( algoStringKeyexTbl, \
740 ALGO_STRING_INFO ) ) );
741
742 case SSH_ALGOCLASS_KEYEX_NOECC:
743 return( writeAlgoList( stream, algoStringKeyexNoECCTbl,
744 FAILSAFE_ARRAYSIZE( algoStringKeyexNoECCTbl, \
745 ALGO_STRING_INFO ) ) );
746
747 case SSH_ALGOCLASS_ENCR:
748 return( writeAlgoList( stream, algoStringEncrTbl,
749 FAILSAFE_ARRAYSIZE( algoStringEncrTbl, \
750 ALGO_STRING_INFO ) ) );
751
752 case SSH_ALGOCLASS_MAC:
753 return( writeAlgoList( stream, algoStringMACTbl,
754 FAILSAFE_ARRAYSIZE( algoStringMACTbl, \
755 ALGO_STRING_INFO ) ) );
756
757 case SSH_ALGOCLASS_COPR:
758 return( writeAlgoList( stream, algoStringCoprTbl,
759 FAILSAFE_ARRAYSIZE( algoStringCoprTbl, \
760 ALGO_STRING_INFO ) ) );
761 }
762
763 retIntError();
764 }
765
766 /****************************************************************************
767 * *
768 * Miscellaneous Functions *
769 * *
770 ****************************************************************************/
771
772 /* Process a client/server hello packet */
773
774 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
processHelloSSH(INOUT SESSION_INFO * sessionInfoPtr,INOUT SSH_HANDSHAKE_INFO * handshakeInfo,OUT_LENGTH_SHORT_Z int * keyexLength,const BOOLEAN isServer)775 int processHelloSSH( INOUT SESSION_INFO *sessionInfoPtr,
776 INOUT SSH_HANDSHAKE_INFO *handshakeInfo,
777 OUT_LENGTH_SHORT_Z int *keyexLength,
778 const BOOLEAN isServer )
779 {
780 CRYPT_ALGO_TYPE dummyAlgo;
781 STREAM stream;
782 ALGOID_INFO algoIDInfo;
783 BOOLEAN preferredAlgoMismatch = FALSE, guessedKeyex = FALSE;
784 int length, status;
785
786 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
787 assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
788 assert( isWritePtr( keyexLength, sizeof( int ) ) );
789
790 /* Clear return value */
791 *keyexLength = 0;
792
793 /* Process the client/server hello:
794
795 byte type = SSH_MSG_KEXINIT
796 byte[16] cookie
797 string keyex algorithms
798 string pubkey algorithms
799 string client_crypto algorithms
800 string server_crypto algorithms
801 string client_mac algorithms
802 string server_mac algorithms
803 string client_compression algorithms
804 string server_compression algorithms
805 string client_language
806 string server_language
807 boolean first_keyex_packet_follows
808 uint32 reserved
809
810 The cookie isn't explicitly processed since it's done implicitly when
811 the hello message is hashed */
812 status = length = \
813 readHSPacketSSH2( sessionInfoPtr, SSH_MSG_KEXINIT, 128 );
814 if( cryptStatusError( status ) )
815 return( status );
816 *keyexLength = length;
817 sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
818 status = sSkip( &stream, SSH2_COOKIE_SIZE, SSH2_COOKIE_SIZE );
819 if( cryptStatusError( status ) )
820 {
821 sMemDisconnect( &stream );
822 return( status );
823 }
824
825 /* Read the keyex algorithm information */
826 if( isServer )
827 {
828 int pkcAlgo;
829
830 setAlgoIDInfo( &algoIDInfo, algoStringKeyexTbl,
831 FAILSAFE_ARRAYSIZE( algoStringKeyexTbl, \
832 ALGO_STRING_INFO ),
833 CRYPT_ALGO_DH, GETALGO_FIRST_MATCH_WARN );
834
835 /* By default the use of ECC algorithms is enabled if support for
836 them is present, however if the server key is a non-ECC key then
837 it can't be used with an ECC keyex so we have to explicitly
838 disable it (technically it is possible to mix ECDH with RSA but
839 this is more likely an error than anything deliberate) */
840 status = krnlSendMessage( sessionInfoPtr->privateKey,
841 IMESSAGE_GETATTRIBUTE, &pkcAlgo,
842 CRYPT_CTXINFO_ALGO );
843 if( cryptStatusError( status ) || !isEccAlgo( pkcAlgo ) )
844 algoIDInfo.allowECC = FALSE;
845 }
846 else
847 {
848 setAlgoIDInfo( &algoIDInfo, algoStringKeyexTbl,
849 FAILSAFE_ARRAYSIZE( algoStringKeyexTbl, \
850 ALGO_STRING_INFO ),
851 CRYPT_ALGO_NONE, GETALGO_BEST_MATCH );
852 }
853 status = readAlgoStringEx( &stream, &algoIDInfo, SESSION_ERRINFO );
854 if( cryptStatusError( status ) )
855 {
856 sMemDisconnect( &stream );
857 return( status );
858 }
859 handshakeInfo->keyexAlgo = algoIDInfo.algo;
860 if( algoIDInfo.prefAlgoMismatch )
861 {
862 /* We didn't get a match for our first choice, remember that we have
863 to discard any guessed keyex that may follow */
864 preferredAlgoMismatch = TRUE;
865 }
866 if( algoIDInfo.algo == CRYPT_ALGO_DH )
867 {
868 /* Switch to the appropriate hash algorithm */
869 handshakeInfo->exchangeHashAlgo = algoIDInfo.subAlgo;
870
871 /* If we're using ephemeral rather than static DH keys then we need
872 to negotiate the keyex key before we can perform the exchange */
873 handshakeInfo->requestedServerKeySize = SSH2_DEFAULT_KEYSIZE;
874 }
875 if( algoIDInfo.algo == CRYPT_ALGO_ECDH )
876 {
877 /* If we're using an ECDH cipher suite then we need to switch to the
878 appropriate hash algorithm for the keyex hashing */
879 handshakeInfo->isECDH = TRUE;
880 handshakeInfo->exchangeHashAlgo = algoIDInfo.subAlgo;
881 }
882
883 /* Read the pubkey (signature) algorithm information */
884 if( isServer )
885 {
886 setAlgoIDInfo( &algoIDInfo, handshakeInfo->algoStringPubkeyTbl,
887 handshakeInfo->algoStringPubkeyTblNoEntries,
888 handshakeInfo->pubkeyAlgo, GETALGO_FIRST_MATCH_WARN );
889 }
890 else
891 {
892 setAlgoIDInfo( &algoIDInfo, handshakeInfo->algoStringPubkeyTbl,
893 handshakeInfo->algoStringPubkeyTblNoEntries,
894 CRYPT_ALGO_NONE, GETALGO_BEST_MATCH );
895 }
896 status = readAlgoStringEx( &stream, &algoIDInfo, SESSION_ERRINFO );
897 if( cryptStatusError( status ) )
898 {
899 sMemDisconnect( &stream );
900 return( status );
901 }
902 if( isServer && handshakeInfo->pubkeyAlgo != algoIDInfo.algo )
903 {
904 sMemDisconnect( &stream );
905 retExt( status,
906 ( status, SESSION_ERRINFO,
907 "Client requested pubkey algorithm %d when we "
908 "advertised %d", algoIDInfo.algo,
909 handshakeInfo->pubkeyAlgo ) );
910 }
911 handshakeInfo->pubkeyAlgo = algoIDInfo.algo;
912 handshakeInfo->hashAlgo = algoIDInfo.subAlgo;
913 if( algoIDInfo.prefAlgoMismatch )
914 {
915 /* We didn't get a match for our first choice, remember that we have
916 to discard any guessed keyex that may follow */
917 preferredAlgoMismatch = TRUE;
918 }
919
920 /* Read the encryption and MAC algorithm information */
921 status = readAlgoStringPair( &stream, algoStringEncrTbl,
922 FAILSAFE_ARRAYSIZE( algoStringEncrTbl, \
923 ALGO_STRING_INFO ),
924 &sessionInfoPtr->cryptAlgo, isServer, FALSE,
925 SESSION_ERRINFO );
926 if( cryptStatusOK( status ) )
927 {
928 status = readAlgoStringPair( &stream, algoStringMACTbl,
929 FAILSAFE_ARRAYSIZE( algoStringMACTbl, \
930 ALGO_STRING_INFO ),
931 &sessionInfoPtr->integrityAlgo,
932 isServer, FALSE, SESSION_ERRINFO );
933 }
934 if( cryptStatusError( status ) )
935 {
936 sMemDisconnect( &stream );
937 return( status );
938 }
939
940 /* Read the remaining algorithm information. The final reserved value
941 should always be zero but we don't specifically check for this since
942 at some point in the future it may become non-zero */
943 status = readAlgoStringPair( &stream, algoStringCoprTbl,
944 FAILSAFE_ARRAYSIZE( algoStringCoprTbl, \
945 ALGO_STRING_INFO ),
946 &dummyAlgo, isServer,
947 ( sessionInfoPtr->protocolFlags & SSH_PFLAG_ASYMMCOPR ) ? \
948 TRUE : FALSE, SESSION_ERRINFO );
949 if( cryptStatusError( status ) )
950 {
951 sMemDisconnect( &stream );
952 return( status );
953 }
954 readUniversal32( &stream );
955 status = readUniversal32( &stream ); /* Language string pair */
956 if( cryptStatusOK( status ) )
957 {
958 int value;
959
960 status = value = sgetc( &stream );
961 if( !cryptStatusError( status ) )
962 {
963 if( value != 0 )
964 guessedKeyex = TRUE;
965 status = readUint32( &stream ); /* Reserved value */
966 }
967 }
968 sMemDisconnect( &stream );
969 if( cryptStatusError( status ) )
970 {
971 retExt( status,
972 ( status, SESSION_ERRINFO,
973 "Invalid hello packet language string/trailer data" ) );
974 }
975
976 /* If we're using an alternative exchange hash algorithm, switch the
977 contexts around to using the alternative one for hashing from now
978 on */
979 if( handshakeInfo->exchangeHashAlgo == CRYPT_ALGO_SHA2 )
980 {
981 const CRYPT_CONTEXT tempContext = handshakeInfo->iExchangeHashContext;
982
983 handshakeInfo->iExchangeHashContext = \
984 handshakeInfo->iExchangeHashAltContext;
985 handshakeInfo->iExchangeHashAltContext = tempContext;
986 }
987
988 /* If there's a guessed keyex following this packet and we didn't match
989 the first-choice keyex/pubkey algorithm, tell the caller to skip it */
990 if( guessedKeyex && preferredAlgoMismatch )
991 return( OK_SPECIAL );
992
993 return( CRYPT_OK );
994 }
995
996 /****************************************************************************
997 * *
998 * Get/Put Data Functions *
999 * *
1000 ****************************************************************************/
1001
1002 /* Process a control message received during the processBodyFunction() */
1003
1004 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
processControlMessage(INOUT SESSION_INFO * sessionInfoPtr,IN_DATALENGTH_Z const int payloadLength)1005 static int processControlMessage( INOUT SESSION_INFO *sessionInfoPtr,
1006 IN_DATALENGTH_Z const int payloadLength )
1007 {
1008 SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
1009 BYTE *bufPtr = sessionInfoPtr->receiveBuffer + \
1010 sessionInfoPtr->receiveBufPos;
1011 STREAM stream;
1012 int localPayloadLength = payloadLength, status;
1013
1014 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
1015
1016 REQUIRES( payloadLength >= 0 && payloadLength < MAX_BUFFER_SIZE );
1017
1018 /* Putty 0.59 erroneously sent zero-length SSH_MSG_IGNORE packets, if
1019 we find one of these then we convert it into a valid packet. Writing
1020 into the buffer at this position is safe because we've got padding
1021 and at least sessionInfoPtr->authBlocksize bytes of MAC following the
1022 current position. We can also modify the localPayloadLength value
1023 for the same reason */
1024 if( ( sessionInfoPtr->protocolFlags & SSH_PFLAG_ZEROLENIGNORE ) && \
1025 sshInfo->packetType == SSH_MSG_IGNORE && localPayloadLength == 0 )
1026 {
1027 memset( bufPtr, 0, UINT32_SIZE );
1028 localPayloadLength = UINT32_SIZE;
1029 }
1030
1031 /* Make sure that the message length is valid. This will be caught
1032 anyway when we try and process the channel control message (and the
1033 excessive-length check has already been performed by the packet-read
1034 code) but checking it here avoids an assertion in the debug build
1035 when we connect the stream, as well as just being good programming
1036 practice */
1037 if( localPayloadLength <= 0 || \
1038 localPayloadLength > sessionInfoPtr->receiveBufEnd - \
1039 sessionInfoPtr->receiveBufPos )
1040 {
1041 retExt( CRYPT_ERROR_BADDATA,
1042 ( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
1043 "Invalid session control message payload length %d for "
1044 "%s (%d), should be 0...%d", localPayloadLength,
1045 getSSHPacketName( sshInfo->packetType ),
1046 sshInfo->packetType, sessionInfoPtr->receiveBufEnd - \
1047 sessionInfoPtr->receiveBufPos ) );
1048 }
1049
1050 /* Process the control message and reset the receive buffer indicators
1051 to clear it */
1052 ENSURES( rangeCheckZ( sessionInfoPtr->receiveBufPos, localPayloadLength,
1053 sessionInfoPtr->receiveBufEnd ) );
1054 sMemConnect( &stream, bufPtr, localPayloadLength );
1055 status = processChannelControlMessage( sessionInfoPtr, &stream );
1056 sMemDisconnect( &stream );
1057 sessionInfoPtr->receiveBufEnd = sessionInfoPtr->receiveBufPos;
1058 sessionInfoPtr->pendingPacketLength = 0;
1059
1060 return( status );
1061 }
1062
1063 /* Read data over the SSH link */
1064
1065 CHECK_RETVAL_LENGTH STDC_NONNULL_ARG( ( 1, 2 ) ) \
readHeaderFunction(INOUT SESSION_INFO * sessionInfoPtr,OUT_ENUM_OPT (READINFO)READSTATE_INFO * readInfo)1066 static int readHeaderFunction( INOUT SESSION_INFO *sessionInfoPtr,
1067 OUT_ENUM_OPT( READINFO ) \
1068 READSTATE_INFO *readInfo )
1069 {
1070 SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
1071 BYTE *bufPtr = sessionInfoPtr->receiveBuffer + \
1072 sessionInfoPtr->receiveBufPos;
1073 long length;
1074 int extraLength, removedDataLength = ( ID_SIZE + PADLENGTH_SIZE );
1075 int partialPayloadLength, status;
1076
1077 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
1078 assert( isWritePtr( readInfo, sizeof( READSTATE_INFO ) ) );
1079
1080 /* Clear return value */
1081 *readInfo = READINFO_NONE;
1082
1083 /* Make sure that there's room left to handle the speculative read */
1084 if( sessionInfoPtr->receiveBufPos >= \
1085 sessionInfoPtr->receiveBufSize - 128 )
1086 return( 0 );
1087
1088 /* Try and read the header data from the remote system */
1089 REQUIRES( sessionInfoPtr->receiveBufPos == sessionInfoPtr->receiveBufEnd );
1090 status = readPacketHeaderSSH2( sessionInfoPtr, SSH_MSG_CHANNEL_DATA,
1091 &length, &extraLength, sshInfo,
1092 readInfo );
1093 if( cryptStatusError( status ) )
1094 {
1095 /* OK_SPECIAL means that we got a soft timeout before the entire
1096 header was read, so we return zero bytes read to tell the
1097 calling code that there's nothing more to do */
1098 return( ( status == OK_SPECIAL ) ? 0 : status );
1099 }
1100 ENSURES( length >= ID_SIZE + PADLENGTH_SIZE + SSH2_MIN_PADLENGTH_SIZE && \
1101 length <= sessionInfoPtr->receiveBufSize - \
1102 sessionInfoPtr->receiveBufPos );
1103 status = checkMacSSHIncremental( sessionInfoPtr->iAuthInContext,
1104 sshInfo->readSeqNo, bufPtr,
1105 MIN_PACKET_SIZE - LENGTH_SIZE,
1106 MIN_PACKET_SIZE - LENGTH_SIZE,
1107 length, MAC_START,
1108 sessionInfoPtr->authBlocksize );
1109 if( cryptStatusError( status ) )
1110 {
1111 /* We don't return an extended status at this point because we
1112 haven't completed the message MAC calculation/check yet so
1113 any errors will be cryptlib-internal ones */
1114 return( status );
1115 }
1116
1117 /* If it's channel data, strip the encapsulation, which allows us to
1118 process the payload directly without having to move it around in
1119 the buffer:
1120
1121 [ uint32 length (excluding MAC size) - Processed in rPHSSH2() ]
1122 byte padLen
1123 byte SSH_MSG_CHANNEL_DATA
1124 uint32 recipient channel
1125 uint32 dataLength | string data
1126 byte[] data |
1127 [ byte[] padding ]
1128 [ byte[] MAC ] */
1129 if( sshInfo->packetType == SSH_MSG_CHANNEL_DATA )
1130 {
1131 STREAM stream;
1132 long payloadLength;
1133
1134 sMemConnect( &stream, bufPtr, SSH_HEADER_REMAINDER_SIZE );
1135
1136 /* Skip the type, padding length, and channel number and make sure
1137 that the payload length matches the packet length */
1138 sSkip( &stream, PADLENGTH_SIZE + ID_SIZE + UINT32_SIZE,
1139 PADLENGTH_SIZE + ID_SIZE + UINT32_SIZE );
1140 status = payloadLength = readUint32( &stream );
1141 if( !cryptStatusError( status ) )
1142 removedDataLength = stell( &stream );
1143 if( cryptStatusError( status ) || \
1144 payloadLength != length - ( removedDataLength + \
1145 sshInfo->padLength ) )
1146 {
1147 sMemDisconnect( &stream );
1148 retExt( CRYPT_ERROR_BADDATA,
1149 ( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
1150 "Invalid data packet payload length %ld for "
1151 "SSH_MSG_CHANNEL_DATA (94), should be %ld",
1152 cryptStatusError( status ) ? 0 : payloadLength,
1153 length - ( removedDataLength + sshInfo->padLength ) ) );
1154 }
1155
1156 /* Move back to the start of the payload and process the channel
1157 data header, required in order to handle window size updates.
1158 This consists of the channel number and yet another length value,
1159 present at the start of the payload which is encoded as an SSH
1160 string (uint32 length + data). This value has already been
1161 checked above, and is only accepted if it matches the outer
1162 length value. This is important because the data hasn't been
1163 verified by the MAC yet, since we need to process the header in
1164 order to find out where the MAC is. This means that the channel
1165 number is processed unverified, but this shouldn't be a major
1166 issue since at most an attacker can corrupt the value, and it
1167 will end up being mapped to an invalid channel with a high
1168 probability */
1169 sseek( &stream, PADLENGTH_SIZE + ID_SIZE );
1170 status = processChannelControlMessage( sessionInfoPtr, &stream );
1171 /* To handle window adjusts */
1172 sMemDisconnect( &stream );
1173 if( cryptStatusError( status ) )
1174 return( status );
1175 }
1176
1177 /* Move the remainder down to the start of the buffer. The general idea
1178 is to remove all of the header data so that only the payload remains
1179 in the buffer, avoiding the need to move it down afterwards:
1180
1181 rBufPos
1182 |
1183 v |<-- pPayloadLen -->|
1184 +-------+---------------+-------------------+-------+
1185 | | |///////////////////| |
1186 +-------+---------------+-------------------+-------+
1187 ^<-removedDLen->|
1188 |
1189 bufPtr
1190
1191 This is complicated by the fact that (unlike SSL) all of the data
1192 (including the header) is encrypted and MAC'd so we can't just read
1193 that separately but have to process it as part of the payload, remove
1194 it, and remember anything that's left for later */
1195 REQUIRES( removedDataLength > 0 && \
1196 removedDataLength < MAX_INTLENGTH_SHORT );
1197 partialPayloadLength = SSH_HEADER_REMAINDER_SIZE - removedDataLength;
1198 ENSURES( partialPayloadLength > 0 && \
1199 removedDataLength + partialPayloadLength <= \
1200 sessionInfoPtr->receiveBufSize - sessionInfoPtr->receiveBufPos && \
1201 removedDataLength + partialPayloadLength < MAX_BUFFER_SIZE );
1202 memmove( bufPtr, bufPtr + removedDataLength, partialPayloadLength );
1203
1204 /* Determine how much data we'll be expecting, adjusted for the fixed
1205 information that we've removed and the (implicitly present) MAC data */
1206 sessionInfoPtr->pendingPacketLength = \
1207 sessionInfoPtr->pendingPacketRemaining = \
1208 ( length + extraLength ) - removedDataLength;
1209 sshInfo->partialPacketDataLength = partialPayloadLength;
1210
1211 /* Indicate that we got some payload as part of the header */
1212 *readInfo = READINFO_HEADERPAYLOAD;
1213 return( partialPayloadLength );
1214 }
1215
1216 CHECK_RETVAL_LENGTH STDC_NONNULL_ARG( ( 1, 2 ) ) \
processBodyFunction(INOUT SESSION_INFO * sessionInfoPtr,OUT_ENUM_OPT (READINFO)READSTATE_INFO * readInfo)1217 static int processBodyFunction( INOUT SESSION_INFO *sessionInfoPtr,
1218 OUT_ENUM_OPT( READINFO ) READSTATE_INFO *readInfo )
1219 {
1220 SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
1221 BYTE *dataRemainingPtr = sessionInfoPtr->receiveBuffer + \
1222 sessionInfoPtr->receiveBufPos + \
1223 sshInfo->partialPacketDataLength;
1224 const int dataRemainingSize = sessionInfoPtr->pendingPacketLength - \
1225 sshInfo->partialPacketDataLength;
1226 const int dataLength = dataRemainingSize - sessionInfoPtr->authBlocksize;
1227 int payloadLength, status;
1228
1229 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
1230 assert( isWritePtr( readInfo, sizeof( READSTATE_INFO ) ) );
1231
1232 REQUIRES( dataRemainingSize >= sessionInfoPtr->authBlocksize && \
1233 dataRemainingSize <= sessionInfoPtr->receiveBufEnd - \
1234 ( sessionInfoPtr->receiveBufPos + \
1235 sshInfo->partialPacketDataLength ) );
1236 REQUIRES( dataLength >= 0 && dataLength < dataRemainingSize );
1237
1238 /* All errors processing the payload are fatal */
1239 *readInfo = READINFO_FATAL;
1240
1241 /* Decrypt the packet in the buffer and MAC the payload. The length may
1242 be zero if the entire message fits into the already-processed fixed-
1243 length header portion, e.g. for channel-close messages that only
1244 contain a channel number:
1245 Key:
1246 Processed in header read +--+
1247 recBufPos | | | Processed
1248 |<----v----- pendingPacketLength ---------->| +--+
1249 v<- pPDL -->| | +--+
1250 ----+-----------+-----------------------+-------+-- |//| Encrypted
1251 | |///////////////////////|\\\\\\\| +--+
1252 ----+-----------+-----------------------+-------+-- +--+
1253 |<---- dataLength ----->| | |\\| MAC
1254 |<------- dataRemaining ------->| +--+ */
1255 if( dataLength > 0 )
1256 {
1257
1258 status = krnlSendMessage( sessionInfoPtr->iCryptInContext,
1259 IMESSAGE_CTX_DECRYPT, dataRemainingPtr,
1260 dataLength );
1261 if( cryptStatusError( status ) )
1262 return( status );
1263 status = checkMacSSHIncremental( sessionInfoPtr->iAuthInContext, 0,
1264 dataRemainingPtr, dataRemainingSize,
1265 dataLength, 0, MAC_END,
1266 sessionInfoPtr->authBlocksize );
1267 }
1268 else
1269 {
1270 status = checkMacSSHIncremental( sessionInfoPtr->iAuthInContext, 0,
1271 dataRemainingPtr, dataRemainingSize,
1272 0, 0, MAC_END,
1273 sessionInfoPtr->authBlocksize );
1274 }
1275 if( cryptStatusError( status ) )
1276 {
1277 retExt( CRYPT_ERROR_SIGNATURE,
1278 ( CRYPT_ERROR_SIGNATURE, SESSION_ERRINFO,
1279 "Bad message MAC for %s (%d) packet, length %d",
1280 getSSHPacketName( sshInfo->packetType ),
1281 sshInfo->packetType,
1282 sshInfo->partialPacketDataLength + dataLength ) );
1283 }
1284
1285 /* Strip the padding and MAC and update the state information */
1286 payloadLength = sessionInfoPtr->pendingPacketLength - \
1287 ( sshInfo->padLength + sessionInfoPtr->authBlocksize );
1288 sshInfo->readSeqNo++;
1289 ENSURES( payloadLength >= 0 && \
1290 payloadLength < sessionInfoPtr->pendingPacketLength + dataLength );
1291 /* Must be '<' rather than '<=' because of the stripped padding */
1292 DEBUG_PRINT(( "Read %s (%d) packet, length %d.\n",
1293 getSSHPacketName( sshInfo->packetType ),
1294 sshInfo->packetType, payloadLength ));
1295 DEBUG_DUMP_DATA( sessionInfoPtr->receiveBuffer + \
1296 sessionInfoPtr->receiveBufPos, payloadLength );
1297
1298 /* If it's not plain data (which was handled at the readHeaderFunction()
1299 stage), handle it as a control message */
1300 if( sshInfo->packetType != SSH_MSG_CHANNEL_DATA )
1301 {
1302 status = processControlMessage( sessionInfoPtr, payloadLength );
1303 if( cryptStatusError( status ) )
1304 {
1305 /* If we got an OK_SPECIAL status then the packet was handled
1306 internally and we can try again. If it was a message that
1307 the user has to respond to it's also not a fatal error
1308 condition and they can continue afterwards */
1309 if( status == OK_SPECIAL || status == CRYPT_ENVELOPE_RESOURCE )
1310 *readInfo = READINFO_NOOP;
1311 return( status );
1312 }
1313 }
1314 sshInfo->partialPacketDataLength = 0;
1315
1316 *readInfo = READINFO_NONE;
1317 return( payloadLength );
1318 }
1319
1320 /* Write data over the SSH link */
1321
1322 CHECK_RETVAL_LENGTH STDC_NONNULL_ARG( ( 1 ) ) \
preparePacketFunction(INOUT SESSION_INFO * sessionInfoPtr)1323 static int preparePacketFunction( INOUT SESSION_INFO *sessionInfoPtr )
1324 {
1325 SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
1326 STREAM stream;
1327 const int dataLength = sessionInfoPtr->sendBufPos - \
1328 ( SSH2_HEADER_SIZE + SSH2_PAYLOAD_HEADER_SIZE );
1329 int length DUMMY_INIT, status;
1330
1331 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
1332
1333 REQUIRES( !( sessionInfoPtr->flags & SESSION_SENDCLOSED ) );
1334 REQUIRES( dataLength > 0 && dataLength < sessionInfoPtr->sendBufPos && \
1335 dataLength < MAX_BUFFER_SIZE );
1336
1337 /* Wrap up the payload ready for sending:
1338
1339 byte SSH_MSG_CHANNEL_DATA
1340 uint32 channel_no
1341 string data
1342
1343 Since this is wrapping in-place data, we first open a write stream to
1344 add the header, then open a read stream covering the full buffer in
1345 preparation for wrapping the packet */
1346 status = openPacketStreamSSHEx( &stream, sessionInfoPtr,
1347 SSH2_PAYLOAD_HEADER_SIZE,
1348 SSH_MSG_CHANNEL_DATA );
1349 if( cryptStatusError( status ) )
1350 return( status );
1351 writeUint32( &stream, getCurrentChannelNo( sessionInfoPtr, \
1352 CHANNEL_WRITE ) );
1353 status = writeUint32( &stream, dataLength );
1354 sMemDisconnect( &stream );
1355 ENSURES( cryptStatusOK( status ) );
1356 sMemConnect( &stream, sessionInfoPtr->sendBuffer,
1357 sessionInfoPtr->sendBufSize );
1358 status = sSkip( &stream, SSH2_HEADER_SIZE + SSH2_PAYLOAD_HEADER_SIZE + \
1359 dataLength, SSKIP_MAX );
1360 if( cryptStatusOK( status ) )
1361 status = wrapPacketSSH2( sessionInfoPtr, &stream, 0, FALSE, FALSE );
1362 if( cryptStatusOK( status ) )
1363 length = stell( &stream );
1364 sMemDisconnect( &stream );
1365 if( cryptStatusError( status ) )
1366 return( status );
1367 INJECT_FAULT( SESSION_CORRUPT_DATA, SESSION_CORRUPT_DATA_SSH_1 );
1368
1369 /* If there's control data enqueued to be written, try and append it to
1370 the existing data to be sent. This may or may not append it
1371 (depending on whether there's room in the send buffer) so we may end
1372 up here more than once */
1373 if( sshInfo->response.type > 0 )
1374 {
1375 int length2;
1376
1377 status = length2 = appendChannelData( sessionInfoPtr, length );
1378 if( !cryptStatusError( status ) )
1379 length += length2;
1380 }
1381
1382 return( length );
1383 }
1384
1385 /* Close a previously-opened SSH session */
1386
1387 STDC_NONNULL_ARG( ( 1 ) ) \
shutdownFunction(INOUT SESSION_INFO * sessionInfoPtr)1388 static void shutdownFunction( INOUT SESSION_INFO *sessionInfoPtr )
1389 {
1390 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
1391
1392 /* If we haven't entered the secure state yet (i.e. we're still in the
1393 middle of the handshake) then this is an abnormal termination, send
1394 a disconnect indication:
1395
1396 byte SSH_MSG_DISCONNECT
1397 uint32 reason_code = SSH_DISCONNECT_PROTOCOL_ERROR
1398 string description = "Handshake failed"
1399 string language_tag = "" */
1400 if( !( sessionInfoPtr->flags & SESSION_ISSECURE_WRITE ) )
1401 {
1402 STREAM stream;
1403 int status;
1404
1405 status = openPacketStreamSSH( &stream, sessionInfoPtr,
1406 SSH_MSG_DISCONNECT );
1407 if( cryptStatusError( status ) )
1408 {
1409 sNetDisconnect( &sessionInfoPtr->stream );
1410 return;
1411 }
1412 writeUint32( &stream, SSH_DISCONNECT_PROTOCOL_ERROR );
1413 writeString32( &stream, "Handshake failed", 16 );
1414 status = writeUint32( &stream, 0 ); /* No language tag */
1415 if( cryptStatusOK( status ) )
1416 status = wrapPacketSSH2( sessionInfoPtr, &stream, 0,
1417 FALSE, TRUE );
1418 if( cryptStatusOK( status ) )
1419 {
1420 const int length = stell( &stream );
1421 void *dataPtr;
1422
1423 /* Since there's nothing much that we can do at this point in
1424 response to an error except continue and close the network
1425 session, we don't check for errors */
1426 status = sMemGetDataBlockAbs( &stream, 0, &dataPtr, length );
1427 if( cryptStatusOK( status ) )
1428 ( void ) sendCloseNotification( sessionInfoPtr, dataPtr,
1429 length );
1430 }
1431 sMemDisconnect( &stream );
1432 sNetDisconnect( &sessionInfoPtr->stream );
1433 return;
1434 }
1435
1436 /* Close all remaining channels. Since this is just a cleanup of a
1437 network session that's about to be closed anyway we ignore any errors
1438 that we encounter at this point (a typical error would be the link
1439 going down, in which case the only useful response is to take down
1440 the network session anyway) */
1441 ( void ) closeChannel( sessionInfoPtr, TRUE );
1442 }
1443
1444 /****************************************************************************
1445 * *
1446 * Session Access Routines *
1447 * *
1448 ****************************************************************************/
1449
1450 /* Set up access to the SSH session processing */
1451
1452 STDC_NONNULL_ARG( ( 1 ) ) \
initSSH2processing(INOUT SESSION_INFO * sessionInfoPtr)1453 void initSSH2processing( INOUT SESSION_INFO *sessionInfoPtr )
1454 {
1455 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
1456
1457 /* Set the access method pointers */
1458 FNPTR_SET( sessionInfoPtr->readHeaderFunction, readHeaderFunction );
1459 FNPTR_SET( sessionInfoPtr->processBodyFunction, processBodyFunction );
1460 FNPTR_SET( sessionInfoPtr->preparePacketFunction, preparePacketFunction );
1461 FNPTR_SET( sessionInfoPtr->shutdownFunction, shutdownFunction );
1462 }
1463 #endif /* USE_SSH */
1464