#define TRACE using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Net; using System.Reflection; using System.Security.Cryptography.X509Certificates; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Discovery; using System.ServiceModel.Security; using System.Threading; using System.Threading.Tasks; using System.Xml; using ArchestrAServices.Common; using ArchestrAServices.Contract; namespace ArchestrAServices.Proxy; public class ManageASBSecurityProxy : IDisposable { public class ConnectionDelegate { private IManageASBSecurity securityClientField; private ConnectionDelegate() { securityClientField = null; } public ConnectionDelegate(IManageASBSecurity securityClient) { securityClientField = securityClient; } public ArchestrAResult CallConnect(out Connection connection, string application, string domain, string host, ArchestrAServices.Contract.PublicKey clientToken) { if (securityClientField != null) { try { return securityClientField.Connect(out connection, application, domain, host, clientToken); } catch (Exception ex) { SvcTrace.DiagControl.TraceEvent(TraceEventType.Warning, 0, "ASBSecurity Proxy: CallConnect delegate caught exception {0}", ex.Message); if (ex.InnerException != null) { SvcTrace.DiagControl.TraceEvent(TraceEventType.Warning, 0, " {0}", ex.InnerException.Message); } } } connection = default(Connection); return ResultFactory.MakeResult(ArchestrAError.OperationFailed, 0); } public ArchestrAResult CallActivate(ConnectionId connection, ConnectionAuthenticationData authentication, ulong timeout) { ArchestrAResult result = ResultFactory.MakeResult(ArchestrAError.OperationFailed, 0); if (securityClientField != null) { try { result = securityClientField.ActivateSession(connection, authentication, timeout); } catch (Exception ex) { SvcTrace.DiagControl.TraceEvent(TraceEventType.Warning, 0, "ASBSecurity Proxy: CallActivate delegate caught exception {0}", ex.Message); if (ex.InnerException != null) { SvcTrace.DiagControl.TraceEvent(TraceEventType.Warning, 0, " {0}", ex.InnerException.Message); } } } return result; } public ArchestrAResult CallDisconnect(ConnectionId id) { ArchestrAResult result = ResultFactory.MakeResult(ArchestrAError.OperationFailed, 0); if (securityClientField != null) { try { result = securityClientField.Disconnect(id); } catch (CommunicationException ex) { SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, "ASBSecurity Proxy: ConnectionDelegate::CallDisconnect delegate caught communication exception {0}", ex.Message); if (ex.InnerException != null) { SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, " {0}", ex.InnerException.Message); } } catch (Exception ex2) { SvcTrace.DiagControl.TraceEvent(TraceEventType.Warning, 0, "ASBSecurity Proxy: ConnectionDelegate::CallDisconnect delegate caught exception {0}", ex2.Message); if (ex2.InnerException != null) { SvcTrace.DiagControl.TraceEvent(TraceEventType.Warning, 0, " {0}", ex2.InnerException.Message); } } } return result; } } private string m_SrbNodeName; private ChannelFactory securityProxyFactory; private IManageASBSecurity manageASBSecurityClient; private object connectionLock; private ManualResetEvent connectionEstablishedEvent; private ClientAuthentication authenticater; private ConnectionId securityConnectionId; private List accumulatedErrorMsg; private object errorMsgLock; public bool SecureSessionEstablished => authenticater.SecureSessionEstablished; public CommunicationState State { get { if (manageASBSecurityClient != null) { return ((IClientChannel)manageASBSecurityClient).State; } return CommunicationState.Closed; } } public string SRNodeName => m_SrbNodeName; public ManageASBSecurityProxy(string SrNode) { if (string.IsNullOrEmpty(SrNode)) { RegistryHandler.GetSrNode(out SrNode); } m_SrbNodeName = SrNode; securityProxyFactory = null; manageASBSecurityClient = null; connectionLock = new object(); securityConnectionId = default(ConnectionId); } public bool Connect(string passphrase, out string errorMessage) { SecureCommunicationModes secureCommunicationMode = RegistryHandler.SecureCommunicationMode; SvcTrace.DiagControl.TraceEvent(TraceEventType.Information, 0, string.Format(CultureInfo.CurrentCulture, "Establishing connection for mode: {0}", new object[1] { secureCommunicationMode })); bool flag; switch (secureCommunicationMode) { case SecureCommunicationModes.Required: flag = ConnectInternal(passphrase, secured: true, out errorMessage); break; case SecureCommunicationModes.Preferred: flag = ConnectInternal(passphrase, secured: true, out errorMessage); if (!flag) { SvcTrace.DiagControl.TraceEvent(TraceEventType.Information, 0, string.Format(CultureInfo.CurrentCulture, "Attempting the fallback connection for mode: {0}, reason: {1}", new object[2] { secureCommunicationMode, errorMessage })); flag = ConnectInternal(passphrase, secured: false, out errorMessage); } break; default: flag = ConnectInternal(passphrase, secured: false, out errorMessage); break; } if (flag) { SvcTrace.DiagControl.TraceEvent(TraceEventType.Information, 0, string.Format(CultureInfo.CurrentCulture, "Successfully connected for mode: {0}", new object[1] { secureCommunicationMode })); } return flag; } private bool ConnectInternal(string passphrase, bool secured, out string errorMessage) { SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, "ASBSecurity Proxy: Connect called, with {0} passphrase", string.IsNullOrEmpty(passphrase) ? "default" : "specific"); errorMessage = string.Empty; EndpointDiscoveryMetadata endpointDiscoveryMetadata = null; FindResponse findResponse = FindPermanentEndPoint(m_SrbNodeName, useLocalHost: true, secured); if (findResponse == null || findResponse.Endpoints == null || findResponse.Endpoints.Count == 0) { findResponse = FindPermanentEndPoint(m_SrbNodeName, useLocalHost: false, secured); } if (findResponse != null && findResponse.Endpoints != null && findResponse.Endpoints.Count != 0) { Random random = new Random(DateTime.Now.Millisecond); endpointDiscoveryMetadata = findResponse.Endpoints[random.Next(findResponse.Endpoints.Count)]; } else if (findResponse != null) { if (findResponse.Endpoints != null) { if (findResponse.Endpoints.Count == 0) { SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, "ASBSecurity Proxy: Connect found no endpoints for IManageASBSecurity on the SR node {0}", m_SrbNodeName); } } else { SvcTrace.DiagException.TraceEvent(TraceEventType.Warning, 0, "ASBSecurity Proxy: Connect FindResponse has a null Endpoints member finding IManageASBSecurity on the SR node {0}", m_SrbNodeName); } } else { SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, "ASBSecurity Proxy: Connect null FindResponse finding IManageASBSecurity on the SR node {0}", m_SrbNodeName); } if (string.IsNullOrEmpty(passphrase)) { errorMessage = RegistryHandler.GetSolutionPassphrase(string.Empty, out passphrase); if (!string.IsNullOrEmpty(errorMessage)) { SvcTrace.DiagControl.TraceEvent(TraceEventType.Warning, 0, "ASBSecurity Proxy: Connect found problem getting solution passphrase: {0}", errorMessage); return false; } } if (endpointDiscoveryMetadata == null) { return false; } return ConnectParallel(passphrase, endpointDiscoveryMetadata.ListenUris, out errorMessage); } public bool Connect(string passphrase, string temporaryEndpoint, out string errorMessage) { SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, "ASBSecurity Proxy: Connect called, with {0} passphrase and temporary endpoint {1}", string.IsNullOrEmpty(passphrase) ? "default" : "specific", temporaryEndpoint); errorMessage = string.Empty; List list = new List(); UriBuilder uriBuilder = new UriBuilder(temporaryEndpoint); list.Add(uriBuilder.Uri); if (uriBuilder.Port > 0) { uriBuilder.Port = -1; list.Add(uriBuilder.Uri); } return ConnectParallel(passphrase, list, out errorMessage); } private bool ConnectParallel(string passphrase, IEnumerable endpoints, out string errorMessage) { SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, "ASBSecurity Proxy: ConnectParallel called, with {0} endpoints", endpoints.Count()); foreach (Uri endpoint in endpoints) { SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, " {0}", endpoint); } errorMessage = string.Empty; bool result = false; lock (connectionLock) { securityProxyFactory = null; manageASBSecurityClient = null; securityConnectionId = default(ConnectionId); authenticater = null; } if (manageASBSecurityClient != null) { if (((IClientChannel)manageASBSecurityClient).State == CommunicationState.Opened || ((IClientChannel)manageASBSecurityClient).State == CommunicationState.Opening) { ((IClientChannel)manageASBSecurityClient).Close(); } else if (((IClientChannel)manageASBSecurityClient).State == CommunicationState.Faulted) { ((IClientChannel)manageASBSecurityClient).Abort(); } manageASBSecurityClient = null; } if (securityProxyFactory != null) { if (securityProxyFactory.State == CommunicationState.Opened || securityProxyFactory.State == CommunicationState.Opening) { securityProxyFactory.Close(); } securityProxyFactory = null; } connectionEstablishedEvent = new ManualResetEvent(initialState: false); accumulatedErrorMsg = new List(); errorMsgLock = new object(); foreach (Uri endpoint2 in endpoints) { string SelectedEndpointUri = endpoint2.ToString(); Task.Factory.StartNew(delegate { bool num = SelectedEndpointUri.EndsWith("/ASBSecurityS", StringComparison.OrdinalIgnoreCase) || SelectedEndpointUri.EndsWith("/RegistrationS", StringComparison.OrdinalIgnoreCase) || SelectedEndpointUri.EndsWith("/PairingS", StringComparison.OrdinalIgnoreCase); EndpointAddress systemAuthenticationEndpoint = new EndpointAddress(SelectedEndpointUri); Binding binding = SvcUtilities.GetBinding(SelectedEndpointUri); if (num) { Uri uri = new Uri(SelectedEndpointUri); IPHostEntry hostEntry = Dns.GetHostEntry(uri.Host); systemAuthenticationEndpoint = new EndpointAddress(uri, EndpointIdentity.CreateDnsIdentity(hostEntry.HostName)); binding = SvcUtilities.GetBinding(SelectedEndpointUri, NetTcpBindingSecurityMode.CertificateEncryption); } SvcTrace.DiagControl.TraceEvent(TraceEventType.Information, 0, "Try connecting with IManageASBSecurity endpoint {0}", SelectedEndpointUri); if (InternalConnect(systemAuthenticationEndpoint, binding, passphrase, out var errorMessage2)) { SvcTrace.DiagControl.TraceEvent(TraceEventType.Information, 0, "Connected to IManageASBSecurity endpoint {0}", SelectedEndpointUri); } else if (!string.IsNullOrEmpty(errorMessage2)) { lock (errorMsgLock) { accumulatedErrorMsg.Add(errorMessage2); } } }); } connectionEstablishedEvent.WaitOne(20000); connectionEstablishedEvent.Dispose(); connectionEstablishedEvent = null; lock (connectionLock) { result = true; if (securityProxyFactory == null) { foreach (string item in accumulatedErrorMsg) { SvcTrace.DiagControl.TraceEvent(TraceEventType.Warning, 0, item); } result = false; } } return result; } private bool InternalConnect(EndpointAddress systemAuthenticationEndpoint, Binding binding, string passphrase, out string errorMessage) { bool result = false; errorMessage = string.Empty; if (systemAuthenticationEndpoint != null && binding != null) { try { ChannelFactory channelFactory = new ChannelFactory(binding, systemAuthenticationEndpoint); if (binding is NetTcpBinding netTcpBinding && netTcpBinding.Security.Mode == SecurityMode.Transport && channelFactory.Credentials != null) { channelFactory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.ChainTrust; channelFactory.Credentials.ServiceCertificate.Authentication.RevocationMode = X509RevocationMode.NoCheck; } channelFactory.Open(); IManageASBSecurity manageASBSecurity = channelFactory.CreateChannel(); if (manageASBSecurity != null) { ((IClientChannel)manageASBSecurity).Open(); if (manageASBSecurity != null && ((IClientChannel)manageASBSecurity).State != CommunicationState.Faulted) { ConnectionDelegate connectionDelegate = new ConnectionDelegate(manageASBSecurity); ClientAuthentication clientAuthentication = new ClientAuthentication(); if (!string.IsNullOrEmpty(passphrase)) { clientAuthentication.DH_passphrase = passphrase; } clientAuthentication.EstablishSecureSession("SystemAuthenticationService", "localdomain", Environment.MachineName, connectionDelegate.CallConnect, connectionDelegate.CallActivate); if (clientAuthentication.SecureSessionEstablished) { bool flag = false; lock (connectionLock) { if (securityProxyFactory == null) { flag = true; securityProxyFactory = channelFactory; manageASBSecurityClient = manageASBSecurity; securityConnectionId = clientAuthentication.connectionId; authenticater = clientAuthentication; if (connectionEstablishedEvent != null) { connectionEstablishedEvent.Set(); } } } if (!flag) { clientAuthentication.DisconnectSecureSession(connectionDelegate.CallDisconnect); if (manageASBSecurity != null) { ((IClientChannel)manageASBSecurity).Close(); ((IClientChannel)manageASBSecurity).Dispose(); } } result = flag; } else { if (manageASBSecurity != null) { ((IClientChannel)manageASBSecurity).Abort(); ((IClientChannel)manageASBSecurity).Dispose(); } if (!string.IsNullOrEmpty(authenticater.ReasonSecureSessionNotEstablished)) { errorMessage = "ManageASBSecurityProxy failed to establish secure session: " + authenticater.ReasonSecureSessionNotEstablished; } else { errorMessage = "ManageASBSecurityProxy failed to establish secure session"; } } } else { errorMessage = "ManageASBSecurityProxy: InternalConnect could not connect with proxy object"; } } } catch (CommunicationException ex) { errorMessage = "ManageASBSecurityProxy caught CommunicationException opening channel: " + ex.Message; if (ex.InnerException != null) { errorMessage += ex.InnerException.Message; } } catch (TimeoutException ex2) { errorMessage = "ManageASBSecurityProxy caught TimeoutException opening channel: " + ex2.Message; if (ex2.InnerException != null) { errorMessage += ex2.InnerException.Message; } } catch (Exception ex3) { SvcTrace.DiagException.TraceEvent(TraceEventType.Warning, 0, "ManageASBSecurityProxy: InternalConnect caught exception {0}, {1}", ex3.Message, ex3.StackTrace); if (ex3.InnerException != null) { SvcTrace.DiagException.TraceEvent(TraceEventType.Warning, 0, " {0}", ex3.InnerException.Message); } errorMessage = "ManageASBSecurityProxy: InternalConnect caught exception: " + ex3.Message; result = false; } } return result; } public void Disconnect() { SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, "Proxy Disconnect called, client connection closing"); if (authenticater != null) { authenticater.DisconnectSecureSession(CallDisconnect); authenticater = null; } if (manageASBSecurityClient != null) { ((IClientChannel)manageASBSecurityClient).Close(); manageASBSecurityClient = null; } if (securityProxyFactory != null) { if (securityProxyFactory.State == CommunicationState.Opened || securityProxyFactory.State == CommunicationState.Opening) { securityProxyFactory.Close(); } securityProxyFactory = null; } } public ArchestrAResult EnumerateSolutions(out string[] SolutionNames) { if (manageASBSecurityClient != null) { return manageASBSecurityClient.EnumerateSolutions(out SolutionNames, securityConnectionId); } SolutionNames = new string[0]; return ResultFactory.MakeResult(ArchestrAError.InvalidConnectionId, 0); } public ArchestrAResult GetRegistrationEndpointStatus(out StatusTemporaryEndpoint[] ConfigurationData) { if (manageASBSecurityClient != null) { return manageASBSecurityClient.GetRegistrationEndpointStatus(out ConfigurationData, securityConnectionId); } ConfigurationData = new StatusTemporaryEndpoint[0]; return ResultFactory.MakeResult(ArchestrAError.InvalidConnectionId, 0); } public ArchestrAResult GetServiceBusPlatformConfiguration(out SystemAuthenticationASBConfiguration ConfigurationData, out string XMLExtraInfo, Guid NodeId, string SolutionName) { if (manageASBSecurityClient != null) { return manageASBSecurityClient.GetServiceBusPlatformConfiguration(out ConfigurationData, out XMLExtraInfo, securityConnectionId, NodeId, SolutionName); } ConfigurationData = default(SystemAuthenticationASBConfiguration); XMLExtraInfo = string.Empty; return ResultFactory.MakeResult(ArchestrAError.InvalidConnectionId, 0); } public ArchestrAResult QueryExtraInfoChanges(out string XMLExtraInfo, string NodeId) { if (manageASBSecurityClient != null) { return manageASBSecurityClient.QueryExtraInfoChanges(out XMLExtraInfo, securityConnectionId, NodeId); } XMLExtraInfo = string.Empty; return ResultFactory.MakeResult(ArchestrAError.InvalidConnectionId, 0); } public ArchestrAResult RegisterServiceBusEnable(SystemAuthenticationASBConfiguration ConfigurationData) { if (manageASBSecurityClient != null) { return manageASBSecurityClient.RegisterServiceBusEnable(securityConnectionId, ConfigurationData); } return ResultFactory.MakeResult(ArchestrAError.InvalidConnectionId, 0); } public ArchestrAResult RegisterServiceBusPlatformId(Guid NodeId) { if (manageASBSecurityClient != null) { return manageASBSecurityClient.RegisterServiceBusPlatformId(securityConnectionId, NodeId); } return ResultFactory.MakeResult(ArchestrAError.InvalidConnectionId, 0); } public ArchestrAResult RegisterSystemAuthenticationConfiguration(SystemAuthenticationASBConfiguration ConfigurationData, string XMLExtraInfo) { if (manageASBSecurityClient != null) { return manageASBSecurityClient.RegisterSystemAuthenticationConfiguration(securityConnectionId, ConfigurationData, XMLExtraInfo); } return ResultFactory.MakeResult(ArchestrAError.InvalidConnectionId, 0); } public ArchestrAResult UnregisterSystemAuthenticationConfiguration(string SolutionName) { if (manageASBSecurityClient != null) { return manageASBSecurityClient.UnregisterSystemAuthenticationConfiguration(securityConnectionId, SolutionName); } return ResultFactory.MakeResult(ArchestrAError.InvalidConnectionId, 0); } private ArchestrAResult CallConnect(out Connection connection, string application, string domain, string host, ArchestrAServices.Contract.PublicKey clientToken) { if (manageASBSecurityClient != null) { try { return manageASBSecurityClient.Connect(out connection, application, domain, host, clientToken); } catch (Exception ex) { SvcTrace.DiagControl.TraceEvent(TraceEventType.Warning, 0, "ASBSecurity Proxy: CallConnect delegate caught exception {0}", ex.Message); if (ex.InnerException != null) { SvcTrace.DiagControl.TraceEvent(TraceEventType.Warning, 0, " {0}", ex.InnerException.Message); } } } connection = default(Connection); return ResultFactory.MakeResult(ArchestrAError.OperationFailed, 0); } private ArchestrAResult CallActivate(ConnectionId connection, ConnectionAuthenticationData authentication, ulong timeout) { ArchestrAResult result = ResultFactory.MakeResult(ArchestrAError.OperationFailed, 0); if (manageASBSecurityClient != null) { try { result = manageASBSecurityClient.ActivateSession(connection, authentication, timeout); } catch (Exception ex) { SvcTrace.DiagControl.TraceEvent(TraceEventType.Warning, 0, "ASBSecurity Proxy: CallActivate delegate caught exception {0}", ex.Message); if (ex.InnerException != null) { SvcTrace.DiagControl.TraceEvent(TraceEventType.Warning, 0, " {0}", ex.InnerException.Message); } } } return result; } private ArchestrAResult CallDisconnect(ConnectionId id) { ArchestrAResult result = ResultFactory.MakeResult(ArchestrAError.OperationFailed, 0); if (manageASBSecurityClient != null) { try { result = manageASBSecurityClient.Disconnect(securityConnectionId); } catch (CommunicationException ex) { SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, "ASBSecurity Proxy: CallDisconnect delegate caught exception {0}", ex.Message); if (ex.InnerException != null) { SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, " {0}", ex.InnerException.Message); } } catch (Exception ex2) { SvcTrace.DiagControl.TraceEvent(TraceEventType.Warning, 0, "ASBSecurity Proxy: CallDisconnect delegate caught exception {0}", ex2.Message); if (ex2.InnerException != null) { SvcTrace.DiagControl.TraceEvent(TraceEventType.Warning, 0, " {0}", ex2.InnerException.Message); } } } return result; } private static FindResponse FindPermanentEndPoint(string srNode, bool useLocalHost, bool secured) { FindResponse result = null; try { Uri uri = RegistryHandler.MakeLDSProbeEndpointAddress(useLocalHost ? "localhost" : srNode); SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, $"FindPermanentEndPoint generated LDS endpoint for SR Node {srNode}: {uri.AbsoluteUri}"); using DiscoveryClient discoveryClient = new DiscoveryClient(new DiscoveryEndpoint(SvcUtilities.GetBinding(uri.AbsoluteUri), new EndpointAddress(uri))); discoveryClient.Open(); if (discoveryClient.InnerChannel.State == CommunicationState.Opened) { result = discoveryClient.Find(GenerateCriteria(srNode, secured)); discoveryClient.Close(); } else { discoveryClient.InnerChannel.Abort(); } } catch (TargetInvocationException ex) { SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, $"FindPermanentEndPoint({srNode}) TargetInvocationException: {ex.Message}"); if (ex.InnerException != null) { SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, $" {ex.InnerException.Message}"); } result = null; } catch (ObjectDisposedException ex2) { SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, $"FindPermanentEndPoint({srNode}) ObjectDisposedException: {ex2.Message}"); if (ex2.InnerException != null) { SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, $" {ex2.InnerException.Message}"); } result = null; } catch (Exception ex3) { SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, $"FindPermanentEndPoint({srNode}) Exception: {ex3.Message}"); if (ex3.InnerException != null) { SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, $" {ex3.InnerException.Message}"); } result = null; } return result; } private static FindCriteria GenerateCriteria(string srNode, bool secure) { FindCriteria findCriteria = new FindCriteria(); XmlQualifiedName item = new XmlQualifiedName(secure ? "IManageASBSecurityS" : "IManageASBSecurity", "http://ArchestrAServices.Contract"); findCriteria.ContractTypeNames.Add(item); findCriteria.Scopes.Clear(); findCriteria.Scopes.Add(new Uri("archestra://coreservices/nodename/" + srNode.ToLower())); return findCriteria; } protected virtual void Dispose(bool disposing) { if (!disposing) { return; } if (manageASBSecurityClient != null) { if (authenticater.SecureSessionEstablished) { Disconnect(); } IClientChannel obj = manageASBSecurityClient as IClientChannel; obj?.Close(); obj?.Dispose(); } connectionEstablishedEvent?.Close(); connectionEstablishedEvent = null; } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } }