1392 lines · 61.4 KB · C#
RawDownload
//******************************************************************************************************//  TlsServer.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.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.Security.Principal;using System.Text;using System.Threading; #if MONO#pragma warning disable 649#endif namespace GSF.Communication{    /// <summary>    /// Represents a TCP-based communication server with SSL authentication and encryption.    /// </summary>    public class TlsServer : ServerBase    {        #region [ Members ]         // Nested Types         /// <summary>        /// Represents a socket that has been wrapped        /// in an <see cref="SslStream"/> for encryption.        /// </summary>        public sealed class TlsSocket : IDisposable        {            /// <summary>            /// Gets the <see cref="Socket"/> connected to the remote host.            /// </summary>            public Socket Socket;             /// <summary>            /// Gets the stream through which data is passed when            /// sending to or receiving from the remote host.            /// </summary>            public SslStream SslStream;             /// <summary>            /// The end point of the remote client connecting to this server.            /// </summary>            public IPEndPoint RemoteEndPoint;             /// <summary>            /// Performs application-defined tasks associated with            /// freeing, releasing, or resetting unmanaged resources.            /// </summary>            public void Dispose()            {                Socket?.Dispose();                 SslStream?.Dispose();            }        }         private class TlsClientInfo        {            public TransportProvider<TlsSocket> Client;            public ICancellationToken TimeoutToken;             public int Sending;            public object SendLock;            public ConcurrentQueue<TlsServerPayload> SendQueue;            public ShortSynchronizedOperation DumpPayloadsOperation;             public NegotiateStream NegotiateStream;            public WindowsPrincipal ClientPrincipal;        }         private class TlsServerPayload        {            // Per payload state            public byte[] Data;            public int Offset;            public int Length;            public ManualResetEventSlim WaitHandle;             // Per client state            public TlsClientInfo ClientInfo;        }         // 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="ServerBase.ConfigurationString"/> property.        /// </summary>        public const string DefaultConfigurationString = "Port=8888";         // Fields        private readonly SimpleCertificateChecker m_defaultCertificateChecker;        private ICertificateChecker m_certificateChecker;        private string m_certificateFile;        private SslProtocols m_enabledSslProtocols;        private byte[] m_payloadMarker;        private EndianOrder m_payloadEndianOrder;        private IPStack m_ipStack;        private SocketAsyncEventArgs m_acceptArgs;        private readonly ConcurrentDictionary<Guid, TlsClientInfo> m_clientInfoLookup;        private Dictionary<string, string> m_configData;         private readonly EventHandler<SocketAsyncEventArgs> m_acceptHandler;         #endregion         #region [ Constructors ]         /// <summary>        /// Initializes a new instance of the <see cref="TcpServer"/> class.        /// </summary>        public TlsServer() : this(DefaultConfigurationString)        {        }         /// <summary>        /// Initializes a new instance of the <see cref="TcpServer"/> class.        /// </summary>        /// <param name="configString">Config string of the <see cref="TcpServer"/>. See <see cref="DefaultConfigurationString"/> for format.</param>        public TlsServer(string configString) : base(TransportProtocol.Tcp, configString)        {            m_defaultCertificateChecker = new SimpleCertificateChecker();            LocalCertificateSelectionCallback = DefaultLocalCertificateSelectionCallback;            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_clientInfoLookup = new ConcurrentDictionary<Guid, TlsClientInfo>();             m_acceptHandler = (sender, args) => ProcessAccept(args);        }         /// <summary>        /// Initializes a new instance of the <see cref="TcpServer"/> class.        /// </summary>        /// <param name="container"><see cref="IContainer"/> object that contains the <see cref="TcpServer"/>.</param>        public TlsServer(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 client 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 client 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. When this is set        /// to true, if the client's credentials are invalid, the <see cref="TryGetClientPrincipal"/>        /// method will return true for that client, but the principal will still be null.        /// </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="TcpServer"/>.        /// </summary>        [Browsable(false)]        public Socket Server { get; private 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 validate remote certificates.        /// </summary>        [Browsable(false)]        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]        public RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; set; }         /// <summary>        /// Gets or sets the callback used to select local certificates.        /// </summary>        [Browsable(false)]        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]        public LocalCertificateSelectionCallback LocalCertificateSelectionCallback { get; set; }         /// <summary>        /// Gets or sets the path to the certificate used for authentication.        /// </summary>        [Category("Settings")]        [DefaultValue(null)]        [Description("Path to the local certificate used by this server 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 certificate used to identify this server.        /// </summary>        [Browsable(false)]        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]        public X509Certificate Certificate { get; set; }         /// <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 server.")]        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 TlsServer 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 flag that determines whether a client certificate is required during authentication.        /// </summary>        [Category("Settings")]        [DefaultValue(false)]        [Description("True if the client certificate is required during authentication, otherwise False.")]        public bool RequireClientCertificate { get; set; }         /// <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 directory containing the trusted certificates.        /// </summary>        [Category("Settings")]        [DefaultValue("Trusted Certificates")]        [Description("Path to the directory containing the trusted remote certificates.")]        public string TrustedCertificatesPath { get; set; }         /// <summary>        /// Gets or sets the set of valid policy errors when validating remote certificates.        /// </summary>        [Category("Settings")]        [DefaultValue(SslPolicyErrors.None)]        [Description("Set of valid policy errors when validating remote certificates.")]        public SslPolicyErrors ValidPolicyErrors        {            get => m_defaultCertificateChecker.ValidPolicyErrors;            set => m_defaultCertificateChecker.ValidPolicyErrors = value;        }         /// <summary>        /// Gets or sets the set of valid chain flags used when validating remote certificates.        /// </summary>        [Category("Settings")]        [DefaultValue(X509ChainStatusFlags.NoError)]        [Description("Set of valid chain flags used when validating remote certificates.")]        public X509ChainStatusFlags ValidChainFlags        {            get => m_defaultCertificateChecker.ValidChainFlags;            set => m_defaultCertificateChecker.ValidChainFlags = value;        }         /// <summary>        /// Gets the descriptive status of the server.        /// </summary>        public override string Status        {            get            {                StringBuilder statusBuilder = new StringBuilder(base.Status);                int count = 0;                 foreach (ConcurrentQueue<TlsServerPayload> sendQueue in m_clientInfoLookup.Values.Select(clientInfo => clientInfo.SendQueue))                {                    statusBuilder.AppendFormat("           Queued payloads: {0} for client {1}", sendQueue.Count, ++count);                    statusBuilder.AppendLine();                }                 return statusBuilder.ToString();            }        }         #endregion         #region [ Methods ]

Lines 1–500 of 1392.

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