static void sendErrorResponse( INOUT SESSION_INFO *sessionInfoPtr, INOUT SCEP_PROTOCOL_INFO *protocolInfo, IN_ERROR const int scepStatus ) { CRYPT_CERTIFICATE iCmsAttributes; int status; assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); assert( isWritePtr( protocolInfo, sizeof( SCEP_PROTOCOL_INFO ) ) ); REQUIRES_V( cryptStatusError( scepStatus ) ); /* Sign the error response using the CA key and SCEP attributes */ status = createScepAttributes( sessionInfoPtr, protocolInfo, &iCmsAttributes, MESSAGETYPE_CERTREP, scepStatus ); if( cryptStatusOK( status ) ) { ERROR_INFO errorInfo; /* Since this message is being sent in response to an existing error, we don't care about the possible error information returned from the function that sends the error response, so the errorInfo result is ignored */ status = envelopeSign( NULL, 0, sessionInfoPtr->receiveBuffer, sessionInfoPtr->receiveBufSize, &sessionInfoPtr->receiveBufEnd, CRYPT_CONTENT_NONE, sessionInfoPtr->privateKey, iCmsAttributes, &errorInfo ); krnlSendNotifier( iCmsAttributes, IMESSAGE_DECREFCOUNT ); } if( cryptStatusError( status ) ) { HTTP_DATA_INFO httpDataInfo; /* If we encounter an error processing the initial request then there won't be enough information available to create an error response. Similarly if we run into problems generating the response then there won't be anything available to send. At this point the best that we can do is send an error at the HTTP level */ initHttpDataInfo( &httpDataInfo, sessionInfoPtr->receiveBuffer, sessionInfoPtr->receiveBufSize ); httpDataInfo.reqStatus = scepStatus; swrite( &sessionInfoPtr->stream, &httpDataInfo, sizeof( HTTP_DATA_INFO ) ); return; } DEBUG_DUMP_FILE( "scep_srespx", sessionInfoPtr->receiveBuffer, sessionInfoPtr->receiveBufEnd ); /* Return the response to the client, discarding any error indication from the write. Since we're already in an error state there's not much that we can do in terms of alerting the user if a further error occurs when writing the error response, so we ignore any potential write errors that occur at this point */ sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_LASTMESSAGE, TRUE ); ( void ) writePkiDatagram( sessionInfoPtr, SCEP_CONTENT_TYPE, SCEP_CONTENT_TYPE_LEN ); }
static int serverTransact( INOUT SESSION_INFO *sessionInfoPtr ) { SCEP_PROTOCOL_INFO protocolInfo; HTTP_DATA_INFO httpDataInfo; HTTP_URI_INFO httpReqInfo; BOOLEAN requestDataOK; int requestCount, length = DUMMY_INIT, status; assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); /* SCEP is a weird protocol that started out as a basic IPsec certificate-provisioning mechanism for routers but then had a pile of additional functionality bolted onto it via HTTP mechanisms rather than having the protocol itself handle the extra functionality. Because of this we have to handle not only the standard HTTP-as-a- substrate mechanism used by the other protocols but also HTTP GET requests for additional information that the original protocol didn't accomodate */ sessionInfoPtr->receiveBufEnd = 0; sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_HTTPREQTYPES, STREAM_HTTPREQTYPE_ANY ); for( requestCount = 0; requestCount < 5; requestCount++ ) { initHttpDataInfoEx( &httpDataInfo, sessionInfoPtr->receiveBuffer, sessionInfoPtr->receiveBufSize, &httpReqInfo ); status = sread( &sessionInfoPtr->stream, &httpDataInfo, sizeof( HTTP_DATA_INFO ) ); if( cryptStatusError( status ) ) { sNetGetErrorInfo( &sessionInfoPtr->stream, &sessionInfoPtr->errorInfo ); return( status ); } /* If it's a proper SCEP protocol message, switch back to handling the main protocol */ if( httpDataInfo.reqType != STREAM_HTTPREQTYPE_GET ) { sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_HTTPREQTYPES, STREAM_HTTPREQTYPE_POST ); length = httpDataInfo.bytesAvail; break; } /* It's one of the bolted-on additions to the basic SCEP protocol, handle it specially */ status = processAdditionalScepRequest( sessionInfoPtr, &httpReqInfo ); if( cryptStatusError( status ) ) return( status ); } if( requestCount >= 5 ) { /* The exact type of error response to send at this point is a bit tricky, the least inappropriate one is probably CRYPT_ERROR_DUPLICATE to indicate that too many duplicate requests were sent, since to get here the client would have had to send repeated identical bolt-on requests */ sendCertErrorResponse( sessionInfoPtr, CRYPT_ERROR_DUPLICATE ); return( CRYPT_ERROR_OVERFLOW ); } /* Unfortunately we can't use readPkiDatagram() because of the weird dual-purpose HTTP transport used in SCEP so we have to duplicate portions of readPkiDatagram() here. See the readPkiDatagram() function for code comments explaining the following operations */ if( length < 4 || length >= MAX_INTLENGTH ) { sendCertErrorResponse( sessionInfoPtr, CRYPT_ERROR_BADDATA ); retExt( CRYPT_ERROR_UNDERFLOW, ( CRYPT_ERROR_UNDERFLOW, SESSION_ERRINFO, "Invalid PKI message length %d", length ) ); } status = length = \ checkObjectEncoding( sessionInfoPtr->receiveBuffer, length ); if( cryptStatusError( status ) ) { sendCertErrorResponse( sessionInfoPtr, CRYPT_ERROR_BADDATA ); retExt( status, ( status, SESSION_ERRINFO, "Invalid PKI message encoding" ) ); } sessionInfoPtr->receiveBufEnd = length; /* Process the initial message from the client */ initSCEPprotocolInfo( &protocolInfo ); status = checkScepRequest( sessionInfoPtr, &protocolInfo, &requestDataOK ); if( cryptStatusError( status ) ) { /* If we got far enough into the request data to be able to send a SCEP-level response, send that, otherwise just send an HTTP-level response */ if( requestDataOK ) sendErrorResponse( sessionInfoPtr, &protocolInfo, status ); else sendCertErrorResponse( sessionInfoPtr, status ); return( status ); } /* Issue a certificate from the request */ status = issueCertFromRequest( sessionInfoPtr, &protocolInfo ); if( cryptStatusError( status ) ) { sendErrorResponse( sessionInfoPtr, &protocolInfo, status ); destroySCEPprotocolInfo( &protocolInfo ); return( status ); } /* Return the certificate to the client */ status = createScepResponse( sessionInfoPtr, &protocolInfo ); if( cryptStatusOK( status ) ) status = writePkiDatagram( sessionInfoPtr, SCEP_CONTENT_TYPE, SCEP_CONTENT_TYPE_LEN ); destroySCEPprotocolInfo( &protocolInfo ); return( status ); }
static int activateConnection( INOUT SESSION_INFO *sessionInfoPtr ) { CRYPT_ATTRIBUTE_TYPE errorAttribute; int status; assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); /* Make sure that everything is set up ready to go */ errorAttribute = isServer( sessionInfoPtr ) ? \ checkServerParameters( sessionInfoPtr ) : \ checkClientParameters( sessionInfoPtr ); if( errorAttribute != CRYPT_ATTRIBUTE_NONE ) { setErrorInfo( sessionInfoPtr, errorAttribute, CRYPT_ERRTYPE_ATTR_ABSENT ); return( CRYPT_ERROR_NOTINITED ); } ENSURES( isServer( sessionInfoPtr ) || \ findSessionInfo( sessionInfoPtr->attributeList, CRYPT_SESSINFO_SERVER_NAME ) != NULL || \ sessionInfoPtr->networkSocket != CRYPT_ERROR ); ENSURES( findSessionInfo( sessionInfoPtr->attributeList, CRYPT_SESSINFO_SERVER_PORT ) != NULL || \ sessionInfoPtr->protocolInfo->port > 0 ); /* Allocate the send and receive buffers if necessary. The send buffer isn't used for request-response session types that use the receive buffer for both outgoing and incoming data so we only allocate it if it's actually required */ if( sessionInfoPtr->sendBuffer == NULL ) { REQUIRES( sessionInfoPtr->receiveBufSize >= MIN_BUFFER_SIZE && \ sessionInfoPtr->receiveBufSize < MAX_BUFFER_SIZE ); REQUIRES( ( sessionInfoPtr->sendBufSize >= MIN_BUFFER_SIZE && \ sessionInfoPtr->sendBufSize < MAX_BUFFER_SIZE ) || \ sessionInfoPtr->sendBufSize == CRYPT_UNUSED ); if( ( sessionInfoPtr->receiveBuffer = \ clAlloc( "activateConnection", \ sessionInfoPtr->receiveBufSize + 8 ) ) == NULL ) return( CRYPT_ERROR_MEMORY ); if( sessionInfoPtr->sendBufSize != CRYPT_UNUSED ) { /* When allocating the send buffer we use the size given for the receive buffer since the user may have overridden the default buffer size */ if( ( sessionInfoPtr->sendBuffer = \ clAlloc( "activateConnection", \ sessionInfoPtr->receiveBufSize + 8 ) ) == NULL ) { clFree( "activateConnection", sessionInfoPtr->receiveBuffer ); sessionInfoPtr->receiveBuffer = NULL; return( CRYPT_ERROR_MEMORY ); } sessionInfoPtr->sendBufSize = sessionInfoPtr->receiveBufSize; } } ENSURES( sessionInfoPtr->receiveBuffer != NULL && \ sessionInfoPtr->receiveBufSize >= MIN_BUFFER_SIZE && \ sessionInfoPtr->receiveBufSize < MAX_BUFFER_SIZE ); ENSURES( sessionInfoPtr->sendBufSize == CRYPT_UNUSED || \ sessionInfoPtr->sendBuffer != NULL ); /* Set timeouts if they're not set yet. If there's an error then we use the default value rather than aborting the entire session because of a minor difference in timeout values, although we also warn the caller in debug mode */ if( sessionInfoPtr->connectTimeout == CRYPT_ERROR ) { int timeout; status = krnlSendMessage( sessionInfoPtr->ownerHandle, IMESSAGE_GETATTRIBUTE, &timeout, CRYPT_OPTION_NET_CONNECTTIMEOUT ); if( cryptStatusOK( status ) ) sessionInfoPtr->connectTimeout = timeout; else { DEBUG_DIAG(( "Couldn't get connect timeout config value" )); assert( DEBUG_WARN ); sessionInfoPtr->connectTimeout = 30; } } if( sessionInfoPtr->readTimeout == CRYPT_ERROR ) { int timeout; status = krnlSendMessage( sessionInfoPtr->ownerHandle, IMESSAGE_GETATTRIBUTE, &timeout, CRYPT_OPTION_NET_READTIMEOUT ); if( cryptStatusOK( status ) ) sessionInfoPtr->readTimeout = timeout; else { DEBUG_DIAG(( "Couldn't get read timeout config value" )); assert( DEBUG_WARN ); sessionInfoPtr->readTimeout = 30; } } if( sessionInfoPtr->writeTimeout == CRYPT_ERROR ) { int timeout; status = krnlSendMessage( sessionInfoPtr->ownerHandle, IMESSAGE_GETATTRIBUTE, &timeout, CRYPT_OPTION_NET_WRITETIMEOUT ); if( cryptStatusOK( status ) ) sessionInfoPtr->writeTimeout = timeout; else { DEBUG_DIAG(( "Couldn't get write timeout config value" )); assert( DEBUG_WARN ); sessionInfoPtr->writeTimeout = 30; } } /* Wait for any async driver binding to complete. We can delay this until this very late stage because no networking functionality is used until this point */ if( !krnlWaitSemaphore( SEMAPHORE_DRIVERBIND ) ) { /* The kernel is shutting down, bail out */ return( CRYPT_ERROR_PERMISSION ); } /* If this is the first time that we've got here, activate the session */ if( !( sessionInfoPtr->flags & SESSION_PARTIALOPEN ) ) { REQUIRES( !( sessionInfoPtr->flags & SESSION_ISOPEN ) ) status = sessionInfoPtr->connectFunction( sessionInfoPtr ); if( cryptStatusError( status ) ) return( status ); } /* If it's a secure data transport session, complete the session state setup. Note that some sessions dynamically change the protocol information during the handshake to accommodate parameters negotiated during the handshake so we can only access the protocol information after the handshake has completed */ if( !sessionInfoPtr->protocolInfo->isReqResp ) { /* Complete the session handshake to set up the secure state */ status = sessionInfoPtr->transactFunction( sessionInfoPtr ); if( cryptStatusError( status ) ) { /* If we need feedback from the user before we can complete the handshake (for example checking a user name and password or certificate supplied by the other side) we remain in the handshake state so that the user can re-activate the session after confirming (or denying) the check */ 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 the context is just 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 session 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 */ sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_PARTIALREAD, TRUE ); sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_PARTIALWRITE, 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 */ sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_HANDSHAKECOMPLETE, TRUE ); sessionInfoPtr->flags &= ~SESSION_PARTIALOPEN; sessionInfoPtr->flags |= SESSION_ISOPEN; return( CRYPT_OK ); }