1 /****************************************************************************
2 *																			*
3 *						cryptlib SSH Session Management						*
4 *					   Copyright Peter Gutmann 1998-2013					*
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 #if defined( USE_SSH )
21 
22 /****************************************************************************
23 *																			*
24 *								Utility Functions							*
25 *																			*
26 ****************************************************************************/
27 
28 #if defined( __WIN32__ ) && !defined( NDEBUG )
29 
30 /* Dump a message to disk for diagnostic purposes.  Rather than using the
31    normal DEBUG_DUMP() macro we have to use a special-purpose function that
32    provides appropriate naming based on what we're processing */
33 
34 STDC_NONNULL_ARG( ( 1, 2 ) ) \
debugDumpSSH(const SESSION_INFO * sessionInfoPtr,IN_BUFFER (length)const void * buffer,IN_LENGTH_SHORT const int length,const BOOLEAN isRead)35 void debugDumpSSH( const SESSION_INFO *sessionInfoPtr,
36 				   IN_BUFFER( length ) const void *buffer,
37 				   IN_LENGTH_SHORT const int length,
38 				   const BOOLEAN isRead )
39 	{
40 	FILE *filePtr;
41 	static int messageCount = 1;
42 	const BYTE *bufPtr = buffer;
43 	const BOOLEAN encryptionActive = \
44 		( isRead && ( sessionInfoPtr->flags & SESSION_ISSECURE_READ ) ) || \
45 		( !isRead && ( sessionInfoPtr->flags & SESSION_ISSECURE_WRITE ) );
46 	const BOOLEAN isServerID = \
47 		( !encryptionActive && messageCount < 10 && length >= 4 && \
48 		  !memcmp( buffer, "SSH-", 4 ) ) ? TRUE : FALSE;
49 	char fileName[ 1024 + 8 ], *slashPtr;
50 
51 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
52 	assert( isReadPtr( buffer,  length ) );
53 
54 	if( messageCount > 20 )
55 		return;	/* Don't dump too many messages */
56 	strlcpy_s( fileName, 1024, "/tmp/" );
57 	sprintf_s( &fileName[ 5 ], 1024, "ssh%02d%c_", messageCount++,
58 			   isRead ? 'r' : 'w' );
59 	if( isServerID )
60 		{
61 		/* The initial server-ID messages don't have defined packet names */
62 		strlcat_s( fileName, 1024, "server_ID" );
63 		}
64 	else
65 		{
66 		if( !encryptionActive || isRead )
67 			{
68 			if( isRead && length > 1 )
69 				strlcat_s( fileName, 1024, getSSHPacketName( bufPtr[ 1 ] ) );
70 			else
71 				{
72 				if( !encryptionActive && length > 5 )
73 					{
74 					strlcat_s( fileName, 1024,
75 							   getSSHPacketName( bufPtr[ 5 ] ) );
76 					}
77 				else
78 					strlcat_s( fileName, 1024, "truncated_packet" );
79 				}
80 			}
81 		else
82 			strlcat_s( fileName, 1024, "encrypted_packet" );
83 		}
84 	slashPtr = strchr( fileName + 5, '/' );
85 	if( slashPtr != NULL )
86 		{
87 		/* Some packet names contain slashes */
88 		*slashPtr = '\0';
89 		}
90 	strlcat_s( fileName, 1024, ".dat" );
91 
92 #ifdef __STDC_LIB_EXT1__
93 	if( fopen_s( &filePtr, fileName, "wb" ) != 0 )
94 		filePtr = NULL;
95 #else
96 	filePtr = fopen( fileName, "wb" );
97 #endif /* __STDC_LIB_EXT1__ */
98 	if( filePtr == NULL )
99 		return;
100 	if( isRead && !isServerID )
101 		{
102 		STREAM stream;
103 		BYTE lengthBuffer[ UINT32_SIZE + 8 ];
104 
105 		/* The read code has stripped the length field at the start so we
106 		   have to reconstruct it and prepend it to the data being written */
107 		sMemOpen( &stream, lengthBuffer, UINT32_SIZE );
108 		writeUint32( &stream, length );
109 		sMemDisconnect( &stream );
110 		fwrite( lengthBuffer, 1, UINT32_SIZE, filePtr );
111 		}
112 	fwrite( buffer, 1, length, filePtr );
113 	fclose( filePtr );
114 	}
115 #endif /* Windows debug mode only */
116 
117 /* Initialise and destroy the handshake state information */
118 
119 STDC_NONNULL_ARG( ( 1 ) ) \
initHandshakeInfo(OUT SSH_HANDSHAKE_INFO * handshakeInfo)120 static void initHandshakeInfo( OUT SSH_HANDSHAKE_INFO *handshakeInfo )
121 	{
122 	assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
123 
124 	/* Initialise the handshake state information values */
125 	memset( handshakeInfo, 0, sizeof( SSH_HANDSHAKE_INFO ) );
126 	handshakeInfo->iExchangeHashContext = \
127 		handshakeInfo->iExchangeHashAltContext = \
128 			handshakeInfo->iServerCryptContext = CRYPT_ERROR;
129 	initHandshakeCrypt( handshakeInfo );
130 	}
131 
132 STDC_NONNULL_ARG( ( 1 ) ) \
destroyHandshakeInfo(INOUT SSH_HANDSHAKE_INFO * handshakeInfo)133 static void destroyHandshakeInfo( INOUT SSH_HANDSHAKE_INFO *handshakeInfo )
134 	{
135 	assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
136 
137 	/* Destroy any active contexts.  We need to do this here (even though
138 	   it's also done in the general session code) to provide a clean exit in
139 	   case the session activation fails, so that a second activation attempt
140 	   doesn't overwrite still-active contexts */
141 	if( handshakeInfo->iExchangeHashContext != CRYPT_ERROR )
142 		krnlSendNotifier( handshakeInfo->iExchangeHashContext,
143 						  IMESSAGE_DECREFCOUNT );
144 	if( handshakeInfo->iExchangeHashAltContext != CRYPT_ERROR )
145 		krnlSendNotifier( handshakeInfo->iExchangeHashAltContext,
146 						  IMESSAGE_DECREFCOUNT );
147 	if( handshakeInfo->iServerCryptContext != CRYPT_ERROR )
148 		krnlSendNotifier( handshakeInfo->iServerCryptContext,
149 						  IMESSAGE_DECREFCOUNT );
150 
151 	/* Clear the handshake state information, then reset it to explicit non-
152 	   initialised values */
153 	zeroise( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) );
154 	initHandshakeInfo( handshakeInfo );
155 	}
156 
157 /* Read the SSH version information string */
158 
159 CHECK_RETVAL_RANGE( 0, 255 ) STDC_NONNULL_ARG( ( 1 ) ) \
readCharFunction(INOUT TYPECAST (STREAM *)void * streamPtr)160 static int readCharFunction( INOUT TYPECAST( STREAM * ) void *streamPtr )
161 	{
162 	STREAM *stream = streamPtr;
163 	BYTE ch;
164 	int status;
165 
166 	assert( isWritePtr( stream, sizeof( STREAM ) ) );
167 
168 	status = sread( stream, &ch, 1 );
169 	return( cryptStatusError( status ) ? status : byteToInt( ch ) );
170 	}
171 
172 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
readVersionString(INOUT SESSION_INFO * sessionInfoPtr)173 static int readVersionString( INOUT SESSION_INFO *sessionInfoPtr )
174 	{
175 	const BYTE *versionStringPtr;
176 	const char *peerType = isServer( sessionInfoPtr ) ? "Client" : "Server";
177 	int versionStringLength, length DUMMY_INIT, linesRead;
178 	int status;
179 
180 	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
181 
182 	/* Read the server version information, with the format for the ID
183 	   string being "SSH-protocolversion-softwareversion comments", which
184 	   (in the original ssh.com interpretation) was "SSH-x.y-x.y vendorname"
185 	   (e.g. "SSH-2.0-3.0.0 SSH Secure Shell") but for almost everyone else
186 	   is "SSH-x.y-vendorname*version" (e.g "SSH-2.0-OpenSSH_3.0").
187 
188 	   This version information handling is rather ugly since it's a
189 	   variable-length string terminated with a newline, so we have to use
190 	   readTextLine() as if we were talking HTTP.
191 
192 	   Unfortunately the SSH RFC then further complicates this by allowing
193 	   implementations to send non-version-related text lines before the
194 	   version line.  The theory is that this will allow applications like
195 	   TCP wrappers to display a (human-readable) error message before
196 	   disconnecting, however some installations use it to display general
197 	   banners before the ID string.  Since the RFC doesn't provide any
198 	   means of distinguishing this banner information from arbitrary data
199 	   we can't quickly reject attempts to connect to something that isn't
200 	   an SSH server.  In other words we have to sit here waiting for
201 	   further data in the hope that eventually an SSH ID turns up, until
202 	   such time as the connect timeout expires */
203 	for( linesRead = 0; linesRead < 20; linesRead++ )
204 		{
205 		BOOLEAN isTextDataError;
206 
207 		/* Get a line of input.  Since this is the first communication that
208 		   we have with the remote system we're a bit more loquacious about
209 		   diagnostics in the event of an error */
210 		status = readTextLine( readCharFunction, &sessionInfoPtr->stream,
211 							   sessionInfoPtr->receiveBuffer,
212 							   SSH_ID_MAX_SIZE, &length, &isTextDataError,
213 							   FALSE );
214 		if( cryptStatusError( status ) )
215 			{
216 			const char *lcPeerType = isServer( sessionInfoPtr ) ? \
217 									 "client" : "server";
218 			ERROR_INFO errorInfo;	/* Lowercase version of peerType */
219 
220 			sNetGetErrorInfo( &sessionInfoPtr->stream, &errorInfo );
221 			retExtErr( status,
222 					   ( status, SESSION_ERRINFO, &errorInfo,
223 					     "Error reading %s's SSH identifier string: ",
224 						 lcPeerType ) );
225 			}
226 
227 		/* If it's the SSH ID/version string, we're done */
228 		if( length >= SSH_ID_SIZE && \
229 			!memcmp( sessionInfoPtr->receiveBuffer, SSH_ID, SSH_ID_SIZE ) )
230 			break;
231 		}
232 	DEBUG_DUMP_SSH( sessionInfoPtr->receiveBuffer, ( length < 1 ) ? 1 : length,
233 					TRUE );		/* Dummy length value if empty line sent */
234 
235 	/* The peer shouldn't be throwing infinite amounts of junk at us, if we
236 	   don't get an SSH ID after reading 20 lines of input then there's a
237 	   problem */
238 	if( linesRead >= 20 )
239 		{
240 		retExt( CRYPT_ERROR_OVERFLOW,
241 				( CRYPT_ERROR_OVERFLOW, SESSION_ERRINFO,
242 				  "%s sent excessive amounts of text without sending an "
243 				  "SSH identifier string", peerType ) );
244 		}
245 
246 	/* Make sure that we got enough data to work with.  We need at least
247 	   "SSH-" (ID, size SSH_ID_SIZE) + "x.y-" (protocol version) + "xxxxx"
248 	   (software version/ID, of which the shortest-known is "ConfD") */
249 	if( length < SSH_ID_SIZE + 9 || length > SSH_ID_MAX_SIZE )
250 		{
251 		retExt( CRYPT_ERROR_BADDATA,
252 				( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
253 				  "%s sent invalid-length identifier string '%s', total "
254 				  "length %d", peerType,
255 				  sanitiseString( sessionInfoPtr->receiveBuffer,
256 								  CRYPT_MAX_TEXTSIZE, length ),
257 				  length ) );
258 		}
259 
260 	/* Remember how much we've got and set a block of memory following the
261 	   string to zeroes in case of any slight range errors in the free-
262 	   format text-string checks that are required further on to identify
263 	   bugs in SSH implementations */
264 	memset( sessionInfoPtr->receiveBuffer + length, 0, 16 );
265 	sessionInfoPtr->receiveBufEnd = length;
266 
267 	/* Determine which version we're talking to */
268 	switch( sessionInfoPtr->receiveBuffer[ SSH_ID_SIZE ] )
269 		{
270 		case '1':
271 			if( !memcmp( sessionInfoPtr->receiveBuffer + SSH_ID_SIZE,
272 						 "1.99", 4 ) )
273 				{
274 				/* SSHv2 server in backwards-compatibility mode */
275 				sessionInfoPtr->version = 2;
276 				break;
277 				}
278 			retExt( CRYPT_ERROR_NOSECURE,
279 					( CRYPT_ERROR_NOSECURE, SESSION_ERRINFO,
280 					  "Server can only do SSHv1" ) );
281 
282 		case '2':
283 			sessionInfoPtr->version = 2;
284 			break;
285 
286 		default:
287 			retExt( CRYPT_ERROR_BADDATA,
288 					( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
289 					  "Invalid SSH version '%s'",
290 					  sanitiseString( &sessionInfoPtr->receiveBuffer[ SSH_ID_SIZE ],
291 									  CRYPT_MAX_TEXTSIZE, 1 ) ) );
292 		}
293 
294 	/* Find the end of the protocol version substring, i.e. locate whatever
295 	   follows the "SSH-x.y" portion of the ID string by searching for the
296 	   second '-' delimiter */
297 	for( versionStringLength = length - SSH_ID_SIZE, \
298 			versionStringPtr = sessionInfoPtr->receiveBuffer + SSH_ID_SIZE;
299 		 versionStringLength > 0 && *versionStringPtr != '-';
300 		 versionStringLength--, versionStringPtr++ );
301 	if( versionStringLength < 4 )
302 		{
303 		/* We need at least "-x.y" after the initial ID string, we can't
304 		   require any more than this because of CuteFTP (see note below) */
305 		retExt( CRYPT_ERROR_BADDATA,
306 				( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
307 				  "%s sent malformed identifier string '%s'", peerType,
308 				  sanitiseString( sessionInfoPtr->receiveBuffer,
309 								  CRYPT_MAX_TEXTSIZE, length ) ) );
310 		}
311 	versionStringPtr++, versionStringLength--;	/* Skip '-' */
312 	ENSURES( versionStringLength >= 3 && \
313 			 versionStringLength < SSH_ID_MAX_SIZE );	/* From earlier checks */
314 
315 	/* Check whether the peer is using cryptlib */
316 	if( versionStringLength >= 8 && \
317 		!memcmp( versionStringPtr, "cryptlib", 8 ) )
318 		sessionInfoPtr->flags |= SESSION_ISCRYPTLIB;
319 
320 	/* Check for various servers that require special-case bug workarounds.
321 	   The versions that we check for are:
322 
323 		BitVise WinSSHD:
324 			This one is a bit hard to identify because it's built on top of
325 			their SSH library which changes names from time to time, so for
326 			WinSSHD 4.x it was identified via the vendor ID string
327 			"sshlib: WinSSHD 4.yy" while for WinSSHD 5.x it was identified
328 			via the vendor ID string "FlowSsh: WinSSHD 5.xx".  In theory we
329 			could handle this by skipping the library name and looking
330 			further inside the string for the "WinSSHD" identifier, but then
331 			there's another version that uses "SrSshServer" instead of
332 			"WinSSHD", and there's also a "GlobalScape" ID used by CuteFTP
333 			(which means that CuteFTP might have finally fixed their buggy
334 			implementation of SSH by using someone else's).  As a result we
335 			can see any of "sshlib: <vendor>" or "FlowSsh: <vendor>", which
336 			we use as the identifier.
337 
338 			Sends mismatched compression algorithm IDs, no compression
339 			client -> server, zlib server -> client, but works fine if no
340 			compression is selected, for versions 4.x and up.
341 
342 		CuteFTP:
343 			Drops the connection after seeing the server hello with no
344 			(usable) error indication.  This implementation is somewhat
345 			tricky to detect since it identifies itself using the dubious
346 			vendor ID string "1.0" (see the ssh.com note below), this
347 			problem still hasn't been fixed several years after the vendor
348 			was notified of it, indicating that it's unlikely to ever be
349 			fixed.  This runs into problems with other implementations like
350 			BitVise WinSSHD 5.x, which has an ID string beginning with "1.0"
351 			(see the comment for WinSSHD above) so when trying to identify
352 			CuteFTP we check for an exact match for "1.0" as the ID string.
353 
354 			CuteFTP also uses the SSHv1 backwards-compatible version string
355 			"1.99" even though it can't actually do SSHv1, which means that
356 			it'll fail if it ever tries to connect to an SSHv1 peer.
357 
358 		OpenSSH:
359 			Omits hashing the exchange hash length when creating the hash
360 			to be signed for client auth for version 2.0 (all subversions).
361 
362 			Requires RSA signatures to be padded out with zeroes to the RSA
363 			modulus size for all versions from 2.5 to 3.2.
364 
365 			Can't handle "password" as a PAM sub-method (meaning an
366 			authentication method hint), it responds with an authentication-
367 			failed response as soon as we send the PAM authentication
368 			request, for versions 3.8 onwards (this doesn't look like it'll
369 			get fixed any time soon so we enable it for all newer versions
370 			until further notice).
371 
372 		Putty:
373 			Sends zero-length SSH_MSG_IGNORE messages for version 0.59.
374 
375 		ssh.com:
376 			This implementation puts the version number first so if we find
377 			something without a vendor name at the start we treat it as an
378 			ssh.com version.  However, Van Dyke's SSH server VShell also
379 			uses the ssh.com-style identification (fronti nulla fides) so
380 			when we check for the ssh.com implementation we habe to make
381 			sure that it isn't really VShell.  In addition CuteFTP
382 			advertises its implementation as "1.0" (without any vendor
383 			name), which is going to cause problems in the future when they
384 			move to 2.x.
385 
386 			Omits the DH-derived shared secret when hashing the keying
387 			material for versions identified as "2.0.0" (all
388 			sub-versions) and "2.0.10".
389 
390 			Uses an SSH2_FIXED_KEY_SIZE-sized key for HMAC instead of the de
391 			facto 160 bits for versions identified as "2.0.", "2.1 ", "2.1.",
392 			and "2.2." (i.e. all sub-versions of 2.0, 2.1, and 2.2), and
393 			specifically version "2.3.0".  This was fixed in 2.3.1.
394 
395 			Omits the signature algorithm name for versions identified as
396 			"2.0" and "2.1" (all sub-versions), requiring a complex rewrite
397 			of the signature data in order to process it.
398 
399 			Mishandles large window sizes in a variety of ways.  Typically
400 			for any size over about 8M the server gets slower and slower,
401 			eventually more or less grinding to halt at about 64MB
402 			(presumably some O(n^2) algorithm, although how you manage to
403 			do this for a window-size notification is a mystery).  Some
404 			versions also reportedly require a window adjust for every 32K
405 			or so sent no matter what the actual window size is, which seems
406 			to occur for versions identified as "2.0" and "2.1" (all
407 			sub-versions).  This may be just a variant of the general mis-
408 			handling of large window sizes so we treat it as the same thing
409 			and advertise a smaller-than-optimal window which, as a side-
410 			effect, results in a constant flow of window adjusts.
411 
412 			Omits hashing the exchange hash length when creating the hash
413 			to be signed for client auth for versions 2.1 and 2.2 (all
414 			subversions).
415 
416 			Sends an empty SSH_SERVICE_ACCEPT response for version 2.0 (all
417 			subversions).
418 
419 			Sends an empty userauth-failure response if no authentication is
420 			required instead of allowing the auth, for uncertain versions
421 			probably in the 2.x range.
422 
423 			Dumps text diagnostics (that is, raw text strings rather than
424 			SSH error packets) onto the connection if something unexpected
425 			occurs, for uncertain versions probably in the 2.x range.
426 
427 		Van Dyke:
428 			Omits hashing the exchange hash length when creating the hash to
429 			be signed for client auth for version 3.0 (SecureCRT = SSH) and
430 			1.7 (SecureFX = SFTP).
431 
432 		WeOnlyDo:
433 			Has the same mismatched compression algorithm ID bug as BitVise
434 			WinSSHD (see comment above) for unknown versions above about 2.x.
435 
436 	   Further quirks and peculiarities abound, some are handled automatically
437 	   by workarounds in the code and for the rest they're fortunately rare
438 	   enough (mostly for long-obsolete SSHv1 versions) that we don't have to
439 	   go out of our way to handle them */
440 	if( versionStringLength >= 8 + 3 && \
441 		!memcmp( versionStringPtr, "OpenSSH_", 8 ) )
442 		{
443 		const BYTE *subVersionStringPtr = versionStringPtr + 8;
444 
445 		if( !memcmp( subVersionStringPtr, "2.0", 3 ) )
446 			sessionInfoPtr->protocolFlags |= SSH_PFLAG_NOHASHLENGTH;
447 		if( !memcmp( subVersionStringPtr, "3.8", 3 ) || \
448 			!memcmp( subVersionStringPtr, "3.9", 3 ) || \
449 			( versionStringLength >= 8 + 4 && \
450 			  !memcmp( subVersionStringPtr, "3.10", 4 ) ) || \
451 			*subVersionStringPtr >= '4' )
452 			sessionInfoPtr->protocolFlags |= SSH_PFLAG_PAMPW;
453 		if( ( !memcmp( subVersionStringPtr, "2.", 2 ) && \
454 			  subVersionStringPtr[ 2 ] >= '5' ) || \
455 			( !memcmp( subVersionStringPtr, "3.", 2 ) && \
456 			  subVersionStringPtr[ 2 ] <= '2' ) )
457 			sessionInfoPtr->protocolFlags |= SSH_PFLAG_RSASIGPAD;
458 
459 		return( CRYPT_OK );
460 		}
461 	if( versionStringLength >= 14 + 4 && \
462 		!memcmp( versionStringPtr, "PuTTY_Release_", 14 ) )
463 		{
464 		const BYTE *subVersionStringPtr = versionStringPtr + 14;
465 
466 		if( !memcmp( subVersionStringPtr, "0.59", 4 ) )
467 			sessionInfoPtr->protocolFlags |= SSH_PFLAG_ZEROLENIGNORE;
468 
469 		return( CRYPT_OK );
470 		}
471 	if( versionStringLength >= 9 && \
472 		!memcmp( versionStringPtr, "WeOnlyDo ", 9 ) )
473 		{
474 		const BYTE *subVersionStringPtr = versionStringPtr + 9;
475 
476 		if( subVersionStringPtr[ 0 ] >= '2' )
477 			sessionInfoPtr->protocolFlags |= SSH_PFLAG_ASYMMCOPR;
478 		return( CRYPT_OK );
479 		}
480 	if( isDigit( *versionStringPtr ) )
481 		{
482 		const BYTE *vendorIDString;
483 		const int versionDigit = byteToInt( *versionStringPtr );
484 		int vendorIDStringLength;
485 
486 		/* Look for a vendor ID after the version information.  This breaks
487 		   down the string "[SSH-x.y-]x.yy vendor-text" to
488 		   'versionStringPtr = "x.yy"' and 'vendorIDString = "vendor-text"' */
489 		for( vendorIDStringLength = versionStringLength, \
490 				vendorIDString = versionStringPtr;
491 			 vendorIDStringLength > 0 && *vendorIDString != ' ';
492 			 vendorIDStringLength--, vendorIDString++ );
493 		if( vendorIDStringLength > 1 )
494 			{
495 			/* There's a vendor ID present, skip the ' ' separator */
496 			vendorIDString++, vendorIDStringLength--;
497 			}
498 		ENSURES( vendorIDStringLength >= 0 && \
499 				 vendorIDStringLength < SSH_ID_MAX_SIZE );
500 		switch( versionDigit )
501 			{
502 			case '1':
503 				if( versionStringLength >= 12 && \
504 					!memcmp( versionStringPtr, "1.7 SecureFX", 12 ) )
505 					sessionInfoPtr->protocolFlags |= SSH_PFLAG_NOHASHLENGTH;
506 				if( versionStringLength == 3 && \
507 					!memcmp( versionStringPtr, "1.0", 3 ) )
508 					sessionInfoPtr->protocolFlags |= SSH_PFLAG_CUTEFTP;
509 				if( ( vendorIDStringLength > 8 && \
510 					!memcmp( vendorIDString, "sshlib: ", 8 ) ) || \
511 					( vendorIDStringLength > 9 && \
512 					!memcmp( vendorIDString, "FlowSsh: ", 9 ) ) )
513 					sessionInfoPtr->protocolFlags |= SSH_PFLAG_ASYMMCOPR;
514 				break;
515 
516 			case '2':
517 				if( vendorIDStringLength >= 6 && \
518 					!memcmp( vendorIDString, "VShell", 6 ) )
519 					break;	/* Make sure that it isn't VShell */
520 
521 				/* ssh.com 2.x versions have quite a number of bugs so we
522 				   check for them as a group */
523 				if( ( versionStringLength >= 5 && \
524 					  !memcmp( versionStringPtr, "2.0.0", 5 ) ) || \
525 					( versionStringLength >= 6 && \
526 					  !memcmp( versionStringPtr, "2.0.10", 6 ) ) )
527 					sessionInfoPtr->protocolFlags |= SSH_PFLAG_NOHASHSECRET;
528 				if( !memcmp( versionStringPtr, "2.0", 3 ) || \
529 					!memcmp( versionStringPtr, "2.1", 3 ) )
530 					sessionInfoPtr->protocolFlags |= SSH_PFLAG_SIGFORMAT;
531 				if( !memcmp( versionStringPtr, "2.0", 3 ) || \
532 					!memcmp( versionStringPtr, "2.1", 3 ) )
533 					sessionInfoPtr->protocolFlags |= SSH_PFLAG_WINDOWSIZE;
534 				if( !memcmp( versionStringPtr, "2.1", 3 ) || \
535 					!memcmp( versionStringPtr, "2.2", 3 ) )
536 					sessionInfoPtr->protocolFlags |= SSH_PFLAG_NOHASHLENGTH;
537 				if( !memcmp( versionStringPtr, "2.0", 3 ) || \
538 					!memcmp( versionStringPtr, "2.1", 3 ) || \
539 					!memcmp( versionStringPtr, "2.2", 3 ) || \
540 					( versionStringLength >= 5 && \
541 					  !memcmp( versionStringPtr, "2.3.0", 5 ) ) )
542 					sessionInfoPtr->protocolFlags |= SSH_PFLAG_HMACKEYSIZE;
543 				if( !memcmp( versionStringPtr, "2.0", 3 ) )
544 					sessionInfoPtr->protocolFlags |= SSH_PFLAG_EMPTYSVCACCEPT;
545 				if( !memcmp( versionStringPtr, "2.", 2 ) )
546 					{
547 					/* Not sure of the exact versions where this occurs */
548 					sessionInfoPtr->protocolFlags |= SSH_PFLAG_EMPTYUSERAUTH;
549 					sessionInfoPtr->protocolFlags |= SSH_PFLAG_TEXTDIAGS;
550 					}
551 				break;
552 
553 			case '3':
554 				if( versionStringLength >= 13 && \
555 					!memcmp( versionStringPtr, "3.0 SecureCRT", 13 ) )
556 					sessionInfoPtr->protocolFlags |= SSH_PFLAG_NOHASHLENGTH;
557 				break;
558 
559 			case '5':
560 				if( versionStringLength >= 10 && \
561 					!memcmp( vendorIDString, "SSH Tectia", 10 ) )
562 					sessionInfoPtr->protocolFlags |= SSH_PFLAG_DUMMYUSERAUTH;
563 			}
564 		}
565 
566 	return( CRYPT_OK );
567 	}
568 
569 /****************************************************************************
570 *																			*
571 *								Init/Shutdown Functions						*
572 *																			*
573 ****************************************************************************/
574 
575 /* Connect to an SSH server */
576 
577 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
initVersion(INOUT SESSION_INFO * sessionInfoPtr,INOUT SSH_HANDSHAKE_INFO * handshakeInfo)578 static int initVersion( INOUT SESSION_INFO *sessionInfoPtr,
579 						INOUT SSH_HANDSHAKE_INFO *handshakeInfo )
580 	{
581 	MESSAGE_CREATEOBJECT_INFO createInfo;
582 	int status;
583 
584 	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
585 	assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
586 
587 	/* Set up handshake function pointers based on the protocol version */
588 	status = readVersionString( sessionInfoPtr );
589 	if( cryptStatusError( status ) )
590 		return( status );
591 
592 	/* SSHv2 hashes parts of the handshake messages for integrity-protection
593 	   purposes so we create a context for the hash.  In addition since the
594 	   handshake can retroactively switch to a different hash algorithm mid-
595 	   exchange we have to speculatively hash the messages with SHA2 as well
596 	   as SHA1 in case the other side decides to switch */
597 	setMessageCreateObjectInfo( &createInfo, CRYPT_ALGO_SHA1 );
598 	status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_CREATEOBJECT,
599 							  &createInfo, OBJECT_TYPE_CONTEXT );
600 	if( cryptStatusError( status ) )
601 		return( status );
602 	handshakeInfo->iExchangeHashContext = createInfo.cryptHandle;
603 	if( algoAvailable( CRYPT_ALGO_SHA2 ) )
604 		{
605 		setMessageCreateObjectInfo( &createInfo, CRYPT_ALGO_SHA2 );
606 		status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_CREATEOBJECT,
607 								  &createInfo, OBJECT_TYPE_CONTEXT );
608 		if( cryptStatusError( status ) )
609 			{
610 			krnlSendNotifier( handshakeInfo->iExchangeHashContext,
611 							  IMESSAGE_DECREFCOUNT );
612 			handshakeInfo->iExchangeHashContext = CRYPT_ERROR;
613 			return( status );
614 			}
615 		handshakeInfo->iExchangeHashAltContext = createInfo.cryptHandle;
616 		}
617 
618 	return( CRYPT_OK );
619 	}
620 
621 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
completeHandshake(INOUT SESSION_INFO * sessionInfoPtr,INOUT SSH_HANDSHAKE_INFO * handshakeInfo)622 static int completeHandshake( INOUT SESSION_INFO *sessionInfoPtr,
623 							  INOUT SSH_HANDSHAKE_INFO *handshakeInfo )
624 	{
625 	const SSH_HANDSHAKE_FUNCTION handshakeFunction = \
626 						FNPTR_GET( handshakeInfo->completeHandshake );
627 	const SES_SHUTDOWN_FUNCTION shutdownFunction = \
628 						FNPTR_GET( sessionInfoPtr->shutdownFunction );
629 	int status;
630 
631 	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
632 	assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
633 
634 	REQUIRES( handshakeFunction != NULL );
635 	REQUIRES( shutdownFunction != NULL );
636 
637 	status = handshakeFunction( sessionInfoPtr, handshakeInfo );
638 	destroyHandshakeInfo( handshakeInfo );
639 	if( cryptStatusError( status ) )
640 		{
641 		/* If we need confirmation from the user before continuing, let
642 		   them know */
643 		if( status == CRYPT_ENVELOPE_RESOURCE )
644 			return( status );
645 
646 		/* At this point we could be in the secure state so we have to
647 		   keep the security information around until after we've called
648 		   the shutdown function, which could require sending secured
649 		   data */
650 		disableErrorReporting( sessionInfoPtr );
651 		shutdownFunction( sessionInfoPtr );
652 		destroySecurityContextsSSH( sessionInfoPtr );
653 		return( status );
654 		}
655 
656 	return( CRYPT_OK );
657 	}
658 
659 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
completeStartup(INOUT SESSION_INFO * sessionInfoPtr)660 static int completeStartup( INOUT SESSION_INFO *sessionInfoPtr )
661 	{
662 	const SES_SHUTDOWN_FUNCTION shutdownFunction = \
663 				FNPTR_GET( sessionInfoPtr->shutdownFunction );
664 	SSH_HANDSHAKE_FUNCTION handshakeFunction;
665 	SSH_HANDSHAKE_INFO handshakeInfo;
666 	int status;
667 
668 	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
669 
670 	REQUIRES( shutdownFunction != NULL );
671 
672 	/* Initialise the handshake information and begin the handshake.  Since
673 	   we don't know what type of peer we're talking to and since the
674 	   protocols aren't compatible in anything but name we have to peek at
675 	   the peer's initial communication and, in initVersion(), redirect
676 	   function pointers based on that */
677 	initHandshakeInfo( &handshakeInfo );
678 	if( isServer( sessionInfoPtr ) )
679 		initSSH2serverProcessing( &handshakeInfo );
680 	else
681 		initSSH2clientProcessing( &handshakeInfo );
682 	status = initVersion( sessionInfoPtr, &handshakeInfo );
683 	if( cryptStatusOK( status ) )
684 		{
685 		handshakeFunction = FNPTR_GET( handshakeInfo.beginHandshake );
686 		ENSURES( handshakeFunction != NULL );
687 		status = handshakeFunction( sessionInfoPtr, &handshakeInfo );
688 		}
689 	if( cryptStatusError( status ) )
690 		{
691 		/* If we run into an error at this point we need to disable error-
692 		   reporting during the shutdown phase since we've already got
693 		   status information present from the already-encountered error */
694 		destroyHandshakeInfo( &handshakeInfo );
695 		disableErrorReporting( sessionInfoPtr );
696 		shutdownFunction( sessionInfoPtr );
697 		return( status );
698 		}
699 
700 	/* Exchange a key with the server */
701 	handshakeFunction = FNPTR_GET( handshakeInfo.exchangeKeys );
702 	ENSURES( handshakeFunction != NULL );
703 	status = handshakeFunction( sessionInfoPtr, &handshakeInfo );
704 	if( cryptStatusError( status ) )
705 		{
706 		destroySecurityContextsSSH( sessionInfoPtr );
707 		destroyHandshakeInfo( &handshakeInfo );
708 		disableErrorReporting( sessionInfoPtr );
709 		shutdownFunction( sessionInfoPtr );
710 		return( status );
711 		}
712 
713 	/* Complete the handshake */
714 	return( completeHandshake( sessionInfoPtr, &handshakeInfo ) );
715 	}
716 
717 /* Start an SSH server */
718 
719 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
serverStartup(INOUT SESSION_INFO * sessionInfoPtr)720 static int serverStartup( INOUT SESSION_INFO *sessionInfoPtr )
721 	{
722 	const char *idString = SSH_ID_STRING "\r\n";
723 	const int idStringLen = SSH_ID_STRING_SIZE + 2;
724 	int status;
725 
726 	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
727 
728 	/* If we're completing a handshake that was interrupted while we got
729 	   confirmation of the client auth, skip the initial handshake stages
730 	   and go straight to the handshake completion stage */
731 	if( sessionInfoPtr->flags & SESSION_PARTIALOPEN )
732 		{
733 		SSH_HANDSHAKE_INFO handshakeInfo;
734 
735 		initHandshakeInfo( &handshakeInfo );
736 		initSSH2serverProcessing( &handshakeInfo );
737 		return( completeHandshake( sessionInfoPtr, &handshakeInfo ) );
738 		}
739 
740 	/* Send the ID string to the client before we continue with the
741 	   handshake.  We don't have to wait for any input from the client since
742 	   we know that if we got here there's a client listening.  Note that
743 	   standard cryptlib practice for sessions is to wait for input from the
744 	   client, make sure that it looks reasonable, and only then send back a
745 	   reply of any kind.  If anything that doesn't look right arrives, we
746 	   close the connection immediately without any response.  Unfortunately
747 	   this isn't possible with SSH, which requires that the server send data
748 	   before the client does */
749 	status = swrite( &sessionInfoPtr->stream, idString, idStringLen );
750 	if( cryptStatusError( status ) )
751 		return( status );
752 
753 	/* Complete the handshake in the shared code */
754 	return( completeStartup( sessionInfoPtr ) );
755 	}
756 
757 /****************************************************************************
758 *																			*
759 *						Control Information Management Functions			*
760 *																			*
761 ****************************************************************************/
762 
763 #ifdef USE_SSH_EXTENDED
764 
765 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
getAttributeFunction(INOUT SESSION_INFO * sessionInfoPtr,OUT void * data,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE type)766 static int getAttributeFunction( INOUT SESSION_INFO *sessionInfoPtr,
767 								 OUT void *data,
768 								 IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE type )
769 	{
770 	int status;
771 
772 	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
773 
774 	REQUIRES( type == CRYPT_SESSINFO_SSH_CHANNEL ||\
775 			  type == CRYPT_SESSINFO_SSH_CHANNEL_TYPE || \
776 			  type == CRYPT_SESSINFO_SSH_CHANNEL_ARG1 || \
777 			  type == CRYPT_SESSINFO_SSH_CHANNEL_ARG2 || \
778 			  type == CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE );
779 
780 	if( type == CRYPT_SESSINFO_SSH_CHANNEL || \
781 		type == CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE )
782 		{
783 		status = getChannelAttribute( sessionInfoPtr, type, data );
784 		}
785 	else
786 		{
787 		MESSAGE_DATA *msgData = data;
788 
789 		status = getChannelAttributeS( sessionInfoPtr, type, msgData->data,
790 									   msgData->length, &msgData->length );
791 		}
792 	return( ( status == CRYPT_ERROR ) ? CRYPT_ARGERROR_NUM1 : status );
793 	}
794 
795 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
setAttributeFunction(INOUT SESSION_INFO * sessionInfoPtr,IN const void * data,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE type)796 static int setAttributeFunction( INOUT SESSION_INFO *sessionInfoPtr,
797 								 IN const void *data,
798 								 IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE type )
799 	{
800 	int value DUMMY_INIT, status;
801 
802 	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
803 	assert( isReadPtr( data, sizeof( int ) ) );
804 
805 	REQUIRES( type == CRYPT_SESSINFO_SSH_CHANNEL || \
806 			  type == CRYPT_SESSINFO_SSH_CHANNEL_TYPE || \
807 			  type == CRYPT_SESSINFO_SSH_CHANNEL_ARG1 || \
808 			  type == CRYPT_SESSINFO_SSH_CHANNEL_ARG2 || \
809 			  type == CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE );
810 
811 	/* Get the data value if it's an integer parameter */
812 	if( type == CRYPT_SESSINFO_SSH_CHANNEL || \
813 		type == CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE )
814 		value = *( ( int * ) data );
815 
816 	/* If we're selecting a channel and there's unwritten data from a
817 	   previous write still in the buffer, we can't change the write
818 	   channel */
819 	if( type == CRYPT_SESSINFO_SSH_CHANNEL && sessionInfoPtr->partialWrite )
820 		return( CRYPT_ERROR_INCOMPLETE );
821 
822 	/* If we're creating a new channel by setting the value to CRYPT_UNUSED,
823 	   create the new channel */
824 	if( type == CRYPT_SESSINFO_SSH_CHANNEL && value == CRYPT_UNUSED )
825 		{
826 		/* If the session hasn't been activated yet, we can only create a
827 		   single channel during session activation, any subsequent ones
828 		   have to be handled later */
829 		if( !( sessionInfoPtr->flags & SESSION_ISOPEN ) && \
830 			getCurrentChannelNo( sessionInfoPtr, \
831 								 CHANNEL_READ ) != UNUSED_CHANNEL_NO )
832 			return( CRYPT_ERROR_INITED );
833 
834 		return( createChannel( sessionInfoPtr ) );
835 		}
836 
837 	/* If we 're setting the channel-active attribute, this implicitly
838 	   activates or deactivates the channel rather than setting any
839 	   attribute value */
840 	if( type == CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE )
841 		{
842 		if( value )
843 			return( sendChannelOpen( sessionInfoPtr ) );
844 		return( closeChannel( sessionInfoPtr, FALSE ) );
845 		}
846 
847 	if( type == CRYPT_SESSINFO_SSH_CHANNEL )
848 		status = setChannelAttribute( sessionInfoPtr, type, value );
849 	else
850 		{
851 		const MESSAGE_DATA *msgData = data;
852 
853 		status = setChannelAttributeS( sessionInfoPtr, type, msgData->data,
854 									   msgData->length );
855 		}
856 	return( ( status == CRYPT_ERROR ) ? CRYPT_ARGERROR_NUM1 : status );
857 	}
858 #endif /* USE_SSH_EXTENDED */
859 
860 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
checkAttributeFunction(SESSION_INFO * sessionInfoPtr,IN const void * data,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE type)861 static int checkAttributeFunction( SESSION_INFO *sessionInfoPtr,
862 								   IN const void *data,
863 								   IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE type )
864 	{
865 	const CRYPT_CONTEXT cryptContext = *( ( CRYPT_CONTEXT * ) data );
866 	MESSAGE_DATA msgData;
867 	HASH_FUNCTION_ATOMIC hashFunctionAtomic;
868 	STREAM stream;
869 	BYTE buffer[ 128 + ( CRYPT_MAX_PKCSIZE * 4 ) + 8 ];
870 	BYTE fingerPrint[ CRYPT_MAX_HASHSIZE + 8 ];
871 	void *blobData DUMMY_INIT_PTR;
872 	int blobDataLength DUMMY_INIT, hashSize, pkcAlgo, status;
873 
874 	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
875 
876 	REQUIRES( isAttribute( type ) );
877 
878 	if( type != CRYPT_SESSINFO_PRIVATEKEY )
879 		return( CRYPT_OK );
880 
881 	/* If it's an ECC key then it has to be one of NIST { P256, P384, P521 }.
882 	   Unfortunately there's no easy way to determine whether the curve
883 	   being used is an SSH-compatible one or not since the user could load
884 	   their own custom 256-bit curve, or conversely load a known NIST curve
885 	   as a series of discrete key parameters, for now we just assume that a
886 	   curve of the given size is the correct one */
887 	status = krnlSendMessage( cryptContext, IMESSAGE_GETATTRIBUTE,
888 							  &pkcAlgo, CRYPT_CTXINFO_ALGO );
889 	if( cryptStatusError( status ) )
890 		return( status );
891 	if( isEccAlgo( pkcAlgo ) )
892 		{
893 		int keySize;
894 
895 		status = krnlSendMessage( cryptContext, IMESSAGE_GETATTRIBUTE,
896 								  &keySize, CRYPT_CTXINFO_KEYSIZE );
897 		if( cryptStatusError( status ) )
898 			return( status );
899 		if( keySize != bitsToBytes( 256 ) && \
900 			keySize != bitsToBytes( 384 ) && \
901 			keySize != bitsToBytes( 521 ) )
902 			return( CRYPT_ARGERROR_NUM1 );
903 		}
904 
905 	/* Only the server key has a fingerprint */
906 	if( !isServer( sessionInfoPtr ) )
907 		return( CRYPT_OK );
908 
909 	getHashAtomicParameters( CRYPT_ALGO_MD5, 0, &hashFunctionAtomic,
910 							 &hashSize );
911 
912 	/* The fingerprint is computed from the "key blob", which is different
913 	   from the server key.  The server key is the full key while the "key
914 	   blob" is only the raw key components (e, n for RSA, p, q, g, y for
915 	   DSA) so we have to skip the key header before we hash the key data:
916 
917 		uint32		length
918 			string	algorithm
919 			byte[]	key_blob
920 
921 	   Note that, as with the old PGP 2.x key hash mechanism, this allows
922 	   key spoofing (although it isn't quite as bad as the PGP 2.x key
923 	   fingerprint mechanism) since it doesn't hash an indication of the key
924 	   type or format */
925 	setMessageData( &msgData, buffer, 128 + ( CRYPT_MAX_PKCSIZE * 4 ) );
926 	status = krnlSendMessage( cryptContext, IMESSAGE_GETATTRIBUTE_S,
927 							  &msgData, CRYPT_IATTRIBUTE_KEY_SSH );
928 	if( cryptStatusError( status ) )
929 		return( status );
930 	sMemConnect( &stream, buffer, msgData.length );
931 	readUint32( &stream );					/* Length */
932 	status = readUniversal32( &stream );	/* Algorithm ID */
933 	if( cryptStatusOK( status ) )
934 		status = sMemGetDataBlockRemaining( &stream, &blobData,
935 											&blobDataLength );
936 	sMemDisconnect( &stream );
937 	if( cryptStatusError( status ) )
938 		return( status );
939 	hashFunctionAtomic( fingerPrint, CRYPT_MAX_HASHSIZE, blobData,
940 						blobDataLength );
941 
942 	/* Add the fingerprint */
943 	return( addSessionInfoS( &sessionInfoPtr->attributeList,
944 							 CRYPT_SESSINFO_SERVER_FINGERPRINT_SHA1,
945 							 fingerPrint, hashSize ) );
946 	}
947 
948 /****************************************************************************
949 *																			*
950 *							Session Access Routines							*
951 *																			*
952 ****************************************************************************/
953 
954 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
setAccessMethodSSH(INOUT SESSION_INFO * sessionInfoPtr)955 int setAccessMethodSSH( INOUT SESSION_INFO *sessionInfoPtr )
956 	{
957 	static const PROTOCOL_INFO protocolInfo = {
958 		/* General session information */
959 		FALSE,						/* Request-response protocol */
960 		SESSION_NONE,				/* Flags */
961 		SSH_PORT,					/* SSH port */
962 		SESSION_NEEDS_USERID |		/* Client attributes */
963 			SESSION_NEEDS_PASSWORD | \
964 			SESSION_NEEDS_KEYORPASSWORD | \
965 			SESSION_NEEDS_PRIVKEYSIGN,
966 				/* The client private key is optional, but if present it has
967 				   to be signature-capable */
968 		SESSION_NEEDS_PRIVATEKEY |	/* Server attributes */
969 			SESSION_NEEDS_PRIVKEYSIGN,
970 		2, 2, 2,					/* Version 2 */
971 
972 		/* Protocol-specific information */
973 		EXTRA_PACKET_SIZE + \
974 			DEFAULT_PACKET_SIZE,	/* Send/receive buffer size */
975 		SSH2_HEADER_SIZE + \
976 			SSH2_PAYLOAD_HEADER_SIZE,/* Payload data start */
977 		DEFAULT_PACKET_SIZE			/* (Default) maximum packet size */
978 		};
979 
980 	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
981 
982 	/* Set the access method pointers */
983 	sessionInfoPtr->protocolInfo = &protocolInfo;
984 	if( isServer( sessionInfoPtr ) )
985 		{
986 		FNPTR_SET( sessionInfoPtr->transactFunction, serverStartup );
987 		}
988 	else
989 		{
990 		FNPTR_SET( sessionInfoPtr->transactFunction, completeStartup );
991 		}
992 	initSSH2processing( sessionInfoPtr );
993 #ifdef USE_SSH_EXTENDED
994 	FNPTR_SET( sessionInfoPtr->getAttributeFunction, getAttributeFunction );
995 	FNPTR_SET( sessionInfoPtr->setAttributeFunction, setAttributeFunction );
996 #endif /* USE_SSH_EXTENDED */
997 	FNPTR_SET( sessionInfoPtr->checkAttributeFunction, checkAttributeFunction );
998 
999 	return( CRYPT_OK );
1000 	}
1001 #endif /* USE_SSH */
1002