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