private bool ProcessOpenSecureChannelRequest(uint messageType, ArraySegment<byte> messageChunk)
{
// validate the channel state.
if (State != TcpChannelState.Opening && State != TcpChannelState.Open)
{
ForceChannelFault(StatusCodes.BadTcpMessageTypeInvalid, "Client sent an unexpected OpenSecureChannel message.");
return false;
}
// parse the security header.
uint channelId = 0;
X509Certificate2 clientCertificate = null;
uint requestId = 0;
uint sequenceNumber = 0;
ArraySegment<byte> messageBody;
try
{
messageBody = ReadAsymmetricMessage(
messageChunk,
ServerCertificate,
out channelId,
out clientCertificate,
out requestId,
out sequenceNumber);
// check for replay attacks.
if (!VerifySequenceNumber(sequenceNumber, "ProcessOpenSecureChannelRequest"))
{
throw new ServiceResultException(StatusCodes.BadSequenceNumberInvalid);
}
}
catch (Exception e)
{
ServiceResultException innerException = e.InnerException as ServiceResultException;
// If the certificate structre, signare and trust list checks pass, we return the other specific validation errors instead of BadSecurityChecksFailed
if (innerException != null && (
innerException.StatusCode == StatusCodes.BadCertificateTimeInvalid ||
innerException.StatusCode == StatusCodes.BadCertificateIssuerTimeInvalid ||
innerException.StatusCode == StatusCodes.BadCertificateHostNameInvalid ||
innerException.StatusCode == StatusCodes.BadCertificateUriInvalid ||
innerException.StatusCode == StatusCodes.BadCertificateUseNotAllowed ||
innerException.StatusCode == StatusCodes.BadCertificateIssuerUseNotAllowed ||
innerException.StatusCode == StatusCodes.BadCertificateRevocationUnknown ||
innerException.StatusCode == StatusCodes.BadCertificateIssuerRevocationUnknown ||
innerException.StatusCode == StatusCodes.BadCertificateRevoked ||
innerException.StatusCode == StatusCodes.BadCertificateIssuerRevoked ))
{
ForceChannelFault(innerException, innerException.StatusCode, e.Message );
return false;
}
else
{
ForceChannelFault(e, StatusCodes.BadSecurityChecksFailed, "Could not verify security on OpenSecureChannel request.");
return false;
}
}
BufferCollection chunksToProcess = null;
try
{
bool firstCall = ClientCertificate == null;
// must ensure the same certificate was used.
if (ClientCertificate != null)
{
CompareCertificates(ClientCertificate, clientCertificate, false);
}
else
{
ClientCertificate = clientCertificate;
}
// check if it is necessary to wait for more chunks.
if (!TcpMessageType.IsFinal(messageType))
{
SaveIntermediateChunk(requestId, messageBody);
return false;
}
// create a new token.
TcpChannelToken token = CreateToken();
token.TokenId = GetNewTokenId();
token.ServerNonce = CreateNonce();
// get the chunks to process.
chunksToProcess = GetSavedChunks(requestId, messageBody);
OpenSecureChannelRequest request = (OpenSecureChannelRequest)BinaryDecoder.DecodeMessage(
new ArraySegmentStream(chunksToProcess),
typeof(OpenSecureChannelRequest),
Quotas.MessageContext);
if (request == null)
{
throw ServiceResultException.Create(StatusCodes.BadStructureMissing, "Could not parse OpenSecureChannel request body.");
}
// check the security mode.
if (request.SecurityMode != SecurityMode)
{
ReviseSecurityMode(firstCall, request.SecurityMode);
}
// check the client nonce.
token.ClientNonce = request.ClientNonce;
if (!ValidateNonce(token.ClientNonce))
{
throw ServiceResultException.Create(StatusCodes.BadNonceInvalid, "Client nonce is not the correct length or not random enough.");
}
// choose the lifetime.
int lifetime = (int)request.RequestedLifetime;
if (lifetime < TcpMessageLimits.MinSecurityTokenLifeTime)
{
lifetime = TcpMessageLimits.MinSecurityTokenLifeTime;
}
if (lifetime > 0 && lifetime < token.Lifetime)
{
token.Lifetime = lifetime;
}
// check the request type.
SecurityTokenRequestType requestType = request.RequestType;
if (requestType == SecurityTokenRequestType.Issue && State != TcpChannelState.Opening)
{
throw ServiceResultException.Create(StatusCodes.BadRequestTypeInvalid, "Cannot request a new token for an open channel.");
}
if (requestType == SecurityTokenRequestType.Renew && State != TcpChannelState.Open)
{
// may be reconnecting to a dropped channel.
if (State == TcpChannelState.Opening)
{
// tell the listener to find the channel that can process the request.
m_listener.ReconnectToExistingChannel(
Socket,
requestId,
sequenceNumber,
channelId,
ClientCertificate,
token,
request);
Utils.Trace(
"TCPSERVERCHANNEL ReconnectToExistingChannel Socket={0:X8}, ChannelId={1}, TokenId={2}",
(Socket != null)?Socket.Handle:0,
(CurrentToken != null)?CurrentToken.ChannelId:0,
(CurrentToken != null)?CurrentToken.TokenId:0);
// close the channel.
ChannelClosed();
// nothing more to do.
return false;
}
throw ServiceResultException.Create(StatusCodes.BadRequestTypeInvalid, "Cannot request to rewew a token for a channel that has not been opened.");
}
// check the channel id.
if (requestType == SecurityTokenRequestType.Renew && channelId != ChannelId)
{
throw ServiceResultException.Create(StatusCodes.BadTcpSecureChannelUnknown, "Do not recognize the secure channel id provided.");
}
// log security information.
if (requestType == SecurityTokenRequestType.Issue)
{
Opc.Ua.Security.Audit.SecureChannelCreated(
g_ImplementationString,
this.m_listener.EndpointUrl.ToString(),
Utils.Format("{0}", this.ChannelId),
this.EndpointDescription,
this.ClientCertificate,
this.ServerCertificate,
BinaryEncodingSupport.Required);
}
else
{
Opc.Ua.Security.Audit.SecureChannelRenewed(
g_ImplementationString,
Utils.Format("{0}", this.ChannelId));
}
if (requestType == SecurityTokenRequestType.Renew)
{
SetRenewedToken(token);
}
else
{
ActivateToken(token);
}
State = TcpChannelState.Open;
// send the response.
SendOpenSecureChannelResponse(requestId, token, request);
return false;
}
catch (Exception e)
{
SendServiceFault(requestId, ServiceResult.Create(e, StatusCodes.BadTcpInternalError, "Unexpected error processing OpenSecureChannel request."));
return false;
}
finally
{
if (chunksToProcess != null)
{
chunksToProcess.Release(BufferManager, "ProcessOpenSecureChannelRequest");
}
}
}