1 /****************************************************************************
2 *																			*
3 *						cryptlib SSHv2 Channel Management					*
4 *						Copyright Peter Gutmann 1998-2008					*
5 *																			*
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9   #include "crypt.h"
10   #include "misc_rw.h"
11   #include "session.h"
12   #include "ssh.h"
13 #else
14   #include "crypt.h"
15   #include "enc_dec/misc_rw.h"
16   #include "session/session.h"
17   #include "session/ssh.h"
18 #endif /* Compiler-specific includes */
19 
20 /* Channel flags */
21 
22 #define CHANNEL_FLAG_NONE		0x00	/* No channel flag */
23 #define CHANNEL_FLAG_ACTIVE		0x01	/* Channel is active */
24 #define CHANNEL_FLAG_WRITECLOSED 0x02	/* Write-side of ch.closed */
25 
26 /* Per-channel information.  SSH channel IDs are 32-bit/4 byte data values
27    and can be reused during sessions so we provide our own guaranteed-unique
28    short int ID for users to identify a particular channel.  Since each
29    channel can have its own distinct characteristics we have to record
30    information like the window size and count and packet size information on
31    a per-channel basis.  In addition if the channel is tied to a forwarded
32    port we also record port-forwarding information in the generic channel-
33    type and channel-type-argument strings */
34 
35 typedef struct {
36 	/* General channel information.  The read and write channel numbers are
37 	   the same for everything but Cisco software */
38 	int channelID;						/* cryptlib-level channel ID */
39 	long readChannelNo, writeChannelNo;	/* SSH-level channel ID */
40 	int flags;							/* Channel flags */
41 
42 	/* External interface information */
43 	CRYPT_ATTRIBUTE_TYPE cursorPos;		/* Virtual cursor position */
44 
45 	/* Channel parameters */
46 	int windowCount, windowSize;		/* Current window usage and tot.size */
47 	int maxPacketSize;					/* Max allowed packet size */
48 
49 	/* Channel naming information */
50 	BUFFER( CRYPT_MAX_TEXTSIZE, typeLen ) \
51 	char type[ CRYPT_MAX_TEXTSIZE + 8 ];
52 	BUFFER( CRYPT_MAX_TEXTSIZE, arg1Len ) \
53 	char arg1[ CRYPT_MAX_TEXTSIZE + 8 ];
54 	BUFFER( CRYPT_MAX_TEXTSIZE, arg2Len ) \
55 	char arg2[ CRYPT_MAX_TEXTSIZE + 8 ];
56 	int typeLen, arg1Len, arg2Len;
57 
58 	/* Channel extra data.  This contains encoded oddball protocol-specific
59 	   SSH packets to be sent or having been received */
60 	BUFFER_FIXED( UINT_SIZE + CRYPT_MAX_TEXTSIZE + ( UINT_SIZE * 4 ) ) \
61 	BYTE extraData[ ( UINT_SIZE + CRYPT_MAX_TEXTSIZE ) + \
62 					( UINT_SIZE * 4 ) + 8 ];
63 	} SSH_CHANNEL_INFO;
64 
65 /* Check whether a channel corresponds to a null channel (a placeholder used
66    when there's currently no channel active) and whether a channel is
67    currently active */
68 
69 #define isNullChannel( channelInfoPtr ) \
70 		( ( channelInfoPtr )->readChannelNo == UNUSED_CHANNEL_NO )
71 #define isActiveChannel( channelInfoPtr ) \
72 		( channelInfoPtr->flags & CHANNEL_FLAG_ACTIVE )
73 
74 /* The maximum allowed number of channels */
75 
76 #ifdef USE_SSH_EXTENDED
77   #define SSH_MAX_CHANNELS		4
78 #else
79   #define SSH_MAX_CHANNELS		1
80 #endif /* USE_SSH_EXTENDED */
81 
82 #ifdef USE_SSH
83 
84 /****************************************************************************
85 *																			*
86 *								Utility Functions							*
87 *																			*
88 ****************************************************************************/
89 
90 /* Check whether there are any active channels still present.  Since a
91    channel can be half-closed (we've closed it for write but the other
92    side hasn't acknowledged the close yet) we allow the caller to specify
93    an excluded channel ID that's treated as logically closed for active
94    channel-check purposes even if a channel entry is still present for it.
95    This can also be used when closing channels to check whether this is the
96    last channel open, since closing the last channel also shuts down the
97    entire session */
98 
99 CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1 ) ) \
isChannelActive(const SESSION_INFO * sessionInfoPtr,IN_INT_SHORT_Z const int excludedChannelID)100 static BOOLEAN isChannelActive( const SESSION_INFO *sessionInfoPtr,
101 								IN_INT_SHORT_Z const int excludedChannelID )
102 	{
103 	ATTRIBUTE_LIST *attributeListPtr;
104 	int iterationCount;
105 
106 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
107 
108 	REQUIRES_B( ( excludedChannelID == UNUSED_CHANNEL_ID ) || \
109 				( excludedChannelID > 0 && \
110 				  excludedChannelID < MAX_INTLENGTH_SHORT ) );
111 
112 	for( attributeListPtr = sessionInfoPtr->attributeList, \
113 			iterationCount = 0;
114 		 attributeListPtr != NULL && \
115 			iterationCount < FAILSAFE_ITERATIONS_MAX;
116 		 attributeListPtr = attributeListPtr->next, iterationCount++ )
117 		{
118 		const SSH_CHANNEL_INFO *channelInfoPtr;
119 
120 		/* If it's not an SSH channel, continue */
121 		if( attributeListPtr->attributeID != CRYPT_SESSINFO_SSH_CHANNEL )
122 			continue;
123 
124 		/* It's an SSH channel, check whether it's the one that we're
125 		   after */
126 		ENSURES( attributeListPtr->valueLength == sizeof( SSH_CHANNEL_INFO ) );
127 		channelInfoPtr = attributeListPtr->value;
128 		if( isActiveChannel( channelInfoPtr ) && \
129 			channelInfoPtr->channelID != excludedChannelID )
130 			return( TRUE );
131 		}
132 	ENSURES_B( iterationCount < FAILSAFE_ITERATIONS_MAX );
133 
134 	return( FALSE );
135 	}
136 
137 /* Helper function used to access SSH-specific internal attributes within
138    an attribute group (== single attribute-list item containing multiple
139    sub-items).  Returns the attribute ID of the currently selected attribute
140    when attrGetType == ATTR_CURRENT, otherwise a boolean indicating whether
141    ATTR_PREV/ATTR_NEXT is still within the current subgroup */
142 
143 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
accessFunction(INOUT ATTRIBUTE_LIST * attributeListPtr,IN_ENUM_OPT (ATTR)const ATTR_TYPE attrGetType,OUT_INT_Z int * value)144 static int accessFunction( INOUT ATTRIBUTE_LIST *attributeListPtr,
145 						   IN_ENUM_OPT( ATTR ) const ATTR_TYPE attrGetType,
146 						   OUT_INT_Z int *value )
147 	{
148 	static const CRYPT_ATTRIBUTE_TYPE attributeOrderList[] = {
149 			CRYPT_SESSINFO_SSH_CHANNEL, CRYPT_SESSINFO_SSH_CHANNEL_TYPE,
150 			CRYPT_SESSINFO_SSH_CHANNEL_ARG1, CRYPT_SESSINFO_SSH_CHANNEL_ARG2,
151 			CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE, CRYPT_ATTRIBUTE_NONE,
152 			CRYPT_ATTRIBUTE_NONE };
153 	SSH_CHANNEL_INFO *channelInfoPtr = attributeListPtr->value;
154 	CRYPT_ATTRIBUTE_TYPE attributeType = channelInfoPtr->cursorPos;
155 	BOOLEAN doContinue;
156 	int iterationCount;
157 
158 	assert( isWritePtr( attributeListPtr, sizeof( ATTRIBUTE_LIST ) ) );
159 	assert( isWritePtr( value, sizeof( int ) ) );
160 
161 	REQUIRES( attrGetType >= ATTR_NONE && attrGetType < ATTR_LAST );
162 
163 	/* Clear return value */
164 	*value = 0;
165 
166 	/* If we've just moved the cursor onto this attribute, reset the
167 	   position to the first internal attribute */
168 	if( attributeListPtr->flags & ATTR_FLAG_CURSORMOVED )
169 		{
170 		attributeType = channelInfoPtr->cursorPos = \
171 						CRYPT_SESSINFO_SSH_CHANNEL;
172 		attributeListPtr->flags &= ~ATTR_FLAG_CURSORMOVED;
173 		}
174 
175 	/* If it's an information fetch, return the currently-selected
176 	   attribute */
177 	if( attrGetType == ATTR_NONE )
178 		{
179 		*value = attributeType;
180 		return( CRYPT_OK );
181 		}
182 
183 	for( doContinue = TRUE, iterationCount = 0;
184 		 doContinue && iterationCount < FAILSAFE_ITERATIONS_MED;
185 		 iterationCount++ )
186 		{
187 		int orderIndex;
188 
189 		/* Find the position of the current sub-attribute in the attribute
190 		   order list and use that to get its successor/predecessor sub-
191 		   attribute */
192 		for( orderIndex = 0;
193 			 attributeOrderList[ orderIndex ] != attributeType && \
194 				attributeOrderList[ orderIndex ] != CRYPT_ATTRIBUTE_NONE && \
195 				orderIndex < FAILSAFE_ARRAYSIZE( attributeOrderList, \
196 												 CRYPT_ATTRIBUTE_TYPE );
197 			 orderIndex++ );
198 		ENSURES( orderIndex < FAILSAFE_ARRAYSIZE( attributeOrderList, \
199 										 CRYPT_ATTRIBUTE_TYPE ) );
200 		if( attributeOrderList[ orderIndex ] == CRYPT_ATTRIBUTE_NONE )
201 			{
202 			/* We've reached the first/last sub-attribute within the current
203 			   item/group, tell the caller that there are no more sub-
204 			   attributes present and they have to move on to the next item/
205 			   group */
206 			*value = FALSE;
207 			return( CRYPT_OK );
208 			}
209 		if( attrGetType == ATTR_PREV )
210 			{
211 			attributeType = ( orderIndex <= 0 ) ? \
212 								CRYPT_ATTRIBUTE_NONE : \
213 								attributeOrderList[ orderIndex - 1 ];
214 			}
215 		else
216 			attributeType = attributeOrderList[ orderIndex + 1 ];
217 		if( attributeType == CRYPT_ATTRIBUTE_NONE )
218 			{
219 			/* We've reached the first/last sub-attribute within the current
220 			   item/group, exit as before */
221 			*value = FALSE;
222 			return( CRYPT_OK );
223 			}
224 
225 		/* Check whether the required sub-attribute is present.  If not, we
226 		   continue and try the next one */
227 		switch( attributeType )
228 			{
229 			case CRYPT_SESSINFO_SSH_CHANNEL:
230 			case CRYPT_SESSINFO_SSH_CHANNEL_TYPE:
231 			case CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE:
232 				doContinue = FALSE;	/* Always present */
233 				break;
234 
235 			case CRYPT_SESSINFO_SSH_CHANNEL_ARG1:
236 				if( channelInfoPtr->arg1Len > 0 )
237 					doContinue = FALSE;
238 				break;
239 
240 			case CRYPT_SESSINFO_SSH_CHANNEL_ARG2:
241 				if( channelInfoPtr->arg2Len > 0 )
242 					doContinue = FALSE;
243 				break;
244 
245 			default:
246 				retIntError();
247 			}
248 		}
249 	ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
250 	channelInfoPtr->cursorPos = attributeType;
251 
252 	*value = TRUE;
253 	return( CRYPT_OK );
254 	}
255 
256 /****************************************************************************
257 *																			*
258 *							Find Channel Information						*
259 *																			*
260 ****************************************************************************/
261 
262 /* Find the attribute entry for a channel */
263 
264 CHECK_RETVAL_PTR STDC_NONNULL_ARG( ( 1 ) ) \
findChannelAttr(const SESSION_INFO * sessionInfoPtr,IN const long channelNo)265 static ATTRIBUTE_LIST *findChannelAttr( const SESSION_INFO *sessionInfoPtr,
266 										IN const long channelNo )
267 	{
268 	ATTRIBUTE_LIST *attributeListPtr;
269 	int iterationCount;
270 
271 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
272 
273 	REQUIRES_N( ( channelNo == CRYPT_USE_DEFAULT ) || \
274 				( channelNo >= 0 && channelNo <= CHANNEL_MAX ) );
275 
276 	for( attributeListPtr = sessionInfoPtr->attributeList, \
277 			iterationCount = 0;
278 		 attributeListPtr != NULL && \
279 			iterationCount < FAILSAFE_ITERATIONS_MAX;
280 		 attributeListPtr = attributeListPtr->next, iterationCount++ )
281 		{
282 		const SSH_CHANNEL_INFO *channelInfoPtr;
283 
284 		/* If it's not an SSH channel, continue */
285 		if( attributeListPtr->attributeID != CRYPT_SESSINFO_SSH_CHANNEL )
286 			continue;
287 
288 		/* It's an SSH channel, check whether it's the one that we're
289 		   after */
290 		ENSURES_N( attributeListPtr->valueLength == sizeof( SSH_CHANNEL_INFO ) );
291 		channelInfoPtr = attributeListPtr->value;
292 		if( channelNo == CRYPT_USE_DEFAULT )
293 			{
294 			/* We're looking for any open channel channel, return the first
295 			   match */
296 			if( channelInfoPtr->flags & CHANNEL_FLAG_WRITECLOSED )
297 				continue;
298 			return( attributeListPtr );
299 			}
300 		if( channelInfoPtr->readChannelNo == channelNo || \
301 			channelInfoPtr->writeChannelNo == channelNo )
302 			return( attributeListPtr );
303 		}
304 	ENSURES_N( iterationCount < FAILSAFE_ITERATIONS_MAX );
305 
306 	return( NULL );
307 	}
308 
309 /* Find the SSH channel information for a channel, matching by channel
310    number, channel ID, and channel host + port information */
311 
312 CHECK_RETVAL_PTR STDC_NONNULL_ARG( ( 1 ) ) \
findChannelByChannelNo(const SESSION_INFO * sessionInfoPtr,IN const long channelNo)313 static SSH_CHANNEL_INFO *findChannelByChannelNo( const SESSION_INFO *sessionInfoPtr,
314 												 IN const long channelNo )
315 	{
316 	const ATTRIBUTE_LIST *attributeListPtr = \
317 							findChannelAttr( sessionInfoPtr, channelNo );
318 
319 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
320 
321 	REQUIRES_N( ( channelNo == CRYPT_USE_DEFAULT ) || \
322 				( channelNo >= 0 && channelNo <= CHANNEL_MAX ) );
323 
324 	return( ( attributeListPtr == NULL ) ? NULL : attributeListPtr->value );
325 	}
326 
327 CHECK_RETVAL_PTR STDC_NONNULL_ARG( ( 1 ) ) \
findChannelByID(const SESSION_INFO * sessionInfoPtr,IN_INT_SHORT const int channelID)328 static SSH_CHANNEL_INFO *findChannelByID( const SESSION_INFO *sessionInfoPtr,
329 											IN_INT_SHORT const int channelID )
330 	{
331 	ATTRIBUTE_LIST *attributeListPtr;
332 	int iterationCount;
333 
334 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
335 
336 	REQUIRES_N( channelID > 0 && channelID < MAX_INTLENGTH_SHORT );
337 
338 	for( attributeListPtr = sessionInfoPtr->attributeList, \
339 			iterationCount = 0;
340 		 attributeListPtr != NULL && \
341 			iterationCount < FAILSAFE_ITERATIONS_MAX;
342 		 attributeListPtr = attributeListPtr->next, iterationCount++ )
343 		{
344 		const SSH_CHANNEL_INFO *channelInfoPtr;
345 
346 		/* If it's not an SSH channel, continue */
347 		if( attributeListPtr->attributeID != CRYPT_SESSINFO_SSH_CHANNEL )
348 			continue;
349 
350 		/* It's an SSH channel, check whether it's the that one we're
351 		   after */
352 		ENSURES_N( attributeListPtr->valueLength == sizeof( SSH_CHANNEL_INFO ) );
353 		channelInfoPtr = attributeListPtr->value;
354 		if( channelInfoPtr->channelID == channelID )
355 			return( ( SSH_CHANNEL_INFO * ) channelInfoPtr );
356 		}
357 	ENSURES_N( iterationCount < FAILSAFE_ITERATIONS_MAX );
358 
359 	return( NULL );
360 	}
361 
362 CHECK_RETVAL_PTR STDC_NONNULL_ARG( ( 1, 2 ) ) \
findChannelByAddr(const SESSION_INFO * sessionInfoPtr,IN_BUFFER (addrInfoLen)const char * addrInfo,IN_LENGTH_SHORT const int addrInfoLen)363 static SSH_CHANNEL_INFO *findChannelByAddr( const SESSION_INFO *sessionInfoPtr,
364 											IN_BUFFER( addrInfoLen ) \
365 												const char *addrInfo,
366 											IN_LENGTH_SHORT \
367 												const int addrInfoLen )
368 	{
369 	ATTRIBUTE_LIST *attributeListPtr;
370 	int iterationCount;
371 
372 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
373 	assert( isReadPtr( addrInfo, addrInfoLen ) );
374 
375 	REQUIRES_N( addrInfoLen > 0 && addrInfoLen < MAX_INTLENGTH_SHORT );
376 
377 	for( attributeListPtr = sessionInfoPtr->attributeList, \
378 			iterationCount = 0;
379 		 attributeListPtr != NULL && \
380 			iterationCount < FAILSAFE_ITERATIONS_MAX;
381 		 attributeListPtr = attributeListPtr->next, iterationCount++ )
382 		{
383 		const SSH_CHANNEL_INFO *channelInfoPtr;
384 
385 		/* If it's not an SSH channel, continue */
386 		if( attributeListPtr->attributeID != CRYPT_SESSINFO_SSH_CHANNEL )
387 			continue;
388 
389 		/* It's an SSH channel, check whether it's the one that we're
390 		   after */
391 		ENSURES_N( attributeListPtr->valueLength == sizeof( SSH_CHANNEL_INFO ) );
392 		channelInfoPtr = attributeListPtr->value;
393 		if( channelInfoPtr->arg1Len == addrInfoLen && \
394 			!memcmp( channelInfoPtr->arg1, addrInfo, addrInfoLen ) )
395 			return( ( SSH_CHANNEL_INFO * ) channelInfoPtr );
396 		}
397 	ENSURES_N( iterationCount < FAILSAFE_ITERATIONS_MAX );
398 
399 	return( NULL );
400 	}
401 
402 CHECK_RETVAL_PTR STDC_NONNULL_ARG( ( 1 ) ) \
getCurrentChannelInfo(const SESSION_INFO * sessionInfoPtr,IN_ENUM (CHANNEL)const CHANNEL_TYPE channelType)403 static const SSH_CHANNEL_INFO *getCurrentChannelInfo( const SESSION_INFO *sessionInfoPtr,
404 													  IN_ENUM( CHANNEL ) \
405 														const CHANNEL_TYPE channelType )
406 	{
407 	SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
408 	static const SSH_CHANNEL_INFO nullChannel = \
409 			{ UNUSED_CHANNEL_ID, UNUSED_CHANNEL_NO, UNUSED_CHANNEL_NO,
410 			  CHANNEL_FLAG_NONE, CRYPT_ATTRIBUTE_NONE, 0 /*...*/ };
411 	const SSH_CHANNEL_INFO *channelInfoPtr;
412 	const int channelID = ( channelType == CHANNEL_READ ) ? \
413 							sshInfo->currReadChannel : \
414 							sshInfo->currWriteChannel;
415 
416 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
417 
418 	REQUIRES_N( channelType > CHANNEL_NONE && \
419 				channelType < CHANNEL_LAST );
420 
421 	/* If there's no channel open yet, return the null channel */
422 	if( channelID == UNUSED_CHANNEL_ID )
423 		return( ( SSH_CHANNEL_INFO * ) &nullChannel );
424 
425 	channelInfoPtr = findChannelByID( sessionInfoPtr,
426 									  ( channelType == CHANNEL_READ ) ? \
427 										sshInfo->currReadChannel : \
428 										sshInfo->currWriteChannel );
429 	return( ( channelInfoPtr == NULL ) ? \
430 			( SSH_CHANNEL_INFO * ) &nullChannel : channelInfoPtr );
431 	}
432 
433 /****************************************************************************
434 *																			*
435 *							Get/Set Channel Information						*
436 *																			*
437 ****************************************************************************/
438 
439 /* Get the currently active channel */
440 
CHECK_RETVAL_RANGE_NOERROR(UNUSED_CHANNEL_NO,CHANNEL_MAX)441 CHECK_RETVAL_RANGE_NOERROR( UNUSED_CHANNEL_NO, CHANNEL_MAX ) STDC_NONNULL_ARG( ( 1 ) ) \
442 long getCurrentChannelNo( const SESSION_INFO *sessionInfoPtr,
443 						  IN_ENUM( CHANNEL ) const CHANNEL_TYPE channelType )
444 	{
445 	const SSH_CHANNEL_INFO *channelInfoPtr = \
446 				getCurrentChannelInfo( sessionInfoPtr, channelType );
447 
448 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
449 
450 	REQUIRES_EXT( channelType == CHANNEL_READ || channelType == CHANNEL_WRITE,
451 				  UNUSED_CHANNEL_NO );
452 	REQUIRES_EXT( channelInfoPtr != NULL, UNUSED_CHANNEL_NO );
453 
454 	return( ( channelType == CHANNEL_READ ) ? \
455 			channelInfoPtr->readChannelNo : channelInfoPtr->writeChannelNo );
456 	}
457 
458 /* Get/set an attribute or SSH-specific internal attribute from/for the
459    current channel */
460 
461 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
getChannelAttribute(const SESSION_INFO * sessionInfoPtr,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute,OUT_INT_Z int * value)462 int getChannelAttribute( const SESSION_INFO *sessionInfoPtr,
463 						 IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute,
464 						 OUT_INT_Z int *value )
465 	{
466 	const SSH_CHANNEL_INFO *channelInfoPtr = \
467 				getCurrentChannelInfo( sessionInfoPtr, CHANNEL_READ );
468 
469 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
470 	assert( isWritePtr( value, sizeof( int ) ) );
471 
472 	REQUIRES( isAttribute( attribute ) );
473 	REQUIRES( channelInfoPtr != NULL );
474 
475 	/* Clear return values */
476 	*value = 0;
477 
478 	if( isNullChannel( channelInfoPtr ) )
479 		return( CRYPT_ERROR_NOTFOUND );
480 
481 	switch( attribute )
482 		{
483 		case CRYPT_SESSINFO_SSH_CHANNEL:
484 			*value = channelInfoPtr->channelID;
485 			return( CRYPT_OK );
486 
487 		case CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE:
488 			*value = isActiveChannel( channelInfoPtr ) ? TRUE : FALSE;
489 			return( CRYPT_OK );
490 		}
491 
492 	retIntError();
493 	}
494 
495 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
getChannelAttributeS(const SESSION_INFO * sessionInfoPtr,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute,OUT_BUFFER_OPT (dataMaxLength,* dataLength)void * data,IN_LENGTH_SHORT_Z const int dataMaxLength,OUT_LENGTH_BOUNDED_Z (dataMaxLength)int * dataLength)496 int getChannelAttributeS( const SESSION_INFO *sessionInfoPtr,
497 						  IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute,
498 						  OUT_BUFFER_OPT( dataMaxLength, *dataLength ) \
499 								void *data,
500 						  IN_LENGTH_SHORT_Z const int dataMaxLength,
501 						  OUT_LENGTH_BOUNDED_Z( dataMaxLength ) \
502 								int *dataLength )
503 	{
504 	const SSH_CHANNEL_INFO *channelInfoPtr = \
505 				getCurrentChannelInfo( sessionInfoPtr, CHANNEL_READ );
506 
507 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
508 	assert( ( data == NULL && dataMaxLength == 0 ) || \
509 			isWritePtr( data, dataMaxLength ) );
510 	assert( isWritePtr( dataLength, sizeof( int ) ) );
511 
512 	REQUIRES( isAttribute( attribute ) );
513 	REQUIRES( ( data == NULL && dataMaxLength == 0 ) || \
514 			  ( data != NULL && \
515 				dataMaxLength > 0 && \
516 				dataMaxLength < MAX_INTLENGTH_SHORT ) );
517 	REQUIRES( channelInfoPtr != NULL );
518 
519 	/* Clear return values */
520 	if( data != NULL )
521 		memset( data, 0, min( 16, dataMaxLength ) );
522 	*dataLength = 0;
523 
524 	if( isNullChannel( channelInfoPtr ) )
525 		return( CRYPT_ERROR_NOTFOUND );
526 
527 	switch( attribute )
528 		{
529 		case CRYPT_SESSINFO_SSH_CHANNEL_TYPE:
530 			return( attributeCopyParams( data, dataMaxLength, dataLength,
531 										 channelInfoPtr->type,
532 										 channelInfoPtr->typeLen ) );
533 
534 		case CRYPT_SESSINFO_SSH_CHANNEL_ARG1:
535 			return( attributeCopyParams( data, dataMaxLength, dataLength,
536 										 channelInfoPtr->arg1,
537 										 channelInfoPtr->arg1Len ) );
538 
539 		case CRYPT_SESSINFO_SSH_CHANNEL_ARG2:
540 			return( attributeCopyParams( data, dataMaxLength, dataLength,
541 										 channelInfoPtr->arg2,
542 										 channelInfoPtr->arg2Len ) );
543 		}
544 
545 	retIntError();
546 	}
547 
548 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
getChannelExtAttribute(const SESSION_INFO * sessionInfoPtr,IN_ENUM (SSH_ATTRIBUTE)const SSH_ATTRIBUTE_TYPE sshAttribute,OUT_INT_Z int * value)549 int getChannelExtAttribute( const SESSION_INFO *sessionInfoPtr,
550 							IN_ENUM( SSH_ATTRIBUTE ) \
551 								const SSH_ATTRIBUTE_TYPE sshAttribute,
552 							OUT_INT_Z int *value )
553 	{
554 	const SSH_CHANNEL_INFO *channelInfoPtr = \
555 				getCurrentChannelInfo( sessionInfoPtr, CHANNEL_READ );
556 
557 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
558 	assert( isWritePtr( value, sizeof( int ) ) );
559 
560 	REQUIRES( sshAttribute > SSH_ATTRIBUTE_NONE && \
561 			  sshAttribute < SSH_ATTRIBUTE_LAST );
562 	REQUIRES( channelInfoPtr != NULL );
563 
564 	/* Clear return value */
565 	*value = 0;
566 
567 	if( isNullChannel( channelInfoPtr ) )
568 		return( CRYPT_ERROR_NOTFOUND );
569 
570 	switch( sshAttribute )
571 		{
572 		case SSH_ATTRIBUTE_WINDOWCOUNT:
573 			*value = channelInfoPtr->windowCount;
574 			return( CRYPT_OK );
575 
576 		case SSH_ATTRIBUTE_WINDOWSIZE:
577 			*value = channelInfoPtr->windowSize;
578 			return( CRYPT_OK );
579 		}
580 
581 	retIntError();
582 	}
583 
584 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
setChannelAttribute(INOUT SESSION_INFO * sessionInfoPtr,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute,IN_INT_SHORT const int value)585 int setChannelAttribute( INOUT SESSION_INFO *sessionInfoPtr,
586 						 IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute,
587 						 IN_INT_SHORT const int value )
588 	{
589 	SSH_CHANNEL_INFO *channelInfoPtr;
590 
591 	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
592 
593 	REQUIRES( isAttribute( attribute ) );
594 	REQUIRES( value > 0 && value < MAX_INTLENGTH_SHORT );
595 
596 	/* If we're setting the channel ID this doesn't change any channel
597 	   attribute but selects the one with the given ID */
598 	if( attribute == CRYPT_SESSINFO_SSH_CHANNEL )
599 		{
600 		channelInfoPtr = findChannelByID( sessionInfoPtr, value );
601 		if( channelInfoPtr == NULL )
602 			return( CRYPT_ERROR_NOTFOUND );
603 		return( selectChannel( sessionInfoPtr, channelInfoPtr->writeChannelNo,
604 							   CHANNEL_WRITE ) );
605 		}
606 
607 	retIntError();
608 	}
609 
610 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
setChannelAttributeS(INOUT SESSION_INFO * sessionInfoPtr,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute,IN_BUFFER (dataLength)const void * data,IN_LENGTH_TEXT const int dataLength)611 int setChannelAttributeS( INOUT SESSION_INFO *sessionInfoPtr,
612 						  IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute,
613 						  IN_BUFFER( dataLength ) const void *data,
614 						  IN_LENGTH_TEXT const int dataLength )
615 	{
616 	SSH_CHANNEL_INFO *channelInfoPtr;
617 
618 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
619 	assert( isReadPtr( data, dataLength ) );
620 
621 	REQUIRES( isAttribute( attribute ) );
622 	REQUIRES( dataLength > 0 && dataLength <= CRYPT_MAX_TEXTSIZE );
623 
624 	/* Set the attribute for the currently-active channel */
625 	channelInfoPtr = ( SSH_CHANNEL_INFO * ) \
626 				getCurrentChannelInfo( sessionInfoPtr, CHANNEL_READ );
627 	REQUIRES( channelInfoPtr != NULL );
628 	if( isNullChannel( channelInfoPtr ) )
629 		return( CRYPT_ERROR_NOTFOUND );
630 	switch( attribute )
631 		{
632 		case CRYPT_SESSINFO_SSH_CHANNEL_TYPE:
633 			return( attributeCopyParams( channelInfoPtr->type,
634 										 CRYPT_MAX_TEXTSIZE,
635 										 &channelInfoPtr->typeLen,
636 										 data, dataLength ) );
637 
638 		case CRYPT_SESSINFO_SSH_CHANNEL_ARG1:
639 			return( attributeCopyParams( channelInfoPtr->arg1,
640 										 CRYPT_MAX_TEXTSIZE,
641 										 &channelInfoPtr->arg1Len,
642 										 data, dataLength ) );
643 
644 		case CRYPT_SESSINFO_SSH_CHANNEL_ARG2:
645 			return( attributeCopyParams( channelInfoPtr->arg2,
646 										 CRYPT_MAX_TEXTSIZE,
647 										 &channelInfoPtr->arg2Len,
648 										 data, dataLength ) );
649 		}
650 
651 	retIntError();
652 	}
653 
654 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
setChannelExtAttribute(const SESSION_INFO * sessionInfoPtr,IN_ATTRIBUTE const SSH_ATTRIBUTE_TYPE attribute,IN_INT_Z const int value)655 int setChannelExtAttribute( const SESSION_INFO *sessionInfoPtr,
656 							IN_ATTRIBUTE const SSH_ATTRIBUTE_TYPE attribute,
657 							IN_INT_Z const int value )
658 	{
659 	SSH_CHANNEL_INFO *channelInfoPtr = ( SSH_CHANNEL_INFO * ) \
660 				getCurrentChannelInfo( sessionInfoPtr, CHANNEL_READ );
661 
662 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
663 
664 	REQUIRES( ( attribute == SSH_ATTRIBUTE_ACTIVE && value == TRUE ) || \
665 			  ( attribute != SSH_ATTRIBUTE_ACTIVE && \
666 				value >= 0 && value < INT_MAX ) );
667 	REQUIRES( channelInfoPtr != NULL );
668 
669 	if( isNullChannel( channelInfoPtr ) )
670 		return( CRYPT_ERROR_NOTFOUND );
671 
672 	switch( attribute )
673 		{
674 		case SSH_ATTRIBUTE_ACTIVE:
675 			channelInfoPtr->flags |= CHANNEL_FLAG_ACTIVE;
676 			return( CRYPT_OK );
677 
678 		case SSH_ATTRIBUTE_WINDOWCOUNT:
679 			channelInfoPtr->windowCount = value;
680 			return( CRYPT_OK );
681 
682 		case SSH_ATTRIBUTE_WINDOWSIZE:
683 			channelInfoPtr->windowSize = value;
684 			return( CRYPT_OK );
685 
686 		case SSH_ATTRIBUTE_ALTCHANNELNO:
687 			channelInfoPtr->writeChannelNo = value;
688 			return( CRYPT_OK );
689 		}
690 
691 	retIntError();
692 	}
693 
694 /* Get the status of a channel: Not open, read-side open/write-side closed,
695    open */
696 
697 CHECK_RETVAL_ENUM( CHANNEL ) STDC_NONNULL_ARG( ( 1 ) ) \
getChannelStatusByChannelNo(const SESSION_INFO * sessionInfoPtr,IN const long channelNo)698 CHANNEL_TYPE getChannelStatusByChannelNo( const SESSION_INFO *sessionInfoPtr,
699 										  IN const long channelNo )
700 	{
701 	SSH_CHANNEL_INFO *channelInfoPtr;
702 
703 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
704 
705 	REQUIRES_EXT( ( channelNo >= 0 && channelNo <= CHANNEL_MAX ), \
706 				  CHANNEL_NONE );
707 
708 	channelInfoPtr = findChannelByChannelNo( sessionInfoPtr, channelNo );
709 	return( ( channelInfoPtr == NULL ) ? CHANNEL_NONE : \
710 			( channelInfoPtr->flags & CHANNEL_FLAG_WRITECLOSED ) ? \
711 				CHANNEL_READ : CHANNEL_BOTH );
712 	}
713 
714 CHECK_RETVAL_ENUM( CHANNEL ) STDC_NONNULL_ARG( ( 1 ) ) \
getChannelStatusByAddr(const SESSION_INFO * sessionInfoPtr,IN_BUFFER (addrInfoLen)const char * addrInfo,IN_LENGTH_SHORT const int addrInfoLen)715 CHANNEL_TYPE getChannelStatusByAddr( const SESSION_INFO *sessionInfoPtr,
716 									 IN_BUFFER( addrInfoLen ) const char *addrInfo,
717 									 IN_LENGTH_SHORT const int addrInfoLen )
718 	{
719 	const SSH_CHANNEL_INFO *channelInfoPtr;
720 
721 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
722 	assert( isReadPtr( addrInfo, addrInfoLen ) );
723 
724 	REQUIRES_EXT( ( addrInfoLen > 0 && addrInfoLen < MAX_INTLENGTH_SHORT ), \
725 				  CHANNEL_NONE );
726 
727 	channelInfoPtr = findChannelByAddr( sessionInfoPtr, addrInfo,
728 										addrInfoLen );
729 	return( ( channelInfoPtr == NULL ) ? CHANNEL_NONE : \
730 			( channelInfoPtr->flags & CHANNEL_FLAG_WRITECLOSED ) ? \
731 				CHANNEL_READ : CHANNEL_BOTH );
732 	}
733 
734 /****************************************************************************
735 *																			*
736 *							Channel Management Functions					*
737 *																			*
738 ****************************************************************************/
739 
740 /* Select a channel */
741 
742 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
selectChannel(INOUT SESSION_INFO * sessionInfoPtr,IN const long channelNo,IN_ENUM_OPT (CHANNEL)const CHANNEL_TYPE channelType)743 int selectChannel( INOUT SESSION_INFO *sessionInfoPtr,
744 				   IN const long channelNo,
745 				   IN_ENUM_OPT( CHANNEL ) const CHANNEL_TYPE channelType )
746 	{
747 	SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
748 	SSH_CHANNEL_INFO *channelInfoPtr;
749 
750 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
751 
752 	REQUIRES( ( channelNo == CRYPT_USE_DEFAULT ) || \
753 			  ( channelNo >= 0 && channelNo <= CHANNEL_MAX ) );
754 			  /* CRYPT_USE_DEFAULT is used to select the first available
755 			     channel, used when closing all channels in a loop */
756 	REQUIRES( ( channelType == CHANNEL_NONE ) || \
757 			  ( channelType > CHANNEL_NONE && channelType < CHANNEL_LAST ) );
758 			  /* We allow CHANNEL_NONE to allow selection of created-but-not-
759 			     yet-active channels */
760 
761 	/* Locate the channel and update the current channel information.  We
762 	   allow a special channel-type indicator of CHANNEL_NONE to specify the
763 	   selection of not-yet-activated channels.  Since it's possible to have
764 	   per-channel packet sizes we also update the overall packet size
765 	   value */
766 	channelInfoPtr = findChannelByChannelNo( sessionInfoPtr, channelNo );
767 	if( channelInfoPtr == NULL )
768 		return( CRYPT_ERROR_NOTFOUND );
769 	if( !isActiveChannel( channelInfoPtr ) && channelType != CHANNEL_NONE )
770 		return( CRYPT_ERROR_NOTINITED );
771 	switch( channelType )
772 		{
773 		case CHANNEL_READ:
774 			sshInfo->currReadChannel = channelInfoPtr->channelID;
775 			break;
776 
777 		case CHANNEL_WRITE:
778 			sshInfo->currWriteChannel = channelInfoPtr->channelID;
779 			break;
780 
781 		case CHANNEL_BOTH:
782 		case CHANNEL_NONE:
783 			sshInfo->currReadChannel = \
784 				sshInfo->currWriteChannel = channelInfoPtr->channelID;
785 			break;
786 
787 		default:
788 			retIntError();
789 		}
790 	sessionInfoPtr->maxPacketSize = channelInfoPtr->maxPacketSize;
791 
792 	return( CRYPT_OK );
793 	}
794 
795 /* Add/create/delete a channel */
796 
797 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 4 ) ) \
798 int addChannel( INOUT SESSION_INFO *sessionInfoPtr,
799 				IN const long channelNo,
800 				IN_LENGTH_MIN( 1024 ) const int maxPacketSize,
801 				IN_BUFFER( typeLen ) const void *type,
802 				IN_LENGTH_SHORT const int typeLen,
803 				IN_BUFFER_OPT( arg1Len ) const void *arg1,
804 				IN_LENGTH_SHORT_Z const int arg1Len )
805 	{
806 	SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
807 	ATTRIBUTE_LIST *attributeListPtr;
808 	SSH_CHANNEL_INFO channelInfo;
809 	int channelCount, iterationCount, status;
810 
811 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
812 	assert( isReadPtr( type, typeLen ) );
813 	assert( ( arg1 == NULL && arg1Len == 0 ) ||
814 			isReadPtr( arg1, arg1Len ) );
815 
816 	REQUIRES( channelNo >= 0 && channelNo <= CHANNEL_MAX );
817 	REQUIRES( maxPacketSize >= 1024 && maxPacketSize < MAX_INTLENGTH );
818 	REQUIRES( typeLen > 0 && typeLen < MAX_INTLENGTH_SHORT );
819 	REQUIRES( ( arg1 == NULL && arg1Len == 0 ) || \
820 			  ( arg1 != NULL && \
821 				arg1Len > 0 && arg1Len < MAX_INTLENGTH_SHORT ) );
822 
823 	/* Make sure that this channel doesn't already exist */
824 	if( findChannelByChannelNo( sessionInfoPtr, channelNo ) != NULL )
825 		{
826 		retExt( CRYPT_ERROR_DUPLICATE,
827 				( CRYPT_ERROR_DUPLICATE, SESSION_ERRINFO,
828 				  "Attempt to add duplicate channel %lX", channelNo ) );
829 		}
830 
831 	/* SSH channels are allocated unique IDs for tracking by cryptlib,
832 	   since (at least in theory) the SSH-level channel IDs may repeat.
833 	   If the initial (not-yet-initialised) channel ID matches the
834 	   UNUSED_CHANNEL_ID magic value we initialise it to one past that
835 	   value */
836 	if( sshInfo->channelIndex <= UNUSED_CHANNEL_ID )
837 		sshInfo->channelIndex = UNUSED_CHANNEL_ID + 1;
838 
839 	/* Make sure that we haven't exceeded the maximum number of channels */
840 	for( channelCount = 0, \
841 			attributeListPtr = sessionInfoPtr->attributeList, \
842 			iterationCount = 0;
843 		 attributeListPtr != NULL && \
844 			iterationCount < FAILSAFE_ITERATIONS_MAX;
845 		 attributeListPtr = attributeListPtr->next, iterationCount++ )
846 		{
847 		if( attributeListPtr->attributeID == CRYPT_SESSINFO_SSH_CHANNEL )
848 			channelCount++;
849 		}
850 	ENSURES( iterationCount < FAILSAFE_ITERATIONS_MAX );
851 	if( channelCount > SSH_MAX_CHANNELS )
852 		{
853 		retExt( CRYPT_ERROR_OVERFLOW,
854 				( CRYPT_ERROR_OVERFLOW, SESSION_ERRINFO,
855 				  "Maximum number (%d) of SSH channels reached",
856 				  SSH_MAX_CHANNELS ) );
857 		}
858 
859 	/* Initialise the information for the new channel and create it */
860 	memset( &channelInfo, 0, sizeof( SSH_CHANNEL_INFO ) );
861 	channelInfo.channelID = sshInfo->channelIndex++;
862 	channelInfo.readChannelNo = channelInfo.writeChannelNo = channelNo;
863 	channelInfo.maxPacketSize = maxPacketSize;
864 	status = attributeCopyParams( channelInfo.type, CRYPT_MAX_TEXTSIZE,
865 								  &channelInfo.typeLen, type, typeLen );
866 	if( cryptStatusOK( status ) && arg1 != NULL )
867 		status = attributeCopyParams( channelInfo.arg1, CRYPT_MAX_TEXTSIZE,
868 									  &channelInfo.arg1Len, arg1, arg1Len );
869 	if( cryptStatusOK( status ) )
870 		{
871 		status = addSessionInfoComposite( &sessionInfoPtr->attributeList,
872 										  CRYPT_SESSINFO_SSH_CHANNEL,
873 										  accessFunction, &channelInfo,
874 										  sizeof( SSH_CHANNEL_INFO ),
875 										  ATTR_FLAG_MULTIVALUED | \
876 										  ATTR_FLAG_COMPOSITE );
877 		}
878 	if( cryptStatusError( status ) )
879 		return( status );
880 
881 	/* Select the newly-created channel.  We have to select it using the
882 	   special-case indicator of CHANNEL_NONE since we can't normally
883 	   select an inactive channel */
884 	return( selectChannel( sessionInfoPtr, channelNo, CHANNEL_NONE ) );
885 	}
886 
887 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
createChannel(INOUT SESSION_INFO * sessionInfoPtr)888 int createChannel( INOUT SESSION_INFO *sessionInfoPtr )
889 	{
890 	SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
891 	int iterationCount;
892 
893 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
894 
895 	/* Find an unused channel number.  Since the peer can request the
896 	   creation of arbitrary-numbered channels we have to be careful to
897 	   ensure that we don't clash with any existing peer-requested channel
898 	   numbers when we create our own one */
899 	for( iterationCount = 0;
900 		 findChannelByChannelNo( sessionInfoPtr, \
901 								 sshInfo->nextChannelNo ) != NULL && \
902 			iterationCount < FAILSAFE_ITERATIONS_MED;
903 		 iterationCount++ )
904 		{
905 		/* This channel number is already in use, move on to the next one */
906 		sshInfo->nextChannelNo++;
907 		}
908 	ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
909 
910 	/* Create a channel with the new channel number */
911 	return( addChannel( sessionInfoPtr, sshInfo->nextChannelNo++,
912 						sessionInfoPtr->sendBufSize - EXTRA_PACKET_SIZE,
913 						"session", 7, NULL, 0 ) );
914 	}
915 
916 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
deleteChannel(INOUT SESSION_INFO * sessionInfoPtr,IN const long channelNo,IN_ENUM (CHANNEL)const CHANNEL_TYPE channelType,const BOOLEAN deleteLastChannel)917 int deleteChannel( INOUT SESSION_INFO *sessionInfoPtr,
918 				   IN const long channelNo,
919 				   IN_ENUM( CHANNEL ) const CHANNEL_TYPE channelType,
920 				   const BOOLEAN deleteLastChannel )
921 	{
922 	SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
923 	SSH_CHANNEL_INFO *channelInfoPtr;
924 	ATTRIBUTE_LIST *attributeListPtr;
925 	int channelID;
926 
927 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
928 
929 	REQUIRES( channelNo >= 0 && channelNo <= CHANNEL_MAX );
930 	REQUIRES( channelType > CHANNEL_NONE && channelType < CHANNEL_LAST );
931 
932 	/* Locate the channel information */
933 	attributeListPtr = findChannelAttr( sessionInfoPtr, channelNo );
934 	if( attributeListPtr == NULL )
935 		return( isChannelActive( sessionInfoPtr, UNUSED_CHANNEL_ID ) ? \
936 				CRYPT_ERROR_NOTFOUND : OK_SPECIAL );
937 	channelInfoPtr = attributeListPtr->value;
938 	channelID = channelInfoPtr->channelID;
939 
940 	/* If we can't delete the last remaining channel (it has to be done
941 	   explicitly via a session close) and there are no active channels
942 	   left besides the current one then we can't do anything */
943 	if( !deleteLastChannel && \
944 		!isChannelActive( sessionInfoPtr, channelID ) )
945 		return( CRYPT_ERROR_PERMISSION );
946 
947 	/* Delete the channel entry.  If we're only closing the write side we
948 	   mark the channel as closed for write but leave the overall channel
949 	   open */
950 	if( channelType == CHANNEL_WRITE )
951 		{
952 		REQUIRES( !( channelInfoPtr->flags & CHANNEL_FLAG_WRITECLOSED ) );
953 		channelInfoPtr->flags |= CHANNEL_FLAG_WRITECLOSED;
954 		if( channelID == sshInfo->currWriteChannel )
955 			sshInfo->currWriteChannel = UNUSED_CHANNEL_ID;
956 		return( isChannelActive( sessionInfoPtr, \
957 								 channelInfoPtr->channelID ) ? \
958 				CRYPT_OK : OK_SPECIAL );
959 		}
960 	deleteSessionInfo( &sessionInfoPtr->attributeList,
961 					   &sessionInfoPtr->attributeListCurrent,
962 					   attributeListPtr );
963 
964 	/* If we've deleted the current channel, select a null channel until a
965 	   new one is created/selected */
966 	if( channelID == sshInfo->currReadChannel )
967 		sshInfo->currReadChannel = UNUSED_CHANNEL_ID;
968 	if( channelID == sshInfo->currWriteChannel )
969 		sshInfo->currWriteChannel = UNUSED_CHANNEL_ID;
970 
971 	/* We've deleted an open channel, check if there are any channels left
972 	   and if not let the caller know */
973 	return( isChannelActive( sessionInfoPtr, UNUSED_CHANNEL_ID ) ? \
974 			CRYPT_OK : OK_SPECIAL );
975 	}
976 
977 #if 0
978 
979 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
980 int deleteChannelByAddr( INOUT SESSION_INFO *sessionInfoPtr,
981 						 IN_BUFFER( addrInfoLen ) const char *addrInfo,
982 						 IN_LENGTH_SHORT const int addrInfoLen )
983 	{
984 	const SSH_CHANNEL_INFO *channelInfoPtr;
985 
986 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
987 	assert( isReadPtr( addrInfo, addrInfoLen ) );
988 
989 	REQUIRES( addrInfoLen > 0 && addrInfoLen < MAX_INTLENGTH_SHORT );
990 
991 	channelInfoPtr = findChannelByAddr( sessionInfoPtr, addrInfo,
992 										addrInfoLen );
993 	if( channelInfoPtr == NULL )
994 		return( CRYPT_ERROR_NOTFOUND );
995 
996 	/* We've found the entry that it corresponds to, clear it.  This doesn't
997 	   actually delete the entire channel but merely deletes the forwarding.
998 	   See the note in ssh2_msg.c for why this is currently unused */
999 	memset( channelInfoPtr->arg1, 0, CRYPT_MAX_TEXTSIZE );
1000 	channelInfoPtr->arg1Len = 0;
1001 	return( CRYPT_OK );
1002 	}
1003 #endif /* 0 */
1004 
1005 /****************************************************************************
1006 *																			*
1007 *							Enqueue/Send Channel Messages					*
1008 *																			*
1009 ****************************************************************************/
1010 
1011 /* Enqueue a response to a request, to be sent at the next available
1012    opportunity.  This is required because we may be in the middle of
1013    assembling or sending a data packet when we need to send the response
1014    so the response has to be deferred until after the data packet has been
1015    completed and sent */
1016 
1017 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
1018 int enqueueResponse( INOUT SESSION_INFO *sessionInfoPtr,
1019 					 IN_RANGE( 1, 255 ) const int type,
1020 					 IN_RANGE( 0, 4 ) const int noParams,
1021 					 IN const long channelNo,
1022 					 const int param1, const int param2, const int param3 )
1023 	{
1024 	SSH_RESPONSE_INFO *respPtr = &sessionInfoPtr->sessionSSH->response;
1025 	STREAM stream;
1026 	int status = CRYPT_OK;
1027 
1028 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
1029 
1030 	REQUIRES( type > 0 && type <= 0xFF );
1031 	REQUIRES( noParams >= 0 && noParams <= 4 );
1032 			  /* channelNo is the first parameter */
1033 	REQUIRES( ( noParams == 0 && channelNo == CRYPT_UNUSED ) || \
1034 			  ( channelNo >= 0 && channelNo <= CHANNEL_MAX ) );
1035 
1036 	/* If there's already a response enqueued we can't enqueue another one
1037 	   until it's been sent */
1038 	REQUIRES( respPtr->type == 0 );
1039 
1040 	respPtr->type = type;
1041 	sMemOpen( &stream, respPtr->data, SSH_MAX_RESPONSESIZE );
1042 	if( noParams > 0 )
1043 		status = writeUint32( &stream, channelNo );
1044 	if( noParams > 1 )
1045 		status = writeUint32( &stream, param1 );
1046 	if( noParams > 2 )
1047 		status = writeUint32( &stream, param2 );
1048 	if( noParams > 3 )
1049 		status = writeUint32( &stream, param3 );
1050 	ENSURES( cryptStatusOK( status ) );
1051 	respPtr->dataLen = stell( &stream );
1052 	sMemDisconnect( &stream );
1053 
1054 	return( CRYPT_OK );
1055 	}
1056 
1057 /* Assemble a packet for and send a previously enqueued response.  This
1058    potentially appends a control packet after the end of an existing payload
1059    packet so the buffer indicator used is sendBufSize rather than
1060    maxPacketSize, since we may already have maxPacketSize bytes worth of
1061    payload data present.
1062 
1063    This can be called in one of two ways, if called as appendChannelData()
1064    then it's being piggybacked onto the end of existing data at a location
1065    specified by the 'offset' parameter and we assemble the packet at that
1066    point but it'll be sent as part of the main packet send.  If called as
1067    enqueueChannelData() or sendEnqueuedResponse() then we have the send
1068    buffer to ourselves (offset == CRYPT_UNUSED) and can assemble and send it
1069    immediately */
1070 
1071 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
encodeSendResponse(INOUT SESSION_INFO * sessionInfoPtr,IN_LENGTH_OPT const int offset,OUT_OPT_LENGTH_Z int * responseSize)1072 static int encodeSendResponse( INOUT SESSION_INFO *sessionInfoPtr,
1073 							   IN_LENGTH_OPT const int offset,
1074 							   OUT_OPT_LENGTH_Z int *responseSize )
1075 	{
1076 	SSH_RESPONSE_INFO *respPtr = &sessionInfoPtr->sessionSSH->response;
1077 	STREAM stream;
1078 	const BOOLEAN assembleOnly = ( offset != CRYPT_UNUSED ) ? TRUE : FALSE;
1079 	BOOLEAN adjustedStartOffset = FALSE;
1080 	int sendBufOffset = assembleOnly ? offset : sessionInfoPtr->sendBufPos;
1081 	int encodedResponseSize DUMMY_INIT, dummy, status;
1082 
1083 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
1084 	assert( responseSize == NULL || \
1085 			isWritePtr( responseSize, sizeof( int ) ) );
1086 
1087 	REQUIRES( ( offset == CRYPT_UNUSED && responseSize == NULL ) || \
1088 			  ( offset >= 0 && offset < sessionInfoPtr->sendBufSize && \
1089 			    responseSize != NULL ) );
1090 	REQUIRES( sendBufOffset >= 0 && offset < sessionInfoPtr->sendBufSize );
1091 
1092 	/* Make sure that there's room for at least two packets worth of
1093 	   wrappers plus a short control packet, needed to handle a control
1094 	   packet piggybacked onto the end of a data packet.  The reason for the
1095 	   doubling of the IV count is because the minimum padding length
1096 	   requirement can result in an expansion by two encrypted blocks rather
1097 	   than one */
1098 	static_assert( EXTRA_PACKET_SIZE > \
1099 						( 2 * ( SSH2_HEADER_SIZE + CRYPT_MAX_HASHSIZE + \
1100 								( 2 * CRYPT_MAX_IVSIZE ) ) ) + 32,
1101 				   "Buffer packet size" );
1102 
1103 	/* Clear return value */
1104 	if( responseSize != NULL )
1105 		*responseSize = 0;
1106 
1107 	/* If there's an incomplete packet in the process of being assembled in
1108 	   the send buffer then we can't do anything.  If we're just assembling
1109 	   a response to append to a completed packet then we know that the
1110 	   packet that's present is a complete one so we skip this check */
1111 	if( assembleOnly && !sessionInfoPtr->partialWrite && \
1112 		( sendBufOffset > sessionInfoPtr->sendBufStartOfs ) )
1113 		return( CRYPT_OK );
1114 
1115 	/* The send buffer is allocated to always allow the piggybacking of one
1116 	   packet of control data */
1117 	ENSURES( sendBufOffset + ( SSH2_HEADER_SIZE + 16 + respPtr->dataLen + \
1118 							   CRYPT_MAX_HASHSIZE + CRYPT_MAX_IVSIZE ) <= \
1119 						sessionInfoPtr->sendBufSize );
1120 
1121 	ENSURES( ( sendBufOffset <= sessionInfoPtr->sendBufStartOfs ) || \
1122 			 ( sessionInfoPtr->partialWrite && \
1123 			   sendBufOffset + ( SSH2_HEADER_SIZE + 16 + respPtr->dataLen + \
1124 								 CRYPT_MAX_HASHSIZE + CRYPT_MAX_IVSIZE ) < \
1125 						sessionInfoPtr->sendBufSize ) );
1126 
1127 	/* If we're in the data transfer phase and there's nothing in the send
1128 	   buffer, set the packet start offset to zero.  We have to do this
1129 	   because it's pre-adjusted to accomodate the header for a payload data
1130 	   packet, since we're assembling our own packet in the buffer there's
1131 	   no need for this additional header room */
1132 	if( ( sessionInfoPtr->flags & SESSION_ISOPEN ) && \
1133 		sendBufOffset == sessionInfoPtr->sendBufStartOfs )
1134 		{
1135 		sendBufOffset = 0;
1136 		adjustedStartOffset = TRUE;
1137 		}
1138 
1139 	/* Assemble the response as a new packet at the end of any existing
1140 	   data */
1141 	REQUIRES( rangeCheckZ( sendBufOffset,
1142 						   sessionInfoPtr->sendBufSize - sendBufOffset,
1143 						   sessionInfoPtr->sendBufSize ) );
1144 	sMemOpen( &stream, sessionInfoPtr->sendBuffer + sendBufOffset,
1145 			  sessionInfoPtr->sendBufSize - sendBufOffset );
1146 	swrite( &stream, "\x00\x00\x00\x00\x00", SSH2_HEADER_SIZE );
1147 	status = sputc( &stream, respPtr->type );
1148 	if( respPtr->dataLen > 0 )
1149 		{
1150 		/* Some responses can consist purely of an ID byte */
1151 		status = swrite( &stream, respPtr->data, respPtr->dataLen );
1152 		}
1153 	if( cryptStatusOK( status ) )
1154 		status = wrapPacketSSH2( sessionInfoPtr, &stream, 0, FALSE, TRUE );
1155 	if( cryptStatusOK( status ) )
1156 		encodedResponseSize = stell( &stream );
1157 	if( cryptStatusError( status ) )
1158 		{
1159 		sMemDisconnect( &stream );
1160 		return( status );
1161 		}
1162 
1163 	/* We've sent (or at least assembled) the response, clear the enqueued
1164 	   data */
1165 	memset( respPtr, 0, sizeof( SSH_RESPONSE_INFO ) );
1166 
1167 	/* If we're only assembling the data and the caller is taking care of
1168 	   sending the assembled packet, we're done */
1169 	if( assembleOnly )
1170 		{
1171 		sMemDisconnect( &stream );
1172 		*responseSize = encodedResponseSize;
1173 		return( CRYPT_OK );
1174 		}
1175 
1176 	/* If we're still in the handshake phase (so this is part of the initial
1177 	   session negotiation, for example a response to a global or channel
1178 	   request) then we need to send the packet directly to avoid messing
1179 	   up the send buffering */
1180 	if( !( sessionInfoPtr->flags & SESSION_ISOPEN ) )
1181 		{
1182 		status = sendPacketSSH2( sessionInfoPtr, &stream, TRUE );
1183 		sMemDisconnect( &stream );
1184 		return( status );
1185 		}
1186 
1187 	/* Write the response, using the standard data-flush mechanism to try
1188 	   and get the data out.  We set the partial-write flag because what
1189 	   we've just added is pre-packaged data that doesn't have to go through
1190 	   the data-payload encoding process */
1191 	sMemDisconnect( &stream );
1192 	if( adjustedStartOffset )
1193 		{
1194 		/* We're in the data transfer phase, in which case the buffer
1195 		   position is offset into the send buffer to leave room for the
1196 		   packet header.  Since we're adding our own header we've started
1197 		   the packet at the start of the send buffer, i.e. with
1198 		   sendBufPos = 0 rather than sendBufPos = sendBufStartOffset, so we
1199 		   set the new position to the total size of the data written rather
1200 		   than adding it to the existing value */
1201 		sessionInfoPtr->sendBufPos = encodedResponseSize;
1202 		}
1203 	else
1204 		{
1205 		assert( 0 );	/* When does this occur? */
1206 		sessionInfoPtr->sendBufPos += encodedResponseSize;
1207 		}
1208 	sessionInfoPtr->partialWrite = TRUE;
1209 	return( putSessionData( sessionInfoPtr, NULL, 0, &dummy ) );
1210 	}
1211 
1212 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
sendEnqueuedResponse(INOUT SESSION_INFO * sessionInfoPtr)1213 int sendEnqueuedResponse( INOUT SESSION_INFO *sessionInfoPtr )
1214 	{
1215 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
1216 
1217 	return( encodeSendResponse( sessionInfoPtr, CRYPT_UNUSED, NULL ) );
1218 	}
1219 
1220 /* Enqueue channel control data ready to be sent, and try and send it if
1221    possible.  This is used to send window-adjust messages for the SSH
1222    performance handbrake by first enqueueing the window adjust and then,
1223    if the send buffer is available, sending it.  If it's not available
1224    then it'll be piggybacked onto the channel payload data later on when
1225    it's being sent via appendChannelData() below */
1226 
1227 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
1228 int enqueueChannelData( INOUT SESSION_INFO *sessionInfoPtr,
1229 						IN_RANGE( 1, 255 ) const int type,
1230 						IN const long channelNo,
1231 						const int param )
1232 	{
1233 	int status;
1234 
1235 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
1236 
1237 	REQUIRES( type > 0 && type <= 0xFF );
1238 	REQUIRES( channelNo >= 0 && channelNo <= CHANNEL_MAX );
1239 
1240 	status = enqueueResponse( sessionInfoPtr, type, 2, channelNo, param,
1241 							  CRYPT_UNUSED, CRYPT_UNUSED );
1242 	if( cryptStatusError( status ) )
1243 		return( status );
1244 	return( encodeSendResponse( sessionInfoPtr, CRYPT_UNUSED, NULL ) );
1245 	}
1246 
1247 /* Append enqueued channel control data to existing channel payload data
1248    without trying to send it.  In this case the control data send is being
1249    piggybacked on a payload data send and will be handled by the caller,
1250    with the control flow on the caller side being:
1251 
1252 	preparepacketFunction:
1253 		wrap channel data;
1254 		if( enqueued control data present )
1255 			appendChannelData();
1256 	send wrapped channel data and wrapped control data */
1257 
1258 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
appendChannelData(INOUT SESSION_INFO * sessionInfoPtr,IN_LENGTH_SHORT_Z const int offset)1259 int appendChannelData( INOUT SESSION_INFO *sessionInfoPtr,
1260 					   IN_LENGTH_SHORT_Z const int offset )
1261 	{
1262 	int channelDataSize, status;
1263 
1264 	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
1265 
1266 	REQUIRES( offset >= 0 && offset < sessionInfoPtr->sendBufSize );
1267 
1268 	status = encodeSendResponse( sessionInfoPtr, offset, &channelDataSize );
1269 	return( cryptStatusError( status ) ? status : channelDataSize );
1270 	}
1271 #endif /* USE_SSH */
1272