private Variant ExecuteTest_AutoReconnect(TestCaseContext testCaseContext, TestCase testCase, int iteration, Variant input) { if (TestUtils.IsSetupIteration(iteration)) { SetEventSink(); return(Variant.Null); } // get the expected input. Variant expectedInput; int processingTime = 0; lock (m_random) { Utils.Trace("Iteration {0}; Server Received", iteration); // compare actual to expected input. m_random.Start( (int)(testCase.Seed + iteration), (int)m_sequenceToExecute.RandomDataStepSize, testCaseContext); expectedInput = m_random.GetVariant(); if (!Compare.CompareVariant(input, expectedInput)) { throw ServiceResultException.Create( StatusCodes.BadUnexpectedError, "Server did not receive expected input\r\nActual = {0}\r\nExpected = {0}", input, expectedInput); } // determine processing time in server. processingTime = m_random.GetInt32Range(0, testCaseContext.MaxResponseDelay); if ((iteration + 1) % testCaseContext.StackEventFrequency == 0) { if (testCaseContext.StackEventType == 4) { InterruptListener(testCaseContext.StackEventFrequency * testCaseContext.RequestInterval / 2); } StackAction action = TestUtils.GetStackAction(testCaseContext, SecureChannelContext.Current.EndpointDescription); if (action != null) { QueueStackAction(action); } } } // wait. Thread.Sleep(processingTime); // generate and return the output. lock (m_random) { m_random.Start((int)( testCase.ResponseSeed + iteration), (int)m_sequenceToExecute.RandomDataStepSize, testCaseContext); return(m_random.GetVariant()); } }
/// <summary> /// Processes an OpenSecureChannel request message. /// </summary> 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, signature and trust list checks pass, we return the other specific validation errors instead of BadSecurityChecksFailed if (innerException != null) { if (innerException.StatusCode == StatusCodes.BadCertificateUntrusted || innerException.StatusCode == StatusCodes.BadCertificateChainIncomplete || innerException.StatusCode == StatusCodes.BadCertificateRevoked || (innerException.InnerResult != null && innerException.InnerResult.StatusCode == StatusCodes.BadCertificateUntrusted)) { ForceChannelFault(StatusCodes.BadSecurityChecksFailed, e.Message); return(false); } else if (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.BadCertificateIssuerRevoked) { ForceChannelFault(innerException, innerException.StatusCode, e.Message); return(false); } } 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. ChannelToken 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. Listener.ReconnectToExistingChannel( Socket, requestId, sequenceNumber, channelId, ClientCertificate, token, request); Utils.Trace( "{0} ReconnectToExistingChannel Socket={0:X8}, ChannelId={1}, TokenId={2}", ChannelName, (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 renew 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( m_ImplementationString, Listener.EndpointUrl.ToString(), Utils.Format("{0}", ChannelId), EndpointDescription, ClientCertificate, ServerCertificate, BinaryEncodingSupport.Required); } else { Opc.Ua.Security.Audit.SecureChannelRenewed( m_ImplementationString, Utils.Format("{0}", ChannelId)); } if (requestType == SecurityTokenRequestType.Renew) { SetRenewedToken(token); } else { ActivateToken(token); } State = TcpChannelState.Open; // send the response. SendOpenSecureChannelResponse(requestId, token, request); // notify reverse CompleteReverseHello(null); // notify any monitors. NotifyMonitors(ServiceResult.Good, false); return(false); } catch (Exception e) { SendServiceFault(requestId, ServiceResult.Create(e, StatusCodes.BadTcpInternalError, "Unexpected error processing OpenSecureChannel request.")); CompleteReverseHello(e); return(false); } finally { if (chunksToProcess != null) { chunksToProcess.Release(BufferManager, "ProcessOpenSecureChannelRequest"); } } }
/// <summary> /// Checks for a valid application instance certificate. /// </summary> /// <param name="silent">if set to <c>true</c> no dialogs will be displayed.</param> /// <param name="minimumKeySize">Minimum size of the key.</param> /// <param name="lifeTimeInMonths">The lifetime in months.</param> public async Task <bool> CheckApplicationInstanceCertificate( bool silent, ushort minimumKeySize, ushort lifeTimeInMonths) { Utils.Trace(Utils.TraceMasks.Information, "Checking application instance certificate."); if (m_applicationConfiguration == null) { await LoadApplicationConfiguration(silent).ConfigureAwait(false); } ApplicationConfiguration configuration = m_applicationConfiguration; bool certificateValid = false; // find the existing certificate. CertificateIdentifier id = configuration.SecurityConfiguration.ApplicationCertificate; if (id == null) { throw ServiceResultException.Create(StatusCodes.BadConfigurationError, "Configuration file does not specify a certificate."); } X509Certificate2 certificate = await id.Find(true).ConfigureAwait(false); // check that it is ok. if (certificate != null) { certificateValid = await CheckApplicationInstanceCertificate(configuration, certificate, silent, minimumKeySize).ConfigureAwait(false); } else { // check for missing private key. certificate = await id.Find(false).ConfigureAwait(false); if (certificate != null) { throw ServiceResultException.Create(StatusCodes.BadConfigurationError, "Cannot access certificate private key. Subject={0}", certificate.Subject); } // check for missing thumbprint. if (!String.IsNullOrEmpty(id.Thumbprint)) { if (!String.IsNullOrEmpty(id.SubjectName)) { CertificateIdentifier id2 = new CertificateIdentifier(); id2.StoreType = id.StoreType; id2.StorePath = id.StorePath; id2.SubjectName = id.SubjectName; certificate = await id2.Find(true).ConfigureAwait(false); } if (certificate != null) { var message = new StringBuilder(); message.AppendLine("Thumbprint was explicitly specified in the configuration."); message.AppendLine("Another certificate with the same subject name was found."); message.AppendLine("Use it instead?"); message.AppendLine("Requested: {0}"); message.AppendLine("Found: {1}"); if (!await ApproveMessage(String.Format(message.ToString(), id.SubjectName, certificate.Subject), silent)) { throw ServiceResultException.Create(StatusCodes.BadConfigurationError, message.ToString(), id.SubjectName, certificate.Subject); } } else { var message = new StringBuilder(); message.AppendLine("Thumbprint was explicitly specified in the configuration. "); message.AppendLine("Cannot generate a new certificate."); throw ServiceResultException.Create(StatusCodes.BadConfigurationError, message.ToString()); } } } if ((certificate == null) || !certificateValid) { certificate = await CreateApplicationInstanceCertificate(configuration, minimumKeySize, lifeTimeInMonths); if (certificate == null) { var message = new StringBuilder(); message.AppendLine("There is no cert with subject {0} in the configuration."); message.AppendLine(" Please generate a cert for your application,"); message.AppendLine(" then copy the new cert to this location:"); message.AppendLine(" {1}"); throw ServiceResultException.Create(StatusCodes.BadConfigurationError, message.ToString(), id.SubjectName, id.StorePath ); } } else { if (configuration.SecurityConfiguration.AddAppCertToTrustedStore) { // ensure it is trusted. await AddToTrustedStore(configuration, certificate); } } return(true); }
/// <summary> /// Activates an existing session /// </summary> public virtual bool ActivateSession( OperationContext context, NodeId authenticationToken, SignatureData clientSignature, List <SoftwareCertificate> clientSoftwareCertificates, ExtensionObject userIdentityToken, SignatureData userTokenSignature, StringCollection localeIds, out byte[] serverNonce) { serverNonce = null; Session session = null; UserIdentityToken newIdentity = null; UserTokenPolicy userTokenPolicy = null; lock (m_lock) { // find session. if (!m_sessions.TryGetValue(authenticationToken, out session)) { throw new ServiceResultException(StatusCodes.BadSessionClosed); } // create new server nonce. serverNonce = new byte[m_minNonceLength]; m_nonceGenerator.GetBytes(serverNonce); // validate before activation. session.ValidateBeforeActivate( context, clientSignature, clientSoftwareCertificates, userIdentityToken, userTokenSignature, localeIds, serverNonce, out newIdentity, out userTokenPolicy); } IUserIdentity identity = null; IUserIdentity effectiveIdentity = null; ServiceResult error = null; try { // check if the application has a callback which validates the identity tokens. lock (m_eventLock) { if (m_ImpersonateUser != null) { ImpersonateEventArgs args = new ImpersonateEventArgs(newIdentity, userTokenPolicy); m_ImpersonateUser(session, args); if (ServiceResult.IsBad(args.IdentityValidationError)) { error = args.IdentityValidationError; } else { identity = args.Identity; effectiveIdentity = args.EffectiveIdentity; } } } // parse the token manually if the identity is not provided. if (identity == null) { identity = new UserIdentity(newIdentity); } // use the identity as the effectiveIdentity if not provided. if (effectiveIdentity == null) { effectiveIdentity = identity; } } catch (Exception e) { if (e is ServiceResultException) { throw; } throw ServiceResultException.Create( StatusCodes.BadIdentityTokenInvalid, e, "Could not validate user identity token: {0}", newIdentity); } // check for validation error. if (ServiceResult.IsBad(error)) { throw new ServiceResultException(error); } // activate session. bool contextChanged = session.Activate( context, clientSoftwareCertificates, newIdentity, identity, effectiveIdentity, localeIds, serverNonce); // raise session related event. if (contextChanged) { RaiseSessionEvent(session, SessionEventReason.Activated); } // indicates that the identity context for the session has changed. return(contextChanged); }
/// <summary> /// Updates the security configuration for an application identified by a file or url. /// </summary> /// <param name="filePath">The file path.</param> /// <param name="configuration">The configuration.</param> public void WriteConfiguration(string filePath, SecuredApplication configuration) { if (configuration == null) { throw new ArgumentNullException("configuration"); } // check for valid file. if (String.IsNullOrEmpty(filePath) || !File.Exists(filePath)) { throw ServiceResultException.Create( StatusCodes.BadNotReadable, "Cannot find the configuration file: {0}", configuration.ConfigurationFile); } // load from file. XmlDocument document = new XmlDocument(); document.Load(filePath); XmlElement element = Find(document.DocumentElement, "SecuredApplication", Namespaces.OpcUaSecurity); // update secured application. if (element != null) { configuration.LastExportTime = DateTime.UtcNow; element.InnerXml = SetObject(typeof(SecuredApplication), configuration); } // update application configuration. else { UpdateDocument(document.DocumentElement, configuration); } try { // update configuration file. Stream ostrm = File.Open(filePath, FileMode.Create, FileAccess.Write); XmlTextWriter writer = new XmlTextWriter(ostrm, System.Text.Encoding.UTF8); writer.Formatting = Formatting.Indented; try { document.Save(writer); } finally { writer.Close(); } } catch (Exception e) { throw ServiceResultException.Create( StatusCodes.BadNotWritable, e, "Cannot update the configuration file: {0}", configuration.ConfigurationFile); } }
/// <summary> /// This method executes a multi channel test. /// </summary> /// <param name="testCaseContext">This parameter stores the test case parameter values.</param> /// <param name="testCase">This parameter stores the test case related data.</param> /// <param name="iteration">This parameter stores the current iteration number.</param> /// <param name="input">Input value.</param> /// <returns>Input variant.</returns> /// <remarks> /// The test parameters required for this test case are of the /// following types: /// <list type="bullet"> /// <item>MaxStringLength <see cref="TestCaseContext.MaxStringLength"/></item> /// <item>ChannelsPerServer <see cref="TestCaseContext.ChannelsPerServer"/></item> /// <item>ServerDetails <see cref="TestCaseContext.ServerDetails"/></item> /// </list> /// </remarks> private Variant ExecuteTest_MultipleChannels(TestCaseContext testCaseContext, TestCase testCase, int iteration, Variant input) { bool isSetupStep = TestUtils.IsSetupIteration(iteration); if (!isSetupStep) { m_logger.LogStartEvent(testCase, iteration); } try { if (isSetupStep) { // No verification for the input is required. return(Variant.Null); } else { Variant expectedInput; lock (m_random) { m_random.Start( (int)(testCase.Seed + iteration), (int)m_sequenceToExecute.RandomDataStepSize, testCaseContext); expectedInput = m_random.GetScalarVariant(false); } try { if (!Compare.CompareVariant(input, expectedInput)) { throw new ServiceResultException( StatusCodes.BadInvalidState, Utils.Format("'{0}' is not equal to '{1}'.", input, expectedInput)); } } catch (Exception e) { throw ServiceResultException.Create( StatusCodes.BadInvalidState, e, "'{0}' is not equal to '{1}'.", input, expectedInput); } lock (m_random) { m_random.Start((int)( testCase.ResponseSeed + iteration), (int)m_sequenceToExecute.RandomDataStepSize, testCaseContext); return(m_random.GetScalarVariant(false)); } } } finally { if (!isSetupStep) { m_logger.LogCompleteEvent(testCase, iteration); } } }
/// <summary> /// Processes an OpenSecureChannel request message. /// </summary> protected ArraySegment <byte> ReadAsymmetricMessage( ArraySegment <byte> buffer, X509Certificate2 receiverCertificate, out uint channelId, out X509Certificate2 senderCertificate, out uint requestId, out uint sequenceNumber) { BinaryDecoder decoder = new BinaryDecoder(buffer.Array, buffer.Offset, buffer.Count, Quotas.MessageContext); string securityPolicyUri = null; // parse the security header. ReadAsymmetricMessageHeader( decoder, receiverCertificate, out channelId, out senderCertificate, out securityPolicyUri); // validate the sender certificate. if (senderCertificate != null && Quotas.CertificateValidator != null && securityPolicyUri != SecurityPolicies.None) { Quotas.CertificateValidator.Validate(senderCertificate); } // check if this is the first open secure channel request. if (!m_uninitialized) { if (securityPolicyUri != m_securityPolicyUri) { throw ServiceResultException.Create(StatusCodes.BadSecurityPolicyRejected, "Cannot change the security policy after creating the channnel."); } } else { // find a matching endpoint description. if (m_endpoints != null) { foreach (EndpointDescription endpoint in m_endpoints) { // There may be multiple endpoints with the same securityPolicyUri. // Just choose the first one that matches. This choice will be re-examined // When the OpenSecureChannel request body is processed. if (endpoint.SecurityPolicyUri == securityPolicyUri || (securityPolicyUri == SecurityPolicies.None && endpoint.SecurityMode == MessageSecurityMode.None)) { m_securityMode = endpoint.SecurityMode; m_securityPolicyUri = securityPolicyUri; m_discoveryOnly = false; m_uninitialized = false; m_selectedEndpoint = endpoint; // recalculate the key sizes. CalculateSymmetricKeySizes(); break; } } } // allow a discovery only channel with no security if policy not suppported if (m_uninitialized) { if (securityPolicyUri != SecurityPolicies.None) { throw ServiceResultException.Create(StatusCodes.BadSecurityPolicyRejected, "The security policy is not supported."); } m_securityMode = MessageSecurityMode.None; m_securityPolicyUri = SecurityPolicies.None; m_discoveryOnly = true; m_uninitialized = false; m_selectedEndpoint = null; } } int headerSize = decoder.Position; // decrypt the body. ArraySegment <byte> plainText = Decrypt( new ArraySegment <byte>(buffer.Array, buffer.Offset + headerSize, buffer.Count - headerSize), new ArraySegment <byte>(buffer.Array, buffer.Offset, headerSize), receiverCertificate); // extract signature. int signatureSize = GetAsymmetricSignatureSize(senderCertificate); byte[] signature = new byte[signatureSize]; for (int ii = 0; ii < signatureSize; ii++) { signature[ii] = plainText.Array[plainText.Offset + plainText.Count - signatureSize + ii]; } // verify the signature. ArraySegment <byte> dataToVerify = new ArraySegment <byte>(plainText.Array, plainText.Offset, plainText.Count - signatureSize); if (!Verify(dataToVerify, signature, senderCertificate)) { Utils.Trace("Could not verify signature on message."); throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, "Could not verify the signature on the message."); } // verify padding. int paddingCount = 0; if (SecurityMode != MessageSecurityMode.None) { int paddingEnd = -1; if (receiverCertificate.GetRSAPublicKey().KeySize > TcpMessageLimits.KeySizeExtraPadding) { paddingEnd = plainText.Offset + plainText.Count - signatureSize - 1; paddingCount = plainText.Array[paddingEnd - 1] + plainText.Array[paddingEnd] * 256; //parse until paddingStart-1; the last one is actually the extrapaddingsize for (int ii = paddingEnd - paddingCount; ii < paddingEnd; ii++) { if (plainText.Array[ii] != plainText.Array[paddingEnd - 1]) { throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, "Could not verify the padding in the message."); } } } else { paddingEnd = plainText.Offset + plainText.Count - signatureSize - 1; paddingCount = plainText.Array[paddingEnd]; for (int ii = paddingEnd - paddingCount; ii < paddingEnd; ii++) { if (plainText.Array[ii] != plainText.Array[paddingEnd]) { throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, "Could not verify the padding in the message."); } } } paddingCount++; } // decode message. decoder = new BinaryDecoder( plainText.Array, plainText.Offset + headerSize, plainText.Count - headerSize, Quotas.MessageContext); sequenceNumber = decoder.ReadUInt32(null); requestId = decoder.ReadUInt32(null); headerSize += decoder.Position; decoder.Close(); Utils.Trace("Security Policy: {0}", SecurityPolicyUri); Utils.Trace("Sender Certificate: {0}", (senderCertificate != null)?senderCertificate.Subject:"(none)"); // return the body. return(new ArraySegment <byte>( plainText.Array, plainText.Offset + headerSize, plainText.Count - headerSize - signatureSize - paddingCount)); }
/// <summary> /// Validates the identity token supplied by the client. /// </summary> private UserIdentityToken ValidateUserIdentityToken( ExtensionObject identityToken, SignatureData userTokenSignature, out UserTokenPolicy policy) { policy = null; // check for empty token. if (identityToken == null || identityToken.Body == null) { // not changing the token if already activated. if (m_activated) { return(null); } // check if an anonymous login is permitted. if (m_endpoint.UserIdentityTokens != null && m_endpoint.UserIdentityTokens.Count > 0) { bool found = false; for (int ii = 0; ii < m_endpoint.UserIdentityTokens.Count; ii++) { if (m_endpoint.UserIdentityTokens[ii].TokenType == UserTokenType.Anonymous) { found = true; policy = m_endpoint.UserIdentityTokens[ii]; break; } } if (!found) { throw ServiceResultException.Create(StatusCodes.BadUserAccessDenied, "Anonymous user token policy not supported."); } } // create an anonymous token to use for subsequent validation. AnonymousIdentityToken anonymousToken = new AnonymousIdentityToken(); anonymousToken.PolicyId = policy.PolicyId; return(anonymousToken); } UserIdentityToken token = null; // check for unrecognized token. if (!typeof(UserIdentityToken).IsInstanceOfType(identityToken.Body)) { //handle the use case when the UserIdentityToken is binary encoded over xml message encoding if (identityToken.Encoding == ExtensionObjectEncoding.Binary && typeof(byte[]).IsInstanceOfType(identityToken.Body)) { UserIdentityToken newToken = BaseVariableState.DecodeExtensionObject(null, typeof(UserIdentityToken), identityToken, false) as UserIdentityToken; if (newToken == null) { throw ServiceResultException.Create(StatusCodes.BadUserAccessDenied, "Invalid user identity token provided."); } policy = m_endpoint.FindUserTokenPolicy(newToken.PolicyId); if (policy == null) { throw ServiceResultException.Create(StatusCodes.BadUserAccessDenied, "User token policy not supported.", "Opc.Ua.Server.Session.ValidateUserIdentityToken"); } switch (policy.TokenType) { case UserTokenType.Anonymous: token = BaseVariableState.DecodeExtensionObject(null, typeof(AnonymousIdentityToken), identityToken, true) as AnonymousIdentityToken; break; case UserTokenType.UserName: token = BaseVariableState.DecodeExtensionObject(null, typeof(UserNameIdentityToken), identityToken, true) as UserNameIdentityToken; break; case UserTokenType.Certificate: token = BaseVariableState.DecodeExtensionObject(null, typeof(X509IdentityToken), identityToken, true) as X509IdentityToken; break; case UserTokenType.IssuedToken: token = BaseVariableState.DecodeExtensionObject(null, typeof(IssuedIdentityToken), identityToken, true) as IssuedIdentityToken; break; default: throw ServiceResultException.Create(StatusCodes.BadUserAccessDenied, "Invalid user identity token provided."); } } else { throw ServiceResultException.Create(StatusCodes.BadUserAccessDenied, "Invalid user identity token provided."); } } else { // get the token. token = (UserIdentityToken)identityToken.Body; } // find the user token policy. policy = m_endpoint.FindUserTokenPolicy(token.PolicyId); if (policy == null) { throw ServiceResultException.Create(StatusCodes.BadIdentityTokenInvalid, "User token policy not supported."); } // determine the security policy uri. string securityPolicyUri = policy.SecurityPolicyUri; if (String.IsNullOrEmpty(securityPolicyUri)) { securityPolicyUri = m_endpoint.SecurityPolicyUri; } if (ServerBase.RequireEncryption(m_endpoint)) { // decrypt the token. if (m_serverCertificate == null) { m_serverCertificate = CertificateFactory.Create(m_endpoint.ServerCertificate, true); // check for valid certificate. if (m_serverCertificate == null) { throw ServiceResultException.Create(StatusCodes.BadConfigurationError, "ApplicationCertificate cannot be found."); } } try { token.Decrypt(m_serverCertificate, m_serverNonce, securityPolicyUri); } catch (Exception e) { if (e is ServiceResultException) { throw; } throw ServiceResultException.Create(StatusCodes.BadIdentityTokenInvalid, e, "Could not decrypt identity token."); } // verify the signature. if (securityPolicyUri != SecurityPolicies.None) { byte[] dataToSign = Utils.Append(m_serverCertificate.RawData, m_serverNonce); if (!token.Verify(dataToSign, userTokenSignature, securityPolicyUri)) { throw new ServiceResultException(StatusCodes.BadUserSignatureInvalid, "Invalid user signature!"); } } } // validate user identity token. return(token); }
/// <summary> /// Starts listening at the specified port. /// </summary> public void Start() { lock (m_lock) { // ensure a valid port. int port = m_uri.Port; if (port <= 0 || port > UInt16.MaxValue) { port = Utils.UaTcpDefaultPort; } bool bindToSpecifiedAddress = true; UriHostNameType hostType = Uri.CheckHostName(m_uri.Host); if (hostType == UriHostNameType.Dns || hostType == UriHostNameType.Unknown || hostType == UriHostNameType.Basic) { bindToSpecifiedAddress = false; } IPAddress ipAddress = IPAddress.Any; if (bindToSpecifiedAddress) { ipAddress = IPAddress.Parse(m_uri.Host); } // create IPv4 or IPv6 socket. try { IPEndPoint endpoint = new IPEndPoint(ipAddress, port); m_listeningSocket = new Socket(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); SocketAsyncEventArgs args = new SocketAsyncEventArgs(); args.Completed += OnAccept; args.UserToken = m_listeningSocket; m_listeningSocket.Bind(endpoint); m_listeningSocket.Listen(Int32.MaxValue); if (!m_listeningSocket.AcceptAsync(args)) { OnAccept(null, args); } } catch (Exception ex) { // no IPv4 support. if (m_listeningSocket != null) { m_listeningSocket.Dispose(); m_listeningSocket = null; } Utils.Trace("failed to create IPv4 listening socket: " + ex.Message); } if (ipAddress == IPAddress.Any) { // create IPv6 socket try { IPEndPoint endpointIPv6 = new IPEndPoint(IPAddress.IPv6Any, port); m_listeningSocketIPv6 = new Socket(endpointIPv6.AddressFamily, SocketType.Stream, ProtocolType.Tcp); SocketAsyncEventArgs args = new SocketAsyncEventArgs(); args.Completed += OnAccept; args.UserToken = m_listeningSocketIPv6; m_listeningSocketIPv6.Bind(endpointIPv6); m_listeningSocketIPv6.Listen(Int32.MaxValue); if (!m_listeningSocketIPv6.AcceptAsync(args)) { OnAccept(null, args); } } catch (Exception ex) { // no IPv6 support if (m_listeningSocketIPv6 != null) { m_listeningSocketIPv6.Dispose(); m_listeningSocketIPv6 = null; } Utils.Trace("failed to create IPv6 listening socket: " + ex.Message); } } if (m_listeningSocketIPv6 == null && m_listeningSocket == null) { throw ServiceResultException.Create( StatusCodes.BadNoCommunication, "Failed to establish tcp listener sockets for Ipv4 and IPv6."); } } }
/// <summary> /// Decrypts the message using RSA PKCS#1 v1.5 encryption. /// </summary> private ArraySegment <byte> Rsa_Decrypt( ArraySegment <byte> dataToDecrypt, ArraySegment <byte> headerToCopy, X509Certificate2 encryptingCertificate, bool useOaep) { RSA rsa = null; try { // get the encrypting key. rsa = encryptingCertificate.GetRSAPrivateKey(); if (rsa == null) { throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, "No private key for certificate."); } int inputBlockSize = RsaUtils.GetCipherTextBlockSize(rsa, useOaep); int outputBlockSize = RsaUtils.GetPlainTextBlockSize(rsa, useOaep); // verify the input data is the correct block size. if (dataToDecrypt.Count % inputBlockSize != 0) { Utils.Trace("Message is not an integral multiple of the block size. Length = {0}, BlockSize = {1}.", dataToDecrypt.Count, inputBlockSize); } byte[] decryptedBuffer = BufferManager.TakeBuffer(SendBufferSize, "Rsa_Decrypt"); Array.Copy(headerToCopy.Array, headerToCopy.Offset, decryptedBuffer, 0, headerToCopy.Count); using (MemoryStream ostrm = new MemoryStream( decryptedBuffer, headerToCopy.Count, decryptedBuffer.Length - headerToCopy.Count)) { // decrypt body. byte[] input = new byte[inputBlockSize]; for (int ii = dataToDecrypt.Offset; ii < dataToDecrypt.Offset + dataToDecrypt.Count; ii += inputBlockSize) { Array.Copy(dataToDecrypt.Array, ii, input, 0, input.Length); if (useOaep == true) { byte[] plainText = rsa.Decrypt(input, RSAEncryptionPadding.OaepSHA1); ostrm.Write(plainText, 0, plainText.Length); } else { byte[] plainText = rsa.Decrypt(input, RSAEncryptionPadding.Pkcs1); ostrm.Write(plainText, 0, plainText.Length); } } } // return buffers. return(new ArraySegment <byte>(decryptedBuffer, 0, (dataToDecrypt.Count / inputBlockSize) * outputBlockSize + headerToCopy.Count)); } finally { RsaUtils.RSADispose(rsa); } }
/// <summary> /// Creates a session with the endpoint. /// </summary> public Session Connect(ConfiguredEndpoint endpoint) { if (endpoint == null) { throw new ArgumentNullException("endpoint"); } EndpointDescriptionCollection availableEndpoints = null; // check if the endpoint needs to be updated. if (endpoint.UpdateBeforeConnect) { ConfiguredServerDlg configurationDialog = new ConfiguredServerDlg(); endpoint = configurationDialog.ShowDialog(endpoint, m_configuration); if (endpoint == null) { return(null); } availableEndpoints = configurationDialog.AvailableEnpoints; } m_endpoint = endpoint; // copy the message context. m_messageContext = m_configuration.CreateMessageContext(); X509Certificate2 clientCertificate = null; X509Certificate2Collection clientCertificateChain = null; if (endpoint.Description.SecurityPolicyUri != SecurityPolicies.None) { if (m_configuration.SecurityConfiguration.ApplicationCertificate == null) { throw ServiceResultException.Create(StatusCodes.BadConfigurationError, "ApplicationCertificate must be specified."); } clientCertificate = m_configuration.SecurityConfiguration.ApplicationCertificate.Find(true); if (clientCertificate == null) { throw ServiceResultException.Create(StatusCodes.BadConfigurationError, "ApplicationCertificate cannot be found."); } //load certificate chain //clientCertificateChain = new X509Certificate2Collection(clientCertificate); //List<CertificateIdentifier> issuers = new List<CertificateIdentifier>(); //m_configuration.CertificateValidator.GetIssuers(clientCertificate, issuers); //for (int i = 0; i < issuers.Count; i++) //{ // clientCertificateChain.Add(issuers[i].Certificate); //} } // create the channel. ITransportChannel channel = SessionChannel.Create( m_configuration, endpoint.Description, endpoint.Configuration, //clientCertificateChain, clientCertificate, m_messageContext); // create the session. return(Connect(endpoint, channel, availableEndpoints)); }
/// <summary> /// Validates the identity token supplied by the client. /// </summary> /// <param name="identityToken"></param> /// <param name="userTokenSignature"></param> /// <returns></returns> private bool ValidateUserIdentityToken(ExtensionObject identityToken, SignatureData userTokenSignature) { UserIdentityToken token = null; UserTokenPolicy policy; if (identityToken == null || identityToken.Body == null) { if (_activated) { // not changing the token if already activated. return(false); } policy = Endpoint.UserIdentityTokens? .FirstOrDefault(t => t.TokenType == UserTokenType.Anonymous); if (policy == null) { throw ServiceResultException.Create(StatusCodes.BadUserAccessDenied, "Anonymous user token policy not supported."); } // create an anonymous token to use for subsequent validation. token = new AnonymousIdentityToken { PolicyId = policy.PolicyId }; } else if (!typeof(UserIdentityToken).IsInstanceOfType(identityToken.Body)) { // Decode identity token from binary. token = DecodeUserIdentityToken(identityToken, out policy); } else { token = (UserIdentityToken)identityToken.Body; // find the user token policy. policy = Endpoint.FindUserTokenPolicy(token.PolicyId); if (policy == null) { throw ServiceResultException.Create(StatusCodes.BadIdentityTokenInvalid, "User token policy not supported."); } } // determine the security policy uri. var securityPolicyUri = policy.SecurityPolicyUri; if (string.IsNullOrEmpty(securityPolicyUri)) { securityPolicyUri = Endpoint.SecurityPolicyUri; } if (securityPolicyUri != SecurityPolicies.None) { // decrypt the user identity token. if (_serverCertificate == null) { _serverCertificate = CertificateFactory.Create( Endpoint.ServerCertificate, true); // check for valid certificate. if (_serverCertificate == null) { throw ServiceResultException.Create(StatusCodes.BadConfigurationError, "ApplicationCertificate cannot be found."); } } try { token.Decrypt(_serverCertificate, _serverNonce, securityPolicyUri); } catch (ServiceResultException) { throw; } catch (Exception e) { throw ServiceResultException.Create(StatusCodes.BadIdentityTokenInvalid, e, "Could not decrypt identity token."); } // ... and verify the signature if any. VerifyUserTokenSignature(userTokenSignature, token, securityPolicyUri); } // We have a valid token - validate it through the handler chain. var arg = new UserIdentityHandlerArgs { CurrentIdentities = Identities, Token = token }; _validator?.Invoke(this, arg); if (arg.ValidationException != null) { throw arg.ValidationException; } if (arg.NewIdentities != null) { Identities = arg.NewIdentities; return(true); } return(false); // No new identities }
/// <summary> /// Calls the method. /// </summary> public void Call() { // build list of methods to call. CallMethodRequestCollection methodsToCall = new CallMethodRequestCollection(); CallMethodRequest methodToCall = new CallMethodRequest(); methodToCall.ObjectId = m_objectId; methodToCall.MethodId = m_methodId; foreach (DataRow row in m_dataset.Tables[0].Rows) { Argument argument = (Argument)row[0]; Variant value = (Variant)row[4]; argument.Value = value.Value; methodToCall.InputArguments.Add(value); } methodsToCall.Add(methodToCall); // call the method. CallMethodResultCollection results = null; DiagnosticInfoCollection diagnosticInfos = null; ResponseHeader responseHeader = m_session.Call( null, methodsToCall, out results, out diagnosticInfos); ClientBase.ValidateResponse(results, methodsToCall); ClientBase.ValidateDiagnosticInfos(diagnosticInfos, methodsToCall); for (int ii = 0; ii < results.Count; ii++) { // display any input argument errors. if (results[ii].InputArgumentResults != null) { for (int jj = 0; jj < results[ii].InputArgumentResults.Count; jj++) { if (StatusCode.IsBad(results[ii].InputArgumentResults[jj])) { DataRow row = m_dataset.Tables[0].Rows[jj]; row[5] = results[ii].InputArgumentResults[jj].ToString(); ResultCH.Visible = true; } } } // throw an exception on error. if (StatusCode.IsBad(results[ii].StatusCode)) { throw ServiceResultException.Create(results[ii].StatusCode, ii, diagnosticInfos, responseHeader.StringTable); } // display the output arguments ResultCH.Visible = false; NoArgumentsLB.Visible = m_outputArguments == null || m_outputArguments.Length == 0; NoArgumentsLB.Text = "Method invoked successfully.\r\nNo output arguments to display."; m_dataset.Tables[0].Rows.Clear(); if (m_outputArguments != null) { for (int jj = 0; jj < m_outputArguments.Length; jj++) { DataRow row = m_dataset.Tables[0].NewRow(); if (results[ii].OutputArguments.Count > jj) { UpdateRow(row, m_outputArguments[jj], results[ii].OutputArguments[jj], true); } else { UpdateRow(row, m_outputArguments[jj], Variant.Null, true); } m_dataset.Tables[0].Rows.Add(row); } } } }
/// <summary> /// Starts listening at the specified port. /// </summary> public void Start() { lock (m_lock) { // ensure a valid port. int port = m_uri.Port; if (port <= 0 || port > UInt16.MaxValue) { port = Utils.UaTcpDefaultPort; } // create IPv4 socket. try { IPEndPoint endpoint = new IPEndPoint(IPAddress.Any, port); m_listeningSocket = new Socket(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); SocketAsyncEventArgs args = new SocketAsyncEventArgs(); args.Completed += OnAccept; args.UserToken = m_listeningSocket; m_listeningSocket.Bind(endpoint); m_listeningSocket.Listen(Int32.MaxValue); if (!m_listeningSocket.AcceptAsync(args)) { OnAccept(null, args); } } catch (Exception ex) { // no IPv4 support. m_listeningSocket = null; Utils.Trace("failed to create IPv4 listening socket: " + ex.Message); } // create IPv6 socket try { IPEndPoint endpointIPv6 = new IPEndPoint(IPAddress.IPv6Any, port); m_listeningSocketIPv6 = new Socket(endpointIPv6.AddressFamily, SocketType.Stream, ProtocolType.Tcp); SocketAsyncEventArgs args = new SocketAsyncEventArgs(); args.Completed += OnAccept; args.UserToken = m_listeningSocketIPv6; m_listeningSocketIPv6.Bind(endpointIPv6); m_listeningSocketIPv6.Listen(Int32.MaxValue); if (!m_listeningSocketIPv6.AcceptAsync(args)) { OnAccept(null, args); } } catch (Exception ex) { // no IPv6 support m_listeningSocketIPv6 = null; Utils.Trace("failed to create IPv6 listening socket: " + ex.Message); } if (m_listeningSocketIPv6 == null && m_listeningSocket == null) { throw ServiceResultException.Create( StatusCodes.BadNoCommunication, "Failed to establish tcp listener sockets for Ipv4 and IPv6.\r\n"); } } }
/// <summary> /// Sets the application access rules for the specified URL. /// </summary> public static void SetAccessRules(string url, IList <HttpAccessRule> rules, bool replaceExisting) { HttpError error = NativeMethods.HttpInitialize( new HTTPAPI_VERSION(1, 0), HttpInitFlag.HTTP_INITIALIZE_CONFIG, IntPtr.Zero); if (error != HttpError.NO_ERROR) { throw ServiceResultException.Create( StatusCodes.BadUnexpectedError, "Could not initialize HTTP library.\r\nError={0}", error); } // fetch existing rules if not replacing them. if (!replaceExisting) { IList <HttpAccessRule> existingRules = GetAccessRules(url); if (existingRules.Count > 0) { List <HttpAccessRule> mergedRules = new List <HttpAccessRule>(existingRules); mergedRules.AddRange(rules); rules = mergedRules; } } HTTP_SERVICE_CONFIG_URLACL_SET update = new HTTP_SERVICE_CONFIG_URLACL_SET(); update.KeyDesc.pUrlPrefix = url; update.ParamDesc.pStringSecurityDescriptor = FormatSddl(rules); IntPtr pStruct = IntPtr.Zero; int updateSize = Marshal.SizeOf(typeof(HTTP_SERVICE_CONFIG_URLACL_SET)); try { pStruct = Marshal.AllocHGlobal(updateSize); NativeMethods.ZeroMemory(pStruct, updateSize); Marshal.StructureToPtr(update, pStruct, false); error = NativeMethods.HttpDeleteServiceConfiguration( IntPtr.Zero, HTTP_SERVICE_CONFIG_ID.HttpServiceConfigUrlAclInfo, pStruct, updateSize, IntPtr.Zero); if (error != HttpError.ERROR_FILE_NOT_FOUND && error != HttpError.NO_ERROR) { throw ServiceResultException.Create( StatusCodes.BadUnexpectedError, "Could not delete existing access rules for HTTP url.\r\nError={1}, Url={0}", url, error); } if (rules.Count > 0) { error = NativeMethods.HttpSetServiceConfiguration( IntPtr.Zero, HTTP_SERVICE_CONFIG_ID.HttpServiceConfigUrlAclInfo, pStruct, updateSize, IntPtr.Zero); if (error != HttpError.NO_ERROR) { throw ServiceResultException.Create( StatusCodes.BadUnexpectedError, "Could not set the access rules for HTTP url.\r\nError={1}, Url={0}", url, error); } } } finally { if (pStruct != IntPtr.Zero) { Marshal.DestroyStructure(pStruct, typeof(HTTP_SERVICE_CONFIG_URLACL_SET)); Marshal.FreeHGlobal(pStruct); } NativeMethods.HttpTerminate(HttpInitFlag.HTTP_INITIALIZE_CONFIG, IntPtr.Zero); } }
/// <summary> /// Deletes the recent history. /// </summary> private void DeleteHistory(NodeId areaId, List <VariantCollection> events, FilterDeclaration filter) { // find the event id. int index = 0; foreach (FilterDeclarationField field in filter.Fields) { if (field.InstanceDeclaration.BrowseName == Opc.Ua.BrowseNames.EventId) { break; } index++; } // can't delete events if no event id. if (index >= filter.Fields.Count) { throw ServiceResultException.Create(StatusCodes.BadEventIdUnknown, "Cannot delete events if EventId was not selected."); } // build list of nodes to delete. DeleteEventDetails details = new DeleteEventDetails(); details.NodeId = areaId; foreach (VariantCollection e in events) { byte[] eventId = null; if (e.Count > index) { eventId = e[index].Value as byte[]; } details.EventIds.Add(eventId); } // delete the events. ExtensionObjectCollection nodesToUpdate = new ExtensionObjectCollection(); nodesToUpdate.Add(new ExtensionObject(details)); HistoryUpdateResultCollection results = null; DiagnosticInfoCollection diagnosticInfos = null; m_session.HistoryUpdate( null, nodesToUpdate, out results, out diagnosticInfos); ClientBase.ValidateResponse(results, nodesToUpdate); ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToUpdate); if (StatusCode.IsBad(results[0].StatusCode)) { throw new ServiceResultException(results[0].StatusCode); } // check for item level errors. if (results[0].OperationResults.Count > 0) { int count = 0; for (int ii = 0; ii < results[0].OperationResults.Count; ii++) { if (StatusCode.IsBad(results[0].OperationResults[ii])) { count++; } } // raise an error. if (count > 0) { throw ServiceResultException.Create( StatusCodes.BadEventIdUnknown, "Error deleting events. Only {0} of {1} deletes succeeded.", events.Count - count, events.Count); } } }
public static EndpointDescription CreateEndpointDescription(UAServer myServer) { // create the endpoint description. EndpointDescription endpointDescription = null; try { string discoveryUrl = myServer.ServerName; //OA 2018-04-27 if (!discoveryUrl.StartsWith(Utils.UriSchemeOpcTcp) && !discoveryUrl.StartsWith(Utils.UriSchemeHttps)) //if (!discoveryUrl.StartsWith(Utils.UriSchemeOpcTcp)) { if (!discoveryUrl.EndsWith("/discovery")) { discoveryUrl += "/discovery"; } } // parse the selected URL. Uri uri = new Uri(discoveryUrl); // set a short timeout because this is happening in the drop down event. EndpointConfiguration configuration = EndpointConfiguration.Create(); configuration.OperationTimeout = 5000; //OA 2018-04-27 https if (discoveryUrl.StartsWith(Utils.UriSchemeHttps)) { configuration.OperationTimeout = 0; } // // Connect to the server's discovery endpoint and find the available configuration. using (DiscoveryClient client = DiscoveryClient.Create(uri, configuration)) { EndpointDescriptionCollection endpoints = client.GetEndpoints(null); // select the best endpoint to use based on the selected URL and the UseSecurity checkbox. for (int ii = 0; ii < endpoints.Count; ii++) { EndpointDescription endpoint = endpoints[ii]; // check for a match on the URL scheme. if (endpoint.EndpointUrl.StartsWith(uri.Scheme)) { // check if security was requested. if (!myServer.SecurityPolicy.Equals("None")) { if (endpoint.SecurityMode == MessageSecurityMode.None) { continue; } } else { if (endpoint.SecurityMode != MessageSecurityMode.None) { continue; } } // pick the first available endpoint by default. if (endpointDescription == null) { endpointDescription = endpoint; } // The security level is a relative measure assigned by the server to the // endpoints that it returns. Clients should always pick the highest level // unless they have a reason not too. if (endpoint.SecurityLevel > endpointDescription.SecurityLevel) { endpointDescription = endpoint; } } } // pick the first available endpoint by default. if (endpointDescription == null && endpoints.Count > 0) { endpointDescription = endpoints[0]; } } // if a server is behind a firewall it may return URLs that are not accessible to the client. // This problem can be avoided by assuming that the domain in the URL used to call // GetEndpoints can be used to access any of the endpoints. This code makes that conversion. // Note that the conversion only makes sense if discovery uses the same protocol as the endpoint. Uri endpointUrl = Utils.ParseUri(endpointDescription.EndpointUrl); if (endpointUrl != null && endpointUrl.Scheme == uri.Scheme) { UriBuilder builder = new UriBuilder(endpointUrl); builder.Host = uri.DnsSafeHost; builder.Port = uri.Port; endpointDescription.EndpointUrl = builder.ToString(); } } catch { endpointDescription = new EndpointDescription(); endpointDescription.EndpointUrl = myServer.ServerName; //ABA 2014-10-10 if (myServer.SecurityPolicy.Equals("None")) { endpointDescription.SecurityPolicyUri = SecurityPolicies.None; } //Commented by MM 03/07/2019 //else if (myServer.SecurityPolicy.Equals("Basic128Rsa15")) //{ // endpointDescription.SecurityPolicyUri = SecurityPolicies.Basic128Rsa15; //} else if (myServer.SecurityPolicy.Equals("Basic256Sha256")) { endpointDescription.SecurityPolicyUri = SecurityPolicies.Basic256Sha256; } else if (myServer.SecurityPolicy.Equals("Basic256")) { endpointDescription.SecurityPolicyUri = SecurityPolicies.Basic256; } try { MessageSecurityMode myMode; myMode = (MessageSecurityMode)Enum.Parse(typeof(MessageSecurityMode), myServer.SecurityMode, false); //endpointDescription.SecurityMode = MessageSecurityMode.SignAndEncrypt; endpointDescription.SecurityMode = myMode; } catch { endpointDescription.SecurityMode = MessageSecurityMode.None; } // specify the transport profile. if (myServer.Protocol.Equals("opc.tcp")) { endpointDescription.TransportProfileUri = Profiles.UaTcpTransport; } //added OA 2018-04-27 else if (myServer.Protocol.Equals("http")) { //OA-2018-06-18 endpointDescription.TransportProfileUri = Profiles.WsHttpXmlOrBinaryTransport; } else //added OA 2018-04-27 { endpointDescription.TransportProfileUri = Profiles.HttpsBinaryTransport; //OA-2018-06-18 HttpsXmlOrBinaryTransport; } //else //OA 2018-04-27 //{ // endpointDescription.TransportProfileUri = Profiles.WsHttpXmlOrBinaryTransport; //} if (myServer.UserIdentity.Equals(UserIdentityType.Certificate)) { // load the the server certificate from the local certificate store. CertificateIdentifier certificateIdentifier = new CertificateIdentifier(); certificateIdentifier.StoreType = CertificateStoreType.X509Store; //OA-2018-06-18 .Windows; if (!String.IsNullOrEmpty(myServer.CertificationStore)) { //certificateIdentifier.StorePath = "LocalMachine\\UA Applications"; certificateIdentifier.StorePath = myServer.CertificationStore; } else { using (System.IO.FileStream fs = System.IO.File.OpenRead(myServer.CertificationPath)) { byte[] bytes = new byte[fs.Length]; fs.Read(bytes, 0, Convert.ToInt32(fs.Length)); certificateIdentifier.RawData = bytes; } } //certificateIdentifier.SubjectName = "ONBServer";//commented by HHA 12/11//2019 //OA-2018-06-25 //X509Certificate2 serverCertificate = certificateIdentifier.Find(); X509Certificate2 serverCertificate = certificateIdentifier.Find().Result; if (serverCertificate == null) { throw ServiceResultException.Create(StatusCodes.BadCertificateInvalid, "Could not find server certificate: {0}", certificateIdentifier.SubjectName); } endpointDescription.ServerCertificate = serverCertificate.GetRawCertData(); } } return(endpointDescription); }
/// <summary> /// Writes the asymmetric security header to the buffer. /// </summary> protected void WriteAsymmetricMessageHeader( BinaryEncoder encoder, uint messageType, uint secureChannelId, string securityPolicyUri, X509Certificate2 senderCertificate, X509Certificate2Collection senderCertificateChain, X509Certificate2 receiverCertificate, out int senderCertificateSize) { int start = encoder.Position; senderCertificateSize = 0; encoder.WriteUInt32(null, messageType); encoder.WriteUInt32(null, 0); encoder.WriteUInt32(null, secureChannelId); encoder.WriteString(null, securityPolicyUri); if (SecurityMode != MessageSecurityMode.None) { if (senderCertificateChain != null && senderCertificateChain.Count > 0) { X509Certificate2 currentCertificate = senderCertificateChain[0]; int maxSenderCertificateSize = GetMaxSenderCertificateSize(currentCertificate, securityPolicyUri); List <byte> senderCertificateList = new List <byte>(currentCertificate.RawData); senderCertificateSize = currentCertificate.RawData.Length; for (int i = 1; i < senderCertificateChain.Count; i++) { currentCertificate = senderCertificateChain[i]; senderCertificateSize += currentCertificate.RawData.Length; if (senderCertificateSize < maxSenderCertificateSize) { senderCertificateList.AddRange(currentCertificate.RawData); } else { senderCertificateSize -= currentCertificate.RawData.Length; break; } } encoder.WriteByteString(null, senderCertificateList.ToArray()); } else { encoder.WriteByteString(null, senderCertificate.RawData); } encoder.WriteByteString(null, GetThumbprintBytes(receiverCertificate.Thumbprint)); } else { encoder.WriteByteString(null, null); encoder.WriteByteString(null, null); } if (encoder.Position - start > SendBufferSize) { throw ServiceResultException.Create( StatusCodes.BadInternalError, "AsymmetricSecurityHeader is {0} bytes which is too large for the send buffer size of {1} bytes.", encoder.Position - start, SendBufferSize); } }
protected void ReadAsymmetricMessageHeader( BinaryDecoder decoder, X509Certificate2 receiverCertificate, out uint secureChannelId, out X509Certificate2 senderCertificate, out string securityPolicyUri) { senderCertificate = null; uint messageType = decoder.ReadUInt32(null); uint messageSize = decoder.ReadUInt32(null); // decode security header. byte[] certificateData = null; byte[] thumbprintData = null; try { secureChannelId = decoder.ReadUInt32(null); securityPolicyUri = decoder.ReadString(null, TcpMessageLimits.MaxSecurityPolicyUriSize); certificateData = decoder.ReadByteString(null, TcpMessageLimits.MaxCertificateSize); thumbprintData = decoder.ReadByteString(null, TcpMessageLimits.CertificateThumbprintSize); } catch (Exception e) { throw ServiceResultException.Create( StatusCodes.BadSecurityChecksFailed, e, "The asymmetric security header could not be parsed."); } // verify sender certificate. if (certificateData != null && certificateData.Length > 0) { senderCertificate = CertificateFactory.Create(certificateData, true); try { string thumbprint = senderCertificate.Thumbprint; if (thumbprint == null) { throw ServiceResultException.Create(StatusCodes.BadCertificateInvalid, "Invalid certificate thumbprint."); } } catch (Exception e) { throw ServiceResultException.Create(StatusCodes.BadCertificateInvalid, e, "The sender's certificate could not be parsed."); } } else { if (securityPolicyUri != SecurityPolicies.None) { throw ServiceResultException.Create(StatusCodes.BadCertificateInvalid, "The sender's certificate was not specified."); } } // verify receiver thumbprint. if (thumbprintData != null && thumbprintData.Length > 0) { if (receiverCertificate.Thumbprint.ToUpperInvariant() != GetThumbprintString(thumbprintData)) { throw ServiceResultException.Create(StatusCodes.BadCertificateInvalid, "The receiver's certificate thumbprint is not valid."); } } else { if (securityPolicyUri != SecurityPolicies.None) { throw ServiceResultException.Create(StatusCodes.BadCertificateInvalid, "The receiver's certificate thumbprint was not specified."); } } }
/// <summary> /// Fetches the references for the node. /// </summary> private List <ReferenceDescription> Browse(Session session, NodeId nodeId) { List <ReferenceDescription> references = new List <ReferenceDescription>(); // specify the references to follow and the fields to return. BrowseDescription nodeToBrowse = new BrowseDescription(); nodeToBrowse.NodeId = nodeId; nodeToBrowse.ReferenceTypeId = ReferenceTypeIds.References; nodeToBrowse.IncludeSubtypes = true; nodeToBrowse.BrowseDirection = BrowseDirection.Both; nodeToBrowse.NodeClassMask = 0; nodeToBrowse.ResultMask = (uint)BrowseResultMask.All; BrowseDescriptionCollection nodesToBrowse = new BrowseDescriptionCollection(); nodesToBrowse.Add(nodeToBrowse); // start the browse operation. BrowseResultCollection results = null; DiagnosticInfoCollection diagnosticInfos = null; ResponseHeader responseHeader = session.Browse( null, null, 2, nodesToBrowse, out results, out diagnosticInfos); // these do sanity checks on the result - make sure response matched the request. ClientBase.ValidateResponse(results, nodesToBrowse); ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToBrowse); // check status. if (StatusCode.IsBad(results[0].StatusCode)) { // embed the diagnostic information in a exception. throw ServiceResultException.Create(results[0].StatusCode, 0, diagnosticInfos, responseHeader.StringTable); } // add first batch. references.AddRange(results[0].References); // check if server limited the results. while (results[0].ContinuationPoint != null && results[0].ContinuationPoint.Length > 0) { ByteStringCollection continuationPoints = new ByteStringCollection(); continuationPoints.Add(results[0].ContinuationPoint); // continue browse operation. responseHeader = session.BrowseNext( null, false, continuationPoints, out results, out diagnosticInfos); ClientBase.ValidateResponse(results, continuationPoints); ClientBase.ValidateDiagnosticInfos(diagnosticInfos, continuationPoints); // check status. if (StatusCode.IsBad(results[0].StatusCode)) { // embed the diagnostic information in a exception. throw ServiceResultException.Create(results[0].StatusCode, 0, diagnosticInfos, responseHeader.StringTable); } // add next batch. references.AddRange(results[0].References); } return(references); }
/// <summary> /// Reads the next block of data from the socket. /// </summary> private void ReadNextBlock() { Socket socket = null; // check if already closed. lock (m_socketLock) { if (m_socket == null) { if (m_receiveBuffer != null) { m_bufferManager.ReturnBuffer(m_receiveBuffer, "ReadNextBlock"); m_receiveBuffer = null; } return; } socket = m_socket; // avoid stale ServiceException when socket is disconnected if (!socket.Connected) { return; } } BufferManager.LockBuffer(m_receiveBuffer); ServiceResult error = ServiceResult.Good; SocketAsyncEventArgs args = new SocketAsyncEventArgs(); try { args.SetBuffer(m_receiveBuffer, m_bytesReceived, m_bytesToReceive - m_bytesReceived); args.Completed += m_ReadComplete; if (!socket.ReceiveAsync(args)) { // I/O completed synchronously if (args.SocketError != SocketError.Success) { throw ServiceResultException.Create(StatusCodes.BadTcpInternalError, args.SocketError.ToString()); } else { m_ReadComplete(null, args); } } } catch (ServiceResultException sre) { args.Dispose(); BufferManager.UnlockBuffer(m_receiveBuffer); throw sre; } catch (Exception ex) { args.Dispose(); BufferManager.UnlockBuffer(m_receiveBuffer); throw ServiceResultException.Create(StatusCodes.BadTcpInternalError, ex, "BeginReceive failed."); } }
/// <summary> /// Fetches the current SSL certificate configuration. /// </summary> public static List <SslCertificateBinding> GetSslCertificateBindings() { List <SslCertificateBinding> bindings = new List <SslCertificateBinding>(); // initialize library. HttpError error = NativeMethods.HttpInitialize( new HTTPAPI_VERSION(1, 0), HttpInitFlag.HTTP_INITIALIZE_CONFIG, IntPtr.Zero); if (error != HttpError.NO_ERROR) { throw ServiceResultException.Create( StatusCodes.BadUnexpectedError, "Could not initialize HTTP library.\r\nError={0}", error); } // set up the iterator. HTTP_SERVICE_CONFIG_SSL_QUERY query = new HTTP_SERVICE_CONFIG_SSL_QUERY(); query.QueryDesc = HTTP_SERVICE_CONFIG_QUERY_TYPE.HttpServiceConfigQueryNext; query.KeyDesc.pIpPort = IntPtr.Zero; IntPtr pInput = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(HTTP_SERVICE_CONFIG_SSL_QUERY))); NativeMethods.ZeroMemory(pInput, Marshal.SizeOf(typeof(HTTP_SERVICE_CONFIG_SSL_QUERY))); IntPtr pOutput = IntPtr.Zero; try { // loop through each record. for (query.dwToken = 0; error == HttpError.NO_ERROR; query.dwToken++) { // get the size of buffer to allocate. Marshal.StructureToPtr(query, pInput, true); int requiredBufferLength = 0; error = NativeMethods.HttpQueryServiceConfiguration( IntPtr.Zero, HTTP_SERVICE_CONFIG_ID.HttpServiceConfigSSLCertInfo, pInput, Marshal.SizeOf(typeof(HTTP_SERVICE_CONFIG_SSL_QUERY)), pOutput, requiredBufferLength, out requiredBufferLength, IntPtr.Zero); if (error == HttpError.ERROR_NO_MORE_ITEMS) { break; } if (error != HttpError.ERROR_INSUFFICIENT_BUFFER) { throw ServiceResultException.Create( StatusCodes.BadUnexpectedError, "Could not read SSL configuration information.\r\nError={0}", error); } // allocate the buffer. pOutput = Marshal.AllocHGlobal(requiredBufferLength); NativeMethods.ZeroMemory(pOutput, requiredBufferLength); // get the actual data. error = NativeMethods.HttpQueryServiceConfiguration( IntPtr.Zero, HTTP_SERVICE_CONFIG_ID.HttpServiceConfigSSLCertInfo, pInput, Marshal.SizeOf(typeof(HTTP_SERVICE_CONFIG_SSL_QUERY)), pOutput, requiredBufferLength, out requiredBufferLength, IntPtr.Zero); if (error != HttpError.NO_ERROR) { throw ServiceResultException.Create( StatusCodes.BadUnexpectedError, "Could not read SSL configuration information.\r\nError={0}", error); } HTTP_SERVICE_CONFIG_SSL_SET sslSet = (HTTP_SERVICE_CONFIG_SSL_SET)Marshal.PtrToStructure(pOutput, typeof(HTTP_SERVICE_CONFIG_SSL_SET)); short family = Marshal.ReadInt16(sslSet.KeyDesc.pIpPort); SslCertificateBinding binding = new SslCertificateBinding(); if (family == AF_INET) { SOCKADDR_IN inet = (SOCKADDR_IN)Marshal.PtrToStructure(sslSet.KeyDesc.pIpPort, typeof(SOCKADDR_IN)); binding.IPAddress = new IPAddress(inet.addr); binding.Port = inet.port; } if (family == AF_INET6) { SOCKADDR_IN6 inet = (SOCKADDR_IN6)Marshal.PtrToStructure(sslSet.KeyDesc.pIpPort, typeof(SOCKADDR_IN6)); binding.IPAddress = new IPAddress(inet.addr, inet.scopeID); binding.Port = inet.port; } binding.Port = (ushort)(((binding.Port & 0xFF00) >> 8) | ((binding.Port & 0x00FF) << 8)); byte[] bytes = new byte[sslSet.ParamDesc.SslHashLength]; Marshal.Copy(sslSet.ParamDesc.pSslHash, bytes, 0, bytes.Length); binding.Thumbprint = Utils.ToHexString(bytes); binding.ApplicationId = sslSet.ParamDesc.AppId; binding.StoreName = sslSet.ParamDesc.pSslCertStoreName; binding.DefaultCertCheckMode = sslSet.ParamDesc.DefaultCertCheckMode; binding.DefaultRevocationFreshnessTime = sslSet.ParamDesc.DefaultRevocationFreshnessTime; binding.DefaultRevocationUrlRetrievalTimeout = sslSet.ParamDesc.DefaultRevocationUrlRetrievalTimeout; binding.DefaultSslCtlIdentifier = sslSet.ParamDesc.pDefaultSslCtlIdentifier; binding.DefaultSslCtlStoreName = sslSet.ParamDesc.pDefaultSslCtlStoreName; binding.DefaultFlags = sslSet.ParamDesc.DefaultFlags; bindings.Add(binding); Marshal.FreeHGlobal(pOutput); pOutput = IntPtr.Zero; } } finally { if (pInput != IntPtr.Zero) { Marshal.DestroyStructure(pInput, typeof(HTTP_SERVICE_CONFIG_SSL_QUERY)); Marshal.FreeHGlobal(pInput); } if (pOutput != IntPtr.Zero) { Marshal.FreeHGlobal(pOutput); } NativeMethods.HttpTerminate(HttpInitFlag.HTTP_INITIALIZE_CONFIG, IntPtr.Zero); } return(bindings); }
/// <summary> /// Browses the specified node. /// </summary> public ReferenceDescriptionCollection Browse(NodeId nodeId) { if (m_session == null) { throw new ServiceResultException(StatusCodes.BadServerNotConnected, "Cannot browse if not connected to a server."); } try { m_browseInProgress = true; // construct request. BrowseDescription nodeToBrowse = new BrowseDescription(); nodeToBrowse.NodeId = nodeId; nodeToBrowse.BrowseDirection = m_browseDirection; nodeToBrowse.ReferenceTypeId = m_referenceTypeId; nodeToBrowse.IncludeSubtypes = m_includeSubtypes; nodeToBrowse.NodeClassMask = m_nodeClassMask; nodeToBrowse.ResultMask = m_resultMask; BrowseDescriptionCollection nodesToBrowse = new BrowseDescriptionCollection(); nodesToBrowse.Add(nodeToBrowse); // make the call to the server. BrowseResultCollection results; DiagnosticInfoCollection diagnosticInfos; ResponseHeader responseHeader = m_session.Browse( null, m_view, m_maxReferencesReturned, nodesToBrowse, out results, out diagnosticInfos); // ensure that the server returned valid results. Session.ValidateResponse(results, nodesToBrowse); Session.ValidateDiagnosticInfos(diagnosticInfos, nodesToBrowse); // check if valid. if (StatusCode.IsBad(results[0].StatusCode)) { throw ServiceResultException.Create(results[0].StatusCode, 0, diagnosticInfos, responseHeader.StringTable); } // fetch initial set of references. byte[] continuationPoint = results[0].ContinuationPoint; ReferenceDescriptionCollection references = results[0].References; // process any continuation point. while (continuationPoint != null) { ReferenceDescriptionCollection additionalReferences; if (!m_continueUntilDone && m_MoreReferences != null) { BrowserEventArgs args = new BrowserEventArgs(references); m_MoreReferences(this, args); // cancel browser and return the references fetched so far. if (args.Cancel) { BrowseNext(ref continuationPoint, true); return(references); } m_continueUntilDone = args.ContinueUntilDone; } additionalReferences = BrowseNext(ref continuationPoint, false); if (additionalReferences != null && additionalReferences.Count > 0) { references.AddRange(additionalReferences); } else { Utils.Trace("Continuation point exists, but the browse results are null/empty."); break; } } // return the results. return(references); } finally { m_browseInProgress = false; } }
/// <summary> /// Creates a new SSL certificate binding. /// </summary> public static void SetSslCertificateBinding(SslCertificateBinding binding) { if (binding == null) { throw new ArgumentNullException("binding"); } // initialize library. HttpError error = NativeMethods.HttpInitialize( new HTTPAPI_VERSION(1, 0), HttpInitFlag.HTTP_INITIALIZE_CONFIG, IntPtr.Zero); if (error != HttpError.NO_ERROR) { throw ServiceResultException.Create( StatusCodes.BadUnexpectedError, "Could not initialize HTTP library.\r\nError={0}", error); } IntPtr pAddress = IntPtr.Zero; IntPtr pThumprint = IntPtr.Zero; IntPtr pConfigInfo = IntPtr.Zero; try { pAddress = ToIntPtr(binding.IPAddress, binding.Port); byte[] thumbprint = Utils.FromHexString(binding.Thumbprint); pThumprint = Marshal.AllocCoTaskMem(thumbprint.Length); Marshal.Copy(thumbprint, 0, pThumprint, thumbprint.Length); HTTP_SERVICE_CONFIG_SSL_SET configSslSet = new HTTP_SERVICE_CONFIG_SSL_SET(); configSslSet.KeyDesc.pIpPort = pAddress; configSslSet.ParamDesc.pSslHash = pThumprint; configSslSet.ParamDesc.SslHashLength = thumbprint.Length; configSslSet.ParamDesc.AppId = binding.ApplicationId; configSslSet.ParamDesc.pSslCertStoreName = binding.StoreName; configSslSet.ParamDesc.DefaultCertCheckMode = binding.DefaultCertCheckMode; configSslSet.ParamDesc.DefaultFlags = binding.DefaultFlags; configSslSet.ParamDesc.DefaultRevocationFreshnessTime = binding.DefaultRevocationFreshnessTime; configSslSet.ParamDesc.DefaultRevocationUrlRetrievalTimeout = binding.DefaultRevocationUrlRetrievalTimeout; configSslSet.ParamDesc.pDefaultSslCtlIdentifier = binding.DefaultSslCtlIdentifier; configSslSet.ParamDesc.pDefaultSslCtlStoreName = binding.DefaultSslCtlStoreName; int size = Marshal.SizeOf(typeof(HTTP_SERVICE_CONFIG_SSL_SET)); pConfigInfo = Marshal.AllocCoTaskMem(size); Marshal.StructureToPtr(configSslSet, pConfigInfo, false); error = NativeMethods.HttpSetServiceConfiguration( IntPtr.Zero, HTTP_SERVICE_CONFIG_ID.HttpServiceConfigSSLCertInfo, pConfigInfo, size, IntPtr.Zero); if (error == HttpError.ERROR_ALREADY_EXISTS) { error = NativeMethods.HttpDeleteServiceConfiguration( IntPtr.Zero, HTTP_SERVICE_CONFIG_ID.HttpServiceConfigSSLCertInfo, pConfigInfo, size, IntPtr.Zero); if (error != HttpError.NO_ERROR) { throw ServiceResultException.Create( StatusCodes.BadUnexpectedError, "Could not delete existing SSL certificate binding.\r\nError={0}", error); } error = NativeMethods.HttpSetServiceConfiguration( IntPtr.Zero, HTTP_SERVICE_CONFIG_ID.HttpServiceConfigSSLCertInfo, pConfigInfo, size, IntPtr.Zero); } if (error != HttpError.NO_ERROR) { throw ServiceResultException.Create( StatusCodes.BadUnexpectedError, "Could not create SSL certificate binding.\r\nError={0}", error); } } finally { if (pConfigInfo != IntPtr.Zero) { Marshal.FreeCoTaskMem(pConfigInfo); } if (pAddress != IntPtr.Zero) { Marshal.FreeCoTaskMem(pAddress); } if (pThumprint != IntPtr.Zero) { Marshal.FreeCoTaskMem(pThumprint); } NativeMethods.HttpTerminate(HttpInitFlag.HTTP_INITIALIZE_CONFIG, IntPtr.Zero); } }
/// <summary> /// Exports the security configuration for an application identified by a file or url. /// </summary> /// <param name="filePath">The file path.</param> /// <returns>The security configuration.</returns> public SecuredApplication ReadConfiguration(string filePath) { if (filePath == null) { throw new ArgumentNullException("filePath"); } string configFilePath = filePath; string exeFilePath = null; // check for valid file. if (!File.Exists(filePath)) { throw ServiceResultException.Create( StatusCodes.BadNotReadable, "Cannot find the executable or configuration file: {0}", filePath); } // find the configuration file for the executable. if (filePath.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) { exeFilePath = filePath; try { FileInfo file = new FileInfo(filePath); string sectionName = file.Name; sectionName = sectionName.Substring(0, sectionName.Length - file.Extension.Length); System.Configuration.Configuration configuration = ConfigurationManager.OpenExeConfiguration(filePath); configFilePath = ApplicationConfiguration.GetFilePathFromAppConfig(sectionName); if (configFilePath == null) { configFilePath = filePath + ".config"; } } catch (Exception e) { throw ServiceResultException.Create( StatusCodes.BadNotReadable, e, "Cannot find the configuration file for the executable: {0}", filePath); } if (!File.Exists(configFilePath)) { throw ServiceResultException.Create( StatusCodes.BadNotReadable, "Cannot find the configuration file: {0}", configFilePath); } } SecuredApplication application = null; ApplicationConfiguration applicationConfiguration = null; try { XmlTextReader reader = new XmlTextReader(File.Open(configFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)); try { reader.MoveToContent(); // find the SecuredApplication element in the file. if (reader.ReadToDescendant("SecuredApplication", Namespaces.OpcUaSecurity)) { DataContractSerializer serializer = new DataContractSerializer(typeof(SecuredApplication)); application = serializer.ReadObject(reader, false) as SecuredApplication; application.ConfigurationFile = configFilePath; application.ExecutableFile = exeFilePath; } // load the application configuration. else { reader.Close(); reader = new XmlTextReader(File.Open(configFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)); DataContractSerializer serializer = new DataContractSerializer(typeof(ApplicationConfiguration)); applicationConfiguration = serializer.ReadObject(reader, false) as ApplicationConfiguration; } } finally { reader.Close(); } } catch (Exception e) { throw ServiceResultException.Create( StatusCodes.BadNotReadable, e, "Cannot load the configuration file: {0}", filePath); } // check if security info store on disk. if (application != null) { return(application); } application = new SecuredApplication(); // copy application info. application.ApplicationName = applicationConfiguration.ApplicationName; application.ApplicationUri = applicationConfiguration.ApplicationUri; application.ProductName = applicationConfiguration.ProductUri; application.ApplicationType = (ApplicationType)(int)applicationConfiguration.ApplicationType; application.ConfigurationFile = configFilePath; application.ExecutableFile = exeFilePath; application.ConfigurationMode = "http://opcfoundation.org/UASDK/ConfigurationTool"; application.LastExportTime = DateTime.UtcNow; // copy the security settings. if (applicationConfiguration.SecurityConfiguration != null) { application.ApplicationCertificate = SecuredApplication.ToCertificateIdentifier(applicationConfiguration.SecurityConfiguration.ApplicationCertificate); if (applicationConfiguration.SecurityConfiguration.TrustedIssuerCertificates != null) { application.IssuerCertificateStore = SecuredApplication.ToCertificateStoreIdentifier(applicationConfiguration.SecurityConfiguration.TrustedIssuerCertificates); if (applicationConfiguration.SecurityConfiguration.TrustedIssuerCertificates.TrustedCertificates != null) { application.IssuerCertificates = SecuredApplication.ToCertificateList(applicationConfiguration.SecurityConfiguration.TrustedIssuerCertificates.TrustedCertificates); } } if (applicationConfiguration.SecurityConfiguration.TrustedPeerCertificates != null) { application.TrustedCertificateStore = SecuredApplication.ToCertificateStoreIdentifier(applicationConfiguration.SecurityConfiguration.TrustedPeerCertificates); if (applicationConfiguration.SecurityConfiguration.TrustedPeerCertificates.TrustedCertificates != null) { application.TrustedCertificates = SecuredApplication.ToCertificateList(applicationConfiguration.SecurityConfiguration.TrustedPeerCertificates.TrustedCertificates); } } if (applicationConfiguration.SecurityConfiguration.RejectedCertificateStore != null) { application.RejectedCertificatesStore = SecuredApplication.ToCertificateStoreIdentifier(applicationConfiguration.SecurityConfiguration.RejectedCertificateStore); } } ServerBaseConfiguration serverConfiguration = null; if (applicationConfiguration.ServerConfiguration != null) { serverConfiguration = applicationConfiguration.ServerConfiguration; } else if (applicationConfiguration.DiscoveryServerConfiguration != null) { serverConfiguration = applicationConfiguration.DiscoveryServerConfiguration; } if (serverConfiguration != null) { application.BaseAddresses = SecuredApplication.ToListOfBaseAddresses(serverConfiguration); application.SecurityProfiles = SecuredApplication.ToListOfSecurityProfiles(serverConfiguration.SecurityPolicies); } // return exported setttings. return(application); }
/// <summary> /// Deletes a new SSL certificate binding. /// </summary> public static void DeleteSslCertificateBinding(IPAddress address, ushort port) { if (address == null) { throw new ArgumentNullException("address"); } // initialize library. HttpError error = NativeMethods.HttpInitialize( new HTTPAPI_VERSION(1, 0), HttpInitFlag.HTTP_INITIALIZE_CONFIG, IntPtr.Zero); if (error != HttpError.NO_ERROR) { throw ServiceResultException.Create( StatusCodes.BadUnexpectedError, "Could not initialize HTTP library.\r\nError={0}", error); } IntPtr pAddress = IntPtr.Zero; IntPtr pConfigInfo = IntPtr.Zero; try { pAddress = ToIntPtr(address, port); HTTP_SERVICE_CONFIG_SSL_SET configSslSet = new HTTP_SERVICE_CONFIG_SSL_SET(); configSslSet.KeyDesc.pIpPort = pAddress; int size = Marshal.SizeOf(typeof(HTTP_SERVICE_CONFIG_SSL_SET)); pConfigInfo = Marshal.AllocCoTaskMem(size); Marshal.StructureToPtr(configSslSet, pConfigInfo, false); error = NativeMethods.HttpDeleteServiceConfiguration( IntPtr.Zero, HTTP_SERVICE_CONFIG_ID.HttpServiceConfigSSLCertInfo, pConfigInfo, size, IntPtr.Zero); if (error != HttpError.NO_ERROR) { throw ServiceResultException.Create( StatusCodes.BadUnexpectedError, "Could not delete existing SSL certificate binding.\r\nError={0}", error); } } finally { if (pConfigInfo != IntPtr.Zero) { Marshal.FreeCoTaskMem(pConfigInfo); } if (pAddress != IntPtr.Zero) { Marshal.FreeCoTaskMem(pAddress); } NativeMethods.HttpTerminate(HttpInitFlag.HTTP_INITIALIZE_CONFIG, IntPtr.Zero); } }
/// <summary> /// Processes an CloseSecureChannel request message. /// </summary> private bool ProcessCloseSecureChannelRequest(uint messageType, ArraySegment <byte> messageChunk) { // validate security on the message. ChannelToken token = null; uint requestId = 0; uint sequenceNumber = 0; ArraySegment <byte> messageBody; try { messageBody = ReadSymmetricMessage(messageChunk, true, out token, out requestId, out sequenceNumber); // check for replay attacks. if (!VerifySequenceNumber(sequenceNumber, "ProcessCloseSecureChannelRequest")) { throw new ServiceResultException(StatusCodes.BadSequenceNumberInvalid); } } catch (Exception e) { throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, e, "Could not verify security on CloseSecureChannel request."); } BufferCollection chunksToProcess = null; try { // check if it is necessary to wait for more chunks. if (!TcpMessageType.IsFinal(messageType)) { SaveIntermediateChunk(requestId, messageBody); return(false); } // get the chunks to process. chunksToProcess = GetSavedChunks(requestId, messageBody); CloseSecureChannelRequest request = BinaryDecoder.DecodeMessage( new ArraySegmentStream(chunksToProcess), typeof(CloseSecureChannelRequest), Quotas.MessageContext) as CloseSecureChannelRequest; if (request == null) { throw ServiceResultException.Create(StatusCodes.BadStructureMissing, "Could not parse CloseSecureChannel request body."); } // send the response. // SendCloseSecureChannelResponse(requestId, token, request); } catch (Exception e) { Utils.Trace(e, "Unexpected error processing OpenSecureChannel request."); } finally { if (chunksToProcess != null) { chunksToProcess.Release(BufferManager, "ProcessCloseSecureChannelRequest"); } Utils.Trace( "{0} ProcessCloseSecureChannelRequest Socket={0:X8}, ChannelId={1}, TokenId={2}", ChannelName, (Socket != null) ? Socket.Handle : 0, (CurrentToken != null) ? CurrentToken.ChannelId : 0, (CurrentToken != null) ? CurrentToken.TokenId : 0); // close the channel. ChannelClosed(); } // return false would double free the buffer return(true); }
/// <summary> /// Gets the application access rules for the specified URL. /// </summary> public static IList <HttpAccessRule> GetAccessRules(string url) { List <HttpAccessRule> accessRules = new List <HttpAccessRule>(); HttpError error = NativeMethods.HttpInitialize( new HTTPAPI_VERSION(1, 0), HttpInitFlag.HTTP_INITIALIZE_CONFIG, IntPtr.Zero); if (error != HttpError.NO_ERROR) { throw ServiceResultException.Create( StatusCodes.BadUnexpectedError, "Could not initialize HTTP library.\r\nError={0}", error); } HTTP_SERVICE_CONFIG_URLACL_QUERY query = new HTTP_SERVICE_CONFIG_URLACL_QUERY(); query.QueryDesc = HTTP_SERVICE_CONFIG_QUERY_TYPE.HttpServiceConfigQueryNext; if (!String.IsNullOrEmpty(url)) { query.QueryDesc = HTTP_SERVICE_CONFIG_QUERY_TYPE.HttpServiceConfigQueryExact; query.KeyDesc.pUrlPrefix = url; } IntPtr pInput = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(HTTP_SERVICE_CONFIG_URLACL_QUERY))); NativeMethods.ZeroMemory(pInput, Marshal.SizeOf(typeof(HTTP_SERVICE_CONFIG_URLACL_QUERY))); IntPtr pOutput = IntPtr.Zero; try { for (query.dwToken = 0; error == HttpError.NO_ERROR; query.dwToken++) { Marshal.StructureToPtr(query, pInput, true); int requiredBufferLength = 0; error = NativeMethods.HttpQueryServiceConfiguration( IntPtr.Zero, HTTP_SERVICE_CONFIG_ID.HttpServiceConfigUrlAclInfo, pInput, Marshal.SizeOf(typeof(HTTP_SERVICE_CONFIG_URLACL_QUERY)), pOutput, requiredBufferLength, out requiredBufferLength, IntPtr.Zero); if (error == HttpError.ERROR_NO_MORE_ITEMS) { break; } if (!String.IsNullOrEmpty(url)) { if (error == HttpError.ERROR_FILE_NOT_FOUND) { break; } } if (error != HttpError.ERROR_INSUFFICIENT_BUFFER) { throw ServiceResultException.Create( StatusCodes.BadUnexpectedError, "Could not read access rules for HTTP url.\r\nError={1}, Url={0}", url, error); } pOutput = Marshal.AllocHGlobal(requiredBufferLength); NativeMethods.ZeroMemory(pOutput, requiredBufferLength); error = NativeMethods.HttpQueryServiceConfiguration( IntPtr.Zero, HTTP_SERVICE_CONFIG_ID.HttpServiceConfigUrlAclInfo, pInput, Marshal.SizeOf(typeof(HTTP_SERVICE_CONFIG_URLACL_QUERY)), pOutput, requiredBufferLength, out requiredBufferLength, IntPtr.Zero); if (error != HttpError.NO_ERROR) { throw ServiceResultException.Create( StatusCodes.BadUnexpectedError, "Could not read access rules for HTTP url.\r\nError={1}, Url={0}", url, error); } HTTP_SERVICE_CONFIG_URLACL_SET result = (HTTP_SERVICE_CONFIG_URLACL_SET)Marshal.PtrToStructure( pOutput, typeof(HTTP_SERVICE_CONFIG_URLACL_SET)); // parse the SDDL and update the access list. ParseSddl(result.KeyDesc.pUrlPrefix, result.ParamDesc.pStringSecurityDescriptor, accessRules); Marshal.FreeHGlobal(pOutput); pOutput = IntPtr.Zero; // all done if requesting the results for a single url. if (!String.IsNullOrEmpty(url)) { break; } } } finally { if (pInput != IntPtr.Zero) { Marshal.DestroyStructure(pInput, typeof(HTTP_SERVICE_CONFIG_URLACL_QUERY)); Marshal.FreeHGlobal(pInput); } if (pOutput != IntPtr.Zero) { Marshal.FreeHGlobal(pOutput); } NativeMethods.HttpTerminate(HttpInitFlag.HTTP_INITIALIZE_CONFIG, IntPtr.Zero); } return(accessRules); }
protected ArraySegment <byte> ReadSymmetricMessage( ArraySegment <byte> buffer, bool isRequest, out ChannelToken token, out uint requestId, out uint sequenceNumber) { BinaryDecoder decoder = new BinaryDecoder(buffer.Array, buffer.Offset, buffer.Count, Quotas.MessageContext); uint messageType = decoder.ReadUInt32(null); uint messageSize = decoder.ReadUInt32(null); uint channelId = decoder.ReadUInt32(null); uint tokenId = decoder.ReadUInt32(null); // ensure the channel is valid. if (channelId != ChannelId) { throw ServiceResultException.Create( StatusCodes.BadTcpSecureChannelUnknown, "SecureChannelId is not known. ChanneId={0}, CurrentChannelId={1}", channelId, ChannelId); } // check for a message secured with the new token. if (RenewedToken != null && RenewedToken.TokenId == tokenId) { ActivateToken(RenewedToken); } // check if activation of the new token should be forced. if (RenewedToken != null && CurrentToken.ActivationRequired) { ActivateToken(RenewedToken); Utils.LogInfo("ChannelId {0}: Token #{1} activated forced.", Id, CurrentToken.TokenId); } // check for valid token. ChannelToken currentToken = CurrentToken; if (currentToken == null) { throw new ServiceResultException(StatusCodes.BadSecureChannelClosed); } // find the token. if (currentToken.TokenId != tokenId && PreviousToken != null && PreviousToken.TokenId != tokenId) { throw ServiceResultException.Create( StatusCodes.BadTcpSecureChannelUnknown, "Channel{0}: TokenId is not known. ChanneId={1}, TokenId={2}, CurrentTokenId={3}, PreviousTokenId={4}", Id, channelId, tokenId, currentToken.TokenId, (PreviousToken != null) ? (int)PreviousToken.TokenId : -1); } token = currentToken; // check for a message secured with the token before it expired. if (PreviousToken != null && PreviousToken.TokenId == tokenId) { token = PreviousToken; } // check if token has expired. if (token.Expired) { throw ServiceResultException.Create(StatusCodes.BadTcpSecureChannelUnknown, "Channel{0}: Token #{1} has expired. Lifetime={2:HH:mm:ss.fff}", Id, token.TokenId, token.CreatedAt); } int headerSize = decoder.Position; if (SecurityMode == MessageSecurityMode.SignAndEncrypt) { // decrypt the message. Decrypt(token, new ArraySegment <byte>(buffer.Array, buffer.Offset + headerSize, buffer.Count - headerSize), isRequest); } if (SecurityMode != MessageSecurityMode.None) { // extract signature. byte[] signature = new byte[SymmetricSignatureSize]; for (int ii = 0; ii < SymmetricSignatureSize; ii++) { signature[ii] = buffer.Array[buffer.Offset + buffer.Count - SymmetricSignatureSize + ii]; } // verify the signature. if (!Verify(token, signature, new ArraySegment <byte>(buffer.Array, buffer.Offset, buffer.Count - SymmetricSignatureSize), isRequest)) { Utils.LogError("ChannelId {0}: Could not verify signature on message.", Id); throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, "Could not verify the signature on the message."); } } int paddingCount = 0; if (SecurityMode == MessageSecurityMode.SignAndEncrypt) { // verify padding. int paddingStart = buffer.Offset + buffer.Count - SymmetricSignatureSize - 1; paddingCount = buffer.Array[paddingStart]; for (int ii = paddingStart - paddingCount; ii < paddingStart; ii++) { if (buffer.Array[ii] != paddingCount) { throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, "Could not verify the padding in the message."); } } // add byte for size. paddingCount++; } // extract request id and sequence number. sequenceNumber = decoder.ReadUInt32(null); requestId = decoder.ReadUInt32(null); // return an the data contained in the message. int startOfBody = buffer.Offset + TcpMessageLimits.SymmetricHeaderSize + TcpMessageLimits.SequenceHeaderSize; int sizeOfBody = buffer.Count - TcpMessageLimits.SymmetricHeaderSize - TcpMessageLimits.SequenceHeaderSize - paddingCount - SymmetricSignatureSize; return(new ArraySegment <byte>(buffer.Array, startOfBody, sizeOfBody)); }
/// <summary> /// Callback method that will be invoked when the asynchronous BeginTestStack method is complete /// </summary> private void EndAutoReconnect(IAsyncResult asyncResult) { AsyncTestState state = (AsyncTestState)asyncResult.AsyncState; long completionTime = (state.CallData is long)?(long)state.CallData:0; lock (m_lock) { m_requestCount--; // check if a fault is expected. bool faultExpected = FaultExpected(completionTime, state.TestCaseContext); // complete the operation. Variant output; try { state.ChannelContext.ClientSession.EndTestStack(asyncResult, out output); } catch (Exception e) { if (!faultExpected) { // allow faults for a short period of time after a black out ends. for (int ii = 0; ii < m_blackouts.Count; ii++) { if (completionTime >= m_blackouts[ii].Start && completionTime < m_blackouts[ii].End + state.TestCaseContext.MaxTransportDelay) { faultExpected = true; break; } } } for (int ii = 0; ii < m_blackouts.Count; ii++) { long period = m_blackouts[ii].Start; if (m_blackouts[ii].End <= 0) { period = DateTime.UtcNow.Ticks / TimeSpan.TicksPerMillisecond - period; } else { period = m_blackouts[ii].End - period; } if (state.TestCaseContext.MaxRecoveryTime + state.TestCaseContext.MaxResponseDelay <= (int)period) { e = ServiceResultException.Create( StatusCodes.BadUnexpectedError, "Iteration {0} MaxRecoveryTime Exceeded {1} ms", state.Iteration, period); faultExpected = false; break; } } // no error if a fault was expected. if (faultExpected) { Utils.Trace("Iteration {0}; Fault Expected {1}", state.Iteration, e.Message); state.ChannelContext.EventLogger.LogCompleteEvent(state.Testcase, state.Iteration); return; } Utils.Trace("Iteration {0}; Fault Unexpected {1}", state.Iteration, e.Message); // log error. state.ChannelContext.EventLogger.LogErrorEvent(state.Testcase, state.Iteration, e); if (m_sequenceToExecute.HaltOnError) { // only report first fault. if (m_fault == null) { m_fault = ServiceResult.Create(e, StatusCodes.BadUnexpectedError, "Unexpected Fault on Iteration {0}", state.Iteration); } } return; } // allow success for a short period of time before a black out begins. for (int ii = 0; ii < m_blackouts.Count; ii++) { if ((completionTime <= m_blackouts[ii].End || m_blackouts[ii].End == 0) && completionTime >= m_blackouts[ii].Start - state.TestCaseContext.MaxTransportDelay) { faultExpected = false; break; } } try { // check if a fault was expected. if (faultExpected) { throw ServiceResultException.Create( StatusCodes.BadUnexpectedError, "Expecting a fault which did not occur."); } // check processing time. long processingTimeError = ((DateTime.UtcNow.Ticks / TimeSpan.TicksPerMillisecond) - completionTime); Utils.Trace("Iteration {0}; Delta {1}.", state.Iteration, processingTimeError); if (Math.Abs(processingTimeError) > state.TestCaseContext.MaxTransportDelay * 2) { bool late = true; if (state.TestCaseContext.StackEventType == 4) { if (Math.Abs(processingTimeError) > state.TestCaseContext.MaxRecoveryTime) { late = false; } } if (late) { throw ServiceResultException.Create( StatusCodes.BadUnexpectedError, "Request did not complete in the specified time (Error = {0}ms).", processingTimeError); } } // black out ends when the first valid request is returned. for (int ii = 0; ii < m_blackouts.Count; ii++) { if (m_blackouts[ii].End <= 0) { m_blackouts[ii].End = (DateTime.UtcNow.Ticks / TimeSpan.TicksPerMillisecond); } } // check output. state.ChannelContext.Random.Start( (int)(state.Testcase.ResponseSeed + state.Iteration), (int)m_sequenceToExecute.RandomDataStepSize, state.TestCaseContext); Variant expectedOutput = state.ChannelContext.Random.GetVariant(); if (!Compare.CompareVariant(output, expectedOutput)) { throw ServiceResultException.Create( StatusCodes.BadUnexpectedError, "Server did not return expected output\r\nActual = {0}\r\nExpected = {0}", output, expectedOutput); } // iteration complete. state.ChannelContext.EventLogger.LogCompleteEvent(state.Testcase, state.Iteration); } catch (Exception e) { state.ChannelContext.EventLogger.LogErrorEvent(state.Testcase, state.Iteration, e); if (m_sequenceToExecute.HaltOnError) { // only report first fault. if (m_fault == null) { m_fault = new ServiceResult(e); } } } } }