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