//----------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- namespace System.ServiceModel.Description { using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Configuration; using System.Diagnostics; using System.IdentityModel.Selectors; using System.Reflection; using System.Runtime; using System.Runtime.Diagnostics; using System.Security; using System.Security.Cryptography.X509Certificates; using System.ServiceModel.Activation; using System.ServiceModel.Channels; using System.ServiceModel.Configuration; using System.ServiceModel.Diagnostics; using System.ServiceModel.Security; using System.Text; using System.Runtime.CompilerServices; class ConfigLoader { //resolvedBindings will be initialized to null on all threads //ThreadStatic gives each thread own copy of object [ThreadStatic] static List resolvedBindings; //resolvedEndpoints will be initialized to null on all threads //ThreadStatic gives each thread own copy of object [ThreadStatic] static List resolvedEndpoints; static readonly object[] emptyObjectArray = new object[] { }; static readonly Type[] emptyTypeArray = new Type[] { }; Dictionary bindingTable; IContractResolver contractResolver; ContextInformation configurationContext; public ConfigLoader() : this((IContractResolver)null) { } public ConfigLoader(ContextInformation configurationContext) : this((IContractResolver)null) { this.configurationContext = configurationContext; } public ConfigLoader(IContractResolver contractResolver) { this.contractResolver = contractResolver; this.bindingTable = new Dictionary(); } [Fx.Tag.SecurityNote(Critical = "Handles config objects, which should not be leaked.", Safe = "Doesn't leak config objects out of SecurityCritical code.")] [SecuritySafeCritical] internal static EndpointIdentity LoadIdentity(IdentityElement element) { EndpointIdentity identity = null; PropertyInformationCollection properties = element.ElementInformation.Properties; if (properties[ConfigurationStrings.UserPrincipalName].ValueOrigin != PropertyValueOrigin.Default) { identity = EndpointIdentity.CreateUpnIdentity(element.UserPrincipalName.Value); } else if (properties[ConfigurationStrings.ServicePrincipalName].ValueOrigin != PropertyValueOrigin.Default) { identity = EndpointIdentity.CreateSpnIdentity(element.ServicePrincipalName.Value); } else if (properties[ConfigurationStrings.Dns].ValueOrigin != PropertyValueOrigin.Default) { identity = EndpointIdentity.CreateDnsIdentity(element.Dns.Value); } else if (properties[ConfigurationStrings.Rsa].ValueOrigin != PropertyValueOrigin.Default) { identity = EndpointIdentity.CreateRsaIdentity(element.Rsa.Value); } else if (properties[ConfigurationStrings.Certificate].ValueOrigin != PropertyValueOrigin.Default) { X509Certificate2Collection collection = new X509Certificate2Collection(); collection.Import(Convert.FromBase64String(element.Certificate.EncodedValue)); if (collection.Count == 0) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.UnableToLoadCertificateIdentity))); } // We assume the first certificate in the list is the primary // certificate. X509Certificate2 primaryCert = collection[0]; collection.RemoveAt(0); identity = EndpointIdentity.CreateX509CertificateIdentity(primaryCert, collection); } else if (properties[ConfigurationStrings.CertificateReference].ValueOrigin != PropertyValueOrigin.Default) { X509CertificateStore store = new X509CertificateStore(element.CertificateReference.StoreName, element.CertificateReference.StoreLocation); X509Certificate2Collection collection = null; try { store.Open(OpenFlags.ReadOnly); collection = store.Find(element.CertificateReference.X509FindType, element.CertificateReference.FindValue, false); if (collection.Count == 0) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.UnableToLoadCertificateIdentity))); } // Just select the first certificate. X509Certificate2 primaryCert = new X509Certificate2(collection[0]); if (element.CertificateReference.IsChainIncluded) { // Build the chain. X509Chain chain = new X509Chain(); chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; chain.Build(primaryCert); identity = EndpointIdentity.CreateX509CertificateIdentity(chain); } else { identity = EndpointIdentity.CreateX509CertificateIdentity(primaryCert); } } finally { SecurityUtils.ResetAllCertificates(collection); store.Close(); } } return identity; } [Fx.Tag.SecurityNote(Critical = "Handles config objects, which should not be leaked.", Safe = "Doesn't leak config objects out of SecurityCritical code.")] [SecuritySafeCritical] internal void LoadChannelBehaviors(ServiceEndpoint serviceEndpoint, string configurationName) { ServiceEndpoint standardEndpoint; bool wildcard = IsWildcardMatch(configurationName); ChannelEndpointElement channelElement = LookupChannel(this.configurationContext, configurationName, serviceEndpoint.Contract, null, wildcard, false, out standardEndpoint); if (channelElement == null) { if (wildcard) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxConfigContractNotFound, serviceEndpoint.Contract.ConfigurationName))); } else { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxConfigChannelConfigurationNotFound, configurationName, serviceEndpoint.Contract.ConfigurationName))); } } if (serviceEndpoint.Binding == null && !string.IsNullOrEmpty(channelElement.Binding)) { serviceEndpoint.Binding = ConfigLoader.LookupBinding(channelElement.Binding, channelElement.BindingConfiguration, ConfigurationHelpers.GetEvaluationContext(channelElement)); } if (serviceEndpoint.Address == null && channelElement.Address != null && channelElement.Address.OriginalString.Length > 0) { serviceEndpoint.Address = new EndpointAddress(channelElement.Address, LoadIdentity(channelElement.Identity), channelElement.Headers.Headers); } CommonBehaviorsSection commonBehaviors = ConfigLoader.LookupCommonBehaviors(ConfigurationHelpers.GetEvaluationContext(channelElement)); if (commonBehaviors != null && commonBehaviors.EndpointBehaviors != null) { LoadBehaviors(commonBehaviors.EndpointBehaviors, serviceEndpoint.Behaviors, true/*commonBehaviors*/); } EndpointBehaviorElement behaviorElement = ConfigLoader.LookupEndpointBehaviors(channelElement.BehaviorConfiguration, ConfigurationHelpers.GetEvaluationContext(channelElement)); if (behaviorElement != null) { LoadBehaviors(behaviorElement, serviceEndpoint.Behaviors, false/*commonBehaviors*/); } } [Fx.Tag.SecurityNote(Critical = "Handles config objects, which should not be leaked.", Safe = "Doesn't leak config objects out of SecurityCritical code.")] [SecuritySafeCritical] internal void LoadCommonClientBehaviors(ServiceEndpoint serviceEndpoint) { // just load commonBehaviors CommonBehaviorsSection commonBehaviors = ConfigLoader.LookupCommonBehaviors(this.configurationContext); if (commonBehaviors != null && commonBehaviors.EndpointBehaviors != null) { LoadBehaviors(commonBehaviors.EndpointBehaviors, serviceEndpoint.Behaviors, true/*commonBehaviors*/); } } [Fx.Tag.SecurityNote(Critical = "Handles config objects, which should not be leaked.", Safe = "Doesn't leak config objects out of SecurityCritical code.")] [SecuritySafeCritical] static void LoadBehaviors(ServiceModelExtensionCollectionElement behaviorElement, KeyedByTypeCollection behaviors, bool commonBehaviors) { Nullable isPT = new Nullable(); KeyedByTypeCollection tempBehaviors = new KeyedByTypeCollection(); for (int i = 0; i < behaviorElement.Count; i++) { BehaviorExtensionElement behaviorExtension = behaviorElement[i]; object behaviorObject = behaviorExtension.CreateBehavior(); if (behaviorObject == null) { continue; } Type type = behaviorObject.GetType(); if (!typeof(T).IsAssignableFrom(type)) { TraceBehaviorWarning(behaviorExtension, TraceCode.SkipBehavior, SR.GetString(SR.TraceCodeSkipBehavior), type, typeof(T)); continue; } if (commonBehaviors) { if (ShouldSkipCommonBehavior(type, ref isPT)) { TraceBehaviorWarning(behaviorExtension, TraceCode.SkipBehavior, SR.GetString(SR.TraceCodeSkipBehavior), type, typeof(T)); continue; } } // if, at this scope, we try to add same type of behavior twice, throw tempBehaviors.Add((T)behaviorObject); // but if the same type of behavior was present from an old scope, just remove the old one if (behaviors.Contains(type)) { TraceBehaviorWarning(behaviorExtension, TraceCode.RemoveBehavior, SR.GetString(SR.TraceCodeRemoveBehavior), type, typeof(T)); behaviors.Remove(type); } behaviors.Add((T)behaviorObject); } } // special processing for common behaviors: // if: // 1. the behavior type (returned from the config element) is in a signed, non-APTCA assembly // 2. the caller stack does not have ConfigurationPermission(Unrestricted) // .. exclude the behavior from the collection and trace a warning [Fx.Tag.SecurityNote(Critical = "Calls SecurityCritical helpers, makes a security decision.")] [SecurityCritical] static bool ShouldSkipCommonBehavior(Type behaviorType, ref Nullable isPT) { bool skip = false; if (!isPT.HasValue) { if (!PartialTrustHelpers.IsTypeAptca(behaviorType)) { isPT = !ThreadHasConfigurationPermission(); skip = isPT.Value; } } else if (isPT.Value) { skip = !PartialTrustHelpers.IsTypeAptca(behaviorType); } return skip; } [Fx.Tag.SecurityNote(Critical = "Handles config objects, which should not be leaked.", Safe = "Doesn't leak config objects out of SecurityCritical code.")] [SecuritySafeCritical] static void TraceBehaviorWarning(BehaviorExtensionElement behaviorExtension, int traceCode, string traceDescription, Type type, Type behaviorType) { if (DiagnosticUtility.ShouldTraceWarning) { Hashtable h = new Hashtable(3) { { "ConfigurationElementName", behaviorExtension.ConfigurationElementName }, { "ConfigurationType", type.AssemblyQualifiedName }, { "BehaviorType", behaviorType.AssemblyQualifiedName } }; TraceUtility.TraceEvent(TraceEventType.Warning, traceCode, traceDescription, new DictionaryTraceRecord(h), null, null); } } [Fx.Tag.SecurityNote(Critical = "Handles config objects, which should not be leaked.", Safe = "Doesn't leak config objects out of SecurityCritical code.")] [SecuritySafeCritical] static void LoadChannelBehaviors(EndpointBehaviorElement behaviorElement, KeyedByTypeCollection channelBehaviors) { if (behaviorElement != null) { LoadBehaviors(behaviorElement, channelBehaviors, false/*commonBehaviors*/ ); } } [Fx.Tag.SecurityNote(Critical = "Handles config objects, which should not be leaked.", Safe = "Doesn't leak config objects out of SecurityCritical code.")] [SecuritySafeCritical] internal static void LoadChannelBehaviors(string behaviorName, ContextInformation context, KeyedByTypeCollection channelBehaviors) { LoadChannelBehaviors( LookupEndpointBehaviors(behaviorName, context), channelBehaviors ); } [Fx.Tag.SecurityNote(Critical = "Handles config objects, which should not be leaked.", Safe = "Doesn't leak config objects out of SecurityCritical code.")] [SecuritySafeCritical] internal static Collection LoadWsdlImporters(WsdlImporterElementCollection wsdlImporterElements, ContextInformation context) { Collection wsdlImporters = new Collection(); foreach (WsdlImporterElement wsdlImporterElement in wsdlImporterElements) { // Verify that the type implements IWsdlImporter Type wsdlImporterType = Type.GetType(wsdlImporterElement.Type, true, true); if (!typeof(IWsdlImportExtension).IsAssignableFrom(wsdlImporterType)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.InvalidWsdlExtensionTypeInConfig, wsdlImporterType.AssemblyQualifiedName))); } // Verify that the type has a default constructor ConstructorInfo constructorInfo = wsdlImporterType.GetConstructor(emptyTypeArray); if (constructorInfo == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.WsdlExtensionTypeRequiresDefaultConstructor, wsdlImporterType.AssemblyQualifiedName))); } wsdlImporters.Add((IWsdlImportExtension)constructorInfo.Invoke(emptyObjectArray)); } return wsdlImporters; } [Fx.Tag.SecurityNote(Critical = "Handles config objects, which should not be leaked.", Safe = "Doesn't leak config objects out of SecurityCritical code.")] [SecuritySafeCritical] internal static Collection LoadPolicyImporters(PolicyImporterElementCollection policyImporterElements, ContextInformation context) { Collection policyImporters = new Collection(); foreach (PolicyImporterElement policyImporterElement in policyImporterElements) { // Verify that the type implements IPolicyImporter Type policyImporterType = Type.GetType(policyImporterElement.Type, true, true); if (!typeof(IPolicyImportExtension).IsAssignableFrom(policyImporterType)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.InvalidPolicyExtensionTypeInConfig, policyImporterType.AssemblyQualifiedName))); } // Verify that the type has a default constructor ConstructorInfo constructorInfo = policyImporterType.GetConstructor(emptyTypeArray); if (constructorInfo == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.PolicyExtensionTypeRequiresDefaultConstructor, policyImporterType.AssemblyQualifiedName))); } policyImporters.Add((IPolicyImportExtension)constructorInfo.Invoke(emptyObjectArray)); } return policyImporters; } [Fx.Tag.SecurityNote(Critical = "Handles config objects, which should not be leaked.", Safe = "Doesn't leak config objects out of SecurityCritical code.")] [SecuritySafeCritical] internal static EndpointAddress LoadEndpointAddress(EndpointAddressElementBase element) { return new EndpointAddress(element.Address, LoadIdentity(element.Identity), element.Headers.Headers); } [Fx.Tag.SecurityNote(Critical = "Handles config objects, which should not be leaked.", Safe = "Doesn't leak config objects out of SecurityCritical code.")] [SecuritySafeCritical] public void LoadHostConfig(ServiceElement serviceElement, ServiceHostBase host, System.Action addBaseAddress) { HostElement hostElement = serviceElement.Host; if (hostElement != null) { if (!AspNetEnvironment.Enabled) { foreach (BaseAddressElement bae in hostElement.BaseAddresses) { string cookedAddress = null; string rawAddress = bae.BaseAddress; int colonIndex = rawAddress.IndexOf(':'); if (colonIndex != -1 && rawAddress.Length >= colonIndex + 4) { if (rawAddress[colonIndex + 1] == '/' && rawAddress[colonIndex + 2] == '/' && rawAddress[colonIndex + 3] == '*') { string beforeAsterisk = rawAddress.Substring(0, colonIndex + 3); string rest = rawAddress.Substring(colonIndex + 4); StringBuilder sb = new StringBuilder(beforeAsterisk); sb.Append(System.Net.Dns.GetHostName()); sb.Append(rest); cookedAddress = sb.ToString(); } } if (cookedAddress == null) { cookedAddress = rawAddress; } Uri uri; if (!Uri.TryCreate(cookedAddress, UriKind.Absolute, out uri)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.BaseAddressMustBeAbsolute))); } addBaseAddress(uri); } } HostTimeoutsElement hte = hostElement.Timeouts; if (hte != null) { if (hte.OpenTimeout != TimeSpan.Zero) { host.OpenTimeout = hte.OpenTimeout; } if (hte.CloseTimeout != TimeSpan.Zero) { host.CloseTimeout = hte.CloseTimeout; } } } } [Fx.Tag.SecurityNote(Critical = "Handles config objects, which should not be leaked.", Safe = "Doesn't leak config objects out of SecurityCritical code.")] [SecuritySafeCritical] public void LoadServiceDescription(ServiceHostBase host, ServiceDescription description, ServiceElement serviceElement, System.Action addBaseAddress, bool skipHost = false) { CommonBehaviorsSection commonBehaviors = ConfigLoader.LookupCommonBehaviors( serviceElement == null ? null : ConfigurationHelpers.GetEvaluationContext(serviceElement)); if (commonBehaviors != null && commonBehaviors.ServiceBehaviors != null) { LoadBehaviors(commonBehaviors.ServiceBehaviors, description.Behaviors, true/*commonBehaviors*/); } string behaviorConfigurationName = ConfigurationStrings.DefaultName; if (serviceElement != null) { if (!skipHost) { this.LoadHostConfig(serviceElement, host, addBaseAddress); } behaviorConfigurationName = serviceElement.BehaviorConfiguration; } ServiceBehaviorElement behaviorElement = ConfigLoader.LookupServiceBehaviors(behaviorConfigurationName, ConfigurationHelpers.GetEvaluationContext(serviceElement)); if (behaviorElement != null) { LoadBehaviors(behaviorElement, description.Behaviors, false/*commonBehaviors*/); } ServiceHostBase.ServiceAndBehaviorsContractResolver resolver = this.contractResolver as ServiceHostBase.ServiceAndBehaviorsContractResolver; if (resolver != null) { resolver.AddBehaviorContractsToResolver(description.Behaviors); } if (serviceElement != null) { foreach (ServiceEndpointElement endpointElement in serviceElement.Endpoints) { if (String.IsNullOrEmpty(endpointElement.Kind)) { ContractDescription contract = LookupContract(endpointElement.Contract, description.Name); // binding Binding binding; string bindingKey = endpointElement.Binding + ":" + endpointElement.BindingConfiguration; if (bindingTable.TryGetValue(bindingKey, out binding) == false) { binding = ConfigLoader.LookupBinding(endpointElement.Binding, endpointElement.BindingConfiguration, ConfigurationHelpers.GetEvaluationContext(serviceElement)); bindingTable.Add(bindingKey, binding); } if (!string.IsNullOrEmpty(endpointElement.BindingName)) { binding.Name = endpointElement.BindingName; } if (!string.IsNullOrEmpty(endpointElement.BindingNamespace)) { binding.Namespace = endpointElement.BindingNamespace; } // address Uri address = endpointElement.Address; ServiceEndpoint serviceEndpoint; if (null == address) { serviceEndpoint = new ServiceEndpoint(contract); serviceEndpoint.Binding = binding; } else { Uri via = ServiceHost.MakeAbsoluteUri(address, binding, host.InternalBaseAddresses); serviceEndpoint = new ServiceEndpoint(contract, binding, new EndpointAddress(via, LoadIdentity(endpointElement.Identity), endpointElement.Headers.Headers)); serviceEndpoint.UnresolvedAddress = endpointElement.Address; } if (endpointElement.ListenUri != null) { serviceEndpoint.ListenUri = ServiceHost.MakeAbsoluteUri(endpointElement.ListenUri, binding, host.InternalBaseAddresses); serviceEndpoint.UnresolvedListenUri = endpointElement.ListenUri; } serviceEndpoint.ListenUriMode = endpointElement.ListenUriMode; if (!string.IsNullOrEmpty(endpointElement.Name)) { serviceEndpoint.Name = endpointElement.Name; } KeyedByTypeCollection behaviors = serviceEndpoint.Behaviors; EndpointBehaviorElement behaviorEndpointElement = ConfigLoader.LookupEndpointBehaviors(endpointElement.BehaviorConfiguration, ConfigurationHelpers.GetEvaluationContext(endpointElement)); if (behaviorEndpointElement != null) { LoadBehaviors(behaviorEndpointElement, behaviors, false/*commonBehaviors*/); } if (endpointElement.ElementInformation.Properties[ConfigurationStrings.IsSystemEndpoint].ValueOrigin != PropertyValueOrigin.Default) { serviceEndpoint.IsSystemEndpoint = endpointElement.IsSystemEndpoint; } description.Endpoints.Add(serviceEndpoint); } else { ServiceEndpoint endpoint = LookupEndpoint(endpointElement, ConfigurationHelpers.GetEvaluationContext(serviceElement), host, description); description.Endpoints.Add(endpoint); } } } } [Fx.Tag.SecurityNote(Critical = "Handles config objects, which should not be leaked.", Safe = "Doesn't leak config objects out of SecurityCritical code.")] [SecuritySafeCritical] public static void LoadDefaultEndpointBehaviors(ServiceEndpoint endpoint) { EndpointBehaviorElement behaviorEndpointElement = ConfigLoader.LookupEndpointBehaviors(ConfigurationStrings.DefaultName, ConfigurationHelpers.GetEvaluationContext(null)); if (behaviorEndpointElement != null) { LoadBehaviors(behaviorEndpointElement, endpoint.Behaviors, false); } } [Fx.Tag.SecurityNote(Critical = "Handles config objects, which should not be leaked.")] [SecurityCritical] static EndpointCollectionElement LookupEndpointCollectionElement(string endpointSectionName, ContextInformation context) { if (string.IsNullOrEmpty(endpointSectionName)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ConfigurationErrorsException(SR.GetString(SR.ConfigEndpointTypeCannotBeNullOrEmpty))); } EndpointCollectionElement endpointCollectionElement = null; if (context == null) { // If no context is passed in, assume that the caller can consume the AppDomain's // current configuration file. endpointCollectionElement = (EndpointCollectionElement)ConfigurationHelpers.UnsafeGetEndpointCollectionElement(endpointSectionName); } else { // Use the configuration file associated with the passed in context. // This may or may not be the same as the file for the current AppDomain. endpointCollectionElement = (EndpointCollectionElement)ConfigurationHelpers.UnsafeGetAssociatedEndpointCollectionElement(context, endpointSectionName); } return endpointCollectionElement; } [Fx.Tag.SecurityNote(Critical = "Handles config objects, which should not be leaked.", Safe = "Doesn't leak config objects out of SecurityCritical code.")] [SecuritySafeCritical] internal static ServiceEndpoint LookupEndpoint(string configurationName, EndpointAddress address, ContractDescription contract) { return LookupEndpoint(configurationName, address, contract, null); } [Fx.Tag.SecurityNote(Critical = "Handles config objects, which should not be leaked.", Safe = "Doesn't leak config objects out of SecurityCritical code.")] [SecuritySafeCritical] internal static ServiceEndpoint LookupEndpoint(string configurationName, EndpointAddress address, ContractDescription contract, ContextInformation configurationContext) { bool wildcard = IsWildcardMatch(configurationName); ServiceEndpoint serviceEndpoint; LookupChannel(configurationContext, configurationName, contract, address, wildcard, true, out serviceEndpoint); return serviceEndpoint; } internal static ServiceEndpoint LookupEndpoint(ChannelEndpointElement channelEndpointElement, ContextInformation context) { return LookupEndpoint(channelEndpointElement, context, null /*address*/, null /*contractDescription*/); } // This method should only return null when endpointConfiguration is specified on the ChannelEndpointElement and no ChannelEndpointElement matching the // endpointConfiguration name is found. All other error conditions should throw. [Fx.Tag.SecurityNote(Critical = "Handles config objects, which should not be leaked.", Safe = "Doesn't leak config objects out of SecurityCritical code.")] [SecuritySafeCritical] static ServiceEndpoint LookupEndpoint(ChannelEndpointElement channelEndpointElement, ContextInformation context, EndpointAddress address, ContractDescription contract) { EndpointCollectionElement endpointCollectionElement = LookupEndpointCollectionElement(channelEndpointElement.Kind, context); ServiceEndpoint retval = null; string endpointConfiguration = channelEndpointElement.EndpointConfiguration ?? String.Empty; // We are looking for a specific instance, not the default. bool configuredEndpointFound = false; // The Endpoints property is always public foreach (StandardEndpointElement standardEndpointElement in endpointCollectionElement.ConfiguredEndpoints) { if (standardEndpointElement.Name.Equals(endpointConfiguration, StringComparison.Ordinal)) { if (null == ConfigLoader.resolvedEndpoints) { ConfigLoader.resolvedEndpoints = new List(); } string resolvedEndpointID = channelEndpointElement.Kind + "/" + endpointConfiguration; if (ConfigLoader.resolvedEndpoints.Contains(resolvedEndpointID)) { ConfigurationElement configErrorElement = (ConfigurationElement)standardEndpointElement; System.Text.StringBuilder detectedCycle = new System.Text.StringBuilder(); foreach (string resolvedEndpoint in ConfigLoader.resolvedEndpoints) { detectedCycle = detectedCycle.AppendFormat("{0}, ", resolvedEndpoint); } detectedCycle = detectedCycle.Append(resolvedEndpointID); // Clear list in case application is written to handle exception // by not starting up channel, etc... ConfigLoader.resolvedEndpoints = null; throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ConfigurationErrorsException(SR.GetString(SR.ConfigEndpointReferenceCycleDetected, detectedCycle.ToString()), configErrorElement.ElementInformation.Source, configErrorElement.ElementInformation.LineNumber)); } try { CheckAccess(standardEndpointElement as IConfigurationContextProviderInternal); ConfigLoader.resolvedEndpoints.Add(resolvedEndpointID); ConfigureEndpoint(standardEndpointElement, channelEndpointElement, address, context, contract, out retval); ConfigLoader.resolvedEndpoints.Remove(resolvedEndpointID); } catch { // Clear list in case application is written to handle exception // by not starting up channel, etc... if (null != ConfigLoader.resolvedEndpoints) { ConfigLoader.resolvedBindings = null; } throw; } if (null != ConfigLoader.resolvedEndpoints && 0 == ConfigLoader.resolvedEndpoints.Count) { ConfigLoader.resolvedEndpoints = null; } configuredEndpointFound = true; } } if (!configuredEndpointFound) { // We expected to find an instance, but didn't. // Return null. retval = null; } if (retval == null && String.IsNullOrEmpty(endpointConfiguration)) { StandardEndpointElement standardEndpointElement = endpointCollectionElement.GetDefaultStandardEndpointElement(); ConfigureEndpoint(standardEndpointElement, channelEndpointElement, address, context, contract, out retval); } if (DiagnosticUtility.ShouldTraceVerbose) { Dictionary values = new Dictionary(3); values["FoundEndpoint"] = retval != null; bool usingDefault = string.IsNullOrEmpty(endpointConfiguration); int traceCode; string traceDescription; if (usingDefault) { traceCode = TraceCode.GetDefaultConfiguredEndpoint; traceDescription = SR.GetString(SR.TraceCodeGetDefaultConfiguredEndpoint); } else { traceCode = TraceCode.GetConfiguredEndpoint; traceDescription = SR.GetString(SR.TraceCodeGetConfiguredEndpoint); values["Name"] = endpointConfiguration; } values["Endpoint"] = channelEndpointElement.Kind; TraceUtility.TraceEvent(TraceEventType.Verbose, traceCode, traceDescription, new DictionaryTraceRecord(values), null, null); } if (retval != null) { retval.IsFullyConfigured = true; } return retval; } [Fx.Tag.SecurityNote(Critical = "Handles config objects, which should not be leaked.", Safe = "Doesn't leak config objects out of SecurityCritical code.")] [SecuritySafeCritical] static void ConfigureEndpoint(StandardEndpointElement standardEndpointElement, ChannelEndpointElement channelEndpointElement, EndpointAddress address, ContextInformation context, ContractDescription contract, out ServiceEndpoint endpoint) { // copy channelEndpointElement so that it can potentially be modified by the StandardEndpointElement // the properties collection of the instance seviceEndpointElement created by System.Configuration is read-only. // keeping original serviceEndpointElement so that its context can be used for the lookups. ChannelEndpointElement channelEndpointElementCopy = new ChannelEndpointElement(); channelEndpointElementCopy.Copy(channelEndpointElement); standardEndpointElement.InitializeAndValidate(channelEndpointElementCopy); endpoint = standardEndpointElement.CreateServiceEndpoint(contract); if (endpoint == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ConfigNoEndpointCreated, standardEndpointElement.GetType().AssemblyQualifiedName, (standardEndpointElement.EndpointType == null) ? string.Empty : standardEndpointElement.EndpointType.AssemblyQualifiedName))); } //binding if (!string.IsNullOrEmpty(channelEndpointElementCopy.Binding)) { endpoint.Binding = ConfigLoader.LookupBinding(channelEndpointElementCopy.Binding, channelEndpointElementCopy.BindingConfiguration, ConfigurationHelpers.GetEvaluationContext(channelEndpointElement)); } //name if (!string.IsNullOrEmpty(channelEndpointElementCopy.Name)) { endpoint.Name = channelEndpointElementCopy.Name; } //address if (address != null) { endpoint.Address = address; } if (endpoint.Address == null && channelEndpointElementCopy.Address != null && channelEndpointElementCopy.Address.OriginalString.Length > 0) { endpoint.Address = new EndpointAddress(channelEndpointElementCopy.Address, LoadIdentity(channelEndpointElementCopy.Identity), channelEndpointElementCopy.Headers.Headers); } //behaviors CommonBehaviorsSection commonBehaviors = ConfigLoader.LookupCommonBehaviors(ConfigurationHelpers.GetEvaluationContext(channelEndpointElement)); if (commonBehaviors != null && commonBehaviors.EndpointBehaviors != null) { LoadBehaviors(commonBehaviors.EndpointBehaviors, endpoint.Behaviors, true/*commonBehaviors*/); } EndpointBehaviorElement behaviorElement = ConfigLoader.LookupEndpointBehaviors(channelEndpointElementCopy.BehaviorConfiguration, ConfigurationHelpers.GetEvaluationContext(channelEndpointElement)); if (behaviorElement != null) { LoadBehaviors(behaviorElement, endpoint.Behaviors, false/*commonBehaviors*/); } standardEndpointElement.ApplyConfiguration(endpoint, channelEndpointElementCopy); } [Fx.Tag.SecurityNote(Critical = "Handles config objects, which should not be leaked.", Safe = "Doesn't leak config objects out of SecurityCritical code.")] [SecuritySafeCritical] internal ServiceEndpoint LookupEndpoint(ServiceEndpointElement serviceEndpointElement, ContextInformation context, ServiceHostBase host, ServiceDescription description, bool omitSettingEndpointAddress = false) { EndpointCollectionElement endpointCollectionElement = LookupEndpointCollectionElement(serviceEndpointElement.Kind, context); ServiceEndpoint retval = null; string endpointConfiguration = serviceEndpointElement.EndpointConfiguration ?? String.Empty; // We are looking for a specific instance, not the default. bool configuredEndpointFound = false; // The Endpoints property is always public foreach (StandardEndpointElement standardEndpointElement in endpointCollectionElement.ConfiguredEndpoints) { if (standardEndpointElement.Name.Equals(endpointConfiguration, StringComparison.Ordinal)) { if (null == ConfigLoader.resolvedEndpoints) { ConfigLoader.resolvedEndpoints = new List(); } string resolvedEndpointID = serviceEndpointElement.Kind + "/" + endpointConfiguration; if (ConfigLoader.resolvedEndpoints.Contains(resolvedEndpointID)) { ConfigurationElement configErrorElement = (ConfigurationElement)standardEndpointElement; System.Text.StringBuilder detectedCycle = new System.Text.StringBuilder(); foreach (string resolvedEndpoint in ConfigLoader.resolvedEndpoints) { detectedCycle = detectedCycle.AppendFormat("{0}, ", resolvedEndpoint); } detectedCycle = detectedCycle.Append(resolvedEndpointID); // Clear list in case application is written to handle exception // by not starting up channel, etc... ConfigLoader.resolvedEndpoints = null; throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ConfigurationErrorsException(SR.GetString(SR.ConfigEndpointReferenceCycleDetected, detectedCycle.ToString()), configErrorElement.ElementInformation.Source, configErrorElement.ElementInformation.LineNumber)); } try { CheckAccess(standardEndpointElement as IConfigurationContextProviderInternal); ConfigLoader.resolvedEndpoints.Add(resolvedEndpointID); ConfigureEndpoint(standardEndpointElement, serviceEndpointElement, context, host, description, out retval); ConfigLoader.resolvedEndpoints.Remove(resolvedEndpointID); } catch { // Clear list in case application is written to handle exception // by not starting up channel, etc... if (null != ConfigLoader.resolvedEndpoints) { ConfigLoader.resolvedBindings = null; } throw; } if (null != ConfigLoader.resolvedEndpoints && 0 == ConfigLoader.resolvedEndpoints.Count) { ConfigLoader.resolvedEndpoints = null; } configuredEndpointFound = true; } } if (!configuredEndpointFound) { // We expected to find an instance, but didn't. // Return null. retval = null; } if (retval == null && String.IsNullOrEmpty(endpointConfiguration)) { StandardEndpointElement standardEndpointElement = endpointCollectionElement.GetDefaultStandardEndpointElement(); ConfigureEndpoint(standardEndpointElement, serviceEndpointElement, context, host, description, out retval, omitSettingEndpointAddress); } if (DiagnosticUtility.ShouldTraceVerbose) { Dictionary values = new Dictionary(3); values["FoundEndpoint"] = retval != null; bool usingDefault = string.IsNullOrEmpty(endpointConfiguration); int traceCode; string traceDescription; if (usingDefault) { traceCode = TraceCode.GetDefaultConfiguredEndpoint; traceDescription = SR.GetString(SR.TraceCodeGetDefaultConfiguredEndpoint); } else { traceCode = TraceCode.GetConfiguredEndpoint; traceDescription = SR.GetString(SR.TraceCodeGetConfiguredEndpoint); values["Name"] = endpointConfiguration; } values["Endpoint"] = serviceEndpointElement.Kind; TraceUtility.TraceEvent(TraceEventType.Verbose, traceCode, traceDescription, new DictionaryTraceRecord(values), null, null); } return retval; } [Fx.Tag.SecurityNote(Critical = "Handles config objects, which should not be leaked.", Safe = "Doesn't leak config objects out of SecurityCritical code.")] [SecuritySafeCritical] void ConfigureEndpoint(StandardEndpointElement standardEndpointElement, ServiceEndpointElement serviceEndpointElement, ContextInformation context, ServiceHostBase host, ServiceDescription description, out ServiceEndpoint endpoint, bool omitSettingEndpointAddress = false) { // copy serviceEndpointElement so that it can potentially be modified by the StandardEndpointElement // the properties collection of the instance seviceEndpointElement created by System.Configuration is read-only. // keeping original serviceEndpointElement so that its context can be used to lookup endpoint behaviors. ServiceEndpointElement serviceEndpointElementCopy = new ServiceEndpointElement(); serviceEndpointElementCopy.Copy(serviceEndpointElement); standardEndpointElement.InitializeAndValidate(serviceEndpointElementCopy); //contract ContractDescription contract = null; if (!string.IsNullOrEmpty(serviceEndpointElementCopy.Contract)) { contract = LookupContractForStandardEndpoint(serviceEndpointElementCopy.Contract, description.Name); } endpoint = standardEndpointElement.CreateServiceEndpoint(contract); if (endpoint == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ConfigNoEndpointCreated, standardEndpointElement.GetType().AssemblyQualifiedName, (standardEndpointElement.EndpointType == null) ? string.Empty : standardEndpointElement.EndpointType.AssemblyQualifiedName))); } //binding Binding binding = null; if (!string.IsNullOrEmpty(serviceEndpointElementCopy.Binding)) { string bindingKey = serviceEndpointElementCopy.Binding + ":" + serviceEndpointElementCopy.BindingConfiguration; if (bindingTable.TryGetValue(bindingKey, out binding) == false) { binding = ConfigLoader.LookupBinding(serviceEndpointElementCopy.Binding, serviceEndpointElementCopy.BindingConfiguration, context); bindingTable.Add(bindingKey, binding); } } else { binding = endpoint.Binding; } if (binding != null) { if (!string.IsNullOrEmpty(serviceEndpointElementCopy.BindingName)) { binding.Name = serviceEndpointElementCopy.BindingName; } if (!string.IsNullOrEmpty(serviceEndpointElementCopy.BindingNamespace)) { binding.Namespace = serviceEndpointElementCopy.BindingNamespace; } endpoint.Binding = binding; if (!omitSettingEndpointAddress) { ConfigureEndpointAddress(serviceEndpointElementCopy, host, endpoint); ConfigureEndpointListenUri(serviceEndpointElementCopy, host, endpoint); } } //listenUriMode endpoint.ListenUriMode = serviceEndpointElementCopy.ListenUriMode; //name if (!string.IsNullOrEmpty(serviceEndpointElementCopy.Name)) { endpoint.Name = serviceEndpointElementCopy.Name; } //behaviors KeyedByTypeCollection behaviors = endpoint.Behaviors; EndpointBehaviorElement behaviorEndpointElement = ConfigLoader.LookupEndpointBehaviors(serviceEndpointElementCopy.BehaviorConfiguration, ConfigurationHelpers.GetEvaluationContext(serviceEndpointElement)); if (behaviorEndpointElement != null) { LoadBehaviors(behaviorEndpointElement, behaviors, false/*commonBehaviors*/); } //isSystemEndpoint if (serviceEndpointElementCopy.ElementInformation.Properties[ConfigurationStrings.IsSystemEndpoint].ValueOrigin != PropertyValueOrigin.Default) { endpoint.IsSystemEndpoint = serviceEndpointElementCopy.IsSystemEndpoint; } standardEndpointElement.ApplyConfiguration(endpoint, serviceEndpointElementCopy); } internal static void ConfigureEndpointAddress(ServiceEndpointElement serviceEndpointElement, ServiceHostBase host, ServiceEndpoint endpoint) { Fx.Assert(endpoint.Binding != null, "The endpoint must be set by the caller."); if (serviceEndpointElement.Address != null) { Uri via = ServiceHost.MakeAbsoluteUri(serviceEndpointElement.Address, endpoint.Binding, host.InternalBaseAddresses); endpoint.Address = new EndpointAddress(via, LoadIdentity(serviceEndpointElement.Identity), serviceEndpointElement.Headers.Headers); endpoint.UnresolvedAddress = serviceEndpointElement.Address; } } internal static void ConfigureEndpointListenUri(ServiceEndpointElement serviceEndpointElement, ServiceHostBase host, ServiceEndpoint endpoint) { Fx.Assert(endpoint.Binding != null, "The endpoint must be set by the caller."); if (serviceEndpointElement.ListenUri != null) { endpoint.ListenUri = ServiceHost.MakeAbsoluteUri(serviceEndpointElement.ListenUri, endpoint.Binding, host.InternalBaseAddresses); endpoint.UnresolvedListenUri = serviceEndpointElement.ListenUri; } } internal static Binding LookupBinding(string bindingSectionName, string configurationName) { return ConfigLoader.LookupBinding(bindingSectionName, configurationName, null); } internal static ComContractElement LookupComContract(Guid contractIID) { ComContractsSection comContracts = (ComContractsSection)ConfigurationHelpers.GetSection(ConfigurationStrings.ComContractsSectionPath); foreach (ComContractElement contract in comContracts.ComContracts) { Guid interfaceID; if (DiagnosticUtility.Utility.TryCreateGuid(contract.Contract, out interfaceID)) { if (interfaceID == contractIID) { return contract; } } } return null; } /// /// Critical - handles config objects, which should not be leaked /// Safe - doesn't leak config objects out of SecurityCritical code /// [SecuritySafeCritical] internal static ProtocolMappingItem LookupProtocolMapping(String scheme) { ProtocolMappingSection protocolMapping = (ProtocolMappingSection)ConfigurationHelpers.UnsafeGetSection(ConfigurationStrings.ProtocolMappingSectionPath); foreach (ProtocolMappingElement pm in protocolMapping.ProtocolMappingCollection) { if (pm.Scheme == scheme) { return new ProtocolMappingItem(pm.Binding, pm.BindingConfiguration); } } return null; } [Fx.Tag.SecurityNote(Critical = "Leaks config objects, caller must ensure that these don't leak to user code.")] [SecurityCritical] static BindingCollectionElement GetBindingCollectionElement(string bindingSectionName, ContextInformation context) { if (string.IsNullOrEmpty(bindingSectionName)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ConfigurationErrorsException(SR.GetString(SR.ConfigBindingTypeCannotBeNullOrEmpty))); } if (context == null) { // If no context is passed in, assume that the caller can consume the AppDomain's // current configuration file. return (BindingCollectionElement)ConfigurationHelpers.UnsafeGetBindingCollectionElement(bindingSectionName); } else { // Use the configuration file associated with the passed in context. // This may or may not be the same as the file for the current AppDomain. return (BindingCollectionElement)ConfigurationHelpers.UnsafeGetAssociatedBindingCollectionElement(context, bindingSectionName); } } // This method should only return null when bindingConfiguration is specified on the BindingElement and no BindingElement matching the // bindingConfiguration name is found. All other error conditions should throw. [Fx.Tag.SecurityNote(Critical = "Handles config objects, which should not be leaked.", Safe = "Doesn't leak config objects out of SecurityCritical code.")] [SecuritySafeCritical] internal static Binding LookupBinding(string bindingSectionName, string configurationName, ContextInformation context) { BindingCollectionElement bindingCollectionElement = GetBindingCollectionElement(bindingSectionName, context); Binding retval; if (configurationName == null) { retval = bindingCollectionElement.GetDefault(); } else { Binding defaultBinding = bindingCollectionElement.GetDefault(); retval = LookupBinding(bindingSectionName, configurationName, bindingCollectionElement, defaultBinding); if (retval == null && configurationName == ConfigurationStrings.DefaultName) { retval = defaultBinding; } } if (DiagnosticUtility.ShouldTraceVerbose) { Dictionary values = new Dictionary(3); values["FoundBinding"] = retval != null; bool usingDefault = string.IsNullOrEmpty(configurationName); int traceCode; string traceDescription; if (usingDefault) { traceCode = TraceCode.GetDefaultConfiguredBinding; traceDescription = SR.GetString(SR.TraceCodeGetDefaultConfiguredBinding); } else { traceCode = TraceCode.GetConfiguredBinding; traceDescription = SR.GetString(SR.TraceCodeGetConfiguredBinding); values["Name"] = string.IsNullOrEmpty(configurationName) ? SR.GetString(SR.Default) : configurationName; } values["Binding"] = bindingSectionName; TraceUtility.TraceEvent(TraceEventType.Verbose, traceCode, traceDescription, new DictionaryTraceRecord(values), null, null); } return retval; } static Binding LookupBinding(string bindingSectionName, string configurationName, BindingCollectionElement bindingCollectionElement, Binding defaultBinding) { Binding retval = defaultBinding; if (configurationName != null) { // We are looking for a specific instance, not the default. bool configuredBindingFound = false; // The Bindings property is always public foreach (object configElement in bindingCollectionElement.ConfiguredBindings) { IBindingConfigurationElement bindingElement = configElement as IBindingConfigurationElement; if (bindingElement != null) { if (bindingElement.Name.Equals(configurationName, StringComparison.Ordinal)) { if (null == ConfigLoader.resolvedBindings) { ConfigLoader.resolvedBindings = new List(); } string resolvedBindingID = bindingSectionName + "/" + configurationName; if (ConfigLoader.resolvedBindings.Contains(resolvedBindingID)) { ConfigurationElement configErrorElement = (ConfigurationElement)configElement; System.Text.StringBuilder detectedCycle = new System.Text.StringBuilder(); foreach (string resolvedBinding in ConfigLoader.resolvedBindings) { detectedCycle = detectedCycle.AppendFormat("{0}, ", resolvedBinding); } detectedCycle = detectedCycle.Append(resolvedBindingID); // Clear list in case application is written to handle exception // by not starting up channel, etc... ConfigLoader.resolvedBindings = null; throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ConfigurationErrorsException(SR.GetString(SR.ConfigBindingReferenceCycleDetected, detectedCycle.ToString()), configErrorElement.ElementInformation.Source, configErrorElement.ElementInformation.LineNumber)); } try { CheckAccess(configElement as IConfigurationContextProviderInternal); ConfigLoader.resolvedBindings.Add(resolvedBindingID); bindingElement.ApplyConfiguration(retval); ConfigLoader.resolvedBindings.Remove(resolvedBindingID); } catch { // Clear list in case application is written to handle exception // by not starting up channel, etc... if (null != ConfigLoader.resolvedBindings) { ConfigLoader.resolvedBindings = null; } throw; } if (null != ConfigLoader.resolvedBindings && 0 == ConfigLoader.resolvedBindings.Count) { ConfigLoader.resolvedBindings = null; } configuredBindingFound = true; } } } if (!configuredBindingFound) { // We expected to find an instance, but didn't. // Return null. retval = null; } } return retval; } [Fx.Tag.SecurityNote(Critical = "Leaks config objects, caller must ensure that these don't leak to user code.")] [SecurityCritical] static EndpointBehaviorElement LookupEndpointBehaviors(string behaviorName, ContextInformation context) { EndpointBehaviorElement retval = null; if (behaviorName != null) { if (DiagnosticUtility.ShouldTraceVerbose) { TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.GetBehaviorElement, SR.GetString(SR.TraceCodeGetBehaviorElement), new StringTraceRecord("BehaviorName", behaviorName), null, null); } BehaviorsSection behaviors = null; if (context == null) { behaviors = BehaviorsSection.UnsafeGetSection(); } else { behaviors = BehaviorsSection.UnsafeGetAssociatedSection(context); } if (behaviors.EndpointBehaviors.ContainsKey(behaviorName)) { retval = behaviors.EndpointBehaviors[behaviorName]; } } if (retval != null) { CheckAccess(retval); } return retval; } [Fx.Tag.SecurityNote(Critical = "Leaks config objects, caller must ensure that these don't leak to user code.")] [SecurityCritical] static ServiceBehaviorElement LookupServiceBehaviors(string behaviorName, ContextInformation context) { ServiceBehaviorElement retval = null; if (behaviorName != null) { if (DiagnosticUtility.ShouldTraceVerbose) { TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.GetBehaviorElement, SR.GetString(SR.TraceCodeGetBehaviorElement), new StringTraceRecord("BehaviorName", behaviorName), null, null); } BehaviorsSection behaviors = null; if (context == null) { behaviors = BehaviorsSection.UnsafeGetSection(); } else { behaviors = BehaviorsSection.UnsafeGetAssociatedSection(context); } if (behaviors.ServiceBehaviors.ContainsKey(behaviorName)) { retval = behaviors.ServiceBehaviors[behaviorName]; } } if (retval != null) { CheckAccess(retval); } return retval; } [Fx.Tag.SecurityNote(Critical = "Leaks config objects, caller must ensure that these don't leak to user code.")] [SecurityCritical] static CommonBehaviorsSection LookupCommonBehaviors(ContextInformation context) { if (DiagnosticUtility.ShouldTraceVerbose) { TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.GetCommonBehaviors, SR.GetString(SR.TraceCodeGetCommonBehaviors), (object)null); } return context == null ? CommonBehaviorsSection.UnsafeGetSection() : CommonBehaviorsSection.UnsafeGetAssociatedSection(context); } static bool IsChannelElementMatch(ChannelEndpointElement channelElement, ContractDescription contract, EndpointAddress address, bool useChannelElementKind, out ServiceEndpoint serviceEndpoint) { serviceEndpoint = null; if (string.IsNullOrEmpty(channelElement.Kind)) { return channelElement.Contract == contract.ConfigurationName; } if (useChannelElementKind) { serviceEndpoint = LookupEndpoint(channelElement, null, address, contract); if (serviceEndpoint != null) { if (serviceEndpoint.Contract.ConfigurationName == contract.ConfigurationName && (string.IsNullOrEmpty(channelElement.Contract) || contract.ConfigurationName == channelElement.Contract)) { return true; } else { serviceEndpoint = null; return false; } } else { return false; // this should not happen with a valid client section since serviceEndpoint will never be null. } } else { // A standard endpoint should not be returned in the case of useChannelElementKind = false. // This is because useChannelElementKind = false only when this method is called by // LoadChannelBehaviors (the overload that takes a ServiceEndpoint and a string(configurationName)). // LoadChannelBehaviors is called for the purposes of applying channel behaviors to a newly created service endpoint. // In the case of standard endpoints, the service endpoints are already fully configured. // Reapplying behaviors would not only be redundant but may cause exceptions to be thrown. return false; } } [Fx.Tag.SecurityNote(Critical = "Leaks config objects, caller must ensure that these don't leak to user code.")] [SecurityCritical] static ChannelEndpointElement LookupChannel(ContextInformation configurationContext, string configurationName, ContractDescription contract, EndpointAddress address, bool wildcard, bool useChannelElementKind, out ServiceEndpoint serviceEndpoint) { serviceEndpoint = null; ClientSection clientSection = (configurationContext == null ? ClientSection.UnsafeGetSection() : ClientSection.UnsafeGetSection(configurationContext)); ChannelEndpointElement retval = null; ServiceEndpoint standardEndpoint; foreach (ChannelEndpointElement channelElement in clientSection.Endpoints) { if (IsChannelElementMatch(channelElement, contract, address, useChannelElementKind, out standardEndpoint)) { if (channelElement.Name == configurationName || wildcard) // match name (or wildcard) { if (retval != null) // oops: >1 { if (wildcard) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxConfigLoaderMultipleEndpointMatchesWildcard1, contract.ConfigurationName))); } else { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxConfigLoaderMultipleEndpointMatchesSpecified2, contract.ConfigurationName, configurationName))); } } retval = channelElement; serviceEndpoint = standardEndpoint; } } } if (retval != null) { CheckAccess(retval); } if (DiagnosticUtility.ShouldTraceInformation) { Dictionary values = new Dictionary(8); values["FoundChannelElement"] = retval != null; values["Name"] = configurationName; values["ContractName"] = contract.ConfigurationName; if (null != retval) { if (!string.IsNullOrEmpty(retval.Binding)) { values["Binding"] = retval.Binding; } if (!string.IsNullOrEmpty(retval.BindingConfiguration)) { values["BindingConfiguration"] = retval.BindingConfiguration; } if (retval.Address != null) { values["RemoteEndpointUri"] = retval.Address.ToString(); } if (!string.IsNullOrEmpty(retval.ElementInformation.Source)) { values["ConfigurationFileSource"] = retval.ElementInformation.Source; values["ConfigurationFileLineNumber"] = retval.ElementInformation.LineNumber; } } TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.GetChannelEndpointElement, SR.GetString(SR.TraceCodeGetChannelEndpointElement), new DictionaryTraceRecord(values), null, null); } return retval; } internal ContractDescription LookupContract(string contractName, string serviceName) { ContractDescription contract = LookupContractForStandardEndpoint(contractName, serviceName); if (contract == null) { if (contractName == String.Empty) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SfxReflectedContractKeyNotFoundEmpty, serviceName))); } else { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SfxReflectedContractKeyNotFound2, contractName, serviceName))); } } return contract; } internal ContractDescription LookupContractForStandardEndpoint(string contractName, string serviceName) { ContractDescription contract = contractResolver.ResolveContract(contractName); if (contract == null) { if (contractName == ServiceMetadataBehavior.MexContractName) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SfxReflectedContractKeyNotFoundIMetadataExchange, serviceName))); } } return contract; } [Fx.Tag.SecurityNote(Critical = "Leaks config objects, caller must ensure that these don't leak to user code.")] [SecurityCritical] public ServiceElement LookupService(string serviceConfigurationName) { ServicesSection servicesSection = ServicesSection.UnsafeGetSection(); return LookupService(serviceConfigurationName, servicesSection); } public ServiceElement LookupService(string serviceConfigurationName, ServicesSection servicesSection) { ServiceElement retval = null; ServiceElementCollection services = servicesSection.Services; for (int i = 0; i < services.Count; i++) { ServiceElement serviceElement = services[i]; if (serviceElement.Name == serviceConfigurationName) { retval = serviceElement; } } if (retval != null) { CheckAccess(retval); } if (DiagnosticUtility.ShouldTraceInformation) { TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.GetServiceElement, SR.GetString(SR.TraceCodeGetServiceElement), new ServiceConfigurationTraceRecord(retval), null, null); } return retval; } static bool IsWildcardMatch(string endpointConfigurationName) { return String.Equals(endpointConfigurationName, "*", StringComparison.Ordinal); } [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - used in a security decision.")] static bool IsConfigAboveApplication(ContextInformation contextInformation) { if (contextInformation != null) { if (contextInformation.IsMachineLevel) { return true; } bool isAppConfig = contextInformation.HostingContext is ExeContext; if (isAppConfig) { return false; // for app.config, the only higher-scope config file is machine.config } else { return IsWebConfigAboveApplication(contextInformation); } } return true; // err on the safe side: absent context information assume a PT app doesn't have access } [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - used in a security decision.")] [MethodImpl(MethodImplOptions.NoInlining)] static bool IsWebConfigAboveApplication(ContextInformation contextInformation) { return AspNetEnvironment.Current.IsWebConfigAboveApplication(contextInformation.HostingContext); } [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - enforces a security decision.")] static void CheckAccess(IConfigurationContextProviderInternal element) { if (IsConfigAboveApplication(ConfigurationHelpers.GetOriginalEvaluationContext(element))) { ConfigurationPermission.Demand(); } } [Fx.Tag.SecurityNote(Critical = "Used in a security decision.")] [SecurityCritical] static ConfigurationPermission configurationPermission; static ConfigurationPermission ConfigurationPermission { [Fx.Tag.SecurityNote(Critical = "Inits the configurationPermission field.", Safe = "Safe for readonly access.")] [SecuritySafeCritical] get { if (configurationPermission == null) { configurationPermission = new ConfigurationPermission(System.Security.Permissions.PermissionState.Unrestricted); } return configurationPermission; } } [Fx.Tag.SecurityNote(Critical = "Uses critical field configurationPermission.")] [SecurityCritical] static bool ThreadHasConfigurationPermission() { try { ConfigurationPermission.Demand(); } catch (SecurityException) { return false; } return true; } } class ProtocolMappingItem { public ProtocolMappingItem(string binding, string bindingConfiguration) { this.Binding = binding; this.BindingConfiguration = bindingConfiguration; } public string Binding { get; set; } public string BindingConfiguration { get; set; } } }