cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
stream.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * Stream I/O Functions *
4 * Copyright Peter Gutmann 1993-2007 *
5 * *
6 ****************************************************************************/
7 
8 #include <stdio.h>
9 #include <stdarg.h>
10 #if defined( INC_ALL )
11  #include "stream_int.h"
12 #else
13  #include "io/stream_int.h"
14 #endif /* Compiler-specific includes */
15 
16 /* Prototypes for functions in file.c */
17 
19 int fileRead( STREAM *stream,
20  OUT_BUFFER( length, *bytesRead ) void *buffer,
21  IN_LENGTH const int length,
22  OUT_LENGTH_Z int *bytesRead );
23 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
24 int fileWrite( STREAM *stream,
25  IN_BUFFER( length ) const void *buffer,
26  IN_LENGTH const int length );
28 int fileFlush( STREAM *stream );
30 int fileSeek( STREAM *stream, IN_LENGTH_Z const long position );
31 
32 /****************************************************************************
33 * *
34 * Utility Functions *
35 * *
36 ****************************************************************************/
37 
38 /* Sanity-check the stream state */
39 
41 static BOOLEAN sanityCheck( const STREAM *stream )
42  {
43  assert( isReadPtr( stream, sizeof( STREAM ) ) );
44 
45  /* Perform stream type-specific checks */
46  switch( stream->type )
47  {
48  case STREAM_TYPE_NULL:
49  if( stream->flags )
50  return( FALSE );
51  break;
52 
53  case STREAM_TYPE_MEMORY:
54  if( stream->flags & STREAM_MFLAG_VFILE )
55  {
56  if( stream->flags & ~( STREAM_FLAG_MASK | STREAM_MFLAG_VFILE | \
58  return( FALSE );
59  }
60  else
61  {
62  if( stream->flags & ~STREAM_MFLAG_MASK )
63  return( FALSE );
64  }
65  break;
66 
67  case STREAM_TYPE_FILE:
68  if( stream->flags & ~STREAM_FFLAG_MASK )
69  return( FALSE );
70  break;
71 
72 #ifdef USE_TCP
74  {
75  NET_STREAM_INFO *netStream = \
76  ( NET_STREAM_INFO * ) stream->netStreamInfo;
77 
78  if( netStream->timeout < 0 || netStream->timeout > 300 )
79  return( FALSE );
80  break;
81  }
82 #endif /* USE_TCP */
83 
84  default:
85  return( FALSE );
86  }
87 
88  /* Null streams have no internal buffer so the buffer position
89  indicators aren't used */
90  if( stream->type == STREAM_TYPE_NULL )
91  {
92  /* Null streams, which act as data sinks, have a virtual content-
93  length indicator so although the buffer size is zero the buffer
94  position values can be nonzero to indicate how much (virtual)
95  data they've absorbed */
96  if( stream->buffer != NULL || stream->bufSize != 0 )
97  return( FALSE );
98  if( stream->bufPos < 0 || stream->bufPos > stream->bufEnd ||
99  stream->bufEnd < 0 || stream->bufEnd >= MAX_INTLENGTH )
100  return( FALSE );
101 
102  return( TRUE );
103  }
104 
105 #ifdef USE_TCP
106  /* Network streams may be buffered, but if they're not then the internal
107  buffer indicators aren't used */
108  if( stream->type == STREAM_TYPE_NETWORK )
109  {
110  NET_STREAM_INFO *netStream = \
111  ( NET_STREAM_INFO * ) stream->netStreamInfo;
112 
113  /* If it's an unbuffered network stream then all buffer values must
114  be zero */
115  if( stream->buffer == NULL )
116  {
117  if( stream->bufPos != 0 || stream->bufSize != 0 || \
118  stream->bufEnd != 0 )
119  return( FALSE );
120  if( netStream->writeBuffer != NULL || \
121  netStream->writeBufSize != 0 || \
122  netStream->writeBufEnd != 0 )
123  return( FALSE );
124 
125  return( TRUE );
126  }
127 
128  /* Network streams have a second buffer used for writes, make sure
129  that the write buffer position is within bounds */
130  if( netStream->writeBuffer == NULL || \
131  netStream->writeBufSize <= 0 || \
132  netStream->writeBufSize >= MAX_INTLENGTH )
133  return( FALSE );
134  if( netStream->writeBufEnd < 0 || \
135  netStream->writeBufEnd > netStream->writeBufSize )
136  return( FALSE );
137 
138  return( TRUE );
139  }
140 #endif /* USE_TCP */
141 
142  /* Everything else requires a buffer, however file streams have to be
143  explicitly connected to a buffer after creation so if it's a
144  partially-initialised file stream we allow an absent buffer */
145  if( stream->buffer == NULL )
146  {
147  if( stream->type == STREAM_TYPE_FILE && \
148  !( stream->flags & STREAM_FFLAG_BUFFERSET ) && \
149  stream->bufPos == 0 && stream->bufEnd == 0 && \
150  stream->bufSize == 0 )
151  return( TRUE );
152 
153  return( FALSE );
154  }
155 
156  /* Make sure that the buffer position is within bounds:
157 
158  bufSize
159  |
160  <------ buffer ------> v
161  +---------------------------+
162  | | |
163  +---------------------------+
164  ^ ^
165  | |
166  bufPos bufEnd */
167  if( stream->bufPos < 0 || stream->bufPos > stream->bufEnd || \
168  stream->bufEnd < 0 || stream->bufEnd > stream->bufSize || \
169  stream->bufSize <= 0 || stream->bufSize >= MAX_INTLENGTH )
170  return( FALSE );
171 
172  /* If it's a file stream make sure that the position within the file
173  makes sense */
174  if( stream->type == STREAM_TYPE_FILE && \
175  ( stream->bufCount < 0 || \
176  stream->bufCount >= ( MAX_INTLENGTH / stream->bufSize ) ) )
177  return( FALSE );
178 
179  return( TRUE );
180  }
181 
182 /* Refill a stream buffer from backing storage */
183 
185 static int refillStream( INOUT STREAM *stream )
186  {
187  int length, status;
188 
189  assert( isWritePtr( stream, sizeof( STREAM ) ) );
190 
191  REQUIRES_S( sanityCheck( stream ) );
192  REQUIRES_S( stream->type == STREAM_TYPE_FILE );
193  REQUIRES_S( stream->bufPos >= stream->bufEnd || \
194  ( stream->flags & STREAM_FFLAG_POSCHANGED ) );
195 
196  /* If we've reached EOF we can't refill the stream */
197  if( stream->flags & STREAM_FFLAG_EOF )
198  {
199  /* If partial reads are allowed return an indication of how much
200  data we got. This only works once, after this the persistent
201  error state will return an underflow error before we get to this
202  point */
203  stream->status = CRYPT_ERROR_UNDERFLOW;
204  return( ( stream->flags & STREAM_FLAG_PARTIALREAD ) ? \
206  }
207 
208  /* If we've moved to a different place in the file prepare to get new
209  data into the buffer at the new location */
210  if( ( stream->flags & STREAM_FFLAG_POSCHANGED ) && \
211  !( stream->flags & STREAM_FFLAG_POSCHANGED_NOSKIP ) )
212  {
213  status = fileSeek( stream, stream->bufCount * stream->bufSize );
214  if( cryptStatusError( status ) )
215  return( sSetError( stream, status ) );
216  }
217 
218  /* Try and read more data into the stream buffer */
219  status = fileRead( stream, stream->buffer, stream->bufSize, &length );
220  if( cryptStatusError( status ) )
221  return( sSetError( stream, status ) );
222  if( length < stream->bufSize )
223  {
224  /* If we got less than we asked for, remember that we're at the end
225  of the file */
226  stream->flags |= STREAM_FFLAG_EOF;
227  if( length == 0 )
228  {
229  /* We ran out of input on an exact buffer boundary, if partial
230  reads are allowed return an indication of how much data we
231  got. This only works once, after this the persistent error
232  state will return an underflow error before we get to this
233  point */
234  stream->status = CRYPT_ERROR_UNDERFLOW;
235  return( ( stream->flags & STREAM_FLAG_PARTIALREAD ) ? \
237  }
238  }
239 
240  /* We've refilled the stream buffer from the file, remember the
241  details */
242  if( !( stream->flags & STREAM_FFLAG_POSCHANGED ) )
243  {
244  stream->bufCount++;
245  stream->bufPos = 0;
246  }
247  stream->bufEnd = length;
248  stream->flags &= ~( STREAM_FFLAG_POSCHANGED | \
249  STREAM_FFLAG_POSCHANGED_NOSKIP );
250 
251  ENSURES_S( sanityCheck( stream ) );
252 
253  return( CRYPT_OK );
254  }
255 
256 /* Empty a stream buffer to backing storage */
257 
259 static int emptyStream( INOUT STREAM *stream, const BOOLEAN forcedFlush )
260  {
261  int status = CRYPT_OK;
262 
263  assert( isWritePtr( stream, sizeof( STREAM ) ) );
264 
265  REQUIRES_S( sanityCheck( stream ) );
266  REQUIRES_S( stream->type == STREAM_TYPE_FILE );
267 
268  /* If the stream position has been changed, this can only have been from
269  a rewind of the stream, in which case we move back to the start of
270  the file */
271  if( stream->flags & STREAM_FFLAG_POSCHANGED )
272  {
273  status = fileSeek( stream, 0 );
274  if( cryptStatusError( status ) )
275  return( sSetError( stream, status ) );
276  }
277 
278  /* Try and write the data to the stream's backing storage */
279  status = fileWrite( stream, stream->buffer, stream->bufPos );
280  if( cryptStatusError( status ) )
281  return( sSetError( stream, status ) );
282 
283  /* Reset the position-changed flag and, if we've written another buffer
284  full of data, remember the details. If it's a forced flush we leave
285  everything as is so that we remember the last write position in the
286  file */
287  stream->flags &= ~STREAM_FFLAG_POSCHANGED;
288  if( !forcedFlush )
289  {
290  stream->bufCount++;
291  stream->bufPos = 0;
292  }
293 
294  ENSURES_S( sanityCheck( stream ) );
295 
296  return( CRYPT_OK );
297  }
298 
299 #ifdef VIRTUAL_FILE_STREAM
300 
301 /* Expand a virtual file stream's buffer to make room for new data when it
302  fills up */
303 
305 static int expandVirtualFileStream( INOUT STREAM *stream,
306  IN_LENGTH const int length )
307  {
308  void *newBuffer;
309  int newSize;
310 
311  assert( isWritePtr( stream, sizeof( STREAM ) ) );
312 
313  REQUIRES_S( sanityCheck( stream ) && \
314  sIsVirtualFileStream( stream ) );
315  REQUIRES_S( length > 0 && length < MAX_INTLENGTH );
316 
317  /* If it's a small buffer allocated when we initially read a file and it
318  doesn't look like we'll be overflowing a standard-size buffer, just
319  expand it up to STREAM_VFILE_BUFSIZE */
320  if( stream->bufSize < STREAM_VFILE_BUFSIZE && \
321  stream->bufPos + length < STREAM_VFILE_BUFSIZE - 1024 )
322  newSize = STREAM_VFILE_BUFSIZE;
323  else
324  {
325  /* Increase the stream buffer size in STREAM_VFILE_BUFSIZE steps */
326  newSize = stream->bufSize + STREAM_VFILE_BUFSIZE;
327  }
328 
329  /* Allocate the buffer and copy the new data across using a safe realloc
330  that wipes the original buffer. If the malloc fails we return
331  CRYPT_ERROR_OVERFLOW rather than CRYPT_ERROR_MEMORY since the former
332  is more appropriate for the emulated-I/O environment */
333  if( ( newBuffer = clDynAlloc( "expandVirtualFileStream", \
334  stream->bufSize + STREAM_VFILE_BUFSIZE ) ) == NULL )
335  return( sSetError( stream, CRYPT_ERROR_OVERFLOW ) );
336  memcpy( newBuffer, stream->buffer, stream->bufEnd );
337  zeroise( stream->buffer, stream->bufEnd );
338  clFree( "expandVirtualFileStream", stream->buffer );
339  stream->buffer = newBuffer;
340  stream->bufSize = newSize;
341 
342  ENSURES_S( sanityCheck( stream ) );
343 
344  return( CRYPT_OK );
345  }
346 #endif /* VIRTUAL_FILE_STREAM */
347 
348 /****************************************************************************
349 * *
350 * Stream Read Functions *
351 * *
352 ****************************************************************************/
353 
354 /* Read data from a stream */
355 
357 int sgetc( INOUT STREAM *stream )
358  {
359  int ch;
360 
361  assert( isWritePtr( stream, sizeof( STREAM ) ) );
362  assert( isReadPtr( stream->buffer, stream->bufSize ) );
363 
364  /* Check that the input parameters are in order */
365  if( !isWritePtrConst( stream, sizeof( STREAM ) ) )
366  retIntError();
367 
368  REQUIRES_S( sanityCheck( stream ) );
369  REQUIRES_S( stream->type == STREAM_TYPE_MEMORY || \
370  stream->type == STREAM_TYPE_FILE );
371 
372  /* If there's a problem with the stream don't try to do anything */
373  if( cryptStatusError( stream->status ) )
374  return( stream->status );
375 
376  switch( stream->type )
377  {
378  case STREAM_TYPE_MEMORY:
379  /* Read the data from the stream buffer */
380  if( stream->bufPos >= stream->bufEnd )
381  return( sSetError( stream, CRYPT_ERROR_UNDERFLOW ) );
382  ch = byteToInt( stream->buffer[ stream->bufPos++ ] );
383  break;
384 
385  case STREAM_TYPE_FILE:
386  REQUIRES_S( stream->flags & STREAM_FFLAG_BUFFERSET );
387 
388  /* Read the data from the file */
389  if( stream->bufPos >= stream->bufEnd || \
390  ( stream->flags & STREAM_FFLAG_POSCHANGED ) )
391  {
392  int status = refillStream( stream );
393  if( cryptStatusError( status ) )
394  return( ( status == OK_SPECIAL ) ? 0 : status );
395  }
396  ch = byteToInt( stream->buffer[ stream->bufPos++ ] );
397  break;
398 
399  default:
400  retIntError_Stream( stream );
401  }
402 
403  ENSURES_S( sanityCheck( stream ) );
404 
405  return( ch );
406  }
407 
409 int sread( INOUT STREAM *stream,
410  OUT_BUFFER_FIXED( length ) void *buffer,
411  IN_LENGTH const int length )
412  {
413  int status;
414 
415  assert( isWritePtr( stream, sizeof( STREAM ) ) );
416  assert( stream->type == STREAM_TYPE_NETWORK || \
417  isReadPtr( stream->buffer, stream->bufSize ) );
418  assert( isWritePtr( buffer, length ) );
419 
420  /* Check that the input parameters are in order */
421  if( !isWritePtrConst( stream, sizeof( STREAM ) ) )
422  retIntError();
423  if( !isWritePtr( buffer, length ) )
424  retIntError_Stream( stream );
425 
426  REQUIRES_S( sanityCheck( stream ) );
427  REQUIRES_S( stream->type == STREAM_TYPE_MEMORY || \
428  stream->type == STREAM_TYPE_FILE || \
429  stream->type == STREAM_TYPE_NETWORK );
430  REQUIRES_S( length > 0 && length < MAX_INTLENGTH );
431 
432  /* If there's a problem with the stream don't try to do anything */
433  if( cryptStatusError( stream->status ) )
434  return( stream->status );
435 
436  switch( stream->type )
437  {
438  case STREAM_TYPE_MEMORY:
439  {
440  int localLength = length;
441 
442 #ifdef VIRTUAL_FILE_STREAM
443  /* If partial reads are allowed return whatever's left in the
444  stream buffer. This only occurs for virtual file streams
445  that have been translated into memory streams */
446  if( stream->flags & STREAM_FLAG_PARTIALREAD )
447  {
448  REQUIRES_S( sIsVirtualFileStream( stream ) );
449 
450  localLength = stream->bufEnd - stream->bufPos;
451  if( localLength > length )
452  localLength = length;
453  }
454 #endif /* VIRTUAL_FILE_STREAM */
455 
456  /* Read the data from the stream buffer */
457  if( stream->bufPos + localLength > stream->bufEnd )
458  {
459  memset( buffer, 0, min( 16, length ) ); /* Clear output buffer */
460  return( sSetError( stream, CRYPT_ERROR_UNDERFLOW ) );
461  }
462  ENSURES_S( rangeCheckZ( stream->bufPos, localLength,
463  stream->bufEnd ) );
464  memcpy( buffer, stream->buffer + stream->bufPos, localLength );
465  stream->bufPos += localLength;
466 
467  /* Usually reads are atomic so we just return an all-OK
468  indicator, however if we're performing partial reads we need
469  to return an exact byte count */
470  status = ( stream->flags & STREAM_FLAG_PARTIALREAD ) ? \
471  localLength : CRYPT_OK;
472  break;
473  }
474 
475  case STREAM_TYPE_FILE:
476  {
477  BYTE *bufPtr = buffer;
478  int dataLength, bytesCopied = 0, iterationCount;
479 
480  REQUIRES_S( stream->flags & STREAM_FFLAG_BUFFERSET );
481 
482  /* Read the data from the file */
483  for( dataLength = length, iterationCount = 0;
484  dataLength > 0 && iterationCount < FAILSAFE_ITERATIONS_LARGE;
485  iterationCount++ )
486  {
487  const int oldDataLength = dataLength;
488  int bytesToCopy;
489 
490  /* If the stream buffer is empty try and refill it */
491  if( stream->bufPos >= stream->bufEnd || \
492  ( stream->flags & STREAM_FFLAG_POSCHANGED ) )
493  {
494  status = refillStream( stream );
495  if( cryptStatusError( status ) )
496  return( ( status == OK_SPECIAL ) ? \
497  bytesCopied : status );
498  }
499 
500  /* Copy as much data as we can out of the stream buffer */
501  bytesToCopy = min( dataLength, \
502  stream->bufEnd - stream->bufPos );
503  ENSURES_S( rangeCheckZ( stream->bufPos, bytesToCopy,
504  stream->bufEnd ) );
505  memcpy( bufPtr, stream->buffer + stream->bufPos,
506  bytesToCopy );
507  stream->bufPos += bytesToCopy;
508  bufPtr += bytesToCopy;
509  bytesCopied += bytesToCopy;
510  dataLength -= bytesToCopy;
511  ENSURES_S( dataLength < oldDataLength );
512  }
513  ENSURES_S( iterationCount < FAILSAFE_ITERATIONS_LARGE );
514 
515  /* Usually reads are atomic so we just return an all-OK
516  indicator, however if we're performing partial reads we need
517  to return an exact byte count */
518  status = ( stream->flags & STREAM_FLAG_PARTIALREAD ) ? \
519  bytesCopied : CRYPT_OK;
520  break;
521  }
522 
523 #ifdef USE_TCP
524  case STREAM_TYPE_NETWORK:
525  {
526  NET_STREAM_INFO *netStream = \
527  ( NET_STREAM_INFO * ) stream->netStreamInfo;
528  int bytesRead;
529 
530  REQUIRES_S( netStream->protocol != STREAM_PROTOCOL_HTTP || \
531  ( netStream->protocol == STREAM_PROTOCOL_HTTP && \
532  length == sizeof( HTTP_DATA_INFO ) ) );
533 
534  /* Read the data from the network. Reads are normally atomic
535  but if the partial-write flag is set can be restarted after
536  a timeout */
537  status = netStream->readFunction( stream, buffer, length,
538  &bytesRead );
539  if( cryptStatusError( status ) )
540  {
541  /* If the lower-level code has indicated that the error
542  condition is fatal, make it persistent for the stream */
543  if( cryptStatusError( netStream->persistentStatus ) )
544  stream->status = netStream->persistentStatus;
545 
546  /* If it's not a special-case CRYPT_ERROR_COMPLETE status,
547  exit. We don't make the error persistent since unlike
548  memory or file stream reads, most errors on network reads
549  are recoverable */
550  if( status != CRYPT_ERROR_COMPLETE )
551  return( status );
552 
553  /* If we get a CRYPT_ERROR_COMPLETE status this means that
554  the other side has closed the connection. This status is
555  returned when there are intermediate protocol layers such
556  as HTTP or tunnelling over a cryptlib session involved.
557  When this occurs we update the stream state and map the
558  status to a standard read error. The exact code to
559  return here is a bit uncertain, it isn't specifically a
560  read error because either the other side is allowed to
561  close the connection after it's said its bit (and so it's
562  not a read error), or it has to perform a
563  cryptographically protected close (in which case any
564  non-OK status indicates a problem). The most sensible
565  status is probably a read error */
566  sioctlSet( stream, STREAM_IOCTL_CONNSTATE, FALSE );
567  return( CRYPT_ERROR_READ );
568  }
569  if( bytesRead < length && \
570  !( ( stream->flags & STREAM_FLAG_PARTIALREAD ) || \
571  ( netStream->nFlags & STREAM_NFLAG_ENCAPS ) ) )
572  {
573  /* If we didn't read all of the data and partial reads
574  aren't allowed report a read timeout. The situation
575  for HTTP streams is a bit special because what we're
576  sending to the read function is an HTTP_DATA_INFO
577  structure so we have to extract the actual length
578  information from that */
579  if( netStream->protocol == STREAM_PROTOCOL_HTTP )
580  {
581 #ifdef USE_ERRMSGS
582  const HTTP_DATA_INFO *httpDataInfo = \
583  ( HTTP_DATA_INFO * ) buffer;
584 #endif /* USE_ERRMSGS */
585 
588  "Read timed out with %d of %d bytes read",
589  httpDataInfo->bytesTransferred,
590  httpDataInfo->bytesAvail ) );
591  }
594  "Read timed out with %d of %d bytes read",
595  bytesRead, length ) );
596  }
597 
598  /* This is an ugly case where we have to follow the Posix
599  semantics of returning a read-bytes count as the return
600  status rather than a by-reference parameter. If we didn't
601  do this then every trivial memory-stream read would need to
602  pass in a dummy parameter for the read-byte-count value just
603  to handle the one or two calls to a network stream read that
604  needs to return a length */
605  status = bytesRead;
606  break;
607  }
608 #endif /* USE_TCP */
609 
610  default:
611  retIntError_Stream( stream );
612  }
613 
614  ENSURES_S( sanityCheck( stream ) );
615 
616  return( status );
617  }
618 
619 /****************************************************************************
620 * *
621 * Stream Write Functions *
622 * *
623 ****************************************************************************/
624 
625 /* Write data to a stream */
626 
627 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
628 int sputc( INOUT STREAM *stream, IN_BYTE const int ch )
629  {
630  assert( isWritePtr( stream, sizeof( STREAM ) ) );
631  assert( stream->type == STREAM_TYPE_NULL || \
632  isWritePtr( stream->buffer, stream->bufSize ) );
633 
634  /* Check that the input parameters are in order */
635  if( !isWritePtrConst( stream, sizeof( STREAM ) ) )
636  retIntError();
637 
638  REQUIRES_S( sanityCheck( stream ) );
639  REQUIRES_S( stream->type == STREAM_TYPE_NULL || \
640  stream->type == STREAM_TYPE_MEMORY || \
641  stream->type == STREAM_TYPE_FILE );
642  REQUIRES_S( !( stream->flags & STREAM_FLAG_READONLY ) );
643  REQUIRES( ch >= 0 && ch <= 0xFF );
644 
645  /* If there's a problem with the stream don't try to do anything until
646  the error is cleared */
647  if( cryptStatusError( stream->status ) )
648  return( stream->status );
649 
650  switch( stream->type )
651  {
652  case STREAM_TYPE_NULL:
653  /* It's a null stream, just record the write and return */
654  stream->bufPos++;
655  if( stream->bufEnd < stream->bufPos )
656  stream->bufEnd = stream->bufPos;
657  break;
658 
659  case STREAM_TYPE_MEMORY:
660  /* Write the data to the stream buffer */
661  if( stream->bufPos >= stream->bufSize )
662  {
663 #ifdef VIRTUAL_FILE_STREAM
664  if( sIsVirtualFileStream( stream ) )
665  {
666  int status;
667 
668  status = expandVirtualFileStream( stream, 1 );
669  if( cryptStatusError( status ) )
670  return( status );
671  }
672  else
673 #endif /* VIRTUAL_FILE_STREAM */
674  return( sSetError( stream, CRYPT_ERROR_OVERFLOW ) );
675  }
676  stream->buffer[ stream->bufPos++ ] = intToByte( ch );
677  if( stream->bufEnd < stream->bufPos )
678  stream->bufEnd = stream->bufPos;
679 #ifdef VIRTUAL_FILE_STREAM
680  if( sIsVirtualFileStream( stream ) )
681  {
682  /* This is a memory stream emulating a file stream, set the
683  dirty bit */
684  stream->flags |= STREAM_FLAG_DIRTY;
685  }
686 #endif /* VIRTUAL_FILE_STREAM */
687  break;
688 
689  case STREAM_TYPE_FILE:
690  REQUIRES_S( stream->flags & STREAM_FFLAG_BUFFERSET );
691 
692  /* Write the data to the file */
693  if( stream->bufPos >= stream->bufSize )
694  {
695  int status;
696 
697  status = emptyStream( stream, FALSE );
698  if( cryptStatusError( status ) )
699  return( status );
700  }
701  stream->buffer[ stream->bufPos++ ] = intToByte( ch );
702  stream->flags |= STREAM_FLAG_DIRTY;
703  break;
704 
705  default:
706  retIntError_Stream( stream );
707  }
708 
709  ENSURES_S( sanityCheck( stream ) );
710 
711  return( CRYPT_OK );
712  }
713 
714 RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
715 int swrite( INOUT STREAM *stream,
716  IN_BUFFER( length ) const void *buffer,
717  IN_LENGTH const int length )
718  {
719  int status;
720 
721  assert( isWritePtr( stream, sizeof( STREAM ) ) );
722  assert( stream->type == STREAM_TYPE_NULL || \
723  stream->type == STREAM_TYPE_NETWORK || \
724  isWritePtr( stream->buffer, stream->bufSize ) );
725  assert( isReadPtr( buffer, length ) );
726 
727  /* Check that the input parameters are in order */
728  if( !isWritePtrConst( stream, sizeof( STREAM ) ) )
729  retIntError();
730  if( !isReadPtr( buffer, length ) )
731  retIntError_Stream( stream );
732 
733  REQUIRES_S( sanityCheck( stream ) );
734  REQUIRES_S( stream->type == STREAM_TYPE_NULL || \
735  stream->type == STREAM_TYPE_MEMORY || \
736  stream->type == STREAM_TYPE_FILE || \
737  stream->type == STREAM_TYPE_NETWORK );
738  REQUIRES_S( length > 0 && length < MAX_INTLENGTH );
739  REQUIRES_S( !( stream->flags & STREAM_FLAG_READONLY ) );
740 
741  /* If there's a problem with the stream don't try to do anything until
742  the error is cleared */
743  if( cryptStatusError( stream->status ) )
744  return( stream->status );
745 
746  switch( stream->type )
747  {
748  case STREAM_TYPE_NULL:
749  /* It's a null stream, just record the write and return */
750  stream->bufPos += length;
751  if( stream->bufEnd < stream->bufPos )
752  stream->bufEnd = stream->bufPos;
753  status = CRYPT_OK;
754  break;
755 
756  case STREAM_TYPE_MEMORY:
757  /* Write the data to the stream buffer */
758  if( stream->bufPos + length > stream->bufSize )
759  {
760 #ifdef VIRTUAL_FILE_STREAM
761  if( sIsVirtualFileStream( stream ) )
762  {
763  status = expandVirtualFileStream( stream, length );
764  if( cryptStatusError( status ) )
765  return( status );
766  }
767  else
768 #endif /* VIRTUAL_FILE_STREAM */
769  return( sSetError( stream, CRYPT_ERROR_OVERFLOW ) );
770  }
771  ENSURES_S( rangeCheckZ( stream->bufPos, length,
772  stream->bufSize ) );
773  memcpy( stream->buffer + stream->bufPos, buffer, length );
774  stream->bufPos += length;
775  if( stream->bufEnd < stream->bufPos )
776  stream->bufEnd = stream->bufPos;
777 #ifdef VIRTUAL_FILE_STREAM
778  if( sIsVirtualFileStream( stream ) )
779  {
780  /* This is a memory stream emulating a file stream, set the
781  dirty bit */
782  stream->flags |= STREAM_FLAG_DIRTY;
783  }
784 #endif /* VIRTUAL_FILE_STREAM */
785  status = CRYPT_OK;
786  break;
787 
788  case STREAM_TYPE_FILE:
789  {
790  const BYTE *bufPtr = buffer;
791  int dataLength, iterationCount;
792 
793  REQUIRES_S( stream->flags & STREAM_FFLAG_BUFFERSET );
794 
795  /* Write the data to the file */
796  for( dataLength = length, iterationCount = 0;
797  dataLength > 0 && iterationCount < FAILSAFE_ITERATIONS_LARGE;
798  iterationCount++ )
799  {
800  const int bytesToCopy = \
801  min( dataLength, stream->bufSize - stream->bufPos );
802 
803  if( bytesToCopy > 0 )
804  {
805  ENSURES_S( rangeCheckZ( stream->bufPos, bytesToCopy,
806  stream->bufSize ) );
807  memcpy( stream->buffer + stream->bufPos, bufPtr,
808  bytesToCopy );
809  stream->bufPos += bytesToCopy;
810  bufPtr += bytesToCopy;
811  dataLength -= bytesToCopy;
812  }
813  if( stream->bufPos >= stream->bufSize )
814  {
815  status = emptyStream( stream, FALSE );
816  if( cryptStatusError( status ) )
817  return( status );
818  }
819  }
820  ENSURES_S( iterationCount < FAILSAFE_ITERATIONS_LARGE );
821  stream->flags |= STREAM_FLAG_DIRTY;
822  status = CRYPT_OK;
823  break;
824  }
825 
826 #ifdef USE_TCP
827  case STREAM_TYPE_NETWORK:
828  {
829  NET_STREAM_INFO *netStream = \
830  ( NET_STREAM_INFO * ) stream->netStreamInfo;
831  int bytesWritten;
832 
833  REQUIRES_S( netStream->protocol != STREAM_PROTOCOL_HTTP || \
834  ( netStream->protocol == STREAM_PROTOCOL_HTTP && \
835  length == sizeof( HTTP_DATA_INFO ) ) );
836 
837  /* Write the data to the network. Writes are normally atomic
838  but if the partial-write flag is set can be restarted after
839  a timeout */
840  status = netStream->writeFunction( stream, buffer, length,
841  &bytesWritten );
842  if( cryptStatusError( status ) )
843  {
844  /* If the lower-level code has indicated that the error
845  condition is fatal, make it persistent for the stream */
846  if( cryptStatusError( netStream->persistentStatus ) )
847  stream->status = netStream->persistentStatus;
848 
849  return( status );
850  }
851  if( bytesWritten < length && \
852  !( stream->flags & STREAM_FLAG_PARTIALWRITE ) )
853  {
854  /* If we didn't write all of the data and partial writes
855  aren't allowed report a write timeout. The situation
856  for HTTP streams is a bit special because what we're
857  sending to the write function is an HTTP_DATA_INFO
858  structure so we have to extract the actual length
859  information from that */
860  if( netStream->protocol == STREAM_PROTOCOL_HTTP )
861  {
862 #ifdef USE_ERRMSGS
863  const HTTP_DATA_INFO *httpDataInfo = \
864  ( HTTP_DATA_INFO * ) buffer;
865 #endif /* USE_ERRMSGS */
866 
869  "Write timed out with %d of %d bytes written",
870  httpDataInfo->bytesTransferred,
871  httpDataInfo->bufSize ) );
872  }
875  "Write timed out with %d of %d bytes written",
876  bytesWritten, length ) );
877  }
878  status = bytesWritten;
879  break;
880  }
881 #endif /* USE_TCP */
882 
883  default:
884  retIntError_Stream( stream );
885  }
886 
887  ENSURES_S( sanityCheck( stream ) );
888 
889  return( status );
890  }
891 
892 /* Commit data in a stream to backing storage */
893 
894 int sflush( STREAM *stream )
895  {
896  int status = CRYPT_OK, flushStatus;
897 
898  assert( isWritePtr( stream, sizeof( STREAM ) ) );
899  assert( isReadPtr( stream->buffer, stream->bufSize ) );
900 
901  /* Check that the input parameters are in order */
902  if( !isWritePtrConst( stream, sizeof( STREAM ) ) )
903  retIntError();
904 
905  if( !isReadPtr( stream->buffer, stream->bufSize ) )
906  retIntError_Stream( stream );
907 
908  REQUIRES_S( sanityCheck( stream ) && \
909  ( ( stream->flags & STREAM_FFLAG_BUFFERSET ) || \
910  sIsVirtualFileStream( stream ) ) );
911  REQUIRES_S( stream->type == STREAM_TYPE_FILE || \
912  sIsVirtualFileStream( stream ) );
913  REQUIRES_S( !( stream->flags & STREAM_FLAG_READONLY ) );
914 
915  /* If there's a problem with the stream don't try to do anything until
916  the error is cleared */
917  if( cryptStatusError( stream->status ) )
918  return( stream->status );
919 
920  /* If the data in the stream buffer is unchanged there's nothing to do */
921  if( !( stream->flags & STREAM_FLAG_DIRTY ) )
922  return( CRYPT_OK );
923 
924  /* If there's data still in the stream buffer and it's not a virtual
925  file stream that's handled via a memory stream (for which the data
926  is committed in an atomic operation when the file is flushed), write
927  it to disk. If there's an error at this point we still try and flush
928  whatever data we have to disk so we don't bail out immediately if
929  there's a problem */
930  if( stream->bufPos > 0 && !sIsVirtualFileStream( stream ) )
931  status = emptyStream( stream, TRUE );
932 
933  /* Commit the data */
934  flushStatus = fileFlush( stream );
935  stream->flags &= ~STREAM_FLAG_DIRTY;
936 
937  return( cryptStatusOK( status ) ? flushStatus : status );
938  }
939 
940 /****************************************************************************
941 * *
942 * Meta-data Functions *
943 * *
944 ****************************************************************************/
945 
946 /* Set/clear the error status of a stream. sSetError() returns the error
947  status that it's passed so that it can be called using
948  'return( sSetError( stream, status ) );' */
949 
950 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
951 int sSetError( INOUT STREAM *stream, IN_ERROR const int status )
952  {
953  assert( isWritePtr( stream, sizeof( STREAM ) ) );
954 
955  REQUIRES_S( cryptStatusError( status ) );
956 
957  /* Check that the input parameters are in order */
958  if( !isWritePtrConst( stream, sizeof( STREAM ) ) )
959  retIntError();
960 
961  /* If there's already an error status set don't try and override it */
962  if( cryptStatusError( stream->status ) )
963  return( stream->status );
964 
965  stream->status = status;
966 
967  return( status );
968  }
969 
970 STDC_NONNULL_ARG( ( 1 ) ) \
971 void sClearError( INOUT STREAM *stream )
972  {
973  assert( isWritePtr( stream, sizeof( STREAM ) ) );
974 
975  /* Check that the input parameters are in order */
976  if( !isWritePtrConst( stream, sizeof( STREAM ) ) )
978 
979  stream->status = CRYPT_OK;
980  }
981 
982 /* Determine whether a stream is a null stream */
983 
985 BOOLEAN sIsNullStream( const STREAM *stream )
986  {
987  assert( isReadPtr( stream, sizeof( STREAM ) ) );
988 
989  /* Check that the input parameters are in order */
990  if( !isReadPtrConst( stream, sizeof( STREAM ) ) )
992 
993  return( ( stream->type == STREAM_TYPE_NULL ) ? TRUE : FALSE );
994  }
995 
996 
997 /* Move to an absolute position in a stream */
998 
999 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
1000 int sseek( INOUT STREAM *stream, IN_LENGTH_Z const long position )
1001  {
1002  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1003 
1004  /* Check that the input parameters are in order */
1005  if( !isWritePtrConst( stream, sizeof( STREAM ) ) )
1006  retIntError();
1007 
1008  REQUIRES_S( sanityCheck( stream ) );
1009  REQUIRES_S( stream->type == STREAM_TYPE_NULL || \
1010  stream->type == STREAM_TYPE_MEMORY || \
1011  stream->type == STREAM_TYPE_FILE );
1012  REQUIRES_S( position >= 0 && position < MAX_INTLENGTH );
1013 
1014  /* If there's a problem with the stream don't try to do anything */
1015  if( cryptStatusError( stream->status ) )
1016  return( stream->status );
1017 
1018  switch( stream->type )
1019  {
1020  case STREAM_TYPE_NULL:
1021  /* Move to the position in the stream buffer. We never get
1022  called directly with an sseek on a memory stream, but end up
1023  here via a translated sSkip() call */
1024  stream->bufPos = ( int ) position;
1025  if( stream->bufEnd < stream->bufPos )
1026  stream->bufEnd = stream->bufPos;
1027  break;
1028 
1029  case STREAM_TYPE_MEMORY:
1030  /* Move to the position in the stream buffer */
1031  if( ( int ) position > stream->bufSize )
1032  {
1033  stream->bufPos = stream->bufSize;
1034  return( sSetError( stream, CRYPT_ERROR_UNDERFLOW ) );
1035  }
1036  stream->bufPos = ( int ) position;
1037  if( stream->bufEnd < stream->bufPos )
1038  stream->bufEnd = stream->bufPos;
1039  break;
1040 
1041  case STREAM_TYPE_FILE:
1042  {
1043  int newBufCount;
1044 
1045  /* If it's a currently-disconnected file stream then all we can
1046  do is rewind the stream. This occurs when we're doing an
1047  atomic flush of data to disk and we rewind the stream prior
1048  to writing the new/updated data. The next buffer-connect
1049  operation will reset the stream state so there's nothing to
1050  do at this point */
1051  if( !( stream->flags & STREAM_FFLAG_BUFFERSET ) )
1052  {
1053  REQUIRES_S( position == 0 );
1054 
1055  return( CRYPT_OK );
1056  }
1057 
1058  /* It's a file stream, remember the new position in the file */
1059  newBufCount = position / stream->bufSize;
1060  if( newBufCount != stream->bufCount )
1061  {
1062  /* We're not within the current buffer any more, remember
1063  that we have to explicitly update the file position on
1064  the next read */
1065  stream->flags |= STREAM_FFLAG_POSCHANGED;
1066 
1067  /* If we're already positioned to read the next bufferful
1068  of data we don't have to explicitly skip ahead to it */
1069  if( newBufCount == stream->bufCount + 1 )
1070  stream->flags |= STREAM_FFLAG_POSCHANGED_NOSKIP;
1071 
1072  stream->bufCount = newBufCount;
1073  }
1074  stream->bufPos = position % stream->bufSize;
1075  break;
1076  }
1077 
1078  default:
1079  retIntError_Stream( stream );
1080  }
1081 
1082  ENSURES_S( sanityCheck( stream ) );
1083 
1084  return( CRYPT_OK );
1085  }
1086 
1087 /* Return the current posision in a stream */
1088 
1090 int stell( const STREAM *stream )
1091  {
1092  assert( isReadPtr( stream, sizeof( STREAM ) ) );
1093 
1094  /* Check that the input parameters are in order */
1095  if( !isReadPtrConst( stream, sizeof( STREAM ) ) )
1096  retIntError();
1097 
1098  /* We can't use REQUIRE_S( sanityCheck() ) in this case because the
1099  stream is a const parameter. Since stell() is expected to return a
1100  value in the range 0...stream->bufSize we don't use REQUIRES() either
1101  but simply return an offset of zero */
1102  REQUIRES_EXT( sanityCheck( stream ), 0 );
1103  REQUIRES_EXT( ( stream->type == STREAM_TYPE_NULL || \
1104  stream->type == STREAM_TYPE_MEMORY || \
1105  stream->type == STREAM_TYPE_FILE ), 0 );
1106 
1107  /* If there's a problem with the stream don't try to do anything */
1108  if( cryptStatusError( stream->status ) )
1109  {
1110  DEBUG_DIAG(( "Stream is in invalid state" ));
1111  assert( DEBUG_WARN );
1112  return( 0 );
1113  }
1114 
1115  switch( stream->type )
1116  {
1117  case STREAM_TYPE_NULL:
1118  case STREAM_TYPE_MEMORY:
1119  return( stream->bufPos );
1120 
1121  case STREAM_TYPE_FILE:
1122  return( ( stream->bufCount * stream->bufSize ) + \
1123  stream->bufPos );
1124  }
1125 
1126  retIntError();
1127  }
1128 
1129 /* Skip a number of bytes in a stream */
1130 
1131 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
1132 int sSkip( INOUT STREAM *stream, IN_LENGTH const long offset )
1133  {
1134  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1135 
1136  /* Check that the input parameters are in order */
1137  if( !isWritePtrConst( stream, sizeof( STREAM ) ) )
1138  retIntError();
1139 
1140  REQUIRES_S( sanityCheck( stream ) );
1141  REQUIRES_S( stream->type == STREAM_TYPE_NULL || \
1142  stream->type == STREAM_TYPE_MEMORY || \
1143  stream->type == STREAM_TYPE_FILE );
1144  REQUIRES_S( offset > 0 && offset < MAX_INTLENGTH );
1145 
1146  /* If there's a problem with the stream don't try to do anything */
1147  if( cryptStatusError( stream->status ) )
1148  return( stream->status );
1149 
1150  return( sseek( stream, stream->bufPos + offset ) );
1151  }
1152 
1153 /* Peek at the next data value in a stream */
1154 
1155 CHECK_RETVAL_RANGE( MAX_ERROR, 0xFF ) STDC_NONNULL_ARG( ( 1 ) ) \
1156 int sPeek( INOUT STREAM *stream )
1157  {
1158  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1159  assert( isReadPtr( stream->buffer, stream->bufSize ) );
1160 
1161  /* Check that the input parameters are in order */
1162  if( !isWritePtrConst( stream, sizeof( STREAM ) ) )
1163  retIntError();
1164 
1165  REQUIRES_S( sanityCheck( stream ) );
1166  REQUIRES_S( stream->type == STREAM_TYPE_MEMORY || \
1167  stream->type == STREAM_TYPE_FILE );
1168 
1169  /* If there's a problem with the stream don't try to do anything until
1170  the error is cleared */
1171  if( cryptStatusError( stream->status ) )
1172  return( stream->status );
1173 
1174  /* Read the data from the buffer, but without advancing the read pointer
1175  like sgetc() does */
1176  switch( stream->type )
1177  {
1178  case STREAM_TYPE_MEMORY:
1179  /* Read the data from the stream buffer */
1180  if( stream->bufPos >= stream->bufEnd )
1181  return( sSetError( stream, CRYPT_ERROR_UNDERFLOW ) );
1182  return( stream->buffer[ stream->bufPos ] );
1183 
1184  case STREAM_TYPE_FILE:
1185  REQUIRES_S( stream->flags & STREAM_FFLAG_BUFFERSET );
1186 
1187  /* Read the data from the file */
1188  if( stream->bufPos >= stream->bufEnd || \
1189  ( stream->flags & STREAM_FFLAG_POSCHANGED ) )
1190  {
1191  int status = refillStream( stream );
1192  if( cryptStatusError( status ) )
1193  return( ( status == OK_SPECIAL ) ? 0 : status );
1194  }
1195  return( stream->buffer[ stream->bufPos ] );
1196  }
1197 
1198  retIntError_Stream( stream );
1199  }
1200 
1201 /****************************************************************************
1202 * *
1203 * IOCTL Functions *
1204 * *
1205 ****************************************************************************/
1206 
1207 /* Perform an IOCTL on a stream. There are two variations of this, a get and
1208  a set form, which helps with type-checking compared to the usual do-anything
1209  ioctl() */
1210 
1211 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
1212 static int setStreamBuffer( INOUT STREAM *stream,
1213  IN_BUFFER_OPT( dataLen ) const void *data,
1214  IN_LENGTH_Z const int dataLen )
1215  {
1216  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1217  assert( ( data == NULL && dataLen == 0 ) || \
1218  isReadPtr( data, dataLen ) );
1219 
1220  REQUIRES_S( ( data == NULL && dataLen == 0 ) || \
1221  ( data != NULL && dataLen > 0 && dataLen < MAX_INTLENGTH ) );
1222  REQUIRES_S( dataLen == 0 || \
1223  dataLen == 512 || dataLen == 1024 || \
1224  dataLen == 2048 || dataLen == 4096 || \
1225  dataLen == 8192 || dataLen == 16384 );
1226 
1227 #ifdef VIRTUAL_FILE_STREAM
1228  /* If it's a virtual file stream emulated in memory, don't do anything */
1229  if( sIsVirtualFileStream( stream ) )
1230  return( CRYPT_OK );
1231 #endif /* VIRTUAL_FILE_STREAM */
1232 
1233  /* Set up the buffer variables. File streams don't make use of the
1234  bufEnd indicator so we set it to the same value as bufSize to ensure
1235  that the stream passes the sanity checks */
1236  stream->buffer = ( void * ) data;
1237  stream->bufSize = stream->bufEnd = dataLen;
1238 
1239  /* We've switched to a new I/O buffer, reset all buffer- and stream-
1240  state related variables and remember that we have to reset the stream
1241  position since there may be a position-change pending that hasn't
1242  been reflected down to the underlying file yet (if the position
1243  change was within the same buffer then the POSCHANGED flag won't have
1244  been set since only the bufPos will have changed) */
1245  stream->bufPos = stream->bufCount = 0;
1246  sClearError( stream );
1247  stream->flags &= ~( STREAM_FFLAG_BUFFERSET | \
1248  STREAM_FFLAG_EOF | \
1249  STREAM_FFLAG_POSCHANGED_NOSKIP );
1250  stream->flags |= STREAM_FFLAG_POSCHANGED;
1251  if( data != NULL )
1252  stream->flags |= STREAM_FFLAG_BUFFERSET;
1253 
1254  return( CRYPT_OK );
1255  }
1256 
1257 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
1258 int sioctlSet( INOUT STREAM *stream,
1259  IN_ENUM( STREAM_IOCTL ) const STREAM_IOCTL_TYPE type,
1260  IN_INT const int value )
1261  {
1262 #ifdef USE_TCP
1263  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
1264  int status;
1265 #endif /* USE_TCP */
1266 
1267  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1268 
1269  /* Check that the input parameters are in order */
1270  if( !isWritePtrConst( stream, sizeof( STREAM ) ) )
1271  retIntError();
1272 
1273  REQUIRES_S( sanityCheck( stream ) );
1274  REQUIRES_S( ( ( stream->type == STREAM_TYPE_FILE || \
1275  sIsVirtualFileStream( stream ) ) && \
1276  ( type == STREAM_IOCTL_IOBUFFER || \
1277  type == STREAM_IOCTL_PARTIALREAD ) ) || \
1278  ( stream->type == STREAM_TYPE_NETWORK ) );
1279  REQUIRES_S( type > STREAM_IOCTL_NONE && type < STREAM_IOCTL_LAST );
1280  REQUIRES_S( value >= 0 && value < MAX_INTLENGTH );
1281 
1282  switch( type )
1283  {
1284  case STREAM_IOCTL_IOBUFFER:
1285  REQUIRES_S( value == 0 );
1286 
1287  return( setStreamBuffer( stream, NULL, 0 ) );
1288 
1290  REQUIRES_S( value == FALSE || value == TRUE );
1291 
1292  if( value )
1293  stream->flags |= STREAM_FLAG_PARTIALREAD;
1294  else
1295  stream->flags &= ~STREAM_FLAG_PARTIALREAD;
1296 
1297  return( CRYPT_OK );
1298 
1300  REQUIRES_S( value == FALSE || value == TRUE );
1301 
1302  if( value )
1303  stream->flags |= STREAM_FLAG_PARTIALWRITE;
1304  else
1305  stream->flags &= ~STREAM_FLAG_PARTIALWRITE;
1306 
1307  return( CRYPT_OK );
1308 
1309 #ifdef USE_TCP
1312  REQUIRES_S( value >= 0 && value < MAX_INTLENGTH );
1313 
1314  netStream->timeout = value;
1315  if( netStream->iTransportSession != CRYPT_ERROR )
1316  {
1317  status = krnlSendMessage( netStream->iTransportSession,
1318  IMESSAGE_SETATTRIBUTE, &netStream->timeout,
1319  ( type == STREAM_IOCTL_READTIMEOUT ) ? \
1322  if( cryptStatusError( status ) )
1323  return( status );
1324  }
1325  return( CRYPT_OK );
1326 
1328  REQUIRES_S( value == TRUE );
1329  REQUIRES_S( netStream->timeout > 0 && \
1330  netStream->timeout < MAX_INTLENGTH );
1331  REQUIRES_S( netStream->savedTimeout >= 0 && \
1332  netStream->savedTimeout < MAX_INTLENGTH );
1333 
1334  /* The security protocol handshake has completed, change the
1335  stream timeout value from the connect/handshake timeout to
1336  the standard data transfer timeout */
1337  netStream->timeout = netStream->savedTimeout;
1338  netStream->savedTimeout = CRYPT_ERROR;
1339  if( netStream->iTransportSession != CRYPT_ERROR )
1340  {
1341  status = krnlSendMessage( netStream->iTransportSession,
1342  IMESSAGE_SETATTRIBUTE, &netStream->timeout,
1344  if( cryptStatusError( status ) )
1345  return( status );
1346  }
1347  return( CRYPT_OK );
1348 
1350  REQUIRES_S( value == TRUE || value == FALSE );
1351 
1352  if( value )
1353  netStream->nFlags &= ~STREAM_NFLAG_LASTMSG;
1354  else
1355  netStream->nFlags |= STREAM_NFLAG_LASTMSG;
1356  return( CRYPT_OK );
1357 
1359  REQUIRES_S( value > STREAM_HTTPREQTYPE_NONE && \
1360  value < STREAM_HTTPREQTYPE_LAST );
1361  REQUIRES_S( netStream->protocol == STREAM_PROTOCOL_HTTP );
1362 
1363  netStream->nFlags &= ~STREAM_NFLAG_HTTPREQMASK;
1364  switch( value )
1365  {
1367  netStream->nFlags |= STREAM_NFLAG_HTTPGET;
1368  break;
1369 
1371  netStream->nFlags |= STREAM_NFLAG_HTTPPOST;
1372  break;
1373 
1375  netStream->nFlags |= STREAM_NFLAG_HTTPGET | \
1376  STREAM_NFLAG_HTTPPOST;
1377  break;
1378 
1379  default:
1380  retIntError();
1381  }
1382 
1383  /* If only an HTTP GET is possible and it's a client-side
1384  stream, it's read-only */
1385  if( value == STREAM_HTTPREQTYPE_GET && \
1386  !( netStream->nFlags & STREAM_NFLAG_ISSERVER ) )
1387  stream->flags = STREAM_FLAG_READONLY;
1388  else
1389  {
1390  /* Reset the read-only flag if we're changing the HTTP
1391  operation type to one that allows writes */
1392  stream->flags &= ~STREAM_FLAG_READONLY;
1393  }
1394  return( CRYPT_OK );
1395 
1397  REQUIRES_S( value == TRUE );
1398  REQUIRES_S( netStream->protocol == STREAM_PROTOCOL_HTTP || \
1399  netStream->protocol == STREAM_PROTOCOL_CMP );
1400 
1401  netStream->nFlags |= STREAM_NFLAG_LASTMSG;
1402  return( CRYPT_OK );
1403 
1405  REQUIRES_S( value == TRUE );
1406  REQUIRES_S( !( netStream->nFlags & STREAM_NFLAG_USERSOCKET ) );
1407 
1408  /* If this is a user-supplied socket we can't perform a partial
1409  close without affecting the socket as seen by the user so we
1410  only perform the partial close if it's a cryptlib-controlled
1411  socket */
1412  if( !( netStream->nFlags & STREAM_NFLAG_USERSOCKET ) )
1413  netStream->transportDisconnectFunction( netStream, FALSE );
1414 
1415  return( CRYPT_OK );
1416 #endif /* USE_TCP */
1417  }
1418 
1419  retIntError_Stream( stream );
1420  }
1421 
1422 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
1423 int sioctlSetString( INOUT STREAM *stream,
1424  IN_ENUM( STREAM_IOCTL ) const STREAM_IOCTL_TYPE type,
1425  IN_BUFFER( dataLen ) const void *data,
1426  IN_LENGTH const int dataLen )
1427  {
1428 #ifdef USE_TCP
1429  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
1430 #endif /* USE_TCP */
1431 
1432  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1433  assert( isReadPtr( data, dataLen ) );
1434 
1435  /* Check that the input parameters are in order */
1436  if( !isWritePtrConst( stream, sizeof( STREAM ) ) )
1437  retIntError();
1438 
1439  REQUIRES_S( sanityCheck( stream ) );
1440  REQUIRES_S( ( ( stream->type == STREAM_TYPE_FILE || \
1441  sIsVirtualFileStream( stream ) ) && \
1442  ( type == STREAM_IOCTL_ERRORINFO || \
1443  type == STREAM_IOCTL_IOBUFFER ) ) || \
1444  ( stream->type == STREAM_TYPE_NETWORK ) );
1445  REQUIRES_S( type > STREAM_IOCTL_NONE && type < STREAM_IOCTL_LAST );
1446  REQUIRES_S( dataLen > 0 && dataLen < MAX_INTLENGTH );
1447 
1448  switch( type )
1449  {
1451  REQUIRES_S( dataLen == sizeof( ERROR_INFO ) );
1452 
1453  /* If this stream type doesn't record extended error information,
1454  there's nothing to do */
1455  if( stream->type != STREAM_TYPE_NETWORK )
1456  return( CRYPT_OK );
1457 
1458 #ifdef USE_TCP
1459  /* Copy the error information to the stream */
1460  copyErrorInfo( NETSTREAM_ERRINFO, data );
1461 #endif /* USE_TCP */
1462  return( CRYPT_OK );
1463 
1464  case STREAM_IOCTL_IOBUFFER:
1465  REQUIRES_S( dataLen == 0 || \
1466  dataLen == 512 || dataLen == 1024 || \
1467  dataLen == 2048 || dataLen == 4096 || \
1468  dataLen == 8192 || dataLen == 16384 );
1469 
1470  return( setStreamBuffer( stream, data, dataLen ) );
1471  }
1472 
1473  retIntError_Stream( stream );
1474  }
1475 
1476 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
1477 int sioctlGet( INOUT STREAM *stream,
1478  IN_ENUM( STREAM_IOCTL ) const STREAM_IOCTL_TYPE type,
1479  OUT_BUFFER_FIXED( dataMaxLen ) void *data,
1480  IN_LENGTH_SHORT const int dataMaxLen )
1481  {
1482 #ifdef USE_TCP
1483  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
1484 #endif /* USE_TCP */
1485 
1486  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1487  assert( isWritePtr( data, dataMaxLen ) );
1488 
1489  /* Check that the input parameters are in order */
1490  if( !isWritePtrConst( stream, sizeof( STREAM ) ) )
1491  retIntError();
1492 
1493  REQUIRES_S( sanityCheck( stream ) );
1494  REQUIRES_S( stream->type == STREAM_TYPE_NETWORK );
1495  REQUIRES_S( type > STREAM_IOCTL_NONE && type < STREAM_IOCTL_LAST );
1496  REQUIRES_S( data != NULL );
1497  REQUIRES_S( dataMaxLen > 0 && dataMaxLen < MAX_INTLENGTH_SHORT );
1498 
1499  switch( type )
1500  {
1501 #ifdef USE_TCP
1504  REQUIRES_S( dataMaxLen == sizeof( int ) );
1505 
1506  /* These two values are stored as a shared timeout value
1507  which is updated on each data read or write by the
1508  caller so there's no need to maintain distinct values */
1509  *( ( int * ) data ) = netStream->timeout;
1510  return( CRYPT_OK );
1511 
1513  REQUIRES_S( dataMaxLen == sizeof( int ) );
1514 
1515  *( ( int * ) data ) = \
1516  ( netStream->nFlags & STREAM_NFLAG_LASTMSG ) ? FALSE : TRUE;
1517  return( CRYPT_OK );
1518 
1520  REQUIRES_S( dataMaxLen > 8 && dataMaxLen < MAX_INTLENGTH );
1521 
1522  if( netStream->clientAddressLen <= 0 )
1523  return( CRYPT_ERROR_NOTFOUND );
1524  if( netStream->clientAddressLen > dataMaxLen )
1525  return( CRYPT_ERROR_OVERFLOW );
1526  memcpy( data, netStream->clientAddress, netStream->clientAddressLen );
1527 
1528  return( CRYPT_OK );
1529 
1531  REQUIRES_S( dataMaxLen == sizeof( int ) );
1532 
1533  if( netStream->clientAddressLen <= 0 )
1534  return( CRYPT_ERROR_NOTFOUND );
1535  *( ( int * ) data ) = netStream->clientAddressLen;
1536 
1537  return( CRYPT_OK );
1538 
1540  REQUIRES_S( dataMaxLen == sizeof( int ) );
1541 
1542  if( netStream->clientPort <= 0 )
1543  return( CRYPT_ERROR_NOTFOUND );
1544  *( ( int * ) data ) = netStream->clientPort;
1545 
1546  return( CRYPT_OK );
1547 
1548 #endif /* USE_TCP */
1549  }
1550 
1551  retIntError_Stream( stream );
1552  }
1553 
1554 /****************************************************************************
1555 * *
1556 * Misc Functions *
1557 * *
1558 ****************************************************************************/
1559 
1560 /* Convert a file stream to a memory stream. Usually this allocates a
1561  buffer and reads the stream into it, however if it's a read-only memory-
1562  mapped file it just creates a second reference to the data to save
1563  memory */
1564 
1566 int sFileToMemStream( OUT STREAM *memStream, INOUT STREAM *fileStream,
1567  OUT_BUFFER_ALLOC_OPT( length ) void **bufPtrPtr,
1568  IN_LENGTH const int length )
1569  {
1570  void *bufPtr;
1571  int status;
1572 
1573  assert( isWritePtr( memStream, sizeof( STREAM ) ) );
1574  assert( isWritePtr( fileStream, sizeof( STREAM ) ) );
1575  assert( isWritePtr( bufPtrPtr, sizeof( void * ) ) );
1576 
1577  /* Check that the input parameters are in order */
1578  if( !isWritePtrConst( memStream, sizeof( STREAM ) ) || \
1579  !isWritePtrConst( fileStream, sizeof( STREAM ) ) || \
1580  !isWritePtrConst( bufPtrPtr, sizeof( void * ) ) )
1581  {
1582  /* Since memStream() is an OUT parameter we can't use it with a
1583  retIntError_Stream() but have to use a plain retIntError() */
1584  retIntError();
1585  }
1586 
1587  /* We have to use REQUIRES() here rather than REQUIRES_S() since it's
1588  not certain which of the two streams to set the status for */
1589  REQUIRES( sanityCheck( fileStream ) && \
1590  fileStream->flags & STREAM_FFLAG_BUFFERSET );
1591  REQUIRES( fileStream->type == STREAM_TYPE_FILE );
1592  REQUIRES( length > 0 && length < MAX_INTLENGTH );
1593 
1594  /* Clear return value */
1595  memset( memStream, 0, sizeof( STREAM ) );
1596  *bufPtrPtr = NULL;
1597 
1598 #ifdef VIRTUAL_FILE_STREAM
1599  /* If it's a read-only memory-mapped file stream create the memory
1600  stream as a reference to the file stream */
1601  if( ( fileStream->flags & \
1604  {
1605  /* Make sure that there's enough data left in the memory-mapped
1606  stream to reference it as a file stream */
1607  if( length > fileStream->bufSize - fileStream->bufPos )
1608  return( CRYPT_ERROR_UNDERFLOW );
1609 
1610  /* Create a second reference to the memory-mapped stream and advance
1611  the read pointer in the memory-mapped file stream to mimic the
1612  behaviour of a read from it to the memory stream */
1613  status = sMemConnect( memStream, fileStream->buffer + \
1614  fileStream->bufPos, length );
1615  if( cryptStatusError( status ) )
1616  return( status );
1617  status = sSkip( fileStream, length );
1618  if( cryptStatusError( status ) )
1619  {
1620  sMemDisconnect( memStream );
1621  return( status );
1622  }
1623  return( CRYPT_OK );
1624  }
1625 #endif /* VIRTUAL_FILE_STREAM */
1626 
1627  /* It's a file stream, allocate a buffer for the data and read it in as
1628  a memory stream */
1629  if( ( bufPtr = clAlloc( "sFileToMemStream", length ) ) == NULL )
1630  return( CRYPT_ERROR_MEMORY );
1631  status = sread( fileStream, bufPtr, length );
1632  if( cryptStatusOK( status ) )
1633  status = sMemConnect( memStream, bufPtr, length );
1634  if( cryptStatusError( status ) )
1635  {
1636  clFree( "sFileToMemStream", bufPtr );
1637  return( status );
1638  }
1639  *bufPtrPtr = bufPtr;
1640  return( CRYPT_OK );
1641  }