cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
ssh2_msg.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib SSHv2 Control Message Management *
4 * Copyright Peter Gutmann 1998-2008 *
5 * *
6 ****************************************************************************/
7 
8 #include <stdio.h>
9 #if defined( INC_ALL )
10  #include "crypt.h"
11  #include "misc_rw.h"
12  #include "session.h"
13  #include "ssh.h"
14 #else
15  #include "crypt.h"
16  #include "enc_dec/misc_rw.h"
17  #include "session/session.h"
18  #include "session/ssh.h"
19 #endif /* Compiler-specific includes */
20 
21 #ifdef USE_SSH
22 
23 /* Forward declaration for channel-close function */
24 
26 static int sendChannelClose( INOUT SESSION_INFO *sessionInfoPtr,
27  IN const long channelNo,
28  IN_ENUM( CHANNEL ) const CHANNEL_TYPE channelType,
29  const BOOLEAN closeLastChannel );
30 
31 /****************************************************************************
32 * *
33 * Utility Functions *
34 * *
35 ****************************************************************************/
36 
37 /* Implement the SSH performance handbrake unless we've managed to disable
38  it during the channel-open */
39 
41 static int handleWindowAdjust( INOUT SESSION_INFO *sessionInfoPtr,
42  IN const long channelNo,
43  IN_LENGTH_Z const int length )
44  {
45  int windowCount, windowSize = DUMMY_INIT, status;
46 
47  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
48 
49  REQUIRES( channelNo >= 0 && channelNo <= LONG_MAX );
50  REQUIRES( length >= 0 && length < MAX_INTLENGTH );
51 
52  /* Get the window parameters */
53  status = getChannelExtAttribute( sessionInfoPtr,
55  &windowCount );
56  if( cryptStatusOK( status ) )
57  status = getChannelExtAttribute( sessionInfoPtr,
59  &windowSize );
60  ENSURES( cryptStatusOK( status ) );
61 
62  /* Adjust the data window and communicate changes to the other side if
63  necessary. This can get quite complicated because for those
64  implementations where we can't disable the SSH handbrake we have to
65  send a constant stream of 1977-vintage XMODEM XON/XOFF messages, but
66  since there may be a half-assembled packet in the send buffer we
67  can't just dispatch it immediately but have to enqueue it pending
68  availability of the send machinery (it also leads to strange error
69  messages returned to the caller when they get a write failure in
70  response to a read). Fortunately only a few old buggy
71  implementations actually reject our disabling of the handbrake so
72  it's usually disabled (at least for the receive side, which we
73  control) and in the rare cases where it's present it's being used
74  for things like network router console interfaces for which the
75  traffic is highly interactive so there's a constant flow of packets
76  written to piggyback the XONs onto.
77 
78  The exact strategy for window handling is a bit complex (mostly
79  because this isn't a good way to do things in the first place so it
80  would require horribly complex processing to really handle properly),
81  to keep things simple we just wait until the window size has fallen
82  to half its initial value and then reset it back to the initial
83  value again. Since this is rarely used except where we can't disable
84  the handbrake it's not really worth introducing a huge amount of
85  extra complexity to manage it */
86  REQUIRES( windowCount > 0 && windowCount <= windowSize );
87  windowCount -= length;
88  if( windowCount < windowSize / 2 )
89  {
90  int adjustCount;
91 
92  /* Send the window adjust to the remote system:
93 
94  byte SSH_MSG_CHANNEL_WINDOW_ADJUST
95  uint32 channel
96  uint32 bytes_to_add
97 
98  Unfortunately the error status that we return from a failed
99  window adjust is going to come as a complete surprise to the
100  caller because we're supposed to be processing a read and not a
101  write at this point, the write is only required by SSH's
102  braindamaged flow-control handling */
103  if( windowCount < 0 || windowCount >= windowSize )
104  {
105  /* We've consumed the remaining window and then some, reset it
106  to it's full size */
107  adjustCount = windowSize;
108  }
109  else
110  {
111  /* Adjust the window back up to it's full size */
112  adjustCount = windowSize - windowCount;
113  }
114  ENSURES( adjustCount > windowSize / 2 && \
115  adjustCount <= windowSize );
116  status = enqueueChannelData( sessionInfoPtr,
118  channelNo, adjustCount );
119  if( cryptStatusError( status ) )
120  {
121  retExt( status,
122  ( status, SESSION_ERRINFO,
123  "Error sending SSH window adjust for data flow "
124  "control" ) );
125  }
126 
127  /* We've reset the window, start again from zero */
128  windowCount += adjustCount;
129  if( windowCount < windowSize / 2 || windowCount > windowSize )
130  {
133  "Invalid SSH flow control window count %d, should be "
134  "%d ... %d", windowCount, windowSize / 2,
135  windowSize ) );
136  }
137  }
138  status = setChannelExtAttribute( sessionInfoPtr,
140  windowCount );
141  ENSURES( cryptStatusOK( status ) );
142 
143  return( CRYPT_OK );
144  }
145 
146 /****************************************************************************
147 * *
148 * General Channel Management *
149 * *
150 ****************************************************************************/
151 
152 /* Get the data window size for a new channel. Some buggy implementations
153  break when sent a window size over a certain limit in which case we have
154  to limit the window size. Typically for these implementations for any
155  size over about 8M the server gets slower and slower, eventually more or
156  less grinding to halt at about 64MB (presumably some O(n^2) algorithm,
157  although how you manage to do this for a window-size notification is a
158  mystery). Some variants of this buggy server reportedly require a window
159  adjust for every 32K or so sent no matter what the actual window size is,
160  but this may be just a variant of the general mis-handling of large
161  window sizes so we treat it as the same thing and advertise a smaller-
162  than-optimal 16MB window which, as a side-effect, results in a constant
163  flow of window adjusts */
164 
166 int getWindowSize( const SESSION_INFO *sessionInfoPtr )
167  {
168  assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
169 
170  return( ( sessionInfoPtr->protocolFlags & SSH_PFLAG_WINDOWSIZE ) ? \
171  0x1000000 : MAX_WINDOW_SIZE );
172  }
173 
174 /* Process a channel control message. Returns OK_SPECIAL to tell the caller
175  to try again with the next packet */
176 
177 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
178 int processChannelControlMessage( INOUT SESSION_INFO *sessionInfoPtr,
179  INOUT STREAM *stream )
180  {
181  SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
182  const long prevChannelNo = \
183  getCurrentChannelNo( sessionInfoPtr, CHANNEL_READ );
184  long channelNo;
185  int status;
186 
187  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
188  assert( isWritePtr( stream, sizeof( STREAM ) ) );
189 
190  /* See what we've got. SSH has a whole pile of no-op equivalents that
191  we have to handle as well as the obvious no-ops. We can also get
192  global and channel requests for assorted reasons and a constant
193  stream of window adjusts to implement the SSH performance handbrake */
194  switch( sshInfo->packetType )
195  {
197  status = processChannelRequest( sessionInfoPtr, stream,
198  CRYPT_UNUSED );
199  if( cryptStatusError( status ) && status != OK_SPECIAL )
200  return( status );
201  return( OK_SPECIAL );
202 
204  /* Process the channel-open request. In theory we could
205  immediately reject any attempts by the server to open a
206  channel to the client at this point, but unfortunately we
207  have to process a considerable portion of the channel-open
208  request in order to use the information in it to send a
209  request-denied response back to the server */
210  status = processChannelOpen( sessionInfoPtr, stream );
211  if( cryptStatusError( status ) )
212  return( status );
213 
214  /* Tell the caller that they have to process the new channel
215  information before they can continue */
216  return( CRYPT_ENVELOPE_RESOURCE );
217 
218  case SSH_MSG_IGNORE:
219  case SSH_MSG_DEBUG:
220  /* Nothing to see here, move along, move along:
221 
222  byte SSH_MSG_IGNORE
223  string data
224 
225  byte SSH_MSG_DEBUG
226  boolean always_display
227  string message
228  string language_tag */
229  return( OK_SPECIAL );
230 
231  case SSH_MSG_DISCONNECT:
232  /* This only really seems to be used during the handshake phase,
233  once a channel is open it (and the session as a whole) is
234  disconnected with a channel EOF/close, but we handle it here
235  anyway just in case */
236  return( getDisconnectInfo( sessionInfoPtr, stream ) );
237 
238  case SSH_MSG_KEXINIT:
239  /* The SSH spec is extremely vague about the sequencing of
240  operations during a rehandshake. Unlike SSL there's no real
241  indication of what happens to the connection-layer transfers
242  while a transport-layer rehandshake is in progress. Also
243  unlike SSL we can't refuse a rehandshake by ignoring the
244  request, so once we've fallen we can't get up any more. This
245  is most obvious with ssh.com's server, which starting with
246  version 2.3.0 would do a rehandshake every hour (for a basic
247  encrypted telnet session, while a high-volume IPsec link can
248  run for hours before it feels the need to do this). To make
249  things even messier, neither side can block for too long
250  waiting for the rehandshake to complete before sending new
251  data because the lack of WINDOW_ADJUSTs (in an implementation
252  that sends these with almost every packet, as most do) will
253  screw up flow control and lead to deadlock. This problem got
254  so bad that as of 2.4.0 the ssh.com implementation would
255  detect OpenSSH (the other main implementation at the time)
256  and disable the rehandshake when it was talking to it, but it
257  may not do this for other implementations.
258 
259  To avoid falling into this hole, or at least to fail
260  obviously when the two sides can't agree on how to handle the
261  layering mismatch problem, we report a rehandshake request as
262  an error. Trying to handle it properly results in hard-to-
263  diagnose errors (it depends on what the layers are doing at
264  the time of the problem), typically some bad-packet error
265  when the other side tries to interpret a connection-layer
266  packet as part of the rehandshake, or when the two sides
267  disagree on when to switch keys and one of the two decrypts
268  with the wrong keys and gets a garbled packet type */
271  "Unexpected KEXINIT request received" ) );
272 
277  case SSH_MSG_CHANNEL_EOF:
279  /* All channel-specific messages end up here */
280  channelNo = readUint32( stream );
281  if( cryptStatusError( channelNo ) )
282  {
283  /* We can't send an error response to a channel request at
284  this point both because we haven't got to the response-
285  required flag yet and because SSH doesn't provide a
286  mechanism for returning an error response without an
287  accompanying channel number. The best that we can do is
288  to quietly ignore the packet */
291  "Invalid channel number in channel-specific packet "
292  "type %d", sshInfo->packetType ) );
293  }
294  if( channelNo != getCurrentChannelNo( sessionInfoPtr, \
295  CHANNEL_READ ) )
296  {
297  /* It's a request on something other than the current
298  channel, try and select the new channel */
299  status = selectChannel( sessionInfoPtr, channelNo,
300  CHANNEL_READ );
301  if( cryptStatusError( status ) )
302  {
303  /* As before for error handling */
306  "Invalid channel number %lX in "
307  "channel-specific packet type %d, current "
308  "channel is %lX", channelNo,
309  sshInfo->packetType, prevChannelNo ) );
310  }
311  }
312  break;
313 
314  default:
315  {
316  BYTE buffer[ 16 + 8 ];
317  int length;
318 
319  /* We got something unexpected, throw an exception in the debug
320  version and let the caller know the details */
321  DEBUG_DIAG(( "Unexpected control packet %d",
322  sshInfo->packetType ));
323  assert( DEBUG_WARN );
324  status = length = sread( stream, buffer, 8 );
325  if( cryptStatusError( status ) || length < 8 )
326  {
327  /* There's not enough data present to dump the start of the
328  packet, provide a more generic response */
331  "Unexpected control packet type %d received",
332  sshInfo->packetType ) );
333  }
336  "Unexpected control packet type %d received, "
337  "beginning %02X %02X %02X %02X %02X %02X %02X %02X",
338  sshInfo->packetType,
339  buffer[ 0 ], buffer[ 1 ], buffer[ 2 ], buffer[ 3 ],
340  buffer[ 4 ], buffer[ 5 ], buffer[ 6 ], buffer[ 7 ] ) );
341  }
342  }
343 
344  /* From here on we're processing a channel-specific message that applies
345  to the currently selected channel */
346  switch( sshInfo->packetType )
347  {
350  {
351  int length;
352 
353  /* Get the payload length and make sure that it's
354  (approximately) valid, more exact checking has already been
355  done by the caller so we don't need to return extended error
356  information as this is just a backup check */
357  status = length = readUint32( stream );
358  if( cryptStatusError( status ) || \
359  length < 0 || length > sessionInfoPtr->receiveBufSize )
360  return( CRYPT_ERROR_BADDATA );
361 
362  /* These are messages that consume window space, adjust the data
363  window and communicate changes to the other side if
364  necessary */
365  status = handleWindowAdjust( sessionInfoPtr, channelNo, length );
366  if( cryptStatusError( status ) )
367  return( status );
368 
369  /* If it's a standard data packet, we're done */
370  if( sshInfo->packetType == SSH_MSG_CHANNEL_DATA )
371  return( CRYPT_OK );
372 
373  /* The extended data message is used for out-of-band data sent
374  over a channel, specifically output sent to stderr from a
375  shell command. What to do with this is somewhat uncertain,
376  the only possible action that we could take apart from just
377  ignoring it is to convert it back to in-band data. However,
378  something running a shell command may not expect to get
379  anything returned in this manner (see the comment for the
380  port-forwarding channel open in the client-side channel-open
381  code for more on this) so for now we just ignore it and
382  assume that the user will rely on results sent as in-band
383  data. This should be fairly safe since this message type
384  seems to be rarely (if ever) used, so apps will function
385  without it */
386  return( OK_SPECIAL );
387  }
388 
390  status = processChannelRequest( sessionInfoPtr, stream,
391  prevChannelNo );
392  if( cryptStatusError( status ) && status != OK_SPECIAL )
393  return( status );
394  return( OK_SPECIAL );
395 
397  /* Another noop-equivalent (but a very performance-affecting
398  one) */
399  return( OK_SPECIAL );
400 
401  case SSH_MSG_CHANNEL_EOF:
402  /* According to the SSH docs the EOF packet is mostly a courtesy
403  notification, however many implementations seem to use a
404  channel EOF in place of a close before sending a disconnect
405  message */
406  return( OK_SPECIAL );
407 
409  /* The peer has closed their side of the channel, if our side
410  isn't already closed (in other words if this message isn't
411  a response to a close that we sent), close our side as well */
412  if( getChannelStatusByChannelNo( sessionInfoPtr,
413  channelNo ) == CHANNEL_BOTH )
414  {
415  status = sendChannelClose( sessionInfoPtr, channelNo,
416  CHANNEL_BOTH, TRUE );
417  }
418  else
419  {
420  /* We've already closed our side of the channel, delete it */
421  status = deleteChannel( sessionInfoPtr, channelNo,
422  CHANNEL_BOTH, TRUE );
423  }
424 
425  /* If this wasn't the last channel, we're done */
426  if( status != OK_SPECIAL )
427  return( OK_SPECIAL );
428 
429  /* We've closed the last channel, indicate that the overall
430  connection is now closed. This behaviour isn't mentioned in
431  the spec but it seems to be the standard way of handling
432  things, particularly for the most common case where
433  channel == session */
434  sessionInfoPtr->flags |= SESSION_SENDCLOSED;
437  "Remote system closed last remaining SSH channel" ) );
438  }
439 
440  retIntError();
441  }
442 
443 /****************************************************************************
444 * *
445 * Channel Close Handling *
446 * *
447 ****************************************************************************/
448 
449 /* Send a channel close notification. Returns OK_SPECIAL if the last
450  channel is being closed */
451 
453 static int sendChannelClose( INOUT SESSION_INFO *sessionInfoPtr,
454  IN const long channelNo,
455  IN_ENUM( CHANNEL ) const CHANNEL_TYPE channelType,
456  const BOOLEAN closeLastChannel )
457  {
458  BOOLEAN lastChannel = FALSE;
459  int status;
460 
461  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
462 
463  REQUIRES( channelNo >= 0 && channelNo <= LONG_MAX );
464  REQUIRES( channelType > CHANNEL_NONE && channelType < CHANNEL_LAST );
465 
466  /* Delete the channel. If we've deleted the last active channel
467  deleteChannel() will return OK_SPECIAL to let us know that there are
468  no more channels left to close */
469  status = deleteChannel( sessionInfoPtr, channelNo, channelType,
470  closeLastChannel );
471  if( cryptStatusError( status ) )
472  {
473  if( status != OK_SPECIAL )
474  return( status );
475 
476  /* We're closing the last remaining channel */
477  lastChannel = TRUE;
478  }
479 
480  /* Prepare the channel-close notification:
481 
482  byte SSH_MSG_CHANNEL_CLOSE
483  uint32 channel_no */
484  status = enqueueResponse( sessionInfoPtr, SSH_MSG_CHANNEL_CLOSE, 1,
485  channelNo, CRYPT_UNUSED, CRYPT_UNUSED,
486  CRYPT_UNUSED );
487  if( cryptStatusError( status ) )
488  return( status );
489 
490  /* We can't safely use anything that ends up at sendPacketSSH2() at this
491  point since we may be closing the connection in response to a link
492  error, in which case the error returned from the packet send would
493  overwrite the actual error information. Because of this we send the
494  response with the no-report-error flag set to suppress reporting of
495  network errors during the send */
496  disableErrorReporting( sessionInfoPtr );
497  status = sendEnqueuedResponse( sessionInfoPtr );
498  enableErrorReporting( sessionInfoPtr );
499 
500  /* If it's the last channel, let the caller know (this overrides any
501  possible error return status, since we're about to close the
502  connection there's not much that we can do with an error anyway) */
503  return( lastChannel ? OK_SPECIAL : status );
504  }
505 
506 /* Close a channel */
507 
509 int closeChannel( INOUT SESSION_INFO *sessionInfoPtr,
510  const BOOLEAN closeAllChannels )
511  {
513  const int currWriteChannelNo = \
514  getCurrentChannelNo( sessionInfoPtr, CHANNEL_WRITE );
515  int noChannels = 1, iterationCount, status;
516 
517  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
518 
519  /* If we've already sent the final channel-close message in response to
520  getting a final close notification from the peer all that's left to
521  do is to disconnect the session */
522  if( sessionInfoPtr->flags & SESSION_SENDCLOSED )
523  {
524  sNetDisconnect( &sessionInfoPtr->stream );
525  return( CRYPT_OK );
526  }
527 
528  /* Normally we can keep closing open channels until we hit the last one
529  whereupon we close the overall session, however if we're closing a
530  single identified channel we can't automatically close the whole
531  session as a side-effect of closing the single channel */
532  if( !closeAllChannels && currWriteChannelNo == UNUSED_CHANNEL_NO )
533  {
536  "No channel information available to identify the "
537  "channel to close" ) );
538  }
539 
540  /* If there's no channel open, close the session with a session
541  disconnect rather than a channel close:
542 
543  byte SSH_MSG_DISCONNECT
544  uint32 reason_code = SSH_DISCONNECT_CONNECTION_LOST
545  string description = ""
546  string language_tag = ""
547 
548  The spec doesn't explain what the reason codes actually mean but
549  SSH_DISCONNECT_CONNECTION_LOST seems to be the least inappropriate
550  disconnect reason at this point */
551  if( currWriteChannelNo == UNUSED_CHANNEL_NO )
552  {
553  /* Since we're closing the session there's not much that we can do
554  in the case of an error because the very next operation is to
555  shut down the network session, so we don't do anything if the
556  send fails */
557  status = enqueueResponse( sessionInfoPtr, SSH_MSG_DISCONNECT, 3,
559  CRYPT_UNUSED );
560  if( cryptStatusOK( status ) )
561  ( void ) sendEnqueuedResponse( sessionInfoPtr );
562  sessionInfoPtr->flags |= SESSION_SENDCLOSED;
563  sNetDisconnect( &sessionInfoPtr->stream );
564  return( CRYPT_OK );
565  }
566 
567  /* Close one or all channels */
568  if( closeAllChannels )
569  {
570  /* Get the first available channel (which must succeed, since we
571  just checked it above) and close each successive channel in
572  turn */
573  status = selectChannel( sessionInfoPtr, CRYPT_USE_DEFAULT,
574  CHANNEL_WRITE );
575  for( noChannels = 0, iterationCount = 0;
576  cryptStatusOK( status ) && \
577  cryptStatusOK( selectChannel( sessionInfoPtr, CRYPT_USE_DEFAULT,
578  CHANNEL_WRITE ) ) && \
579  iterationCount < FAILSAFE_ITERATIONS_MED;
580  noChannels++, iterationCount++ )
581  {
582  status = sendChannelClose( sessionInfoPtr,
583  getCurrentChannelNo( sessionInfoPtr, CHANNEL_WRITE ),
584  CHANNEL_WRITE, TRUE );
585  }
586  ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
587  }
588  else
589  {
590  /* We're just closing one channel, close the write side. The
591  complete close will be done when the other side acknowledges our
592  close. If this isn't the last open channel then the response to
593  our close will be handled as part of normal packet processing and
594  we're done */
595  status = sendChannelClose( sessionInfoPtr, currWriteChannelNo,
596  CHANNEL_WRITE, FALSE );
597  if( status != OK_SPECIAL )
598  {
599  /* If this is the last remaining channel, we similarly can't
600  close it */
601  if( status == CRYPT_ERROR_PERMISSION )
602  {
605  "Cannot close last remaining channel without "
606  "closing the overall session" ) );
607  }
608 
609  return( CRYPT_OK );
610  }
611  }
612 
613  /* It's the last open channel, flush through the remaining data */
614  status = sendCloseNotification( sessionInfoPtr, NULL, 0 );
615  if( cryptStatusError( status ) || \
616  ( sessionInfoPtr->protocolFlags & SESSION_SENDCLOSED ) )
617  {
618  /* There's a problem at the network level or the other side has
619  already closed the session, close the network link and exit */
620  sNetDisconnect( &sessionInfoPtr->stream );
621  return( CRYPT_OK );
622  }
623 
624  /* If there's not enough room in the receive buffer to read at least 1K
625  of packet data we can't try anything further */
626  if( sessionInfoPtr->receiveBufSize - sessionInfoPtr->receiveBufEnd < \
627  min( sessionInfoPtr->pendingPacketRemaining, 1024 ) )
628  {
629  sNetDisconnect( &sessionInfoPtr->stream );
630  return( CRYPT_OK );
631  }
632 
633  /* If we're in the middle of reading other data then there's no hope of
634  reading back the other side's channel close without first clearing all
635  of the other data, which is unlikely to happen because the channel
636  has been closed (this can happen when the caller requests a shutdown
637  of the session in the middle of a data transfer), in which case we
638  just exit */
639  if( sessionInfoPtr->receiveBufPos != sessionInfoPtr->receiveBufEnd )
640  {
641  sNetDisconnect( &sessionInfoPtr->stream );
642  return( CRYPT_OK );
643  }
644 
645  /* Read back the other side's channel close(s). This is somewhat messy
646  since the other side could decide that it still wants to send us
647  arbitrary amounts of data (the spec is rather vague about how urgent
648  a channel close is, the general idea among implementors seems to be
649  that you should let output drain before you close your side but if
650  you're in the middle of sending a 2GB file that's a lot of output to
651  drain). This can also be complicated by implementation-specific
652  quirks, for example OpenSSH may hang more or less indefinitely if
653  there's output coming from a background process on the server. This
654  is because of a rather obscure race condition that would occur if it
655  exited immediately in which the SSH server gets the SIGCHLD from the
656  (local) background process exiting before it's written all of its
657  data to the (local) pipe connecting it to the SSH server, so it
658  closes the (remote) SSH channel/connection before the last piece of
659  data comes over the (local) pipe. Because the server won't close the
660  (remote) SSH connection until it's certain that the (local) process
661  has written all of its data, and it'll never get the EOF over the
662  pipe, it hangs forever. This is a piece of Unix plumbing arcana that
663  doesn't really concern us so again just exiting after a short wait
664  seems to be the best response.
665 
666  Since we're about to shut down the session anyway we try to read a
667  basic channel close ack from the other side, if there's anything more
668  than that we drop it. This is complicated somewhat by the fact that
669  what we're doing here is something that's normally handled by the
670  high-level read code in sess_rw.c. What we implement here is the
671  absolute minimum needed to clear the stream (sendCloseNotification()
672  has already set the necessary (small) nonzero timeout for us) */
673  for( iterationCount = 0;
674  noChannels > 0 && iterationCount < FAILSAFE_ITERATIONS_SMALL;
675  noChannels--, iterationCount++ )
676  {
677  status = sessionInfoPtr->readHeaderFunction( sessionInfoPtr, &readInfo );
678  if( cryptStatusError( status ) )
679  break;
680 
681  /* Adjust the packet information for the packet header data that was
682  just read */
683  sessionInfoPtr->receiveBufEnd += status;
684  sessionInfoPtr->pendingPacketRemaining -= status;
685  if( sessionInfoPtr->pendingPacketRemaining <= 512 )
686  {
687  const int bytesLeft = sessionInfoPtr->receiveBufSize - \
688  sessionInfoPtr->receiveBufEnd;
689 
690  /* We got a packet and it's probably the channel close ack, read
691  it */
692  status = sread( &sessionInfoPtr->stream,
693  sessionInfoPtr->receiveBuffer + \
694  sessionInfoPtr->receiveBufEnd,
695  min( sessionInfoPtr->pendingPacketRemaining, \
696  bytesLeft ) );
697  if( cryptStatusError( status ) )
698  break;
699  }
700  }
701  ENSURES( iterationCount < FAILSAFE_ITERATIONS_SMALL );
702  sNetDisconnect( &sessionInfoPtr->stream );
703  return( CRYPT_OK );
704  }
705 #endif /* USE_SSH */