| File: System\ServiceModel\Security\SecurityProtocolFactory.cs | Web Access |
| Project: src\src\System.ServiceModel.Primitives\src\System.ServiceModel.Primitives.csproj (System.ServiceModel.Primitives) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Collections.Generic; using System.Collections.ObjectModel; using System.IdentityModel.Selectors; using System.Runtime; using System.Security.Authentication.ExtendedProtection; using System.ServiceModel.Channels; using System.ServiceModel.Security.Tokens; using System.Globalization; using System.Threading.Tasks; namespace System.ServiceModel.Security { /* * Concrete implementations are required to be thread safe after * Open() is called; * instances of concrete protocol factories are scoped to a * channel/listener factory; * Each channel/listener factory must have a * SecurityProtocolFactory set on it before open/first use; the * factory instance cannot be changed once the factory is opened * or listening; * security protocol instances are scoped to a channel and will be * created by the Create calls on protocol factories; * security protocol instances are required to be thread-safe. * for typical subclasses, factory wide state and immutable * settings are expected to be on the ProtocolFactory itself while * channel-wide state is maintained internally in each security * protocol instance; * the security protocol instance set on a channel cannot be * changed; however, the protocol instance may change internal * state; this covers RM's SCT renego case; by keeping state * change internal to protocol instances, we get better * coordination with concurrent message security on channels; * the primary pivot in creating a security protocol instance is * initiator (client) vs. responder (server), NOT sender vs * receiver * Create calls for input and reply channels will contain the * listener-wide state (if any) created by the corresponding call * on the factory; */ internal abstract class SecurityProtocolFactory : ISecurityCommunicationObject { internal const bool defaultAddTimestamp = true; internal const bool defaultDeriveKeys = true; internal const bool defaultDetectReplays = true; internal const string defaultMaxClockSkewString = "00:05:00"; internal const string defaultReplayWindowString = "00:05:00"; internal static readonly TimeSpan defaultMaxClockSkew = TimeSpan.Parse(defaultMaxClockSkewString, CultureInfo.InvariantCulture); internal static readonly TimeSpan defaultReplayWindow = TimeSpan.Parse(defaultReplayWindowString, CultureInfo.InvariantCulture); internal const int defaultMaxCachedNonces = 900000; internal const string defaultTimestampValidityDurationString = "00:05:00"; internal static readonly TimeSpan defaultTimestampValidityDuration = TimeSpan.Parse(defaultTimestampValidityDurationString, CultureInfo.InvariantCulture); internal const SecurityHeaderLayout defaultSecurityHeaderLayout = SecurityHeaderLayout.Strict; private static ReadOnlyCollection<SupportingTokenAuthenticatorSpecification> s_emptyTokenAuthenticators; private bool _addTimestamp = defaultAddTimestamp; private bool _detectReplays = defaultDetectReplays; private bool _expectIncomingMessages; private bool _expectOutgoingMessages; private SecurityAlgorithmSuite _incomingAlgorithmSuite = SecurityAlgorithmSuite.Default; // per receiver protocol factory lists private ICollection<SupportingTokenAuthenticatorSpecification> _channelSupportingTokenAuthenticatorSpecification; private int _maxCachedNonces = defaultMaxCachedNonces; private TimeSpan _maxClockSkew = defaultMaxClockSkew; private NonceCache _nonceCache = null; private SecurityAlgorithmSuite _outgoingAlgorithmSuite = SecurityAlgorithmSuite.Default; private TimeSpan _replayWindow = defaultReplayWindow; private SecurityStandardsManager _standardsManager = SecurityStandardsManager.DefaultInstance; private SecurityTokenManager _securityTokenManager; private SecurityBindingElement _securityBindingElement; private string _requestReplyErrorPropertyName; private TimeSpan _timestampValidityDuration = defaultTimestampValidityDuration; private SecurityHeaderLayout _securityHeaderLayout; #pragma warning disable CS0649 // Field is never assign to private bool _expectChannelBasicTokens; private bool _expectChannelSignedTokens; private bool _expectChannelEndorsingTokens; #pragma warning restore CS0649 // Field is never assign to private BufferManager _streamBufferManager = null; protected SecurityProtocolFactory() { _channelSupportingTokenAuthenticatorSpecification = new Collection<SupportingTokenAuthenticatorSpecification>(); //scopedSupportingTokenAuthenticatorSpecification = new Dictionary<string, ICollection<SupportingTokenAuthenticatorSpecification>>(); CommunicationObject = new WrapperSecurityCommunicationObject(this); } internal SecurityProtocolFactory(SecurityProtocolFactory factory) : this() { if (factory == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(factory)); } ActAsInitiator = factory.ActAsInitiator; _addTimestamp = factory._addTimestamp; _detectReplays = factory._detectReplays; _incomingAlgorithmSuite = factory._incomingAlgorithmSuite; _maxCachedNonces = factory._maxCachedNonces; _maxClockSkew = factory._maxClockSkew; _outgoingAlgorithmSuite = factory._outgoingAlgorithmSuite; _replayWindow = factory._replayWindow; _channelSupportingTokenAuthenticatorSpecification = new Collection<SupportingTokenAuthenticatorSpecification>(new List<SupportingTokenAuthenticatorSpecification>(factory._channelSupportingTokenAuthenticatorSpecification)); _standardsManager = factory._standardsManager; _timestampValidityDuration = factory._timestampValidityDuration; _securityBindingElement = (SecurityBindingElement)factory._securityBindingElement?.Clone(); _securityTokenManager = factory._securityTokenManager; _nonceCache = factory._nonceCache; } protected WrapperSecurityCommunicationObject CommunicationObject { get; } // The ActAsInitiator value is set automatically on Open and // remains unchanged thereafter. ActAsInitiator is true for // the initiator of the message exchange, such as the sender // of a datagram, sender of a request and sender of either leg // of a duplex exchange. public bool ActAsInitiator { get; private set; } public BufferManager StreamBufferManager { get { if (_streamBufferManager == null) { _streamBufferManager = BufferManager.CreateBufferManager(0, int.MaxValue); } return _streamBufferManager; } set { _streamBufferManager = value; } } public ExtendedProtectionPolicy ExtendedProtectionPolicy { get; set; } public bool AddTimestamp { get { return _addTimestamp; } set { ThrowIfImmutable(); _addTimestamp = value; } } public bool DetectReplays { get { return _detectReplays; } set { ThrowIfImmutable(); _detectReplays = value; } } private static ReadOnlyCollection<SupportingTokenAuthenticatorSpecification> EmptyTokenAuthenticators { get { if (s_emptyTokenAuthenticators == null) { s_emptyTokenAuthenticators = Array.AsReadOnly(new SupportingTokenAuthenticatorSpecification[0]); } return s_emptyTokenAuthenticators; } } internal bool ExpectKeyDerivation { get; set; } internal bool ExpectSupportingTokens { get; set; } public SecurityAlgorithmSuite IncomingAlgorithmSuite { get { return _incomingAlgorithmSuite; } set { ThrowIfImmutable(); _incomingAlgorithmSuite = value ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(value))); } } public int MaxCachedNonces { get { return _maxCachedNonces; } set { ThrowIfImmutable(); if (value <= 0) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value))); } _maxCachedNonces = value; } } public TimeSpan MaxClockSkew { get { return _maxClockSkew; } set { ThrowIfImmutable(); if (value < TimeSpan.Zero) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value))); } _maxClockSkew = value; } } public NonceCache NonceCache { get { return _nonceCache; } set { ThrowIfImmutable(); _nonceCache = value; } } public SecurityAlgorithmSuite OutgoingAlgorithmSuite { get { return _outgoingAlgorithmSuite; } set { ThrowIfImmutable(); _outgoingAlgorithmSuite = value ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(value))); } } public TimeSpan ReplayWindow { get { return _replayWindow; } set { ThrowIfImmutable(); if (value <= TimeSpan.Zero) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), SRP.TimeSpanMustbeGreaterThanTimeSpanZero)); } _replayWindow = value; } } public SecurityBindingElement SecurityBindingElement { get { return _securityBindingElement; } set { ThrowIfImmutable(); if (value != null) { value = (SecurityBindingElement)value.Clone(); } _securityBindingElement = value; } } public SecurityTokenManager SecurityTokenManager { get { return _securityTokenManager; } set { ThrowIfImmutable(); _securityTokenManager = value; } } public virtual bool SupportsDuplex { get { return false; } } public SecurityHeaderLayout SecurityHeaderLayout { get { return _securityHeaderLayout; } set { ThrowIfImmutable(); _securityHeaderLayout = value; } } public virtual bool SupportsReplayDetection { get { return true; } } public virtual bool SupportsRequestReply { get { return true; } } public SecurityStandardsManager StandardsManager { get { return _standardsManager; } set { ThrowIfImmutable(); _standardsManager = value ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(value))); } } public TimeSpan TimestampValidityDuration { get { return _timestampValidityDuration; } set { ThrowIfImmutable(); if (value <= TimeSpan.Zero) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), SRP.TimeSpanMustbeGreaterThanTimeSpanZero)); } _timestampValidityDuration = value; } } internal MessageSecurityVersion MessageSecurityVersion { get; private set; } // ISecurityCommunicationObject members public TimeSpan DefaultOpenTimeout => ServiceDefaults.OpenTimeout; public TimeSpan DefaultCloseTimeout => ServiceDefaults.CloseTimeout; public void OnClosed() { } public void OnClosing() { } public void OnFaulted() { } public void OnOpened() { } public void OnOpening() { } public virtual void OnAbort() { if (!ActAsInitiator) { throw ExceptionHelper.PlatformNotSupported(); } } public virtual Task OnCloseAsync(TimeSpan timeout) { if (!ActAsInitiator) { throw ExceptionHelper.PlatformNotSupported(); } return Task.CompletedTask; } public SecurityProtocol CreateSecurityProtocol(EndpointAddress target, Uri via, object listenerSecurityState, bool isReturnLegSecurityRequired, TimeSpan timeout) { ThrowIfNotOpen(); SecurityProtocol securityProtocol = OnCreateSecurityProtocol(target, via, listenerSecurityState, timeout); if (securityProtocol == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SRP.ProtocolFactoryCouldNotCreateProtocol)); } return securityProtocol; } public virtual T GetProperty<T>() { if (typeof(T) == typeof(Collection<ISecurityContextSecurityTokenCache>)) { ThrowIfNotOpen(); Collection<ISecurityContextSecurityTokenCache> result = new Collection<ISecurityContextSecurityTokenCache>(); if (_channelSupportingTokenAuthenticatorSpecification != null) { foreach (SupportingTokenAuthenticatorSpecification spec in _channelSupportingTokenAuthenticatorSpecification) { if (spec.TokenAuthenticator is ISecurityContextSecurityTokenCacheProvider) { result.Add(((ISecurityContextSecurityTokenCacheProvider)spec.TokenAuthenticator).TokenCache); } } } return (T)(object)(result); } else { return default(T); } } protected abstract SecurityProtocol OnCreateSecurityProtocol(EndpointAddress target, Uri via, object listenerSecurityState, TimeSpan timeout); internal IList<SupportingTokenAuthenticatorSpecification> GetSupportingTokenAuthenticators(string action, out bool expectSignedTokens, out bool expectBasicTokens, out bool expectEndorsingTokens) { expectSignedTokens = _expectChannelSignedTokens; expectBasicTokens = _expectChannelBasicTokens; expectEndorsingTokens = _expectChannelEndorsingTokens; // in case the channelSupportingTokenAuthenticators is empty return null so that its Count does not get accessed. return (Object.ReferenceEquals(_channelSupportingTokenAuthenticatorSpecification, EmptyTokenAuthenticators)) ? null : (IList<SupportingTokenAuthenticatorSpecification>)_channelSupportingTokenAuthenticatorSpecification; } public virtual Task OnOpenAsync(TimeSpan timeout) { if (SecurityBindingElement == null) { OnPropertySettingsError(nameof(SecurityBindingElement), true); } if (SecurityTokenManager == null) { OnPropertySettingsError(nameof(SecurityTokenManager), true); } MessageSecurityVersion = _standardsManager.MessageSecurityVersion; TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); _expectOutgoingMessages = ActAsInitiator || SupportsRequestReply; _expectIncomingMessages = !ActAsInitiator || SupportsRequestReply; if (!ActAsInitiator) { throw ExceptionHelper.PlatformNotSupported(); } if (DetectReplays) { if (!SupportsReplayDetection) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(DetectReplays), SRP.Format(SRP.SecurityProtocolCannotDoReplayDetection, this)); } if (MaxClockSkew == TimeSpan.MaxValue || ReplayWindow == TimeSpan.MaxValue) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.NoncesCachedInfinitely)); } // If DetectReplays is true and nonceCache is null then use the default InMemoryNonceCache. if (_nonceCache == null) { // The nonce needs to be cached for replayWindow + 2*clockSkew to eliminate replays _nonceCache = new InMemoryNonceCache(ReplayWindow + MaxClockSkew + MaxClockSkew, MaxCachedNonces); } } //derivedKeyTokenAuthenticator = new NonValidatingSecurityTokenAuthenticator<DerivedKeySecurityToken>(); return Task.CompletedTask; } public Task OpenAsync(bool actAsInitiator, TimeSpan timeout) { ActAsInitiator = actAsInitiator; return ((IAsyncCommunicationObject)CommunicationObject).OpenAsync(timeout); } public Task CloseAsync(bool aborted, TimeSpan timeout) { if (aborted) { CommunicationObject.Abort(); return Task.CompletedTask; } else { return ((IAsyncCommunicationObject)CommunicationObject).CloseAsync(timeout); } } internal void OnPropertySettingsError(string propertyName, bool requiredForForwardDirection) { if (requiredForForwardDirection) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException( SRP.Format(SRP.PropertySettingErrorOnProtocolFactory, propertyName, this), propertyName)); } else if (_requestReplyErrorPropertyName == null) { _requestReplyErrorPropertyName = propertyName; } } internal void ThrowIfImmutable() { CommunicationObject.ThrowIfDisposedOrImmutable(); } private void ThrowIfNotOpen() { CommunicationObject.ThrowIfNotOpened(); } } }