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