📄 session.c
字号:
the user can re-activate the session after confirming (or
denying) the resource */
if( status == CRYPT_ENVELOPE_RESOURCE )
sessionInfoPtr->flags |= SESSION_PARTIALOPEN;
return( status );
}
/* Notify the kernel that the session key context is attached to the
session object. Note that we increment its reference count even
though it's an internal object used only by the session, because
otherwise it'll be automatically destroyed by the kernel as a
zero-reference dependent object when the session object is
destroyed (but before the session object itself, since it's a
dependent object). This automatic cleanup could cause problems
for lower-level session management code that tries to work with
the (apparently still-valid) handle, for example protocols that
need to encrypt a close-channel message on shutdown */
krnlSendMessage( sessionInfoPtr->objectHandle, IMESSAGE_SETDEPENDENT,
&sessionInfoPtr->iCryptInContext,
SETDEP_OPTION_INCREF );
/* Set up the buffer management variables */
sessionInfoPtr->receiveBufPos = sessionInfoPtr->receiveBufEnd = 0;
sessionInfoPtr->sendBufPos = sessionInfoPtr->sendBufStartOfs;
/* For data transport sessions, partial reads and writes (that is,
sending and receiving partial packets in the presence of
timeouts) are permitted */
sioctl( &sessionInfoPtr->stream, STREAM_IOCTL_PARTIALREAD, NULL,
TRUE );
sioctl( &sessionInfoPtr->stream, STREAM_IOCTL_PARTIALWRITE, NULL,
TRUE );
}
/* The handshake has been completed, switch from the handshake timeout
to the data transfer timeout and remember that the session has been
successfully established */
sioctl( &sessionInfoPtr->stream, STREAM_IOCTL_HANDSHAKECOMPLETE, NULL, 0 );
sessionInfoPtr->flags &= ~SESSION_PARTIALOPEN;
sessionInfoPtr->flags |= SESSION_ISOPEN;
return( CRYPT_OK );
}
/* Activate a session */
static void cleanupReqResp( SESSION_INFO *sessionInfoPtr,
const BOOLEAN isPostTransaction )
{
const BOOLEAN isServer = isServer( sessionInfoPtr );
/* Clean up server requests left over from a previous transaction/
created by the just-completed transaction */
if( isServer && sessionInfoPtr->iCertRequest != CRYPT_ERROR )
{
krnlSendNotifier( sessionInfoPtr->iCertRequest,
IMESSAGE_DECREFCOUNT );
sessionInfoPtr->iCertRequest = CRYPT_ERROR;
}
/* Clean up client/server responses left over from a previous
transaction and server responses created by the just-completed
transaction */
if( ( isServer || !isPostTransaction ) && \
sessionInfoPtr->iCertResponse != CRYPT_ERROR )
{
krnlSendNotifier( sessionInfoPtr->iCertResponse,
IMESSAGE_DECREFCOUNT );
sessionInfoPtr->iCertResponse = CRYPT_ERROR;
}
}
int activateSession( SESSION_INFO *sessionInfoPtr )
{
int streamState, status;
/* Activate the connection if necessary */
if( !( sessionInfoPtr->flags & SESSION_ISOPEN ) )
{
/* Try and activate the session */
status = activateConnection( sessionInfoPtr );
if( cryptStatusError( status ) )
return( status );
/* The session activation succeeded, make sure that we don't try
and replace the ephemeral attributes established during the
session setup during any later operations */
if( sessionInfoPtr->attributeList != NULL )
lockEphemeralAttributes( sessionInfoPtr->attributeList );
}
/* If it's a secure data transport session, it's up to the caller to
move data over it, and we're done */
if( !sessionInfoPtr->protocolInfo->isReqResp )
return( CRYPT_OK );
/* Carry out the transaction on the request-response connection. We
perform a cleanup of request/response data around the activation,
beforehand to catch data such as responses left over from a previous
transaction, and afterwards to clean up ephemeral data such as
requests sent to a server */
cleanupReqResp( sessionInfoPtr, FALSE );
status = sessionInfoPtr->transactFunction( sessionInfoPtr );
cleanupReqResp( sessionInfoPtr, TRUE );
if( cryptStatusError( status ) )
return( status );
/* Check whether the other side has indicated that it's closing the
stream and if it has, shut down our side as well and record the fact
that the session is now closed */
sioctl( &sessionInfoPtr->stream, STREAM_IOCTL_CONNSTATE,
&streamState, 0 );
if( !streamState )
{
sessionInfoPtr->flags &= ~SESSION_ISOPEN;
sessionInfoPtr->shutdownFunction( sessionInfoPtr );
}
return( CRYPT_OK );
}
/****************************************************************************
* *
* Session Shutdown Functions *
* *
****************************************************************************/
/* Send a close notification. This requires special-case handling because
it's not certain how long we should wait around for the close to happen.
If we're in the middle of a cryptlib shutdown we don't want to wait
around forever since this would stall the overall shutdown, but if it's a
standard session shutdown we should wait for at least a small amount of
time to ensure that all of the data is sent */
int sendCloseNotification( SESSION_INFO *sessionInfoPtr,
const void *data, const int length )
{
BOOLEAN isShutdown = FALSE;
int dummy, status = CRYPT_OK;
assert( ( data == NULL && length == 0 ) || \
isReadPtr( data, length ) );
/* Determine whether we're being shut down as a part of a general
cryptlib shutdown or just a session shutdown. We do this by trying
to read a config option from the owning user object, if the kernel is
in the middle of a shutdown it disallows all frivolous messages so
if we get a permission error we're in the middle of the shutdown */
if( krnlSendMessage( sessionInfoPtr->ownerHandle, IMESSAGE_GETATTRIBUTE,
&dummy, CRYPT_OPTION_INFO_MAJORVERSION ) == CRYPT_ERROR_PERMISSION )
isShutdown = TRUE;
/* If necessary set a timeout sufficient to at least provide a chance of
sending our close alert and receiving the other side's ack of the
close, but without leading to excessive delays during the shutdown */
if( isShutdown )
{
/* It's a cryptlib-wide shutdown, try and get out as quickly as
possible */
sioctl( &sessionInfoPtr->stream, STREAM_IOCTL_WRITETIMEOUT,
NULL, 2 );
}
else
{
int timeout;
/* It's a standard session shutdown, wait around for at least five
seconds, but not more than fifteen */
sioctl( &sessionInfoPtr->stream, STREAM_IOCTL_WRITETIMEOUT,
&timeout, 0 );
if( timeout < 5 )
timeout = 5;
if( timeout > 15 )
timeout = 15;
sioctl( &sessionInfoPtr->stream, STREAM_IOCTL_WRITETIMEOUT,
NULL, timeout );
}
/* Send the close notification to the peer */
if( data != NULL )
status = swrite( &sessionInfoPtr->stream, data, length );
/* Close the send side of the connection if it's a cryptlib-internal
socket. This is needed by some implementations that want to see a
FIN before they react to a shutdown notification, as well as being
a hint to the network code to flush any remaining data enqueued for
sending before the arrival of the full close. If it's a user-managed
socket we can't perform the partial close since this would affect the
state of the socket as seen by the user, since the need to see the
FIN is fairly rare we choose this as the less problematic of the two
options */
if( sessionInfoPtr->networkSocket == CRYPT_ERROR )
sioctl( &sessionInfoPtr->stream, STREAM_IOCTL_CLOSESENDCHANNEL,
NULL, 0 );
return( ( data == NULL || !cryptStatusError( status ) ) ? \
CRYPT_OK : status );
}
/****************************************************************************
* *
* Default Action Handlers *
* *
****************************************************************************/
/* Default init/shutdown functions used when no session-specific ones are
provided */
static int defaultClientStartupFunction( SESSION_INFO *sessionInfoPtr )
{
const PROTOCOL_INFO *protocolInfoPtr = sessionInfoPtr->protocolInfo;
NET_CONNECT_INFO connectInfo;
int status;
/* Connect to the server */
initSessionNetConnectInfo( sessionInfoPtr, &connectInfo );
if( sessionInfoPtr->flags & SESSION_ISHTTPTRANSPORT )
status = sNetConnect( &sessionInfoPtr->stream, STREAM_PROTOCOL_HTTP,
&connectInfo, &sessionInfoPtr->errorInfo );
else
{
if( sessionInfoPtr->flags & SESSION_USEALTTRANSPORT )
{
const ALTPROTOCOL_INFO *altProtocolInfoPtr = \
protocolInfoPtr->altProtocolInfo;
/* If we'd be using the HTTP port for a session-specific
protocol, change it to the default port for the session-
specific protocol instead */
if( connectInfo.port == 80 )
connectInfo.port = altProtocolInfoPtr->port;
status = sNetConnect( &sessionInfoPtr->stream,
altProtocolInfoPtr->type,
&connectInfo, &sessionInfoPtr->errorInfo );
}
else
status = sNetConnect( &sessionInfoPtr->stream,
STREAM_PROTOCOL_TCPIP,
&connectInfo, &sessionInfoPtr->errorInfo );
}
return( status );
}
static int defaultServerStartupFunction( SESSION_INFO *sessionInfoPtr )
{
const PROTOCOL_INFO *protocolInfoPtr = sessionInfoPtr->protocolInfo;
NET_CONNECT_INFO connectInfo;
int nameLen, port, status;
/* Wait for a client connection */
initSessionNetConnectInfo( sessionInfoPtr, &connectInfo );
if( sessionInfoPtr->flags & SESSION_ISHTTPTRANSPORT )
status = sNetListen( &sessionInfoPtr->stream, STREAM_PROTOCOL_HTTP,
&connectInfo, &sessionInfoPtr->errorInfo );
else
{
if( sessionInfoPtr->flags & SESSION_USEALTTRANSPORT )
{
const ALTPROTOCOL_INFO *altProtocolInfoPtr = \
protocolInfoPtr->altProtocolInfo;
/* If we'd be using the HTTP port for a session-specific
protocol, change it to the default port for the session-
specific protocol instead */
if( connectInfo.port == 80 )
connectInfo.port = altProtocolInfoPtr->port;
status = sNetListen( &sessionInfoPtr->stream,
altProtocolInfoPtr->type,
&connectInfo, &sessionInfoPtr->errorInfo );
}
else
status = sNetListen( &sessionInfoPtr->stream,
STREAM_PROTOCOL_TCPIP,
&connectInfo, &sessionInfoPtr->errorInfo );
}
if( cryptStatusError( status ) )
return( status );
/* Save the client details for the caller, using the (always-present)
receive buffer as the intermediate store */
status = sioctl( &sessionInfoPtr->stream, STREAM_IOCTL_GETCLIENTNAMELEN,
&nameLen, 0 );
if( cryptStatusOK( status ) )
status = sioctl( &sessionInfoPtr->stream, STREAM_IOCTL_GETCLIENTNAME,
sessionInfoPtr->receiveBuffer, CRYPT_MAX_TEXTSIZE );
if( cryptStatusError( status ) )
{
/* No client info available, exit */
return( CRYPT_OK );
}
status = addSessionInfo( &sessionInfoPtr->attributeList,
CRYPT_SESSINFO_CLIENT_NAME,
sessionInfoPtr->receiveBuffer, nameLen );
if( cryptStatusError( status ) )
return( status );
status = sioctl( &sessionInfoPtr->stream, STREAM_IOCTL_GETCLIENTPORT,
&port, 0 );
if( cryptStatusError( status ) )
{
/* No port info available, exit */
return( CRYPT_OK );
}
return( addSessionInfo( &sessionInfoPtr->attributeList,
CRYPT_SESSINFO_CLIENT_PORT, NULL, port ) );
}
static void defaultShutdownFunction( SESSION_INFO *sessionInfoPtr )
{
sNetDisconnect( &sessionInfoPtr->stream );
}
/* Default get-attribute function used when no session-specific one is
provided */
static int defaultGetAttributeFunction( SESSION_INFO *sessionInfoPtr,
void *data,
const CRYPT_ATTRIBUTE_TYPE type )
{
CRYPT_CERTIFICATE *responsePtr = ( CRYPT_CERTIFICATE * ) data;
assert( type == CRYPT_SESSINFO_RESPONSE );
/* If we didn't get a response there's nothing to return */
if( sessionInfoPtr->iCertResponse == CRYPT_ERROR )
return( CRYPT_ERROR_NOTFOUND );
/************************************************************************/
/* SCEP gets a bit complicated because a single object has to fill
multiple roles, so that for example the issued cert has to do double
duty for both encryption and authentication. For now we work around
this by juggling the values around */
if( sessionInfoPtr->type == CRYPT_SESSION_SCEP && \
sessionInfoPtr->iAuthInContext != CRYPT_ERROR )
{
*responsePtr = sessionInfoPtr->iCertResponse;
sessionInfoPtr->iCertResponse = sessionInfoPtr->iAuthInContext;
sessionInfoPtr->iAuthInContext = CRYPT_ERROR;
return( CRYPT_OK );
}
/************************************************************************/
/* Return the info to the caller */
krnlSendNotifier( sessionInfoPtr->iCertResponse, IMESSAGE_INCREFCOUNT );
*responsePtr = sessionInfoPtr->iCertResponse;
return( CRYPT_OK );
}
/* Set up the function pointers to the session I/O methods */
int initSessionIO( SESSION_INFO *sessionInfoPtr )
{
const PROTOCOL_INFO *protocolInfoPtr = sessionInfoPtr->protocolInfo;
/* Install default handler functions if required */
if( sessionInfoPtr->shutdownFunction == NULL )
sessionInfoPtr->shutdownFunction = defaultShutdownFunction;
if( sessionInfoPtr->connectFunction == NULL )
sessionInfoPtr->connectFunction = isServer( sessionInfoPtr ) ?
defaultServerStartupFunction : defaultClientStartupFunction;
if( protocolInfoPtr->isReqResp && \
sessionInfoPtr->getAttributeFunction == NULL )
sessionInfoPtr->getAttributeFunction = defaultGetAttributeFunction;
return( CRYPT_OK );
}
#endif /* USE_SESSIONS */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -