cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
session.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib Session Support Routines *
4 * Copyright Peter Gutmann 1998-2008 *
5 * *
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9  #include "crypt.h"
10  #include "session.h"
11 #else
12  #include "crypt.h"
13  #include "session/session.h"
14 #endif /* Compiler-specific includes */
15 
16 #ifdef USE_SESSIONS
17 
18 /****************************************************************************
19 * *
20 * Utility Functions *
21 * *
22 ****************************************************************************/
23 
24 /* Reset the state of a request/response session for reuse so that it can
25  process another request or response */
26 
27 STDC_NONNULL_ARG( ( 1 ) ) \
28 static void cleanupReqResp( INOUT SESSION_INFO *sessionInfoPtr,
29  const BOOLEAN isPostTransaction )
30  {
31  const BOOLEAN isServer = isServer( sessionInfoPtr );
32 
33  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
34 
35  /* Clean up server requests left over from a previous transaction or
36  that have been created by the just-completed transaction */
37  if( isServer && sessionInfoPtr->iCertRequest != CRYPT_ERROR )
38  {
39  krnlSendNotifier( sessionInfoPtr->iCertRequest,
41  sessionInfoPtr->iCertRequest = CRYPT_ERROR;
42  }
43 
44  /* Clean up client/server responses left over from a previous
45  transaction and server responses created by the just-completed
46  transaction */
47  if( ( isServer || !isPostTransaction ) && \
48  sessionInfoPtr->iCertResponse != CRYPT_ERROR )
49  {
50  krnlSendNotifier( sessionInfoPtr->iCertResponse,
52  sessionInfoPtr->iCertResponse = CRYPT_ERROR;
53  }
54  }
55 
56 /* Initialise network connection information based on the contents of the
57  session object */
58 
59 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
60 int initSessionNetConnectInfo( const SESSION_INFO *sessionInfoPtr,
62  {
63  const ATTRIBUTE_LIST *clientNamePtr, *serverNamePtr, *portInfoPtr;
64 
65  assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
66  assert( isWritePtr( connectInfo, sizeof( NET_CONNECT_INFO ) ) );
67 
68  initNetConnectInfo( connectInfo, sessionInfoPtr->ownerHandle,
69  sessionInfoPtr->readTimeout, sessionInfoPtr->connectTimeout,
70  ( sessionInfoPtr->transportSession != CRYPT_ERROR ) ? \
72  ( sessionInfoPtr->networkSocket != CRYPT_ERROR ) ? \
74  ( sessionInfoPtr->flags & SESSION_USEHTTPTUNNEL ) ? \
77 
78  /* If the user has supplied the network transport information, there's
79  nothing further to do */
80  if( sessionInfoPtr->transportSession != CRYPT_ERROR )
81  {
82  connectInfo->iCryptSession = sessionInfoPtr->transportSession;
83  return( CRYPT_OK );
84  }
85  if( sessionInfoPtr->networkSocket != CRYPT_ERROR )
86  {
87  connectInfo->networkSocket = sessionInfoPtr->networkSocket;
88  return( CRYPT_OK );
89  }
90 
91  /* If there are explicit client and/or server names set, record them.
92  For a client the server name is the remote system to connect to
93  and the client name is the optional local interface to bind to, for
94  the server the server name is the optional local interface to bind
95  to */
96  clientNamePtr = findSessionInfo( sessionInfoPtr->attributeList,
98  serverNamePtr = findSessionInfo( sessionInfoPtr->attributeList,
100  if( isServer( sessionInfoPtr ) )
101  {
102  if( serverNamePtr != NULL )
103  {
104  connectInfo->interface = serverNamePtr->value;
105  connectInfo->interfaceLength = serverNamePtr->valueLength;
106  }
107  }
108  else
109  {
110  REQUIRES( serverNamePtr != NULL );
111 
112  connectInfo->name = serverNamePtr->value;
113  connectInfo->nameLength = serverNamePtr->valueLength;
114  if( clientNamePtr != NULL )
115  {
116  connectInfo->interface = clientNamePtr->value;
117  connectInfo->interfaceLength = clientNamePtr->valueLength;
118  }
119  }
120 
121  /* If there's an explicit port set, connect/bind to it, otherwise use the
122  default port for the protocol */
123  if( ( portInfoPtr = \
124  findSessionInfo( sessionInfoPtr->attributeList,
125  CRYPT_SESSINFO_SERVER_PORT ) ) != NULL )
126  connectInfo->port = portInfoPtr->intValue;
127  else
128  connectInfo->port = sessionInfoPtr->protocolInfo->port;
129 
130  return( CRYPT_OK );
131  }
132 
133 /* Make sure that mutually exclusive session attributes haven't been set.
134  The checks performed are:
135 
136  CRYPT_SESSINFO_REQUEST -> !CRYPT_SESSINFO_REQUEST,
137  !CRYPT_SESSINFO_PRIVATEKEY,
138  !CRYPT_SESSINFO_CMP_PRIVKEYSET
139 
140  CRYPT_SESSINFO_PRIVATEKEY -> !CRYPT_SESSINFO_PRIVATEKEY,
141  !CRYPT_SESSINFO_CMP_PRIVKEYSET
142 
143  CRYPT_SESSINFO_CACERTIFICATE-> !CRYPT_SESSINFO_CACERTIFICATE,
144  !CRYPT_SESSINFO_SERVER_FINGERPRINT
145 
146  CRYPT_SESSINFO_SERVER_FINGERPRINT
147  -> !CRYPT_SESSINFO_SERVER_FINGERPRINT,
148  !CRYPT_SESSINFO_CACERTIFICATE */
149 
150 #define CHECK_ATTR_NONE 0x00
151 #define CHECK_ATTR_REQUEST 0x01
152 #define CHECK_ATTR_PRIVKEY 0x02
153 #define CHECK_ATTR_PRIVKEYSET 0x04
154 #define CHECK_ATTR_CACERT 0x08
155 #define CHECK_ATTR_FINGERPRINT 0x10
156 
158 BOOLEAN checkAttributesConsistent( INOUT SESSION_INFO *sessionInfoPtr,
160  {
161  static const MAP_TABLE excludedAttrTbl[] = {
163  CHECK_ATTR_REQUEST | CHECK_ATTR_PRIVKEY | CHECK_ATTR_PRIVKEYSET },
165  CHECK_ATTR_PRIVKEY | CHECK_ATTR_PRIVKEYSET },
167  CHECK_ATTR_CACERT | CHECK_ATTR_FINGERPRINT },
169  CHECK_ATTR_FINGERPRINT | CHECK_ATTR_CACERT },
170  { CRYPT_ERROR, 0 }, { CRYPT_ERROR, 0 }
171  };
172  int flags = 0, status;
173 
174  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
175 
176  REQUIRES_B( attribute == CRYPT_SESSINFO_REQUEST || \
177  attribute == CRYPT_SESSINFO_PRIVATEKEY || \
178  attribute == CRYPT_SESSINFO_CACERTIFICATE || \
179  attribute == CRYPT_SESSINFO_SERVER_FINGERPRINT );
180 
181  /* Find the excluded-attribute information for this attribute */
182  status = mapValue( attribute, &flags, excludedAttrTbl,
183  FAILSAFE_ARRAYSIZE( excludedAttrTbl, MAP_TABLE ) );
185 
186  /* Make sure that none of the excluded attributes are present */
187  if( ( flags & CHECK_ATTR_REQUEST ) && \
188  sessionInfoPtr->iCertRequest != CRYPT_ERROR )
189  {
190  setErrorInfo( sessionInfoPtr, CRYPT_SESSINFO_REQUEST,
192  return( FALSE );
193  }
194  if( ( flags & CHECK_ATTR_PRIVKEYSET ) && \
195  sessionInfoPtr->privKeyset != CRYPT_ERROR )
196  {
199  return( FALSE );
200  }
201  if( ( flags & CHECK_ATTR_CACERT ) && \
202  sessionInfoPtr->iAuthInContext != CRYPT_ERROR )
203  {
206  return( FALSE );
207  }
208  if( ( flags & CHECK_ATTR_FINGERPRINT ) && \
209  findSessionInfo( sessionInfoPtr->attributeList,
211  {
214  return( FALSE );
215  }
216 
217  return( TRUE );
218  }
219 
220 /* Check that a server's certificate is currently valid. This self-check
221  avoids ugly silent failures where everything appears to work just fine on
222  the server side but the client gets invalid data back */
223 
224 CHECK_RETVAL STDC_NONNULL_ARG( ( 2, 3 ) ) \
225 int checkServerCertValid( const CRYPT_CERTIFICATE iServerCert,
226  OUT_ENUM_OPT( CRYPT_ATTRIBUTE ) \
227  CRYPT_ATTRIBUTE_TYPE *errorLocus,
228  OUT_ENUM_OPT( CRYPT_ERRTYPE ) \
229  CRYPT_ERRTYPE_TYPE *errorType )
230  {
231  static const int complianceLevelStandard = CRYPT_COMPLIANCELEVEL_STANDARD;
232  int complianceLevel, value, status;
233 
234  assert( isWritePtr( errorLocus, sizeof( CRYPT_ATTRIBUTE_TYPE ) ) );
235  assert( isWritePtr( errorType, sizeof( CRYPT_ERRTYPE_TYPE ) ) );
236 
237  REQUIRES( isHandleRangeValid( iServerCert ) );
238 
239  status = krnlSendMessage( iServerCert, IMESSAGE_GETATTRIBUTE,
240  &complianceLevel,
242  if( cryptStatusError( status ) )
243  {
244  /* We can't do much more if we can't even get the initial compliance
245  level */
246  return( CRYPT_OK );
247  }
248 
249  /* Check whether the certificate is valid at a standard level of
250  compliance, which catches expired certificates and other obvious
251  problems */
252  krnlSendMessage( iServerCert, IMESSAGE_SETATTRIBUTE,
253  ( MESSAGE_CAST ) &complianceLevelStandard,
255  status = krnlSendMessage( iServerCert, IMESSAGE_CHECK, NULL,
257  krnlSendMessage( iServerCert, IMESSAGE_SETATTRIBUTE,
258  ( MESSAGE_CAST ) &complianceLevel,
260  if( cryptStatusOK( status ) )
261  return( CRYPT_OK );
262 
263  /* The certificate isn't valid, copy the extended error information up
264  from the certificate if possible. This leads to rather odd extended
265  error information for the session since the error information is
266  pointing at certificate attributes for a session object, but it's
267  unlikely that users will think of checking the certificate for error
268  details and the presence of certificate-related error information
269  should make it obvious what it pertains to */
270  status = krnlSendMessage( iServerCert, IMESSAGE_GETATTRIBUTE, &value,
272  if( cryptStatusOK( status ) )
273  {
274  *errorLocus = value;
275  status = krnlSendMessage( iServerCert, IMESSAGE_GETATTRIBUTE, &value,
277  }
278  if( cryptStatusOK( status ) )
279  *errorType = value;
280 
281  return( CRYPT_ERROR_INVALID );
282  }
283 
284 /****************************************************************************
285 * *
286 * Session Activation Functions *
287 * *
288 ****************************************************************************/
289 
290 /* Check client/server-specific required values */
291 
293 static CRYPT_ATTRIBUTE_TYPE checkClientParameters( const SESSION_INFO *sessionInfoPtr )
294  {
295  assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
296 
297  /* Make sure that the network comms parameters are present */
298  if( sessionInfoPtr->transportSession == CRYPT_ERROR && \
299  sessionInfoPtr->networkSocket == CRYPT_ERROR && \
300  findSessionInfo( sessionInfoPtr->attributeList,
301  CRYPT_SESSINFO_SERVER_NAME ) == NULL )
302  return( CRYPT_SESSINFO_SERVER_NAME );
303 
304  /* Make sure that the username + password and/or user private key are
305  present if required */
306  if( ( sessionInfoPtr->clientReqAttrFlags & SESSION_NEEDS_USERID ) && \
307  findSessionInfo( sessionInfoPtr->attributeList,
308  CRYPT_SESSINFO_USERNAME ) == NULL )
309  return( CRYPT_SESSINFO_USERNAME );
310  if( ( sessionInfoPtr->clientReqAttrFlags & SESSION_NEEDS_PASSWORD ) && \
311  findSessionInfo( sessionInfoPtr->attributeList,
312  CRYPT_SESSINFO_PASSWORD ) == NULL )
313  {
314  /* There's no password present, see if we can use a private key as
315  an alternative */
316  if( !( sessionInfoPtr->clientReqAttrFlags & \
318  sessionInfoPtr->privateKey == CRYPT_ERROR )
319  return( CRYPT_SESSINFO_PASSWORD );
320  }
321  if( ( sessionInfoPtr->clientReqAttrFlags & SESSION_NEEDS_PRIVATEKEY ) && \
322  sessionInfoPtr->privateKey == CRYPT_ERROR )
323  {
324  /* There's no private key present, see if we can use a password as
325  an alternative */
326  if( !( sessionInfoPtr->clientReqAttrFlags & \
328  findSessionInfo( sessionInfoPtr->attributeList,
329  CRYPT_SESSINFO_PASSWORD ) == NULL )
330  return( CRYPT_SESSINFO_PRIVATEKEY );
331  }
332 
333  /* Make sure that request/response protocol data is present if required */
334  if( ( sessionInfoPtr->clientReqAttrFlags & SESSION_NEEDS_REQUEST ) && \
335  sessionInfoPtr->iCertRequest == CRYPT_ERROR )
336  return( CRYPT_SESSINFO_REQUEST );
337 
338  return( CRYPT_ATTRIBUTE_NONE );
339  }
340 
342 static CRYPT_ATTRIBUTE_TYPE checkServerParameters( const SESSION_INFO *sessionInfoPtr )
343  {
344  assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
345 
346  /* Make sure that server key and keyset information is present if
347  required */
348  if( ( sessionInfoPtr->serverReqAttrFlags & SESSION_NEEDS_PRIVATEKEY ) && \
349  sessionInfoPtr->privateKey == CRYPT_ERROR )
350  {
351  /* There's no private key present, see if we can use a username +
352  password as an alternative */
353  if( !( sessionInfoPtr->serverReqAttrFlags & \
355  findSessionInfo( sessionInfoPtr->attributeList,
356  CRYPT_SESSINFO_PASSWORD ) == NULL )
357  return( CRYPT_SESSINFO_PRIVATEKEY );
358  }
359  if( ( sessionInfoPtr->serverReqAttrFlags & SESSION_NEEDS_KEYSET ) && \
360  sessionInfoPtr->cryptKeyset == CRYPT_ERROR )
361  return( CRYPT_SESSINFO_KEYSET );
362 
363  return( CRYPT_ATTRIBUTE_NONE );
364  }
365 
366 /* Activate the network connection for a session */
367 
369 static int activateConnection( INOUT SESSION_INFO *sessionInfoPtr )
370  {
371  CRYPT_ATTRIBUTE_TYPE errorAttribute;
372  int status;
373 
374  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
375 
376  /* Make sure that everything is set up ready to go */
377  errorAttribute = isServer( sessionInfoPtr ) ? \
378  checkServerParameters( sessionInfoPtr ) : \
379  checkClientParameters( sessionInfoPtr );
380  if( errorAttribute != CRYPT_ATTRIBUTE_NONE )
381  {
382  setErrorInfo( sessionInfoPtr, errorAttribute,
384  return( CRYPT_ERROR_NOTINITED );
385  }
386  ENSURES( isServer( sessionInfoPtr ) || \
387  findSessionInfo( sessionInfoPtr->attributeList,
388  CRYPT_SESSINFO_SERVER_NAME ) != NULL || \
389  sessionInfoPtr->networkSocket != CRYPT_ERROR );
390  ENSURES( findSessionInfo( sessionInfoPtr->attributeList,
391  CRYPT_SESSINFO_SERVER_PORT ) != NULL || \
392  sessionInfoPtr->protocolInfo->port > 0 );
393 
394  /* Allocate the send and receive buffers if necessary. The send buffer
395  isn't used for request-response session types that use the receive
396  buffer for both outgoing and incoming data so we only allocate it if
397  it's actually required */
398  if( sessionInfoPtr->sendBuffer == NULL )
399  {
400  REQUIRES( sessionInfoPtr->receiveBufSize >= MIN_BUFFER_SIZE && \
401  sessionInfoPtr->receiveBufSize < MAX_INTLENGTH );
402  REQUIRES( ( sessionInfoPtr->sendBufSize >= MIN_BUFFER_SIZE && \
403  sessionInfoPtr->sendBufSize < MAX_INTLENGTH ) || \
404  sessionInfoPtr->sendBufSize == CRYPT_UNUSED );
405 
406  if( ( sessionInfoPtr->receiveBuffer = \
407  clAlloc( "activateConnection", \
408  sessionInfoPtr->receiveBufSize + 8 ) ) == NULL )
409  return( CRYPT_ERROR_MEMORY );
410  if( sessionInfoPtr->sendBufSize != CRYPT_UNUSED )
411  {
412  /* When allocating the send buffer we use the size given for the
413  receive buffer since the user may have overridden the default
414  buffer size */
415  if( ( sessionInfoPtr->sendBuffer = \
416  clAlloc( "activateConnection", \
417  sessionInfoPtr->receiveBufSize + 8 ) ) == NULL )
418  {
419  clFree( "activateConnection", sessionInfoPtr->receiveBuffer );
420  sessionInfoPtr->receiveBuffer = NULL;
421  return( CRYPT_ERROR_MEMORY );
422  }
423  sessionInfoPtr->sendBufSize = sessionInfoPtr->receiveBufSize;
424  }
425  }
426  ENSURES( sessionInfoPtr->receiveBuffer != NULL && \
427  sessionInfoPtr->receiveBufSize >= MIN_BUFFER_SIZE && \
428  sessionInfoPtr->receiveBufSize < MAX_INTLENGTH );
429  ENSURES( sessionInfoPtr->sendBufSize == CRYPT_UNUSED || \
430  sessionInfoPtr->sendBuffer != NULL );
431 
432  /* Set timeouts if they're not set yet. If there's an error then we use
433  the default value rather than aborting the entire session because of
434  a minor difference in timeout values, although we also warn the
435  caller in debug mode */
436  if( sessionInfoPtr->connectTimeout == CRYPT_ERROR )
437  {
438  int timeout;
439 
440  status = krnlSendMessage( sessionInfoPtr->ownerHandle,
441  IMESSAGE_GETATTRIBUTE, &timeout,
443  if( cryptStatusOK( status ) )
444  sessionInfoPtr->connectTimeout = timeout;
445  else
446  {
447  DEBUG_DIAG(( "Couldn't get connect timeout config value" ));
448  assert( DEBUG_WARN );
449  sessionInfoPtr->connectTimeout = 30;
450  }
451  }
452  if( sessionInfoPtr->readTimeout == CRYPT_ERROR )
453  {
454  int timeout;
455 
456  status = krnlSendMessage( sessionInfoPtr->ownerHandle,
457  IMESSAGE_GETATTRIBUTE, &timeout,
459  if( cryptStatusOK( status ) )
460  sessionInfoPtr->readTimeout = timeout;
461  else
462  {
463  DEBUG_DIAG(( "Couldn't get read timeout config value" ));
464  assert( DEBUG_WARN );
465  sessionInfoPtr->readTimeout = 30;
466  }
467  }
468  if( sessionInfoPtr->writeTimeout == CRYPT_ERROR )
469  {
470  int timeout;
471 
472  status = krnlSendMessage( sessionInfoPtr->ownerHandle,
473  IMESSAGE_GETATTRIBUTE, &timeout,
475  if( cryptStatusOK( status ) )
476  sessionInfoPtr->writeTimeout = timeout;
477  else
478  {
479  DEBUG_DIAG(( "Couldn't get write timeout config value" ));
480  assert( DEBUG_WARN );
481  sessionInfoPtr->writeTimeout = 30;
482  }
483  }
484 
485  /* Wait for any async driver binding to complete. We can delay this
486  until this very late stage because no networking functionality is
487  used until this point */
489  {
490  /* The kernel is shutting down, bail out */
491  return( CRYPT_ERROR_PERMISSION );
492  }
493 
494  /* If this is the first time that we've got here, activate the session */
495  if( !( sessionInfoPtr->flags & SESSION_PARTIALOPEN ) )
496  {
497  REQUIRES( !( sessionInfoPtr->flags & SESSION_ISOPEN ) )
498 
499  status = sessionInfoPtr->connectFunction( sessionInfoPtr );
500  if( cryptStatusError( status ) )
501  return( status );
502  }
503 
504  /* If it's a secure data transport session, complete the session state
505  setup. Note that some sessions dynamically change the protocol
506  information during the handshake to accommodate parameters negotiated
507  during the handshake so we can only access the protocol information
508  after the handshake has completed */
509  if( !sessionInfoPtr->protocolInfo->isReqResp )
510  {
511  /* Complete the session handshake to set up the secure state */
512  status = sessionInfoPtr->transactFunction( sessionInfoPtr );
513  if( cryptStatusError( status ) )
514  {
515  /* If we need feedback from the user before we can complete the
516  handshake (for example checking a user name and password or
517  certificate supplied by the other side) we remain in the
518  handshake state so that the user can re-activate the session
519  after confirming (or denying) the check */
520  if( status == CRYPT_ENVELOPE_RESOURCE )
521  sessionInfoPtr->flags |= SESSION_PARTIALOPEN;
522 
523  return( status );
524  }
525 
526  /* Notify the kernel that the session key context is attached to the
527  session object. Note that we increment its reference count even
528  though it's an internal object used only by the session because
529  otherwise it'll be automatically destroyed by the kernel as a
530  zero-reference dependent object when the session object is
531  destroyed (but before the session object itself since the context
532  is just a dependent object). This automatic cleanup could cause
533  problems for lower-level session management code that tries to
534  work with the (apparently still-valid) handle, for example
535  protocols that need to encrypt a close-channel message on session
536  shutdown */
537  krnlSendMessage( sessionInfoPtr->objectHandle, IMESSAGE_SETDEPENDENT,
538  &sessionInfoPtr->iCryptInContext,
540 
541  /* Set up the buffer management variables */
542  sessionInfoPtr->receiveBufPos = sessionInfoPtr->receiveBufEnd = 0;
543  sessionInfoPtr->sendBufPos = sessionInfoPtr->sendBufStartOfs;
544 
545  /* For data transport sessions, partial reads and writes (that is,
546  sending and receiving partial packets in the presence of
547  timeouts) are permitted */
548  sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_PARTIALREAD, TRUE );
549  sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_PARTIALWRITE, TRUE );
550  }
551 
552  /* The handshake has been completed, switch from the handshake timeout
553  to the data transfer timeout and remember that the session has been
554  successfully established */
555  sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_HANDSHAKECOMPLETE, TRUE );
556  sessionInfoPtr->flags &= ~SESSION_PARTIALOPEN;
557  sessionInfoPtr->flags |= SESSION_ISOPEN;
558 
559  return( CRYPT_OK );
560  }
561 
562 /* Activate a session */
563 
565 int activateSession( INOUT SESSION_INFO *sessionInfoPtr )
566  {
567  int streamState, status;
568 
569  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
570 
571  /* Activate the connection if necessary */
572  if( !( sessionInfoPtr->flags & SESSION_ISOPEN ) )
573  {
574  /* Try and activate the session */
575  status = activateConnection( sessionInfoPtr );
576  if( cryptStatusError( status ) )
577  return( status );
578 
579  /* The session activation succeeded, make sure that we don't try
580  and replace the ephemeral attributes established during the
581  session setup during any later operations. This is used for
582  example when we're the server and the client provides us with
583  authentication data but the validity of the data hasn't been
584  confirmed yet by the user (see the comment about the
585  CRYPT_ENVELOPE_RESOURCE status in activateConnection()), normally
586  this would be deleted/overwritten when the session is recycled
587  but once the caller has confirmed it as being valid we lock it to
588  make sure that it won't be changed any more */
589  if( sessionInfoPtr->attributeList != NULL )
590  lockEphemeralAttributes( sessionInfoPtr->attributeList );
591  }
592 
593  /* If it's a secure data transport session it's up to the caller to move
594  data over it, and we're done */
595  if( !sessionInfoPtr->protocolInfo->isReqResp )
596  return( CRYPT_OK );
597 
598  /* Carry out a transaction on the request-response connection. We
599  perform a cleanup of request/response data around the activation,
600  beforehand to catch data such as responses left over from a previous
601  transaction and afterwards to clean up ephemeral data such as
602  requests sent to a server */
603  cleanupReqResp( sessionInfoPtr, FALSE );
604  status = sessionInfoPtr->transactFunction( sessionInfoPtr );
605  cleanupReqResp( sessionInfoPtr, TRUE );
606  if( cryptStatusError( status ) )
607  return( status );
608 
609  /* Check whether the other side has indicated that it's closing the
610  stream and if it has, shut down our side as well and record the fact
611  that the session is now closed */
612  status = sioctlGet( &sessionInfoPtr->stream, STREAM_IOCTL_CONNSTATE,
613  &streamState, sizeof( int ) );
614  if( cryptStatusError( status ) || !streamState )
615  {
616  sessionInfoPtr->flags &= ~SESSION_ISOPEN;
617  sessionInfoPtr->shutdownFunction( sessionInfoPtr );
618  }
619  return( CRYPT_OK );
620  }
621 
622 /****************************************************************************
623 * *
624 * Session Shutdown Functions *
625 * *
626 ****************************************************************************/
627 
628 /* Send a close notification. This requires special-case handling because
629  it's not certain how long we should wait around for the close to happen.
630  If we're in the middle of a cryptlib shutdown then we don't want to wait
631  around forever since this would stall the overall shutdown, but if it's a
632  standard session shutdown then we should wait for at least a small amount
633  of time to ensure that all of the data is sent */
634 
636 int sendCloseNotification( INOUT SESSION_INFO *sessionInfoPtr,
637  IN_BUFFER_OPT( length ) const void *data,
638  IN_LENGTH_SHORT_Z const int length )
639  {
640  BOOLEAN isShutdown = FALSE;
641  int dummy, status = CRYPT_OK;
642 
643  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
644  assert( ( data == NULL && length == 0 ) || \
645  isReadPtr( data, length ) );
646 
647  REQUIRES( ( data == NULL && length == 0 ) || \
648  ( data != NULL && \
649  length > 0 && length < MAX_INTLENGTH_SHORT ) );
650 
651  /* Determine whether we're being shut down as a part of a general
652  cryptlib shutdown or just a session shutdown. We do this by trying
653  to read a configuration option from the owning user object, if the
654  kernel is in the middle of a shutdown it disallows all frivolous
655  messages so if we get a permission error then we're in the middle of
656  the shutdown */
657  if( krnlSendMessage( sessionInfoPtr->ownerHandle,
658  IMESSAGE_GETATTRIBUTE, &dummy,
660  isShutdown = TRUE;
661 
662  /* If necessary set a timeout sufficient to at least provide a chance of
663  sending our close notification and receiving the other side's ack of
664  the close, but without leading to excessive delays during the
665  shutdown */
666  if( isShutdown )
667  {
668  /* It's a cryptlib-wide shutdown, try and get out as quickly as
669  possible */
670  sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_WRITETIMEOUT, 2 );
671  }
672  else
673  {
674  int timeout;
675 
676  /* It's a standard session shutdown, wait around for at least five
677  seconds, but not more than fifteen */
678  status = sioctlGet( &sessionInfoPtr->stream,
679  STREAM_IOCTL_WRITETIMEOUT, &timeout,
680  sizeof( int ) );
681  if( cryptStatusError( status ) || timeout < 5 )
682  timeout = 5;
683  else
684  {
685  if( timeout > 15 )
686  timeout = 15;
687  }
688  sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_WRITETIMEOUT,
689  timeout );
690  }
691 
692  /* Send the close notification to the peer */
693  if( data != NULL )
694  status = swrite( &sessionInfoPtr->stream, data, length );
695 
696  /* Close the send side of the connection if it's a cryptlib-internal
697  socket. This is needed by some implementations that want to see a
698  FIN before they react to a shutdown notification, as well as being
699  a hint to the network code to flush any remaining data enqueued for
700  sending before the arrival of the full close. If it's a user-managed
701  socket we can't perform the partial close since this would affect the
702  state of the socket as seen by the user, since the need to see the
703  FIN is fairly rare we choose this as the less problematic of the two
704  options */
705  if( sessionInfoPtr->networkSocket == CRYPT_ERROR )
706  {
707  sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_CLOSESENDCHANNEL,
708  TRUE );
709  }
710 
711  return( ( data == NULL || !cryptStatusError( status ) ) ? \
712  CRYPT_OK : status );
713  }
714 
715 /****************************************************************************
716 * *
717 * Default Action Handlers *
718 * *
719 ****************************************************************************/
720 
721 /* Default init/shutdown functions used when no session-specific ones are
722  provided */
723 
725 static int defaultClientStartupFunction( INOUT SESSION_INFO *sessionInfoPtr )
726  {
728  int status;
729 
730  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
731 
732  /* Connect to the server */
733  status = initSessionNetConnectInfo( sessionInfoPtr, &connectInfo );
734  if( cryptStatusError( status ) )
735  return( status );
736  if( sessionInfoPtr->flags & SESSION_ISHTTPTRANSPORT )
737  status = sNetConnect( &sessionInfoPtr->stream, STREAM_PROTOCOL_HTTP,
738  &connectInfo, &sessionInfoPtr->errorInfo );
739  else
740  {
741 #ifdef USE_CMP_TRANSPORT
742  if( sessionInfoPtr->flags & SESSION_USEALTTRANSPORT )
743  {
744  const ALTPROTOCOL_INFO *altProtocolInfoPtr = \
745  sessionInfoPtr->protocolInfo->altProtocolInfo;
746 
747  /* If we'd be using the HTTP port for a session-specific
748  protocol, change it to the default port for the session-
749  specific protocol instead */
750  if( connectInfo.port == 80 )
751  connectInfo.port = altProtocolInfoPtr->port;
752  status = sNetConnect( &sessionInfoPtr->stream,
753  altProtocolInfoPtr->type,
754  &connectInfo, &sessionInfoPtr->errorInfo );
755  }
756  else
757 #endif /* USE_CMP_TRANSPORT */
758  {
759  status = sNetConnect( &sessionInfoPtr->stream,
761  &connectInfo, &sessionInfoPtr->errorInfo );
762  }
763  }
764  return( status );
765  }
766 
768 static int defaultServerStartupFunction( INOUT SESSION_INFO *sessionInfoPtr )
769  {
771  int nameLen, port, status;
772 
773  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
774 
775  /* Wait for a client connection */
776  status = initSessionNetConnectInfo( sessionInfoPtr, &connectInfo );
777  if( cryptStatusError( status ) )
778  return( status );
779  if( sessionInfoPtr->flags & SESSION_ISHTTPTRANSPORT )
780  status = sNetListen( &sessionInfoPtr->stream, STREAM_PROTOCOL_HTTP,
781  &connectInfo, &sessionInfoPtr->errorInfo );
782  else
783  {
784 #ifdef USE_CMP_TRANSPORT
785  if( sessionInfoPtr->flags & SESSION_USEALTTRANSPORT )
786  {
787  const ALTPROTOCOL_INFO *altProtocolInfoPtr = \
788  sessionInfoPtr->protocolInfo->altProtocolInfo;
789 
790  /* If we'd be using the HTTP port for a session-specific
791  protocol, change it to the default port for the session-
792  specific protocol instead */
793  if( connectInfo.port == 80 )
794  connectInfo.port = altProtocolInfoPtr->port;
795  status = sNetListen( &sessionInfoPtr->stream,
796  altProtocolInfoPtr->type,
797  &connectInfo, &sessionInfoPtr->errorInfo );
798  }
799  else
800 #endif /* USE_CMP_TRANSPORT */
801  {
802  status = sNetListen( &sessionInfoPtr->stream,
804  &connectInfo, &sessionInfoPtr->errorInfo );
805  }
806  }
807  if( cryptStatusError( status ) )
808  return( status );
809 
810  /* Save the client details for the caller, using the (always-present)
811  receive buffer as the intermediate store */
812  status = sioctlGet( &sessionInfoPtr->stream,
814  &nameLen, sizeof( int ) );
815  if( cryptStatusOK( status ) )
816  {
817  status = sioctlGet( &sessionInfoPtr->stream,
819  sessionInfoPtr->receiveBuffer,
821  }
822  if( cryptStatusError( status ) )
823  {
824  /* No client information available, exit */
825  return( CRYPT_OK );
826  }
827  status = addSessionInfoS( &sessionInfoPtr->attributeList,
829  sessionInfoPtr->receiveBuffer, nameLen );
830  if( cryptStatusError( status ) )
831  return( status );
832  status = sioctlGet( &sessionInfoPtr->stream, STREAM_IOCTL_GETCLIENTPORT,
833  &port, sizeof( int ) );
834  if( cryptStatusError( status ) )
835  {
836  /* No port information available, exit */
837  return( CRYPT_OK );
838  }
839  return( addSessionInfo( &sessionInfoPtr->attributeList,
840  CRYPT_SESSINFO_CLIENT_PORT, port ) );
841  }
842 
843 STDC_NONNULL_ARG( ( 1 ) ) \
844 static void defaultShutdownFunction( INOUT SESSION_INFO *sessionInfoPtr )
845  {
846  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
847 
848  sNetDisconnect( &sessionInfoPtr->stream );
849  }
850 
851 /* Default get-attribute function used when no session-specific one is
852  provided */
853 
855 static int defaultGetAttributeFunction( INOUT SESSION_INFO *sessionInfoPtr,
856  OUT void *data,
858  {
859  CRYPT_CERTIFICATE *responsePtr = ( CRYPT_CERTIFICATE * ) data;
860 
861  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
862  assert( isWritePtr( data, sizeof( int ) ) );
863 
865 
866  /* If we didn't get a response there's nothing to return */
867  if( sessionInfoPtr->iCertResponse == CRYPT_ERROR )
868  return( CRYPT_ERROR_NOTFOUND );
869 
870 /************************************************************************/
871 /* SCEP gets a bit complicated because a single object has to fill
872  multiple roles so that for example the issued certificate has to do
873  double duty for both encryption and authentication. For now we work
874  around this by juggling the values around */
875 if( sessionInfoPtr->type == CRYPT_SESSION_SCEP && \
876  sessionInfoPtr->iAuthInContext != CRYPT_ERROR )
877  {
878  *responsePtr = sessionInfoPtr->iCertResponse;
879  sessionInfoPtr->iCertResponse = sessionInfoPtr->iAuthInContext;
880  sessionInfoPtr->iAuthInContext = CRYPT_ERROR;
881 
882  return( CRYPT_OK );
883  }
884 /************************************************************************/
885 
886  /* Return the information to the caller */
887  krnlSendNotifier( sessionInfoPtr->iCertResponse, IMESSAGE_INCREFCOUNT );
888  *responsePtr = sessionInfoPtr->iCertResponse;
889  return( CRYPT_OK );
890  }
891 
892 /* Set up the function pointers to the session I/O methods */
893 
895 int initSessionIO( INOUT SESSION_INFO *sessionInfoPtr )
896  {
897  const PROTOCOL_INFO *protocolInfoPtr = sessionInfoPtr->protocolInfo;
898 
899  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
900 
901  /* Install default handler functions if required */
902  if( sessionInfoPtr->shutdownFunction == NULL )
903  sessionInfoPtr->shutdownFunction = defaultShutdownFunction;
904  if( sessionInfoPtr->connectFunction == NULL )
905  sessionInfoPtr->connectFunction = isServer( sessionInfoPtr ) ?
906  defaultServerStartupFunction : defaultClientStartupFunction;
907  if( protocolInfoPtr->isReqResp && \
908  sessionInfoPtr->getAttributeFunction == NULL )
909  sessionInfoPtr->getAttributeFunction = defaultGetAttributeFunction;
910 
911  return( CRYPT_OK );
912  }
913 #endif /* USE_SESSIONS */