//----------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- namespace System.ServiceModel.Security { using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.IdentityModel.Policy; using System.IdentityModel.Tokens; using System.Runtime; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Description; using System.ServiceModel.Diagnostics; using System.ServiceModel.Dispatcher; using System.ServiceModel.Security.Tokens; using System.Xml; abstract class NegotiationTokenAuthenticator : CommunicationObjectSecurityTokenAuthenticator, IIssuanceSecurityTokenAuthenticator, ISecurityContextSecurityTokenCacheProvider where T : NegotiationTokenAuthenticatorState { internal const string defaultServerMaxNegotiationLifetimeString = "00:01:00"; internal const string defaultServerIssuedTokenLifetimeString = "10:00:00"; internal const string defaultServerIssuedTransitionTokenLifetimeString = "00:15:00"; internal const int defaultServerMaxActiveNegotiations = 128; internal static readonly TimeSpan defaultServerMaxNegotiationLifetime = TimeSpan.Parse(defaultServerMaxNegotiationLifetimeString, CultureInfo.InvariantCulture); internal static readonly TimeSpan defaultServerIssuedTokenLifetime = TimeSpan.Parse(defaultServerIssuedTokenLifetimeString, CultureInfo.InvariantCulture); internal static readonly TimeSpan defaultServerIssuedTransitionTokenLifetime = TimeSpan.Parse(defaultServerIssuedTransitionTokenLifetimeString, CultureInfo.InvariantCulture); internal const int defaultServerMaxCachedTokens = 1000; internal const bool defaultServerMaintainState = true; internal static readonly SecurityStandardsManager defaultStandardsManager = SecurityStandardsManager.DefaultInstance; internal static readonly SecurityStateEncoder defaultSecurityStateEncoder = new DataProtectionSecurityStateEncoder(); NegotiationTokenAuthenticatorStateCache stateCache; RenewedSecurityTokenHandler renewedSecurityTokenHandler; NegotiationHost negotiationHost; bool encryptStateInServiceToken; TimeSpan serviceTokenLifetime; int maximumCachedNegotiationState; TimeSpan negotiationTimeout; bool isClientAnonymous; SecurityStandardsManager standardsManager; SecurityAlgorithmSuite securityAlgorithmSuite; SecurityTokenParameters issuedSecurityTokenParameters; ISecurityContextSecurityTokenCache issuedTokenCache; BindingContext issuerBindingContext; Uri listenUri; string sctUri; AuditLogLocation auditLogLocation; bool suppressAuditFailure; AuditLevel messageAuthenticationAuditLevel; SecurityStateEncoder securityStateEncoder; SecurityContextCookieSerializer cookieSerializer; IMessageFilterTable endpointFilterTable; IssuedSecurityTokenHandler issuedSecurityTokenHandler; int maxMessageSize; IList knownTypes; int maximumConcurrentNegotiations; List activeNegotiationChannels1; List activeNegotiationChannels2; IOThreadTimer idlingNegotiationSessionTimer; bool isTimerCancelled; protected NegotiationTokenAuthenticator() : base() { InitializeDefaults(); } public IssuedSecurityTokenHandler IssuedSecurityTokenHandler { get { return this.issuedSecurityTokenHandler; } set { this.issuedSecurityTokenHandler = value; } } public RenewedSecurityTokenHandler RenewedSecurityTokenHandler { get { return this.renewedSecurityTokenHandler; } set { this.renewedSecurityTokenHandler = value; } } // settings public bool EncryptStateInServiceToken { get { return this.encryptStateInServiceToken; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); this.encryptStateInServiceToken = value; } } public TimeSpan ServiceTokenLifetime { get { return this.serviceTokenLifetime; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); if (value <= TimeSpan.Zero) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", SR.GetString(SR.TimeSpanMustbeGreaterThanTimeSpanZero))); } if (TimeoutHelper.IsTooLarge(value)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SR.GetString(SR.SFxTimeoutOutOfRangeTooBig))); } this.serviceTokenLifetime = value; } } public int MaximumCachedNegotiationState { get { return this.maximumCachedNegotiationState; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); if (value < 0) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", SR.GetString(SR.ValueMustBeNonNegative))); } this.maximumCachedNegotiationState = value; } } public int MaximumConcurrentNegotiations { get { return this.maximumConcurrentNegotiations; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); if (value < 0) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", SR.GetString(SR.ValueMustBeNonNegative))); } this.maximumConcurrentNegotiations = value; } } public TimeSpan NegotiationTimeout { get { return this.negotiationTimeout; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); if (value <= TimeSpan.Zero) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", SR.GetString(SR.TimeSpanMustbeGreaterThanTimeSpanZero))); } if (TimeoutHelper.IsTooLarge(value)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SR.GetString(SR.SFxTimeoutOutOfRangeTooBig))); } this.negotiationTimeout = value; } } public bool IsClientAnonymous { get { return this.isClientAnonymous; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); this.isClientAnonymous = value; } } public SecurityAlgorithmSuite SecurityAlgorithmSuite { get { return this.securityAlgorithmSuite; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); this.securityAlgorithmSuite = value; } } public IMessageFilterTable EndpointFilterTable { get { return this.endpointFilterTable; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); this.endpointFilterTable = value; } } ISecurityContextSecurityTokenCache ISecurityContextSecurityTokenCacheProvider.TokenCache { get { return this.IssuedTokenCache; } } public virtual XmlDictionaryString RequestSecurityTokenAction { get { return this.StandardsManager.TrustDriver.RequestSecurityTokenAction; } } public virtual XmlDictionaryString RequestSecurityTokenResponseAction { get { return this.StandardsManager.TrustDriver.RequestSecurityTokenResponseAction; } } public virtual XmlDictionaryString RequestSecurityTokenResponseFinalAction { get { return this.StandardsManager.TrustDriver.RequestSecurityTokenResponseFinalAction; } } public SecurityStandardsManager StandardsManager { get { return this.standardsManager; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); this.standardsManager = (value != null ? value : SecurityStandardsManager.DefaultInstance); } } public SecurityTokenParameters IssuedSecurityTokenParameters { get { return this.issuedSecurityTokenParameters; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); this.issuedSecurityTokenParameters = value; } } public ISecurityContextSecurityTokenCache IssuedTokenCache { get { return this.issuedTokenCache; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); this.issuedTokenCache = value; } } public AuditLogLocation AuditLogLocation { get { return this.auditLogLocation; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); this.auditLogLocation = value; } } public bool SuppressAuditFailure { get { return this.suppressAuditFailure; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); this.suppressAuditFailure = value; } } public AuditLevel MessageAuthenticationAuditLevel { get { return this.messageAuthenticationAuditLevel; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); this.messageAuthenticationAuditLevel = value; } } public BindingContext IssuerBindingContext { get { return this.issuerBindingContext; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); if (value == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); } this.issuerBindingContext = value.Clone(); } } public Uri ListenUri { get { return this.listenUri; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); this.listenUri = value; } } public SecurityStateEncoder SecurityStateEncoder { get { return this.securityStateEncoder; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); this.securityStateEncoder = value; } } public IList KnownTypes { get { return this.knownTypes; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); if (value != null) { this.knownTypes = new Collection(value); } else { this.knownTypes = null; } } } public int MaxMessageSize { get { return this.maxMessageSize; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); this.maxMessageSize = value; } } protected string SecurityContextTokenUri { get { this.CommunicationObject.ThrowIfNotOpened(); return this.sctUri; } } Object ThisLock { get { return this.CommunicationObject; } } // helpers protected SecurityContextSecurityToken IssueSecurityContextToken(UniqueId contextId, string id, byte[] key, DateTime tokenEffectiveTime, DateTime tokenExpirationTime, ReadOnlyCollection authorizationPolicies, bool isCookieMode) { return IssueSecurityContextToken(contextId, id, key, tokenEffectiveTime, tokenExpirationTime, null, tokenEffectiveTime, tokenExpirationTime, authorizationPolicies, isCookieMode); } protected SecurityContextSecurityToken IssueSecurityContextToken(UniqueId contextId, string id, byte[] key, DateTime tokenEffectiveTime, DateTime tokenExpirationTime, UniqueId keyGeneration, DateTime keyEffectiveTime, DateTime keyExpirationTime, ReadOnlyCollection authorizationPolicies, bool isCookieMode) { this.CommunicationObject.ThrowIfClosedOrNotOpen(); if (this.securityStateEncoder == null && isCookieMode) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SctCookieNotSupported))); } byte[] cookieBlob = (isCookieMode) ? this.cookieSerializer.CreateCookieFromSecurityContext(contextId, id, key, tokenEffectiveTime, tokenExpirationTime, keyGeneration, keyEffectiveTime, keyExpirationTime, authorizationPolicies) : null; SecurityContextSecurityToken issuedToken = new SecurityContextSecurityToken(contextId, id, key, tokenEffectiveTime, tokenExpirationTime, authorizationPolicies, isCookieMode, cookieBlob, keyGeneration, keyEffectiveTime, keyExpirationTime); return issuedToken; } void InitializeDefaults() { this.encryptStateInServiceToken = !defaultServerMaintainState; this.serviceTokenLifetime = defaultServerIssuedTokenLifetime; this.maximumCachedNegotiationState = defaultServerMaxActiveNegotiations; this.negotiationTimeout = defaultServerMaxNegotiationLifetime; this.isClientAnonymous = false; this.standardsManager = defaultStandardsManager; this.securityStateEncoder = defaultSecurityStateEncoder; this.maximumConcurrentNegotiations = defaultServerMaxActiveNegotiations; // we rely on the transport encoders to enforce the message size except in the // mixed mode nego case, where the client is unauthenticated and the maxMessageSize is too // large to be a mitigation this.maxMessageSize = Int32.MaxValue; } public override void OnClose(TimeSpan timeout) { TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); if (this.negotiationHost != null) { this.negotiationHost.Close(timeoutHelper.RemainingTime()); this.negotiationHost = null; } lock (ThisLock) { if (this.idlingNegotiationSessionTimer != null && !this.isTimerCancelled) { this.isTimerCancelled = true; this.idlingNegotiationSessionTimer.Cancel(); } } base.OnClose(timeoutHelper.RemainingTime()); } public override void OnAbort() { if (this.negotiationHost != null) { this.negotiationHost.Abort(); this.negotiationHost = null; } lock (ThisLock) { if (this.idlingNegotiationSessionTimer != null && !this.isTimerCancelled) { this.isTimerCancelled = true; this.idlingNegotiationSessionTimer.Cancel(); } } base.OnAbort(); } public override void OnOpen(TimeSpan timeout) { if (this.IssuerBindingContext == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.IssuerBuildContextNotSet, this.GetType()))); } if (this.IssuedSecurityTokenParameters == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.IssuedSecurityTokenParametersNotSet, this.GetType()))); } if (this.SecurityAlgorithmSuite == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SecurityAlgorithmSuiteNotSet, this.GetType()))); } if (this.IssuedTokenCache == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.IssuedTokenCacheNotSet, this.GetType()))); } TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); this.SetupServiceHost(); negotiationHost.Open(timeoutHelper.RemainingTime()); this.stateCache = new NegotiationTokenAuthenticatorStateCache(this.NegotiationTimeout, this.MaximumCachedNegotiationState); this.sctUri = this.StandardsManager.SecureConversationDriver.TokenTypeUri; if (this.SecurityStateEncoder != null) { this.cookieSerializer = new SecurityContextCookieSerializer(this.SecurityStateEncoder, this.KnownTypes); } if (this.negotiationTimeout < TimeSpan.MaxValue) { lock (ThisLock) { this.activeNegotiationChannels1 = new List(); this.activeNegotiationChannels2 = new List(); this.idlingNegotiationSessionTimer = new IOThreadTimer(new Action(this.OnIdlingNegotiationSessionTimer), this, false); this.isTimerCancelled = false; this.idlingNegotiationSessionTimer.Set(this.negotiationTimeout); } } base.OnOpen(timeoutHelper.RemainingTime()); } protected override bool CanValidateTokenCore(SecurityToken token) { return (token is SecurityContextSecurityToken); } protected override ReadOnlyCollection ValidateTokenCore(SecurityToken token) { SecurityContextSecurityToken sct = (SecurityContextSecurityToken)token; return sct.AuthorizationPolicies; } protected abstract Binding GetNegotiationBinding(Binding binding); protected abstract bool IsMultiLegNegotiation { get; } protected abstract MessageFilter GetListenerFilter(); void SetupServiceHost() { ChannelBuilder channelBuilder = new ChannelBuilder(this.IssuerBindingContext.Clone(), true); channelBuilder.Binding.Elements.Insert(0, new ReplyAdapterBindingElement()); channelBuilder.Binding = new CustomBinding(this.GetNegotiationBinding(channelBuilder.Binding)); negotiationHost = new NegotiationHost(this, this.ListenUri, channelBuilder, this.GetListenerFilter()); } // message processing abstract method protected abstract BodyWriter ProcessRequestSecurityToken(Message request, RequestSecurityToken requestSecurityToken, out T negotiationState); protected abstract BodyWriter ProcessRequestSecurityTokenResponse(T negotiationState, Message request, RequestSecurityTokenResponse requestSecurityTokenResponse); // message handlers protected virtual void ParseMessageBody(Message message, out string context, out RequestSecurityToken requestSecurityToken, out RequestSecurityTokenResponse requestSecurityTokenResponse) { requestSecurityToken = null; requestSecurityTokenResponse = null; if (message.Headers.Action == this.RequestSecurityTokenAction.Value) { XmlDictionaryReader reader = message.GetReaderAtBodyContents(); using (reader) { requestSecurityToken = RequestSecurityToken.CreateFrom(this.StandardsManager, reader); message.ReadFromBodyContentsToEnd(reader); } context = requestSecurityToken.Context; } else if (message.Headers.Action == this.RequestSecurityTokenResponseAction.Value) { XmlDictionaryReader reader = message.GetReaderAtBodyContents(); using (reader) { requestSecurityTokenResponse = RequestSecurityTokenResponse.CreateFrom(this.StandardsManager, reader); message.ReadFromBodyContentsToEnd(reader); } context = requestSecurityTokenResponse.Context; } else { throw TraceUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.InvalidActionForNegotiationMessage, message.Headers.Action)), message); } } static Message CreateReply(Message request, XmlDictionaryString action, BodyWriter body) { if (request.Headers.MessageId != null) { Message reply = Message.CreateMessage(request.Version, ActionHeader.Create(action, request.Version.Addressing), body); reply.InitializeReply(request); return reply; } else { // the message id may not be present if MapToHttp is true return Message.CreateMessage(request.Version, ActionHeader.Create(action, request.Version.Addressing), body); } } void OnTokenIssued(SecurityToken token) { if (this.issuedSecurityTokenHandler != null) { this.issuedSecurityTokenHandler(token, null); } } void AddNegotiationChannelForIdleTracking() { if (OperationContext.Current.SessionId == null) { return; } lock (ThisLock) { if (this.idlingNegotiationSessionTimer == null) { return; } IChannel channel = OperationContext.Current.Channel; if (!this.activeNegotiationChannels1.Contains(channel) && !this.activeNegotiationChannels2.Contains(channel)) { this.activeNegotiationChannels1.Add(channel); } if (this.isTimerCancelled) { this.isTimerCancelled = false; this.idlingNegotiationSessionTimer.Set(this.negotiationTimeout); } } } void RemoveNegotiationChannelFromIdleTracking() { if (OperationContext.Current.SessionId == null) { return; } lock (ThisLock) { if (this.idlingNegotiationSessionTimer == null) { return; } IChannel channel = OperationContext.Current.Channel; this.activeNegotiationChannels1.Remove(channel); this.activeNegotiationChannels2.Remove(channel); if (this.activeNegotiationChannels1.Count == 0 && this.activeNegotiationChannels2.Count == 0) { this.isTimerCancelled = true; this.idlingNegotiationSessionTimer.Cancel(); } } } void OnIdlingNegotiationSessionTimer(object state) { lock (ThisLock) { if (this.isTimerCancelled || (this.CommunicationObject.State != CommunicationState.Opened && this.CommunicationObject.State != CommunicationState.Opening)) { return; } try { for (int i = 0; i < this.activeNegotiationChannels2.Count; ++i) { this.activeNegotiationChannels2[i].Abort(); } List temp = this.activeNegotiationChannels2; temp.Clear(); this.activeNegotiationChannels2 = this.activeNegotiationChannels1; this.activeNegotiationChannels1 = temp; } #pragma warning suppress 56500 catch (Exception e) { if (Fx.IsFatal(e)) { throw; } } finally { if (this.CommunicationObject.State == CommunicationState.Opened || this.CommunicationObject.State == CommunicationState.Opening) { if (this.activeNegotiationChannels1.Count == 0 && this.activeNegotiationChannels2.Count == 0) { this.isTimerCancelled = true; this.idlingNegotiationSessionTimer.Cancel(); } else { this.idlingNegotiationSessionTimer.Set(this.negotiationTimeout); } } } } } Message ProcessRequestCore(Message request) { if (request == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("request"); } Uri to = null; RequestSecurityToken rst = null; RequestSecurityTokenResponse rstr = null; string context = null; bool disposeRequest = false; bool isNegotiationFailure = true; T negotiationState = null; try { // validate the message size if needed if (this.maxMessageSize < int.MaxValue) { string action = request.Headers.Action; try { using (MessageBuffer buffer = request.CreateBufferedCopy(this.maxMessageSize)) { request = buffer.CreateMessage(); disposeRequest = true; } } catch (QuotaExceededException e) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.SecurityNegotiationMessageTooLarge, action, this.maxMessageSize), e)); } } try { to = request.Headers.To; ParseMessageBody(request, out context, out rst, out rstr); // check if there is existing state if (context != null) { negotiationState = this.stateCache.GetState(context); } else { negotiationState = null; } bool disposeState = false; BodyWriter replyBody; try { if (rst != null) { if (negotiationState != null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new SecurityNegotiationException(SR.GetString(SR.NegotiationStateAlreadyPresent, context))); } replyBody = this.ProcessRequestSecurityToken(request, rst, out negotiationState); lock (negotiationState.ThisLock) { if (negotiationState.IsNegotiationCompleted) { // if session-sct add it to cache and add a redirect header if (!negotiationState.ServiceToken.IsCookieMode) { this.IssuedTokenCache.AddContext(negotiationState.ServiceToken); } this.OnTokenIssued(negotiationState.ServiceToken); SecurityTraceRecordHelper.TraceServiceSecurityNegotiationCompleted(request, this, negotiationState.ServiceToken); disposeState = true; } else { this.stateCache.AddState(context, negotiationState); disposeState = false; } AddNegotiationChannelForIdleTracking(); } } else { if (negotiationState == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new SecurityNegotiationException(SR.GetString(SR.CannotFindNegotiationState, context))); } lock (negotiationState.ThisLock) { replyBody = this.ProcessRequestSecurityTokenResponse(negotiationState, request, rstr); if (negotiationState.IsNegotiationCompleted) { // if session-sct add it to cache and add a redirect header if (!negotiationState.ServiceToken.IsCookieMode) { this.IssuedTokenCache.AddContext(negotiationState.ServiceToken); } this.OnTokenIssued(negotiationState.ServiceToken); SecurityTraceRecordHelper.TraceServiceSecurityNegotiationCompleted(request, this, negotiationState.ServiceToken); disposeState = true; } else { disposeState = false; } } } if (negotiationState.IsNegotiationCompleted && null != this.ListenUri) { if (AuditLevel.Success == (this.messageAuthenticationAuditLevel & AuditLevel.Success)) { string primaryIdentity = negotiationState.GetRemoteIdentityName(); SecurityAuditHelper.WriteSecurityNegotiationSuccessEvent(this.auditLogLocation, this.suppressAuditFailure, request, request.Headers.To, request.Headers.Action, primaryIdentity, this.GetType().Name); } } isNegotiationFailure = false; } catch (Exception exception) { if (Fx.IsFatal(exception)) throw; if (PerformanceCounters.PerformanceCountersEnabled && null != this.ListenUri) { PerformanceCounters.AuthenticationFailed(request, this.ListenUri); } if (AuditLevel.Failure == (this.messageAuthenticationAuditLevel & AuditLevel.Failure)) { try { string primaryIdentity = (negotiationState != null) ? negotiationState.GetRemoteIdentityName() : String.Empty; SecurityAuditHelper.WriteSecurityNegotiationFailureEvent(this.auditLogLocation, this.suppressAuditFailure, request, request.Headers.To, request.Headers.Action, primaryIdentity, this.GetType().Name, exception); } #pragma warning suppress 56500 catch (Exception auditException) { if (Fx.IsFatal(auditException)) throw; DiagnosticUtility.TraceHandledException(auditException, TraceEventType.Error); } } disposeState = true; throw; } finally { if (disposeState) { if (negotiationState != null) { if (context != null) { stateCache.RemoveState(context); } negotiationState.Dispose(); } } } return CreateReply(request, (replyBody is RequestSecurityTokenResponseCollection) ? RequestSecurityTokenResponseFinalAction : RequestSecurityTokenResponseAction, replyBody); } finally { if (disposeRequest) { request.Close(); } } } finally { if (isNegotiationFailure) { AddNegotiationChannelForIdleTracking(); } else if (negotiationState != null && negotiationState.IsNegotiationCompleted) { RemoveNegotiationChannelFromIdleTracking(); } } } // negotiation failure methods Message HandleNegotiationException(Message request, Exception e) { SecurityTraceRecordHelper.TraceServiceSecurityNegotiationFailure( EventTraceActivityHelper.TryExtractActivity(request), this, e); return CreateFault(request, e); } Message CreateFault(Message request, Exception e) { MessageVersion version = request.Version; FaultCode subCode; FaultReason reason; bool isSenderFault; if (e is SecurityTokenValidationException || e is System.ComponentModel.Win32Exception) { subCode = new FaultCode(TrustApr2004Strings.FailedAuthenticationFaultCode, TrustFeb2005Strings.Namespace); reason = new FaultReason(SR.GetString(SR.FailedAuthenticationTrustFaultCode), CultureInfo.CurrentCulture); isSenderFault = true; } else if (e is QuotaExceededException) { // send a receiver fault so that the sender can retry subCode = new FaultCode(DotNetSecurityStrings.SecurityServerTooBusyFault, DotNetSecurityStrings.Namespace); reason = new FaultReason(SR.GetString(SR.NegotiationQuotasExceededFaultReason), CultureInfo.CurrentCulture); isSenderFault = false; } else { subCode = new FaultCode(TrustApr2004Strings.InvalidRequestFaultCode, TrustFeb2005Strings.Namespace); reason = new FaultReason(SR.GetString(SR.InvalidRequestTrustFaultCode), CultureInfo.CurrentCulture); isSenderFault = true; } FaultCode faultCode; if (isSenderFault) { faultCode = FaultCode.CreateSenderFaultCode(subCode); } else { faultCode = FaultCode.CreateReceiverFaultCode(subCode); } MessageFault fault = MessageFault.CreateFault(faultCode, reason); Message faultReply = Message.CreateMessage(version, fault, version.Addressing.DefaultFaultAction); faultReply.Headers.RelatesTo = request.Headers.MessageId; return faultReply; } class NegotiationHost : ServiceHostBase { NegotiationTokenAuthenticator authenticator; Uri listenUri; ChannelBuilder channelBuilder; MessageFilter listenerFilter; public NegotiationHost(NegotiationTokenAuthenticator authenticator, Uri listenUri, ChannelBuilder channelBuilder, MessageFilter listenerFilter) { this.authenticator = authenticator; this.listenUri = listenUri; this.channelBuilder = channelBuilder; this.listenerFilter = listenerFilter; } protected override ServiceDescription CreateDescription(out IDictionary implementedContracts) { implementedContracts = null; return null; } protected override void InitializeRuntime() { MessageFilter contractFilter = this.listenerFilter; int filterPriority = 10; Type[] endpointChannelTypes = new Type[] { typeof(IReplyChannel), typeof(IDuplexChannel), typeof(IReplySessionChannel), typeof(IDuplexSessionChannel) }; IChannelListener listener = null; BindingParameterCollection parameters = new BindingParameterCollection(this.channelBuilder.BindingParameters); Binding binding = this.channelBuilder.Binding; binding.ReceiveTimeout = this.authenticator.NegotiationTimeout; parameters.Add(new ChannelDemuxerFilter(contractFilter, filterPriority)); DispatcherBuilder.MaybeCreateListener(true, endpointChannelTypes, binding, parameters, this.listenUri, "", ListenUriMode.Explicit, this.ServiceThrottle, out listener); if (listener == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.CannotCreateTwoWayListenerForNegotiation))); } ChannelDispatcher channelDispatcher = new ChannelDispatcher(listener, null, binding); channelDispatcher.MessageVersion = binding.MessageVersion; channelDispatcher.ManualAddressing = true; channelDispatcher.ServiceThrottle = new ServiceThrottle(this); channelDispatcher.ServiceThrottle.MaxConcurrentCalls = this.authenticator.MaximumConcurrentNegotiations; channelDispatcher.ServiceThrottle.MaxConcurrentSessions = this.authenticator.MaximumConcurrentNegotiations; EndpointDispatcher endpointDispatcher = new EndpointDispatcher(new EndpointAddress(this.listenUri), "SecurityNegotiationContract", NamingHelper.DefaultNamespace, true); endpointDispatcher.DispatchRuntime.SingletonInstanceContext = new InstanceContext(null, this.authenticator, false); endpointDispatcher.DispatchRuntime.ConcurrencyMode = ConcurrencyMode.Multiple; endpointDispatcher.AddressFilter = new MatchAllMessageFilter(); endpointDispatcher.ContractFilter = contractFilter; endpointDispatcher.FilterPriority = filterPriority; endpointDispatcher.DispatchRuntime.PrincipalPermissionMode = PrincipalPermissionMode.None; endpointDispatcher.DispatchRuntime.InstanceContextProvider = new SingletonInstanceContextProvider(endpointDispatcher.DispatchRuntime); endpointDispatcher.DispatchRuntime.SynchronizationContext = null; DispatchOperation operation = new DispatchOperation(endpointDispatcher.DispatchRuntime, "*", MessageHeaders.WildcardAction, MessageHeaders.WildcardAction); operation.Formatter = new MessageOperationFormatter(); operation.Invoker = new NegotiationSyncInvoker(this.authenticator); endpointDispatcher.DispatchRuntime.UnhandledDispatchOperation = operation; channelDispatcher.Endpoints.Add(endpointDispatcher); this.ChannelDispatchers.Add(channelDispatcher); } class NegotiationSyncInvoker : IOperationInvoker { NegotiationTokenAuthenticator parent; internal NegotiationSyncInvoker(NegotiationTokenAuthenticator parent) { this.parent = parent; } public bool IsSynchronous { get { return true; } } public object[] AllocateInputs() { return EmptyArray.Allocate(1); } public object Invoke(object instance, object[] inputs, out object[] outputs) { Message request = (Message)inputs[0]; outputs = EmptyArray.Allocate(0); try { return parent.ProcessRequestCore(request); } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } return parent.HandleNegotiationException(request, e); } } public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException()); } public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException()); } } } } }