cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
sess_rw.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib Session Read/Write Support Routines *
4 * Copyright Peter Gutmann 1998-2008 *
5 * *
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9  #include "crypt.h"
10  #include "asn1.h"
11  #include "session.h"
12 #else
13  #include "crypt.h"
14  #include "enc_dec/asn1.h"
15  #include "session/session.h"
16 #endif /* Compiler-specific includes */
17 
18 #ifdef USE_SESSIONS
19 
20 /* Common code to read and write data over a secure connection. This is
21  called by the protocol-specific handlers, which supply three functions:
22 
23  readHeaderFunction() - Reads the header for a packet and sets up
24  length information.
25  processBodyFunction() - Processes the body of a packet.
26  preparePacketFunction() - Wraps a packet in preparation for sending it.
27 
28  The behaviour of the network-level stream handlers when called with given
29  timeout and byte-count values is as follows:
30 
31  Timeout byteCount Result
32  ------- --------- ------
33  - error - error
34  0 0 0
35  0 > 0 byteCount
36  > 0 0 CRYPT_ERROR_TIMEOUT
37  > 0 > 0 byteCount
38 
39  Errors encountered in processBodyFunction() and preparePacketFunction()
40  are always fatal. In theory we could try to recover, however the
41  functions update assorted crypto state such as packet sequence numbers
42  and IVs that would be tricky to roll back, and in practice recoverable
43  errors are likely to be extremely rare (at best perhaps a
44  CRYPT_ERROR_TIMEOUT for a context tied to a device, however even this
45  won't occur since the conventional encryption and MAC contexts are all
46  internal native contexts) so there's little point in trying to make the
47  functions recoverable */
48 
49 /****************************************************************************
50 * *
51 * Utility Functions *
52 * *
53 ****************************************************************************/
54 
55 /* Sanity-check the session state */
56 
58 static BOOLEAN sanityCheckRead( const SESSION_INFO *sessionInfoPtr )
59  {
60  const int pendingPacketLength = sessionInfoPtr->pendingPacketLength;
61  const int pendingPacketRemaining = sessionInfoPtr->pendingPacketRemaining;
62 
63  assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
64 
65  /* Make sure that the general state is in order */
66  if( sessionInfoPtr->receiveBufSize < MIN_BUFFER_SIZE || \
67  sessionInfoPtr->receiveBufSize >= MAX_INTLENGTH )
68  return( FALSE );
69 
70  /* Make sure that the buffer position values are within bounds */
71  if( sessionInfoPtr->receiveBufEnd < 0 || \
72  sessionInfoPtr->receiveBufEnd > sessionInfoPtr->receiveBufSize || \
73  sessionInfoPtr->receiveBufPos < 0 || \
74  sessionInfoPtr->receiveBufPos > sessionInfoPtr->receiveBufEnd )
75  return( FALSE );
76  if( sessionInfoPtr->partialHeaderRemaining < 0 || \
77  sessionInfoPtr->partialHeaderRemaining > FIXED_HEADER_MAX )
78  return( FALSE );
79 
80  /* If we haven't started processing data yet there's no packet
81  information present */
82  if( pendingPacketLength == 0 && pendingPacketRemaining == 0 )
83  return( TRUE );
84 
85  /* Make sure that packet information is within bounds */
86  if( pendingPacketLength < 0 || \
87  pendingPacketLength >= sessionInfoPtr->receiveBufSize || \
88  pendingPacketRemaining < 0 || \
89  pendingPacketRemaining >= sessionInfoPtr->receiveBufSize )
90  return( FALSE );
91  if( ( sessionInfoPtr->receiveBufEnd - \
92  sessionInfoPtr->receiveBufPos ) + pendingPacketRemaining != \
93  pendingPacketLength )
94  return( FALSE );
95 
96  /* Make sure that packet header information is within bounds */
97  if( sessionInfoPtr->partialHeaderRemaining < 0 || \
98  sessionInfoPtr->partialHeaderRemaining > 16 )
99  return( FALSE ); /* 16 = SSH header size */
100 
101  return( TRUE );
102  }
103 
105 static BOOLEAN sanityCheckWrite( const SESSION_INFO *sessionInfoPtr )
106  {
107  assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
108 
109  /* Make sure that the general state is in order */
110  if( sessionInfoPtr->sendBufSize < MIN_BUFFER_SIZE || \
111  sessionInfoPtr->sendBufSize >= MAX_INTLENGTH )
112  return( FALSE );
113  if( sessionInfoPtr->sendBufStartOfs < 0 || \
114  sessionInfoPtr->sendBufStartOfs > FIXED_HEADER_MAX )
115  return( FALSE );
116 
117  /* Make sure that the buffer position values are within bounds */
118  if( sessionInfoPtr->sendBufPos < sessionInfoPtr->sendBufStartOfs || \
119  sessionInfoPtr->sendBufPos >= sessionInfoPtr->sendBufSize )
120  return( FALSE );
121  if( sessionInfoPtr->sendBufPartialBufPos < 0 || \
122  sessionInfoPtr->sendBufPartialBufPos >= sessionInfoPtr->sendBufPos )
123  return( FALSE );
124  if( !sessionInfoPtr->partialWrite )
125  {
126  if( sessionInfoPtr->sendBufPos > sessionInfoPtr->sendBufStartOfs + \
127  sessionInfoPtr->maxPacketSize )
128  return( FALSE );
129  }
130  else
131  {
132  if( sessionInfoPtr->sendBufPartialBufPos >= sessionInfoPtr->sendBufPos )
133  return( FALSE );
134  }
135 
136 
137  return( TRUE );
138  }
139 
140 /****************************************************************************
141 * *
142 * Secure Session Data Read Functions *
143 * *
144 ****************************************************************************/
145 
146 /* The read data code uses a helper function tryRead() that either reads
147  everything which is available or to the end of the current packet. In
148  other words it's an atomic, all-or-nothing function that can be used by
149  higher-level code to handle network-level packetisation.
150 
151  Buffer management is handled as follows: The bufPos index always points
152  to the end of the decoded data (i.e. data that can be used by the user),
153  if there's no partial packet present this index is the same as bufEnd:
154 
155  bPos/bEnd
156  |
157  v
158  ----+------------------------
159  ....|
160  ----+------------------------
161 
162  After readHeaderFunction() has been called pendingPacketRemaining
163  contains the number of bytes required to complete the packet:
164 
165  bPos/bEnd
166  |
167  v
168  ----+-----------------------+----
169  ....| |
170  ----+-----------------------+----
171  |<---- pPL == pPR ----->|
172 
173  tryRead() then attempts to fill the buffer with the packet data, with
174  bufEnd pointing to the end of the received data and advancing as more
175  data is read:
176 
177  bPos bEnd
178  | |
179  v v
180  ----+---------------+-------+----
181  ....|///////////////| |
182  ----+---------------+-------+----
183  | |<-pPR->|
184  |<-------- pPL -------->|
185 
186  When we reach the end of tryRead(), which means that
187  pendingPacketRemaining reaches zero, we process the complete packet in
188  the buffer with processBody():
189 
190  bPos bEnd
191  | |
192  v v
193  ----+-----------------------+----
194  ....|///////////////////////|
195  ----+-----------------------+----
196  |<-------- pPL -------->|
197  | pPR = 0
198 
199  If processBody() completes successfully then bufPos and bufEnd are
200  adjusted to point to the end of the new data:
201 
202  bPos/bEnd
203  |
204  v
205  ----+-----------------------+----
206  ....|.......................|
207  ----+-----------------------+----
208 
209  The handling of any header data present at the start of the packet
210  depends on the packet format, if the header is independent of the
211  encrypted data it's handled entirely by the readHeaderFunction() and
212  there's no need to provide special-case handling. If the header is part
213  of the encrypted data then decryption is a two-stage operation in which
214  readHeaderFunction() decrypts just enough of the packet to extract and
215  process the header (depositing any leftover non-header data at the start
216  of the buffer) and processBodyFunction() processes the rest of the data.
217 
218  Errors in the readHeaderFunction() are fatal if they come from the session
219  protocol level (e.g. a MAC failure or bad packet) and nonfatal if they
220  come from the network layer below the session (the stream-level code has
221  its own handling of fatal vs. nonfatal errors, so we don't try and get
222  down to that level) */
223 
224 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
225 static int tryRead( INOUT SESSION_INFO *sessionInfoPtr,
226  OUT_LENGTH_Z int *bytesRead,
227  OUT_ENUM_OPT( READSTATE ) READSTATE_INFO *readInfo )
228  {
229  int length, bytesLeft, status;
230 
231  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
232  assert( isWritePtr( bytesRead, sizeof( int ) ) );
233  assert( isWritePtr( readInfo, sizeof( READSTATE_INFO ) ) );
234 
235  REQUIRES( sanityCheckRead( sessionInfoPtr ) );
236 
237  /* Clear return values */
238  *bytesRead = 0;
239  *readInfo = READINFO_NONE;
240 
241  /* If there's no pending packet information present, try and read it.
242  This can return one of four classes of values:
243 
244  1. An error code.
245  2. Zero, to indicate that nothing was read.
246  3. OK_SPECIAL and read information READINFO_NOOP to indicate that
247  header data but no payload data was read.
248  4. A byte count and read information READINFO_HEADERPAYLOAD to
249  indicate that some payload data was read as part of the header */
250  if( sessionInfoPtr->pendingPacketLength <= 0 )
251  {
252  status = length = \
253  sessionInfoPtr->readHeaderFunction( sessionInfoPtr, readInfo );
254  if( cryptStatusError( status ) )
255  {
256  /* Anything other than OK_SPECIAL to indicate a no-op read is an
257  error */
258  if( status != OK_SPECIAL )
259  return( status );
260  ENSURES( *readInfo == READINFO_NOOP );
261  }
262  else
263  {
264  /* If nothing was read, we're done */
265  if( length <= 0 )
266  {
267  *bytesRead = 0;
268  return( CRYPT_OK );
269  }
270  }
271  ENSURES( ( status == OK_SPECIAL && *readInfo == READINFO_NOOP ) || \
272  ( length > 0 && *readInfo == READINFO_HEADERPAYLOAD ) );
273  if( *readInfo == READINFO_HEADERPAYLOAD )
274  {
275  /* Some protocols treat the header information for a secured
276  data packet as part of the data so when we read the header we
277  can get part of the payload included in the read. When the
278  protocol-specific header read code obtains some payload data
279  alongside the header it returns READINFO_HEADERPAYLOAD to
280  indicate that the packet information needs to be adjusted for
281  the packet header data that was just read */
282  sessionInfoPtr->receiveBufEnd += length;
283  sessionInfoPtr->pendingPacketRemaining -= length;
284  }
285  }
286  ENSURES( sessionInfoPtr->partialHeaderRemaining == 0 );
287 
288  /* Figure out how much we can read. If there's not enough room in the
289  receive buffer to read at least 1K of packet data, don't try anything
290  until the user has emptied more data from the buffer */
291  bytesLeft = sessionInfoPtr->receiveBufSize - sessionInfoPtr->receiveBufEnd;
292  if( bytesLeft < 1024 )
293  {
294  ENSURES( sanityCheckRead( sessionInfoPtr ) );
295 
296  *bytesRead = 0;
297  return( CRYPT_OK );
298  }
299  if( bytesLeft > sessionInfoPtr->pendingPacketRemaining )
300  {
301  /* Limit the amount of data to read to the remaining packet size */
302  bytesLeft = sessionInfoPtr->pendingPacketRemaining;
303  }
304 
305  /* Try and read more of the packet */
306  ENSURES( rangeCheckZ( sessionInfoPtr->receiveBufEnd, bytesLeft,
307  sessionInfoPtr->receiveBufSize ) );
308  status = length = \
309  sread( &sessionInfoPtr->stream,
310  sessionInfoPtr->receiveBuffer + sessionInfoPtr->receiveBufEnd,
311  bytesLeft );
312  if( cryptStatusError( status ) )
313  {
314  sNetGetErrorInfo( &sessionInfoPtr->stream,
315  &sessionInfoPtr->errorInfo );
316  return( status );
317  }
318  if( length <= 0 )
319  {
320  /* Nothing read, try again later. This happens only if we're using
321  non-blocking reads (i.e. polled I/O), if any kind of timeout is
322  specified then we'll get a timeout error if no data is read */
323  ENSURES( sanityCheckRead( sessionInfoPtr ) );
324 
325  return( 0 );
326  }
327  sessionInfoPtr->receiveBufEnd += length;
328  sessionInfoPtr->pendingPacketRemaining -= length;
329  if( sessionInfoPtr->pendingPacketRemaining > 0 )
330  {
331  /* We got some but not all of the data, try again later */
332  *readInfo = READINFO_PARTIAL;
333 
334  ENSURES( sanityCheckRead( sessionInfoPtr ) );
335 
336  return( OK_SPECIAL );
337  }
338  ENSURES( sessionInfoPtr->pendingPacketRemaining == 0 );
339  ENSURES( sanityCheckRead( sessionInfoPtr ) );
340 
341  /* We've got a complete packet in the buffer, process it */
342  status = length = \
343  sessionInfoPtr->processBodyFunction( sessionInfoPtr, readInfo );
344  if( cryptStatusError( status ) )
345  return( status );
346 
347  /* Adjust the data size indicators to account for the processed packet */
348  sessionInfoPtr->receiveBufEnd = sessionInfoPtr->receiveBufPos + length;
349  sessionInfoPtr->receiveBufPos = sessionInfoPtr->receiveBufEnd;
350  sessionInfoPtr->pendingPacketLength = 0;
351  *bytesRead = length;
352 
353  ENSURES( sanityCheckRead( sessionInfoPtr ) );
354 
355  return( CRYPT_OK );
356  }
357 
358 /* Get data from the remote system */
359 
360 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
361 static int getData( INOUT SESSION_INFO *sessionInfoPtr,
362  OUT_BUFFER( length, *bytesCopied ) BYTE *buffer,
363  IN_LENGTH const int length,
365  {
366  const int bytesToCopy = min( length, sessionInfoPtr->receiveBufPos );
368  int bytesRead, status;
369 
370  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
371  assert( isWritePtr( bytesCopied, sizeof( int ) ) );
372 
373  REQUIRES( length > 0 && length < MAX_INTLENGTH );
374  REQUIRES( bytesToCopy >= 0 && bytesToCopy < MAX_INTLENGTH );
375  REQUIRES( sanityCheckRead( sessionInfoPtr ) );
376 
377  /* Clear return values */
378  memset( buffer, 0, min( 16, length ) );
379  *bytesCopied = 0;
380 
381  /* Copy over as much data as we can and move any remaining data down to
382  the start of the receive buffer. We copy out up to receiveBufPos,
383  the end of the decoded data, but move up to receiveBufEnd, the
384  combined decoded data and any as-yet-undecoded partial data that
385  follows the decoded data */
386  if( bytesToCopy > 0 )
387  {
388  const int remainder = sessionInfoPtr->receiveBufEnd - bytesToCopy;
389 
390  ENSURES( remainder >= 0 && remainder < MAX_INTLENGTH );
391 
392  memcpy( buffer, sessionInfoPtr->receiveBuffer, bytesToCopy );
393  if( remainder > 0 )
394  {
395  /* There's decoded and/or non-decoded data left, move it down to
396  the start of the buffer */
397  ENSURES( rangeCheck( bytesToCopy, remainder,
398  sessionInfoPtr->receiveBufEnd ) );
399  memmove( sessionInfoPtr->receiveBuffer,
400  sessionInfoPtr->receiveBuffer + bytesToCopy, remainder );
401  sessionInfoPtr->receiveBufPos -= bytesToCopy;
402  sessionInfoPtr->receiveBufEnd = remainder;
403  }
404  else
405  {
406  /* We've consumed all of the data in the buffer, reset the buffer
407  information */
408  sessionInfoPtr->receiveBufPos = sessionInfoPtr->receiveBufEnd = 0;
409  }
410 
411  /* Remember how much we've copied and, if we've satisfied the
412  request, exit */
413  *bytesCopied = bytesToCopy;
414  if( bytesToCopy >= length )
415  {
416  ENSURES( sanityCheckRead( sessionInfoPtr ) );
417 
418  return( CRYPT_OK );
419  }
420  }
421  ENSURES( sessionInfoPtr->receiveBufPos == 0 );
422 
423  /* Try and read a complete packet. This can return one of four classes
424  of values:
425 
426  1. An error code.
427  2. Zero to indicate that nothing was read (only happens on non-
428  blocking reads performing polled I/O, a blocking read will return
429  a timeout error) or that there isn't enough room left in the read
430  buffer to read any more.
431  3a.OK_SPECIAL and read information READINFO_PARTIAL to indicate that
432  a partial packet (not enough to process) was read.
433  3b.OK_SPECIAL and read information READINFO_NOOP to indicate that a
434  no-op packet was read and the caller should try again without
435  changing the read timeout value.
436  4. A byte count if a complete packet was read and processed */
437  status = tryRead( sessionInfoPtr, &bytesRead, &readInfo );
438  if( cryptStatusError( status ) && status != OK_SPECIAL )
439  {
440  /* If there's an error reading data, only return an error status if
441  we haven't already returned all existing/earlier data. This
442  ensures that the caller can drain out any remaining data from the
443  session buffer before they start getting error returns */
444  if( *bytesCopied <= 0 )
445  {
446  if( readInfo == READINFO_FATAL )
447  sessionInfoPtr->readErrorState = status;
448  return( status );
449  }
450 
451  /* We got some data before encountering the error, if it's fatal
452  save the pending error state for later while returning the read
453  byte count to the caller. Note that this results in non-fatal
454  errors being quietly dropped if data is otherwise available, the
455  alternative would be to save it as a pending (specially-marked)
456  non-fatal error, however since this error type by definition can
457  be resumed it may already have resolved itself by the next time
458  that we're called so this is safe to do */
459  if( readInfo == READINFO_FATAL )
460  sessionInfoPtr->pendingReadErrorState = status;
461  return( OK_SPECIAL );
462  }
463 
464  /* If we read a partial packet and there's room for the rest of the
465  packet in the buffer, set a minimum timeout to try and get the rest
466  of the packet. This is safe because tryRead() could have behaved in
467  only one of two ways:
468 
469  1. Blocking read, in which case we waited for the full timeout
470  period anyway and a small additional timeout won't be noticed.
471  2. Nonblocking read, in which case waiting for a nonzero time could
472  potentially have retrieved more data */
473  if( status == OK_SPECIAL )
474  {
475  REQUIRES( readInfo == READINFO_PARTIAL || \
476  readInfo == READINFO_NOOP );
477  if( readInfo == READINFO_PARTIAL && \
478  sessionInfoPtr->pendingPacketRemaining <= \
479  sessionInfoPtr->receiveBufSize - sessionInfoPtr->receiveBufEnd )
480  {
481  sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_READTIMEOUT, 1 );
482  }
483 
484  ENSURES( sanityCheckRead( sessionInfoPtr ) );
485 
486  return( CRYPT_OK );
487  }
488  ENSURES( cryptStatusOK( status ) );
489 
490  /* If we got nothing, exit */
491  if( bytesRead <= 0 )
492  {
493  ENSURES( sanityCheckRead( sessionInfoPtr ) );
494 
495  return( OK_SPECIAL );
496  }
497 
498  /* Make the stream nonblocking if it was blocking before. This is
499  necessary to avoid having the stream always block for the set timeout
500  value on the last read */
501  ENSURES( bytesRead > 0 && bytesRead < MAX_INTLENGTH );
502  sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_READTIMEOUT, 0 );
503 
504  ENSURES( sanityCheckRead( sessionInfoPtr ) );
505 
506  return( CRYPT_OK );
507  }
508 
509 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
510 int getSessionData( INOUT SESSION_INFO *sessionInfoPtr,
511  OUT_BUFFER( dataMaxLength, *bytesCopied ) void *data,
512  IN_LENGTH const int dataMaxLength,
513  OUT_LENGTH_Z int *bytesCopied )
514  {
515  BYTE *dataPtr = data;
516  int dataLength = dataMaxLength, iterationCount, status = CRYPT_OK;
517 
518  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
519  assert( isWritePtr( bytesCopied, sizeof( int ) ) );
520 
521  REQUIRES( dataMaxLength > 0 && dataMaxLength < MAX_INTLENGTH );
522  REQUIRES( sanityCheckRead( sessionInfoPtr ) );
523 
524  /* Clear return values */
525  memset( data, 0, min( 16, dataMaxLength ) );
526  *bytesCopied = 0;
527 
528  /* If there's an error pending (which will always be fatal, see the
529  comment after the tryRead() call in getData()) set the current error
530  state to the pending state and return */
531  if( cryptStatusError( sessionInfoPtr->pendingReadErrorState ) )
532  {
533  REQUIRES( sessionInfoPtr->receiveBufPos == 0 );
534 
535  status = sessionInfoPtr->readErrorState = \
536  sessionInfoPtr->pendingReadErrorState;
537  sessionInfoPtr->pendingReadErrorState = CRYPT_OK;
538  return( status );
539  }
540 
541  /* Update the stream read timeout to the current user-selected read
542  timeout in case the user has changed the timeout setting */
543  sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_READTIMEOUT,
544  sessionInfoPtr->readTimeout );
545 
546  for( iterationCount = 0;
547  dataLength > 0 && iterationCount < FAILSAFE_ITERATIONS_MAX;
548  iterationCount++ )
549  {
550  int count;
551 
552  /* Get the next packets-worth of data. This can return one of three
553  classes of values:
554 
555  1. An error code.
556  2. OK_SPECIAL to indicate that some data was read but no more is
557  available.
558  3. CRYPT_OK to indicate that data was read and more may be
559  available */
560  status = getData( sessionInfoPtr, dataPtr, dataLength, &count );
561  if( cryptStatusError( status ) && status != OK_SPECIAL )
562  break;
563 
564  /* We got at least some data, update the buffer indicators */
565  if( count > 0 )
566  {
567  *bytesCopied += count;
568  dataPtr += count;
569  dataLength -= count;
570  }
571  if( status == OK_SPECIAL )
572  {
573  /* The was the last of the data, exit */
574  break;
575  }
576  }
577  ENSURES( iterationCount < FAILSAFE_ITERATIONS_MAX );
578 
579  ENSURES( sanityCheckRead( sessionInfoPtr ) );
580 
581  /* If we got at least some data or encountered a soft timeout then the
582  operation was (nominally) successful, otherwise it's an error */
583  return( ( *bytesCopied > 0 || status == OK_SPECIAL ) ? \
584  CRYPT_OK : status );
585  }
586 
587 /* Read a fixed-size packet header, called by the secure data session
588  routines to read the fixed header on a data packet. There are two
589  variations of this, an atomic-read readFixedHeaderAtomic() used during
590  the handshake phase that requires all data to be read and treats timeouts
591  as hard errors and a partial-read readFixedHeader() used during the
592  data-transfer phase that treats timeouts as soft errors.
593 
594  Buffer handling for the soft-timeout version is as follows:
595 
596  | <- hdrSize -> |
597  +---------------+
598  |///////| |
599  +---------------+
600  |<--+-->|
601  |
602  partialHdrRem
603 
604  The data is read into the header buffer until partialHeaderRemaining
605  drops to zero. The function returns OK_SPECIAL until this happens */
606 
607 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
608 int readFixedHeaderAtomic( INOUT SESSION_INFO *sessionInfoPtr,
609  OUT_BUFFER_FIXED( headerLength ) void *headerBuffer,
611  const int headerLength )
612  {
613  int length, status;
614 
615  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
616  assert( isWritePtr( headerBuffer, sizeof( headerLength ) ) );
617 
618  REQUIRES( headerLength >= FIXED_HEADER_MIN && \
619  headerLength <= FIXED_HEADER_MAX );
620  REQUIRES( sanityCheckRead( sessionInfoPtr ) );
621 
622  /* Clear return value */
623  memset( headerBuffer, 0, min( 16, headerLength ) );
624 
625  /* Try and read the remaining header bytes */
626  status = length = \
627  sread( &sessionInfoPtr->stream, headerBuffer, headerLength );
628  if( cryptStatusError( status ) )
629  {
630  /* We could be trying to read an ack for a close packet sent in
631  response to an earlier error, in which case we don't want the
632  already-present error information overwritten by network
633  error information, so if the no-report-error flag is set we
634  don't update the extended error information */
635  if( sessionInfoPtr->flags & SESSION_NOREPORTERROR )
636  return( status );
637 
638  sNetGetErrorInfo( &sessionInfoPtr->stream,
639  &sessionInfoPtr->errorInfo );
640  return( status );
641  }
642 
643  /* We've timed out during the handshake phase, it's a hard timeout
644  error */
645  if( length != headerLength )
646  {
647  if( sessionInfoPtr->flags & SESSION_NOREPORTERROR )
648  return( status );
651  "Timeout during packet header read, only got %d of %d "
652  "bytes", length, headerLength ) );
653  }
654  ENSURES( sanityCheckRead( sessionInfoPtr ) );
655 
656  return( CRYPT_OK );
657  }
658 
659 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
660 int readFixedHeader( INOUT SESSION_INFO *sessionInfoPtr,
661  OUT_BUFFER_FIXED( headerLength ) void *headerBuffer,
663  const int headerLength )
664  {
665  BYTE *bufPtr = headerBuffer;
666  int bytesToRead, length, status;
667 
668  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
669  assert( isWritePtr( headerBuffer, sizeof( headerLength ) ) );
670 
671  REQUIRES( headerLength >= FIXED_HEADER_MIN && \
672  headerLength <= FIXED_HEADER_MAX );
673  REQUIRES( sanityCheckRead( sessionInfoPtr ) );
674 
675  /* We can't clear the return value at this point because there may
676  already be a partial header present in the buffer */
677 
678  /* If it's the first attempt at reading the header, set the total byte
679  count */
680  if( sessionInfoPtr->partialHeaderRemaining <= 0 )
681  {
682  sessionInfoPtr->partialHeaderRemaining = headerLength;
683  bytesToRead = headerLength;
684  }
685  else
686  {
687  /* We've already got a partial header present in the buffer, read
688  the remaining header data. Note that the existing partial header
689  size may be zero (i.e. partialHeaderRemaining == headerLength)
690  if we got a soft-timeout on a previous call to readFixedHeader().
691  This happens on any read in which the peer has sent only a single
692  packet and the packet fits entirely in the read buffer, and occurs
693  because we follow up every full packet read with an opportunistic
694  zero-timeout second read to check if further packets are
695  pending */
696  bufPtr += headerLength - sessionInfoPtr->partialHeaderRemaining;
697  bytesToRead = sessionInfoPtr->partialHeaderRemaining;
698  }
699  ENSURES( bytesToRead > 0 && bytesToRead <= headerLength );
700  ENSURES( sessionInfoPtr->partialHeaderRemaining > 0 && \
701  sessionInfoPtr->partialHeaderRemaining <= headerLength );
702 
703  /* Now we can clear the return value */
704  memset( bufPtr, 0, min( 16, bytesToRead ) );
705 
706  /* Try and read the remaining header bytes */
707  ENSURES( rangeCheckZ( headerLength - sessionInfoPtr->partialHeaderRemaining,
708  bytesToRead, headerLength ) );
709  status = length = \
710  sread( &sessionInfoPtr->stream, bufPtr, bytesToRead );
711  if( cryptStatusError( status ) )
712  {
713  /* We could be trying to read an ack for a close packet sent in
714  response to an earlier error, in which case we don't want the
715  already-present error information overwritten by network
716  error information, so if the no-report-error flag is set we
717  don't update the extended error information */
718  if( sessionInfoPtr->flags & SESSION_NOREPORTERROR )
719  return( status );
720 
721  sNetGetErrorInfo( &sessionInfoPtr->stream,
722  &sessionInfoPtr->errorInfo );
723  return( status );
724  }
725  sessionInfoPtr->partialHeaderRemaining -= length;
726 
727  /* If we didn't get the whole header, treat it as a soft timeout error */
728  if( sessionInfoPtr->partialHeaderRemaining > 0 )
729  {
730  ENSURES( sanityCheckRead( sessionInfoPtr ) );
731 
732  return( OK_SPECIAL );
733  }
734 
735  /* We've got the whole header ready to process */
736  ENSURES( sessionInfoPtr->partialHeaderRemaining == 0 );
737 
738  ENSURES( sanityCheckRead( sessionInfoPtr ) );
739 
740  return( CRYPT_OK );
741  }
742 
743 /****************************************************************************
744 * *
745 * Secure Session Data Write Functions *
746 * *
747 ****************************************************************************/
748 
749 /* Get the amount of space remaining in the send buffer
750 
751  startOfs bufPos
752  | |
753  v v
754  +-------+-----------+-----------+---+
755  |.......|///////////|...........| |
756  +-------+-----------+-----------+---+
757  |<----- maxPacket ----->|
758  |<- remain->| */
759 
761 static int getRemainingBufferSpace( const SESSION_INFO *sessionInfoPtr )
762  {
763  const int currentByteCount = sessionInfoPtr->sendBufPos - \
764  sessionInfoPtr->sendBufStartOfs;
765  int remainingByteCount;
766 
767  assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
768 
769  REQUIRES( currentByteCount >= 0 && \
770  currentByteCount <= sessionInfoPtr->maxPacketSize && \
771  currentByteCount < MAX_INTLENGTH );
772  remainingByteCount = sessionInfoPtr->maxPacketSize - currentByteCount;
773  ENSURES( remainingByteCount >= 0 && remainingByteCount < MAX_INTLENGTH );
774 
775  return( remainingByteCount );
776  }
777 
778 /* Send data to the remote system. There are two strategies for handling
779  buffer filling and partial writes, either to fill the buffer as full as
780  possible and write it all at once, or to write complete packets as soon
781  as they're available. We use the latter strategy both because it
782  considerably simplifies buffer management and because interleaving
783  (asynchronous) writes and packet processing increases the chances that
784  the current packet will be successfully dispatched across the network
785  while the next one is being encrypted - trying to asynchronously write a
786  large amount of data in one go practically guarantees that the write
787  won't complete.
788 
789  Session buffer management is handled as follows: The startOfs index
790  points to the start of the payload space in the buffer (everything before
791  this is header data). The maxPacketSize value indicates the end of the
792  payload space relative to the startOfs:
793 
794  <- hdr->|<-- payload -->|
795  +-------+---------------+---+
796  | |///////////////| |
797  +-------+---------------+---+
798  ^ ^
799  | |
800  startOfs maxPacketSize
801 
802  The bufPos index moves from startsOfs to maxPacketSize after which the
803  data is wrapped up by the protocol-specific code. At this point bufPos
804  usually points past the end of maxPacketSize due to the addition of
805  trailer data such as encryption block padding and a MAC. Once the
806  packet is assembled, the data is flushed and the bufPos index reset to
807  startOfs:
808 
809  startOfs maxPacketSize
810  | |
811  v v
812  +-------+-------+-------+---+
813  |.......|.......|///////|///|
814  +-------+-------+-------+---+
815  |<-- writtem -->^<--- to -->^
816  | write |
817  partialBufPos bufPos
818 
819  As with reads, writes can be non-atomic, although on a more restrictive
820  scale than reads: Once an encrypted packet has been assembled in the
821  write buffer the entire contents must be written before a new packet can
822  be assembled. This guarantees that when the caller flushes data through
823  to the other side all of the data will be sent (and the other side will
824  have a chance to react to it) before the next load of data can be flushed
825  through.
826 
827  Once we have partial data in the send buffer all further attempts to add
828  more data fail until the remainder of the partially-written data has been
829  flushed. This is handled by setting sendBufPartialBufPos to point to the
830  first byte of unwritten data, so that sendBufPartialBufPos ... sendBufPos
831  remains to be written */
832 
834 static int flushData( SESSION_INFO *sessionInfoPtr )
835  {
836  int bytesToWrite, length, status;
837 
838  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
839 
840  REQUIRES( sanityCheckWrite( sessionInfoPtr ) );
841 
842  /* If there's no data to flush, exit */
843  if( sessionInfoPtr->sendBufPos <= sessionInfoPtr->sendBufStartOfs )
844  return( CRYPT_OK );
845 
846  /* If there's no unwritten data from a previous write attempt still
847  present, prepare to send the new data */
848  if( !sessionInfoPtr->partialWrite )
849  {
850  ENSURES( sessionInfoPtr->sendBufPartialBufPos == 0 );
851 
852  status = length = \
853  sessionInfoPtr->preparePacketFunction( sessionInfoPtr );
854  if( cryptStatusError( status ) )
855  {
856  /* Errors in the crypto are immediately fatal */
857  sessionInfoPtr->writeErrorState = status;
858  return( status );
859  }
860 
861  /* Adjust the buffer position to account for the wrapped packet
862  size */
863  sessionInfoPtr->sendBufPos = length;
864  ENSURES( sessionInfoPtr->sendBufPos > 0 && \
865  sessionInfoPtr->sendBufPos <= sessionInfoPtr->sendBufSize );
866  }
867  bytesToWrite = sessionInfoPtr->sendBufPos - \
868  sessionInfoPtr->sendBufPartialBufPos;
869  ENSURES( bytesToWrite > 0 && bytesToWrite < MAX_INTLENGTH );
870 
871  /* Send the data through to the remote system */
872  ENSURES( rangeCheckZ( sessionInfoPtr->sendBufPartialBufPos, bytesToWrite,
873  sessionInfoPtr->sendBufPos ) );
874  status = length = swrite( &sessionInfoPtr->stream,
875  sessionInfoPtr->sendBuffer + \
876  sessionInfoPtr->sendBufPartialBufPos,
877  bytesToWrite );
878  if( cryptStatusError( status ) && status != CRYPT_ERROR_TIMEOUT )
879  {
880  /* There was an error other than a (restartable) send timeout,
881  return the error details to the caller */
882  sessionInfoPtr->writeErrorState = status;
883  sNetGetErrorInfo( &sessionInfoPtr->stream,
884  &sessionInfoPtr->errorInfo );
885  return( status );
886  }
887 
888  /* If the send timed out before all of the data could be written,
889  record how much still remains to be sent and inform the caller. We
890  return this special-case code rather than either a timeout or
891  CRYPT_OK / 0 bytes because the caller can turn this into a situation-
892  specific status at the higher level, a timeout error for an explicit
893  flush but a CRYPT_OK / 0 for an implicit flush performed as part of a
894  write */
895  if( status == CRYPT_ERROR_TIMEOUT )
896  {
897  /* We timed out with nothing written, let the caller know */
898  sessionInfoPtr->partialWrite = TRUE;
899  ENSURES( sanityCheckWrite( sessionInfoPtr ) );
900 
901  return( OK_SPECIAL );
902  }
903  if( length < bytesToWrite )
904  {
905  /* We wrote at least some part of the packet, adjust the partial-
906  write position by the amount that we wrote */
907  sessionInfoPtr->sendBufPartialBufPos += length;
908  sessionInfoPtr->partialWrite = TRUE;
909  ENSURES( sanityCheckWrite( sessionInfoPtr ) );
910 
911  return( OK_SPECIAL );
912  }
913 
914  ENSURES( length == bytesToWrite );
915 
916  /* We sent everything, reset the buffer status values */
917  sessionInfoPtr->sendBufPos = sessionInfoPtr->sendBufStartOfs;
918  sessionInfoPtr->partialWrite = FALSE;
919  sessionInfoPtr->sendBufPartialBufPos = 0;
920 
921  ENSURES( sanityCheckWrite( sessionInfoPtr ) );
922 
923  return( CRYPT_OK );
924  }
925 
926 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 4 ) ) \
927 int putSessionData( INOUT SESSION_INFO *sessionInfoPtr,
928  IN_BUFFER_OPT( dataLength ) const void *data,
929  IN_LENGTH_Z const int dataLength,
930  OUT_LENGTH_Z int *bytesCopied )
931  {
932  BYTE *dataPtr = ( BYTE * ) data;
933  int length = dataLength, availableBuffer, iterationCount, status;
934 
935  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
936  assert( data == NULL || isReadPtr( data, dataLength ) );
937  assert( isWritePtr( bytesCopied, sizeof( int ) ) );
938 
939  REQUIRES( ( data == NULL && dataLength == 0 ) || \
940  ( data != NULL && \
941  dataLength > 0 && dataLength < MAX_INTLENGTH ) );
942  REQUIRES( sanityCheckWrite( sessionInfoPtr ) );
943 
944  /* Clear return value */
945  *bytesCopied = 0;
946 
947  /* If there's an error pending (which will always be fatal, see the
948  comment after the flushData() call below), set the current error state
949  to the pending state and return */
950  if( cryptStatusError( sessionInfoPtr->pendingWriteErrorState ) )
951  {
952  REQUIRES( sessionInfoPtr->receiveBufPos == 0 );
953 
954  status = sessionInfoPtr->writeErrorState = \
955  sessionInfoPtr->pendingWriteErrorState;
956  sessionInfoPtr->pendingWriteErrorState = CRYPT_OK;
957  return( status );
958  }
959 
960  /* Update the stream write timeout to the current user-selected write
961  timeout in case the user has changed the timeout setting */
962  sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_WRITETIMEOUT,
963  sessionInfoPtr->writeTimeout );
964 
965  /* If it's a flush, send the data through to the server. If there's a
966  timeout error during an explicit flush (that is, some but not all of
967  the data is written, so it's a soft timeout) it's converted into an
968  explicit hard timeout failure */
969  if( length <= 0 )
970  {
971  const int oldBufPos = sessionInfoPtr->sendBufPartialBufPos;
972  int bytesWritten;
973 
974  status = flushData( sessionInfoPtr );
975  if( status != OK_SPECIAL )
976  return( status );
977 
978  /* Since a partial write isn't a network-level error condition (it's
979  only treated as a problem once it gets to the putSessionData()
980  layer) there's no extended error information set for it so we
981  have to set the error information here when we turn the partial
982  write into a timeout error */
983  bytesWritten = sessionInfoPtr->sendBufPartialBufPos - oldBufPos;
984  if( bytesWritten > 0 )
985  {
988  "Timeout during flush, only %d bytes were written "
989  "before the timeout of %d seconds expired",
990  sessionInfoPtr->sendBufPartialBufPos,
991  sessionInfoPtr->writeTimeout ) );
992  }
995  "Timeout during flush, no data could be written before "
996  "the timeout of %d seconds expired",
997  sessionInfoPtr->writeTimeout ) );
998  }
999 
1000  /* If there's unwritten data from a previous write still in the buffer,
1001  flush that through first. Since this isn't an explicit flush by the
1002  caller we convert a soft timeout indicator into CRYPT_OK / 0 bytes */
1003  if( sessionInfoPtr->partialWrite )
1004  {
1005  status = flushData( sessionInfoPtr );
1006  if( cryptStatusError( status ) )
1007  return( ( status == OK_SPECIAL ) ? CRYPT_OK : status );
1008  }
1009 
1010  /* If there's too much data to fit into the send buffer we need to send
1011  it through to the host to make room for more */
1012  availableBuffer = getRemainingBufferSpace( sessionInfoPtr );
1013  if( cryptStatusError( availableBuffer ) )
1014  return( availableBuffer );
1015  for( iterationCount = 0;
1016  length > availableBuffer && iterationCount < FAILSAFE_ITERATIONS_LARGE;
1017  iterationCount++ )
1018  {
1019  ENSURES( availableBuffer >= 0 && availableBuffer <= length );
1020 
1021  /* Copy in as much data as we have room for and send it through. The
1022  flush can return one of three classes of values:
1023 
1024  1. An error code, but not CRYPT_ERROR_TIMEOUT, which is handled
1025  as case (2) below.
1026  2. OK_SPECIAL to indicate that some of the requested data
1027  (possibly 0 bytes) was written.
1028  3. CRYPT_OK to indicate that all of the requested data was
1029  written and more can be written if necessary */
1030  if( availableBuffer > 0 )
1031  {
1032  ENSURES( rangeCheck( sessionInfoPtr->sendBufPos, availableBuffer,
1033  sessionInfoPtr->sendBufSize ) );
1034  memcpy( sessionInfoPtr->sendBuffer + sessionInfoPtr->sendBufPos,
1035  dataPtr, availableBuffer );
1036  sessionInfoPtr->sendBufPos += availableBuffer;
1037  dataPtr += availableBuffer;
1038  length -= availableBuffer;
1039  *bytesCopied += availableBuffer;
1040  }
1041  status = flushData( sessionInfoPtr );
1042  if( cryptStatusError( status ) )
1043  {
1044  /* If it's a soft timeout indicator convert it to a CRYPT_OK /
1045  0 bytes written */
1046  if( status == OK_SPECIAL )
1047  {
1048  ENSURES( sanityCheckWrite( sessionInfoPtr ) );
1049 
1050  return( CRYPT_OK );
1051  }
1052 
1053  /* There was a problem flushing the data through, if we managed
1054  to copy anything into the buffer we've made some progress so
1055  we defer it until the next call */
1056  if( *bytesCopied > 0 )
1057  {
1058  sessionInfoPtr->pendingWriteErrorState = status;
1059 
1060  ENSURES( sanityCheckWrite( sessionInfoPtr ) );
1061 
1062  return( CRYPT_OK );
1063  }
1064 
1065  /* Nothing was copied before the error occurred, it's
1066  immediately fatal */
1067  return( status );
1068  }
1069 
1070  /* We've flushed the buffer, update the available-space value */
1071  availableBuffer = getRemainingBufferSpace( sessionInfoPtr );
1072  if( cryptStatusError( availableBuffer ) )
1073  return( availableBuffer );
1074  }
1075  ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE );
1076 
1077  /* If there's anything left, it'll fit completely into the send buffer,
1078  just copy it in */
1079  if( length > 0 )
1080  {
1081  ENSURES( length < availableBuffer );
1082 
1083  ENSURES( rangeCheck( sessionInfoPtr->sendBufPos, length,
1084  sessionInfoPtr->maxPacketSize ) );
1085  memcpy( sessionInfoPtr->sendBuffer + sessionInfoPtr->sendBufPos,
1086  dataPtr, length );
1087  sessionInfoPtr->sendBufPos += length;
1088  *bytesCopied += length;
1089  }
1090 
1091  ENSURES( sanityCheckWrite( sessionInfoPtr ) );
1092 
1093  return( CRYPT_OK );
1094  }
1095 
1096 /****************************************************************************
1097 * *
1098 * Request/response Session Data Handling Functions *
1099 * *
1100 ****************************************************************************/
1101 
1102 /* Read/write a PKI (i.e. ASN.1-encoded) datagram. Unlike the secure
1103  session protocols these operations are always atomic so the read/write
1104  process is much simpler */
1105 
1106 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
1107 int readPkiDatagram( INOUT SESSION_INFO *sessionInfoPtr )
1108  {
1109  HTTP_DATA_INFO httpDataInfo;
1110  int length = DUMMY_INIT, status;
1111 
1112  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
1113 
1114  /* Read the datagram */
1115  sessionInfoPtr->receiveBufEnd = 0;
1116 #ifdef USE_CMP_TRANSPORT
1117  if( sessionInfoPtr->flags & SESSION_ISHTTPTRANSPORT )
1118  {
1119  initHttpDataInfo( &httpDataInfo, sessionInfoPtr->receiveBuffer,
1120  sessionInfoPtr->receiveBufSize );
1121  status = sread( &sessionInfoPtr->stream, &httpDataInfo,
1122  sizeof( HTTP_DATA_INFO ) );
1123  if( !cryptStatusError( status ) )
1124  length = httpDataInfo.bytesAvail;
1125  }
1126  else
1127  {
1128  status = length = \
1129  sread( &sessionInfoPtr->stream,
1130  sessionInfoPtr->receiveBuffer,
1131  sessionInfoPtr->receiveBufSize );
1132  }
1133 #else
1134  initHttpDataInfo( &httpDataInfo, sessionInfoPtr->receiveBuffer,
1135  sessionInfoPtr->receiveBufSize );
1136  status = sread( &sessionInfoPtr->stream, &httpDataInfo,
1137  sizeof( HTTP_DATA_INFO ) );
1138 #endif /* USE_CMP_TRANSPORT */
1139  if( cryptStatusError( status ) )
1140  {
1141  sNetGetErrorInfo( &sessionInfoPtr->stream,
1142  &sessionInfoPtr->errorInfo );
1143  return( status );
1144  }
1145  length = httpDataInfo.bytesAvail;
1146  if( length < 4 || length >= MAX_INTLENGTH )
1147  {
1148  /* Perform a sanity check on the length. This avoids triggering
1149  assertions in the debug build and provides somewhat more specific
1150  information for the caller than the invalid-encoding error that
1151  we'd get later */
1154  "Invalid PKI message length %d", status ) );
1155  }
1156 
1157  /* Find out how much data we got and perform a firewall check that
1158  everything is OK. We rely on this rather than the read byte count
1159  since checking the ASN.1, which is the data that will actually be
1160  processed, avoids any potential problems with implementations where
1161  the transport layer gets data lengths slightly wrong */
1162  status = length = checkObjectEncoding( sessionInfoPtr->receiveBuffer,
1163  length );
1164  if( cryptStatusError( status ) )
1165  {
1166  retExt( status,
1167  ( status, SESSION_ERRINFO,
1168  "Invalid PKI message encoding" ) );
1169  }
1170  sessionInfoPtr->receiveBufEnd = length;
1171  return( CRYPT_OK );
1172  }
1173 
1174 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
1175 int writePkiDatagram( INOUT SESSION_INFO *sessionInfoPtr,
1177  const char *contentType,
1179  {
1180  HTTP_DATA_INFO httpDataInfo;
1181  int status;
1182 
1183  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
1184  assert( contentType == NULL || \
1185  isReadPtr( contentType, contentTypeLength ) );
1186 
1187  REQUIRES( ( contentType == NULL && contentTypeLength ) || \
1188  ( contentType != NULL && \
1189  contentTypeLength > 0 && \
1190  contentTypeLength < MAX_INTLENGTH_SHORT ) );
1191  REQUIRES( sessionInfoPtr->receiveBufEnd > 4 && \
1192  sessionInfoPtr->receiveBufEnd < MAX_INTLENGTH );
1193 
1194  /* Write the datagram. Request/response sessions use a single buffer
1195  for both reads and writes, which is why we're (apparently) writing
1196  the contents of the read buffer */
1197 #ifdef USE_CMP_TRANSPORT
1198  if( sessionInfoPtr->flags & SESSION_ISHTTPTRANSPORT )
1199  {
1200  initHttpDataInfo( &httpDataInfo, sessionInfoPtr->receiveBuffer,
1201  sessionInfoPtr->receiveBufEnd );
1202  httpDataInfo.contentType = contentType;
1203  httpDataInfo.contentTypeLen = contentTypeLength;
1204  status = swrite( &sessionInfoPtr->stream, &httpDataInfo,
1205  sizeof( HTTP_DATA_INFO ) );
1206  }
1207  else
1208  {
1209  status = swrite( &sessionInfoPtr->stream,
1210  sessionInfoPtr->receiveBuffer,
1211  sessionInfoPtr->receiveBufEnd );
1212  }
1213 #else
1214  initHttpDataInfo( &httpDataInfo, sessionInfoPtr->receiveBuffer,
1215  sessionInfoPtr->receiveBufEnd );
1216  httpDataInfo.contentType = contentType;
1217  httpDataInfo.contentTypeLen = contentTypeLength;
1218  status = swrite( &sessionInfoPtr->stream, &httpDataInfo,
1219  sizeof( HTTP_DATA_INFO ) );
1220 #endif /* USE_CMP_TRANSPORT */
1221  if( cryptStatusError( status ) )
1222  sNetGetErrorInfo( &sessionInfoPtr->stream,
1223  &sessionInfoPtr->errorInfo );
1224  sessionInfoPtr->receiveBufEnd = 0;
1225 
1226  return( CRYPT_OK ); /* swrite() returns a byte count */
1227  }
1228 #endif /* USE_SESSIONS */