1850 lines · 76.6 KB · C#
RawDownload
//******************************************************************************************************//  TlsClient.cs - Gbtc////  Copyright © 2012, Grid Protection Alliance.  All Rights Reserved.////  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See//  the NOTICE file distributed with this work for additional information regarding copyright ownership.//  The GPA licenses this file to you under the MIT License (MIT), the "License"; you may//  not use this file except in compliance with the License. You may obtain a copy of the License at:////      http://www.opensource.org/licenses/MIT////  Unless agreed to in writing, the subject software distributed under the License is distributed on an//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the//  License for the specific language governing permissions and limitations.////  Code Modification History://  ----------------------------------------------------------------------------------------------------//  07/12/2012 - Stephen C. Wills//       Generated original version of source code.//  12/13/2012 - Starlynn Danyelle Gilliam//       Modified Header.////****************************************************************************************************** using GSF.Configuration;using GSF.Diagnostics;using GSF.IO;using GSF.Net.Security;using GSF.Threading;using System;using System.Collections.Concurrent;using System.Collections.Generic;using System.ComponentModel;using System.Diagnostics;using System.IO;using System.Linq;using System.Net;using System.Net.Security;using System.Net.Sockets;using System.Security;using System.Security.Authentication;using System.Security.Cryptography.X509Certificates;using System.Text;using System.Text.RegularExpressions;using System.Threading; // ReSharper disable AccessToDisposedClosure// ReSharper disable ArrangeAccessorOwnerBodynamespace GSF.Communication{    /// <summary>    /// Represents a TCP-based communication client with SSL authentication and encryption.    /// </summary>    /// <seealso cref="TcpClient"/>    public class TlsClient : ClientBase    {        #region [ Members ]         // Nested Types        private sealed class ConnectState : IDisposable        {            public Socket Socket;            public NetworkStream NetworkStream;            public SslStream SslStream;            public NegotiateStream NegotiateStream;             public readonly SocketAsyncEventArgs ConnectArgs = new SocketAsyncEventArgs();            public int ConnectionAttempts;             public readonly CancellationToken Token = new CancellationToken();            public ICancellationToken TimeoutToken;             public void Dispose()            {                ConnectArgs.Dispose();                Socket?.Dispose();                NetworkStream?.Dispose();                SslStream?.Dispose();                NegotiateStream?.Dispose();            }        }         private sealed class ReceiveState : IDisposable        {            public Socket Socket;            public NetworkStream NetworkStream;            public SslStream SslStream;             public byte[] Buffer;            public int Offset;            public int PayloadLength = -1;             public CancellationToken Token;             public void Dispose()            {                Dispose(Socket);                Dispose(NetworkStream);                Dispose(SslStream);            }             private static void Dispose(IDisposable obj) => obj?.Dispose();        }         private sealed class SendState : IDisposable        {            public Socket Socket;            public NetworkStream NetworkStream;            public SslStream SslStream;             public readonly ConcurrentQueue<TlsClientPayload> SendQueue = new ConcurrentQueue<TlsClientPayload>();            public TlsClientPayload Payload;            public int Sending;             public CancellationToken Token;             public void Dispose()            {                Dispose(Socket);                Dispose(NetworkStream);                Dispose(SslStream);                 while (SendQueue.TryDequeue(out TlsClientPayload payload))                {                    payload.WaitHandle.Set();                    payload.WaitHandle.Dispose();                }            }             private static void Dispose(IDisposable obj) => obj?.Dispose();        }         private class TlsClientPayload        {            public byte[] Data;            public int Offset;            public int Length;            public ManualResetEvent WaitHandle;        }         private class CancellationToken        {            private int m_cancelled;             public bool Cancelled => Interlocked.CompareExchange(ref m_cancelled, 0, 0) != 0;             public bool Cancel() => Interlocked.Exchange(ref m_cancelled, 1) != 0;        }         // Constants         /// <summary>        /// Specifies the default value for the <see cref="TrustedCertificatesPath"/> property.        /// </summary>        public readonly string DefaultTrustedCertificatesPath = FilePath.GetAbsolutePath(@"Certs\Remotes");         /// <summary>        /// Specifies the default value for the <see cref="PayloadAware"/> property.        /// </summary>        public const bool DefaultPayloadAware = false;         /// <summary>        /// Specifies the default value for the <see cref="IntegratedSecurity"/> property.        /// </summary>        public const bool DefaultIntegratedSecurity = false;         /// <summary>        /// Specifies the default value for the <see cref="IgnoreInvalidCredentials"/> property.        /// </summary>        public const bool DefaultIgnoreInvalidCredentials = false;         /// <summary>        /// Specifies the default value for the <see cref="AllowDualStackSocket"/> property.        /// </summary>        public const bool DefaultAllowDualStackSocket = true;         /// <summary>        /// Specifies the default value for the <see cref="MaxSendQueueSize"/> property.        /// </summary>        public const int DefaultMaxSendQueueSize = 500000;         /// <summary>        /// Specifies the default value for the <see cref="NoDelay"/> property.        /// </summary>        public const bool DefaultNoDelay = false;         /// <summary>        /// Specifies the default value for the <see cref="ClientBase.ConnectionString"/> property.        /// </summary>        public const string DefaultConnectionString = "Server=localhost:8888";         // Fields        private readonly SimpleCertificateChecker m_defaultCertificateChecker;        private ICertificateChecker m_certificateChecker;        private readonly X509Certificate2Collection m_clientCertificates;        private SslProtocols m_enabledSslProtocols;        private string m_certificateFile;        private byte[] m_payloadMarker;        private EndianOrder m_payloadEndianOrder;        private IPStack m_ipStack;        private readonly ShortSynchronizedOperation m_dumpPayloadsOperation;        private string[] m_serverList;        private Dictionary<string, string> m_connectData;        private ManualResetEvent m_connectWaitHandle;        private ConnectState m_connectState;        private ReceiveState m_receiveState;        private SendState m_sendState;        private bool m_disposed;         #endregion         #region [ Constructors ]         /// <summary>        /// Initializes a new instance of the <see cref="TlsClient"/> class.        /// </summary>        public TlsClient() : this(DefaultConnectionString)        {        }         /// <summary>        /// Initializes a new instance of the <see cref="TlsClient"/> class.        /// </summary>        /// <param name="connectString">Connect string of the <see cref="TlsClient"/>. See <see cref="DefaultConnectionString"/> for format.</param>        public TlsClient(string connectString) : base(TransportProtocol.Tcp, connectString)        {            m_defaultCertificateChecker = new SimpleCertificateChecker();            LocalCertificateSelectionCallback = DefaultLocalCertificateSelectionCallback;            m_clientCertificates = new X509Certificate2Collection();            m_enabledSslProtocols = SslProtocols.Tls12;            CheckCertificateRevocation = true;             TrustedCertificatesPath = DefaultTrustedCertificatesPath;            PayloadAware = DefaultPayloadAware;            m_payloadMarker = Payload.DefaultMarker;            m_payloadEndianOrder = EndianOrder.LittleEndian;            IntegratedSecurity = DefaultIntegratedSecurity;            IgnoreInvalidCredentials = DefaultIgnoreInvalidCredentials;            AllowDualStackSocket = DefaultAllowDualStackSocket;            MaxSendQueueSize = DefaultMaxSendQueueSize;            NoDelay = DefaultNoDelay;            m_dumpPayloadsOperation = new ShortSynchronizedOperation(DumpPayloads, OnSendDataException);        }         /// <summary>        /// Initializes a new instance of the <see cref="TlsClient"/> class.        /// </summary>        /// <param name="container"><see cref="IContainer"/> object that contains the <see cref="TlsClient"/>.</param>        public TlsClient(IContainer container) : this() => container?.Add(this);         #endregion         #region [ Properties ]         /// <summary>        /// Gets or sets a boolean value that indicates whether the payload boundaries are to be preserved during transmission.        /// </summary>        [Category("Data")]        [DefaultValue(DefaultPayloadAware)]        [Description("Indicates whether the payload boundaries are to be preserved during transmission.")]        public bool PayloadAware { get; set; }         /// <summary>        /// Gets or sets the byte sequence used to mark the beginning of a payload in a <see cref="PayloadAware"/> transmission.        /// </summary>        /// <remarks>        /// Setting property to <c>null</c> will create a zero-length payload marker.        /// </remarks>        [Browsable(false)]        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]        public byte[] PayloadMarker        {            get => m_payloadMarker;            set => m_payloadMarker = value ?? Array.Empty<byte>();        }         /// <summary>        /// Gets or sets the endian order to apply for encoding and decoding payload size in a <see cref="PayloadAware"/> transmission.        /// </summary>        /// <remarks>        /// Setting property to <c>null</c> will force use of little-endian encoding.        /// </remarks>        [Browsable(false)]        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]        public EndianOrder PayloadEndianOrder        {            get => m_payloadEndianOrder;            set => m_payloadEndianOrder = value ?? EndianOrder.LittleEndian;        }         /// <summary>        /// Gets or sets a boolean value that indicates whether the current Windows account credentials are used for authentication.        /// </summary>        /// <remarks>           /// This option is ignored under Mono deployments.        /// </remarks>        [Category("Security")]        [DefaultValue(DefaultIntegratedSecurity)]        [Description("Indicates whether the current Windows account credentials are used for authentication.")]        public bool IntegratedSecurity { get; set; }         /// <summary>        /// Gets or sets a boolean value that indicates whether the server        /// should ignore errors when the client's credentials are invalid.        /// </summary>        /// <remarks>        /// This property should only be set to true if there is an alternative by which        /// to authenticate the client when integrated security fails.        /// </remarks>        [Category("Security")]        [DefaultValue(DefaultIgnoreInvalidCredentials)]        [Description("Indicates whether the client Windows account credentials are validated during authentication.")]        public bool IgnoreInvalidCredentials { get; set; }         /// <summary>        /// Gets or sets a boolean value that determines if dual-mode socket is allowed when endpoint address is IPv6.        /// </summary>        [Category("Settings")]        [DefaultValue(DefaultAllowDualStackSocket)]        [Description("Determines if dual-mode socket is allowed when endpoint address is IPv6.")]        public bool AllowDualStackSocket { get; set; }         /// <summary>        /// Gets or sets the maximum size for the send queue before payloads are dumped from the queue.        /// </summary>        [Category("Settings")]        [DefaultValue(DefaultMaxSendQueueSize)]        [Description("The maximum size for the send queue before payloads are dumped from the queue.")]        public int MaxSendQueueSize { get; set; }         /// <summary>        /// Gets or sets a boolean value that determines if small packets are delivered to the remote host without delay.        /// </summary>        [Category("Settings")]        [DefaultValue(DefaultNoDelay)]        [Description("Determines if small packets are delivered to the remote host without delay.")]        public bool NoDelay { get; set; }         /// <summary>        /// Gets the <see cref="Socket"/> object for the <see cref="TlsClient"/>.        /// </summary>        [Browsable(false)]        public Socket Client => m_connectState?.Socket;         /// <summary>        /// Gets the <see cref="SslStream"/> object for the <see cref="TlsClient"/>.        /// </summary>        [Browsable(false)]        public SslStream SslStream => m_connectState?.SslStream;         /// <summary>        /// Gets the server URI of the <see cref="TlsClient"/>.        /// </summary>        [Browsable(false)]        public override string ServerUri => $"{TransportProtocol}://{ServerList[ServerIndex]}".ToLower();         /// <summary>        /// Gets or sets network credential that is used when        /// <see cref="IntegratedSecurity"/> is set to <c>true</c>.        /// </summary>        [Browsable(false)]        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]        public NetworkCredential NetworkCredential { get; set; }         /// <summary>        /// Gets or sets the certificate checker used to validate remote certificates.        /// </summary>        /// <remarks>        /// The certificate checker will only be used to validate certificates if        /// the <see cref="RemoteCertificateValidationCallback"/> is set to null.        /// </remarks>        [Browsable(false)]        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]        public ICertificateChecker CertificateChecker        {            get => m_certificateChecker ?? m_defaultCertificateChecker;            set => m_certificateChecker = value;        }         /// <summary>        /// Gets or sets the callback used to verify remote certificates.        /// </summary>        /// <remarks>        /// Setting this property overrides the validation        /// callback in the <see cref="CertificateChecker"/>.        /// </remarks>        [Browsable(false)]        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]        public RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; set; }         /// <summary>        /// Gets or sets the callback used to select a local certificate.        /// </summary>        [Browsable(false)]        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]        public LocalCertificateSelectionCallback LocalCertificateSelectionCallback { get; set; }         /// <summary>        /// Gets the collection of X509 certificates for this client.        /// </summary>        [Browsable(false)]        public X509CertificateCollection ClientCertificates => m_clientCertificates;         /// <summary>        /// Gets or sets a set of flags which determine the enabled <see cref="SslProtocols"/>.        /// </summary>        /// <exception cref="SecurityException">Failed to write event log entry for security warning about use of less secure TLS/SSL protocols.</exception>        [Category("Settings")]        [DefaultValue(SslProtocols.Tls12)]        [Description("The set of SSL protocols that are enabled for this client.")]        public SslProtocols EnabledSslProtocols        {            get => m_enabledSslProtocols;            set            {                m_enabledSslProtocols = value;                 // As of July 2014, Tls12 is the latest and most secure TLS protocol - all other protocols                // represent a security risk when used, as such we log a security message when any of the                // older protocols are being used.                if (m_enabledSslProtocols >= SslProtocols.Tls12)                    return;                 try                {                    string applicationName;                     // Get application name                    try                    {                        // Attempt to retrieve application name as defined in common security settings - this name                        // is typically preconfigured as the desired event source for event log entries                        ConfigurationFile config = ConfigurationFile.Current;                        CategorizedSettingsElementCollection settings = config.Settings["SecurityProvider"];                        applicationName = settings["ApplicationName"].Value;                    }                    catch                    {                        applicationName = null;                    }                     // Fall back on running executable name                    if (string.IsNullOrWhiteSpace(applicationName))                        applicationName = FilePath.GetFileNameWithoutExtension(AppDomain.CurrentDomain.FriendlyName);                     string message = $"One or more less secure TLS/SSL protocols \"{m_enabledSslProtocols}\" are being used by an instance of the TlsClient in {applicationName}";                    EventLog.WriteEntry(applicationName, message, EventLogEntryType.Warning, 1);                }                catch (Exception ex)                {                    throw new SecurityException($"Failed to write event log entry for security warning about use of less secure TLS/SSL protocols \"{m_enabledSslProtocols}\": {ex.Message}", ex);                }            }        }         /// <summary>        /// Gets or sets a boolean value that determines whether the certificate revocation list is checked during authentication.        /// </summary>        [Category("Settings")]        [DefaultValue(true)]        [Description("True if the certificate revocation list is to be checked during authentication, otherwise False.")]        public bool CheckCertificateRevocation { get; set; }         /// <summary>        /// Gets or sets the path to the certificate used for authentication.        /// </summary>        [Category("Settings")]        [DefaultValue(null)]        [Description("Path to the certificate used by this client for authentication.")]        public string CertificateFile        {            get => m_certificateFile;            set            {                if (string.IsNullOrWhiteSpace(value))                {                    m_certificateFile = null;                    Certificate = null;                }                else                {                    m_certificateFile = FilePath.GetAbsolutePath(value);                     if (File.Exists(m_certificateFile))                        Certificate = new X509Certificate2(m_certificateFile);                }            }        }         /// <summary>        /// Gets or sets the local certificate selected by the default <see cref="LocalCertificateSelectionCallback"/>.        /// </summary>        [Browsable(false)]        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]        public X509Certificate Certificate { get; set; }         /// <summary>        /// Gets or sets the path to the directory containing the trusted certificates.        /// </summary>

Lines 1–500 of 1850.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t