Opc.Ua.Bindings.TcpServerChannel.ProcessOpenSecureChannelRequest C# (CSharp) Method

ProcessOpenSecureChannelRequest() private method

Processes an OpenSecureChannel request message.
private ProcessOpenSecureChannelRequest ( uint messageType, ArraySegment messageChunk ) : bool
messageType uint
messageChunk ArraySegment
return bool
        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");
                }
            }
        }