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