//----------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- namespace System.ServiceModel.Security { using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.IdentityModel.Tokens; using System.Runtime; using System.Runtime.Diagnostics; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Diagnostics; using System.Xml; using System.ServiceModel.Diagnostics.Application; // IssuanceTokenProviderBase is a base class for token providers that fetch tokens from // another party. // This class manages caching of tokens, async messaging, concurrency abstract class IssuanceTokenProviderBase : CommunicationObjectSecurityTokenProvider where T : IssuanceTokenProviderState { internal const string defaultClientMaxTokenCachingTimeString = "10675199.02:48:05.4775807"; internal const bool defaultClientCacheTokens = true; internal const int defaultServiceTokenValidityThresholdPercentage = 60; // if an issuer is explicitly specified it will be used otherwise target is the issuer EndpointAddress issuerAddress; // the target service's address and via EndpointAddress targetAddress; Uri via = null; // This controls whether the token provider caches the service tokens it obtains bool cacheServiceTokens = defaultClientCacheTokens; // This is a fudge factor that controls how long the client can use a service token int serviceTokenValidityThresholdPercentage = defaultServiceTokenValidityThresholdPercentage; // the maximum time that the client is willing to cache service tokens TimeSpan maxServiceTokenCachingTime; SecurityStandardsManager standardsManager; SecurityAlgorithmSuite algorithmSuite; ChannelProtectionRequirements applicationProtectionRequirements; SecurityToken cachedToken; Object thisLock = new Object(); string sctUri; protected IssuanceTokenProviderBase() : base() { this.cacheServiceTokens = defaultClientCacheTokens; this.serviceTokenValidityThresholdPercentage = defaultServiceTokenValidityThresholdPercentage; this.maxServiceTokenCachingTime = DefaultClientMaxTokenCachingTime; this.standardsManager = null; } // settings public EndpointAddress IssuerAddress { get { return this.issuerAddress; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); this.issuerAddress = value; } } public EndpointAddress TargetAddress { get { return this.targetAddress; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); this.targetAddress = value; } } public bool CacheServiceTokens { get { return this.cacheServiceTokens; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); this.cacheServiceTokens = value; } } internal static TimeSpan DefaultClientMaxTokenCachingTime { get { Fx.Assert(TimeSpan.Parse(defaultClientMaxTokenCachingTimeString, CultureInfo.InvariantCulture) == TimeSpan.MaxValue, "TimeSpan value not correct"); return TimeSpan.MaxValue; } } public int ServiceTokenValidityThresholdPercentage { get { return this.serviceTokenValidityThresholdPercentage; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); if (value <= 0 || value > 100) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", SR.GetString(SR.ValueMustBeInRange, 1, 100))); } this.serviceTokenValidityThresholdPercentage = value; } } public SecurityAlgorithmSuite SecurityAlgorithmSuite { get { return this.algorithmSuite; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); this.algorithmSuite = value; } } public TimeSpan MaxServiceTokenCachingTime { get { return this.maxServiceTokenCachingTime; } 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.maxServiceTokenCachingTime = value; } } public SecurityStandardsManager StandardsManager { get { if (this.standardsManager == null) return SecurityStandardsManager.DefaultInstance; return this.standardsManager; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); this.standardsManager = value; } } public ChannelProtectionRequirements ApplicationProtectionRequirements { get { return this.applicationProtectionRequirements; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); this.applicationProtectionRequirements = value; } } public Uri Via { get { return this.via; } set { this.CommunicationObject.ThrowIfDisposedOrImmutable(); this.via = value; } } public override bool SupportsTokenCancellation { get { return true; } } protected Object ThisLock { get { return this.thisLock; } } protected virtual bool IsMultiLegNegotiation { get { return true; } } protected abstract MessageVersion MessageVersion { get; } protected abstract bool RequiresManualReplyAddressing { get; } public abstract XmlDictionaryString RequestSecurityTokenAction { get; } public abstract XmlDictionaryString RequestSecurityTokenResponseAction { get; } protected string SecurityContextTokenUri { get { ThrowIfCreated(); return this.sctUri; } } protected void ThrowIfCreated() { CommunicationState state = this.CommunicationObject.State; if (state == CommunicationState.Created) { Exception e = new InvalidOperationException(SR.GetString(SR.CommunicationObjectCannotBeUsed, this.GetType().ToString(), state.ToString())); throw TraceUtility.ThrowHelperError(e, Guid.Empty, this); } } protected void ThrowIfClosedOrCreated() { this.CommunicationObject.ThrowIfClosed(); ThrowIfCreated(); } // ISecurityCommunicationObject methods public override void OnOpen(TimeSpan timeout) { if (this.targetAddress == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.TargetAddressIsNotSet, this.GetType()))); } if (this.SecurityAlgorithmSuite == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SecurityAlgorithmSuiteNotSet, this.GetType()))); } this.sctUri = this.StandardsManager.SecureConversationDriver.TokenTypeUri; } // helper methods protected void EnsureEndpointAddressDoesNotRequireEncryption(EndpointAddress target) { if (this.ApplicationProtectionRequirements == null || this.ApplicationProtectionRequirements.OutgoingEncryptionParts == null) { return; } MessagePartSpecification channelEncryptionParts = this.ApplicationProtectionRequirements.OutgoingEncryptionParts.ChannelParts; if (channelEncryptionParts == null) { return; } for (int i = 0; i < this.targetAddress.Headers.Count; ++i) { AddressHeader header = target.Headers[i]; if (channelEncryptionParts.IsHeaderIncluded(header.Name, header.Namespace)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.SecurityNegotiationCannotProtectConfidentialEndpointHeader, target, header.Name, header.Namespace))); } } } DateTime GetServiceTokenEffectiveExpirationTime(SecurityToken serviceToken) { // if the token never expires, return the max date time // else return effective expiration time if (serviceToken.ValidTo.ToUniversalTime() >= SecurityUtils.MaxUtcDateTime) { return serviceToken.ValidTo; } TimeSpan interval = serviceToken.ValidTo.ToUniversalTime() - serviceToken.ValidFrom.ToUniversalTime(); long serviceTokenTicksInterval = interval.Ticks; long effectiveTicksInterval = Convert.ToInt64((double)this.ServiceTokenValidityThresholdPercentage / 100.0 * (double)serviceTokenTicksInterval, NumberFormatInfo.InvariantInfo); DateTime effectiveExpirationTime = TimeoutHelper.Add(serviceToken.ValidFrom.ToUniversalTime(), new TimeSpan(effectiveTicksInterval)); DateTime maxCachingTime = TimeoutHelper.Add(serviceToken.ValidFrom.ToUniversalTime(), this.MaxServiceTokenCachingTime); if (effectiveExpirationTime <= maxCachingTime) { return effectiveExpirationTime; } else { return maxCachingTime; } } bool IsServiceTokenTimeValid(SecurityToken serviceToken) { DateTime effectiveExpirationTime = GetServiceTokenEffectiveExpirationTime(serviceToken); return (DateTime.UtcNow <= effectiveExpirationTime); } SecurityToken GetCurrentServiceToken() { if (this.CacheServiceTokens && this.cachedToken != null && IsServiceTokenTimeValid(cachedToken)) { return this.cachedToken; } else { return null; } } static protected void ThrowIfFault(Message message, EndpointAddress target) { SecurityUtils.ThrowIfNegotiationFault(message, target); } protected override IAsyncResult BeginGetTokenCore(TimeSpan timeout, AsyncCallback callback, object state) { this.CommunicationObject.ThrowIfClosedOrNotOpen(); IAsyncResult asyncResult; lock (ThisLock) { SecurityToken token = GetCurrentServiceToken(); if (token != null) { SecurityTraceRecordHelper.TraceUsingCachedServiceToken(this, token, this.targetAddress); asyncResult = new CompletedAsyncResult(token, callback, state); } else { asyncResult = BeginNegotiation(timeout, callback, state); } } return asyncResult; } protected override SecurityToken EndGetTokenCore(IAsyncResult result) { if (result is CompletedAsyncResult) { return CompletedAsyncResult.End(result); } else { return this.EndNegotiation(result); } } protected override SecurityToken GetTokenCore(TimeSpan timeout) { this.CommunicationObject.ThrowIfClosedOrNotOpen(); SecurityToken result; lock (ThisLock) { result = GetCurrentServiceToken(); if (result != null) { SecurityTraceRecordHelper.TraceUsingCachedServiceToken(this, result, this.targetAddress); } } if (result == null) { result = DoNegotiation(timeout); } return result; } protected override void CancelTokenCore(TimeSpan timeout, SecurityToken token) { if (this.CacheServiceTokens) { lock (ThisLock) { if (Object.ReferenceEquals(token, this.cachedToken)) { this.cachedToken = null; } } } } // Negotiation state creation methods protected abstract bool CreateNegotiationStateCompletesSynchronously(EndpointAddress target, Uri via); protected abstract IAsyncResult BeginCreateNegotiationState(EndpointAddress target, Uri via, TimeSpan timeout, AsyncCallback callback, object state); protected abstract T CreateNegotiationState(EndpointAddress target, Uri via, TimeSpan timeout); protected abstract T EndCreateNegotiationState(IAsyncResult result); // Negotiation message processing methods protected abstract BodyWriter GetFirstOutgoingMessageBody(T negotiationState, out MessageProperties properties); protected abstract BodyWriter GetNextOutgoingMessageBody(Message incomingMessage, T negotiationState); protected abstract bool WillInitializeChannelFactoriesCompleteSynchronously(EndpointAddress target); protected abstract void InitializeChannelFactories(EndpointAddress target, TimeSpan timeout); protected abstract IAsyncResult BeginInitializeChannelFactories(EndpointAddress target, TimeSpan timeout, AsyncCallback callback, object state); protected abstract void EndInitializeChannelFactories(IAsyncResult result); protected abstract IRequestChannel CreateClientChannel(EndpointAddress target, Uri via); void PrepareRequest(Message nextMessage) { PrepareRequest(nextMessage, null); } void PrepareRequest(Message nextMessage, RequestSecurityToken rst) { if (rst != null && !rst.IsReadOnly) { rst.Message = nextMessage; } RequestReplyCorrelator.PrepareRequest(nextMessage); if (this.RequiresManualReplyAddressing) { // if we are on HTTP, we need to explicitly add a reply-to header for interop nextMessage.Headers.ReplyTo = EndpointAddress.AnonymousAddress; } } /* * Negotiation consists of the following steps (some may be async in the async case): * 1. Create negotiation state * 2. Initialize channel factories * 3. Create an channel * 4. Open the channel * 5. Create the next message to send to server * 6. Send the message and get reply * 8. Process incoming message and get next outgoing message. * 9. If no outgoing message, then negotiation is over. Go to step 11. * 10. Goto step 6 * 11. Close the IRequest channel and complete */ protected SecurityToken DoNegotiation(TimeSpan timeout) { ThrowIfClosedOrCreated(); SecurityTraceRecordHelper.TraceBeginSecurityNegotiation(this, this.targetAddress); TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); IRequestChannel rstChannel = null; T negotiationState = null; TimeSpan timeLeft = timeout; int legs = 1; try { negotiationState = this.CreateNegotiationState(this.targetAddress, this.via, timeoutHelper.RemainingTime()); InitializeNegotiationState(negotiationState); this.InitializeChannelFactories(negotiationState.RemoteAddress, timeoutHelper.RemainingTime()); rstChannel = this.CreateClientChannel(negotiationState.RemoteAddress, this.via); rstChannel.Open(timeoutHelper.RemainingTime()); Message nextOutgoingMessage = null; Message incomingMessage = null; SecurityToken serviceToken = null; for (;;) { nextOutgoingMessage = this.GetNextOutgoingMessage(incomingMessage, negotiationState); if (incomingMessage != null) { incomingMessage.Close(); } if (nextOutgoingMessage != null) { using (nextOutgoingMessage) { EventTraceActivity eventTraceActivity = null; if (TD.MessageSentToTransportIsEnabled()) { eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(nextOutgoingMessage); } TraceUtility.ProcessOutgoingMessage(nextOutgoingMessage, eventTraceActivity); timeLeft = timeoutHelper.RemainingTime(); incomingMessage = rstChannel.Request(nextOutgoingMessage, timeLeft); if (incomingMessage == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(SR.GetString(SR.FailToRecieveReplyFromNegotiation))); } if (eventTraceActivity == null && TD.MessageReceivedFromTransportIsEnabled()) { eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(incomingMessage); } TraceUtility.ProcessIncomingMessage(incomingMessage, eventTraceActivity); } legs += 2; } else { if (!negotiationState.IsNegotiationCompleted) { throw TraceUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.NoNegotiationMessageToSend)), incomingMessage); } try { rstChannel.Close(timeoutHelper.RemainingTime()); } catch (CommunicationException e) { DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); rstChannel.Abort(); } catch (TimeoutException e) { if (TD.CloseTimeoutIsEnabled()) { TD.CloseTimeout(e.Message); } DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); rstChannel.Abort(); } rstChannel = null; this.ValidateAndCacheServiceToken(negotiationState); serviceToken = negotiationState.ServiceToken; SecurityTraceRecordHelper.TraceEndSecurityNegotiation(this, serviceToken, this.targetAddress); break; } } return serviceToken; } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } if (e is TimeoutException) { e = new TimeoutException(SR.GetString(SR.ClientSecurityNegotiationTimeout, timeout, legs, timeLeft), e); } EndpointAddress temp = (negotiationState == null) ? null : negotiationState.RemoteAddress; throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(WrapExceptionIfRequired(e, temp, this.issuerAddress)); } finally { Cleanup(rstChannel, negotiationState); } } void InitializeNegotiationState(T negotiationState) { negotiationState.TargetAddress = this.targetAddress; if (negotiationState.Context == null && this.IsMultiLegNegotiation) { negotiationState.Context = SecurityUtils.GenerateId(); } if (this.IssuerAddress != null) { negotiationState.RemoteAddress = this.IssuerAddress; } else { negotiationState.RemoteAddress = negotiationState.TargetAddress; } } Message GetNextOutgoingMessage(Message incomingMessage, T negotiationState) { BodyWriter nextMessageBody; MessageProperties nextMessageProperties = null; if (incomingMessage == null) { nextMessageBody = this.GetFirstOutgoingMessageBody(negotiationState, out nextMessageProperties); } else { nextMessageBody = this.GetNextOutgoingMessageBody(incomingMessage, negotiationState); } if (nextMessageBody != null) { Message nextMessage; if (incomingMessage == null) { nextMessage = Message.CreateMessage(this.MessageVersion, ActionHeader.Create(this.RequestSecurityTokenAction, this.MessageVersion.Addressing), nextMessageBody); } else { nextMessage = Message.CreateMessage(this.MessageVersion, ActionHeader.Create(this.RequestSecurityTokenResponseAction, this.MessageVersion.Addressing), nextMessageBody); } if (nextMessageProperties != null) { nextMessage.Properties.CopyProperties(nextMessageProperties); } PrepareRequest(nextMessage, nextMessageBody as RequestSecurityToken); return nextMessage; } else { return null; } } void Cleanup(IChannel rstChannel, T negotiationState) { if (negotiationState != null) { negotiationState.Dispose(); } if (rstChannel != null) { rstChannel.Abort(); } } protected IAsyncResult BeginNegotiation(TimeSpan timeout, AsyncCallback callback, object state) { ThrowIfClosedOrCreated(); SecurityTraceRecordHelper.TraceBeginSecurityNegotiation(this, this.targetAddress); return new SecurityNegotiationAsyncResult(this, timeout, callback, state); } protected SecurityToken EndNegotiation(IAsyncResult result) { SecurityToken token = SecurityNegotiationAsyncResult.End(result); SecurityTraceRecordHelper.TraceEndSecurityNegotiation(this, token, this.targetAddress); return token; } protected virtual void ValidateKeySize(GenericXmlSecurityToken issuedToken) { if (this.SecurityAlgorithmSuite == null) { return; } ReadOnlyCollection issuedKeys = issuedToken.SecurityKeys; if (issuedKeys != null && issuedKeys.Count == 1) { SymmetricSecurityKey symmetricKey = issuedKeys[0] as SymmetricSecurityKey; if (symmetricKey != null) { if (this.SecurityAlgorithmSuite.IsSymmetricKeyLengthSupported(symmetricKey.KeySize)) { return; } else { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.InvalidIssuedTokenKeySize, symmetricKey.KeySize))); } } } else { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.CannotObtainIssuedTokenKeySize))); } } static bool ShouldWrapException(Exception e) { return (e is System.ComponentModel.Win32Exception || e is XmlException || e is InvalidOperationException || e is ArgumentException || e is QuotaExceededException || e is System.Security.SecurityException || e is System.Security.Cryptography.CryptographicException || e is SecurityTokenException); } static Exception WrapExceptionIfRequired(Exception e, EndpointAddress targetAddress, EndpointAddress issuerAddress) { if (ShouldWrapException(e)) { Uri targetUri; if (targetAddress != null) { targetUri = targetAddress.Uri; } else { targetUri = null; } Uri issuerUri; if (issuerAddress != null) { issuerUri = issuerAddress.Uri; } else { issuerUri = targetUri; } // => issuerUri != null if (targetUri != null) { e = new SecurityNegotiationException(SR.GetString(SR.SoapSecurityNegotiationFailedForIssuerAndTarget, issuerUri, targetUri), e); } else { e = new SecurityNegotiationException(SR.GetString(SR.SoapSecurityNegotiationFailed), e); } } return e; } void ValidateAndCacheServiceToken(T negotiationState) { this.ValidateKeySize(negotiationState.ServiceToken); lock (ThisLock) { if (this.CacheServiceTokens) { this.cachedToken = negotiationState.ServiceToken; } } } class SecurityNegotiationAsyncResult : AsyncResult { static AsyncCallback createNegotiationStateCallback = Fx.ThunkCallback(new AsyncCallback(CreateNegotiationStateCallback)); static AsyncCallback initializeChannelFactoriesCallback = Fx.ThunkCallback(new AsyncCallback(InitializeChannelFactoriesCallback)); static AsyncCallback closeChannelCallback = Fx.ThunkCallback(new AsyncCallback(CloseChannelCallback)); static AsyncCallback sendRequestCallback = Fx.ThunkCallback(new AsyncCallback(SendRequestCallback)); static AsyncCallback openChannelCallback = Fx.ThunkCallback(new AsyncCallback(OpenChannelCallback)); TimeSpan timeout; TimeoutHelper timeoutHelper; SecurityToken serviceToken; IssuanceTokenProviderBase tokenProvider; IRequestChannel rstChannel; T negotiationState; Message nextOutgoingMessage; EndpointAddress target; EndpointAddress issuer; Uri via; public SecurityNegotiationAsyncResult(IssuanceTokenProviderBase tokenProvider, TimeSpan timeout, AsyncCallback callback, object state) : base(callback, state) { this.timeout = timeout; timeoutHelper = new TimeoutHelper(timeout); this.tokenProvider = tokenProvider; this.target = tokenProvider.targetAddress; this.issuer = tokenProvider.issuerAddress; this.via = tokenProvider.via; bool completeSelf = false; try { completeSelf = this.StartNegotiation(); } #pragma warning suppress 56500 // covered by FxCOP catch (Exception e) { if (Fx.IsFatal(e)) { throw; } throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(this.OnSyncNegotiationFailure(e)); } if (completeSelf) { this.OnNegotiationComplete(); Complete(true); } } bool StartNegotiation() { if (this.tokenProvider.CreateNegotiationStateCompletesSynchronously(this.target, this.via)) { this.negotiationState = this.tokenProvider.CreateNegotiationState(target, this.via, timeoutHelper.RemainingTime()); } else { IAsyncResult createStateResult = this.tokenProvider.BeginCreateNegotiationState(target, this.via, timeoutHelper.RemainingTime(), createNegotiationStateCallback, this); if (!createStateResult.CompletedSynchronously) { return false; } this.negotiationState = this.tokenProvider.EndCreateNegotiationState(createStateResult); } return this.OnCreateStateComplete(); } static void CreateNegotiationStateCallback(IAsyncResult result) { if (result.CompletedSynchronously) { return; } SecurityNegotiationAsyncResult self = (SecurityNegotiationAsyncResult)result.AsyncState; bool completeSelf = false; Exception completionException = null; try { self.negotiationState = self.tokenProvider.EndCreateNegotiationState(result); completeSelf = self.OnCreateStateComplete(); if (completeSelf) { self.OnNegotiationComplete(); } } #pragma warning suppress 56500 // covered by FxCOP catch (Exception e) { if (Fx.IsFatal(e)) throw; completeSelf = true; completionException = self.OnAsyncNegotiationFailure(e); } if (completeSelf) { self.Complete(false, completionException); } } bool OnCreateStateComplete() { this.tokenProvider.InitializeNegotiationState(negotiationState); return InitializeChannelFactories(); } bool InitializeChannelFactories() { if (this.tokenProvider.WillInitializeChannelFactoriesCompleteSynchronously(negotiationState.RemoteAddress)) { this.tokenProvider.InitializeChannelFactories(negotiationState.RemoteAddress, timeoutHelper.RemainingTime()); } else { IAsyncResult result = this.tokenProvider.BeginInitializeChannelFactories(negotiationState.RemoteAddress, timeoutHelper.RemainingTime(), initializeChannelFactoriesCallback, this); if (!result.CompletedSynchronously) { return false; } this.tokenProvider.EndInitializeChannelFactories(result); } return this.OnChannelFactoriesInitialized(); } static void InitializeChannelFactoriesCallback(IAsyncResult result) { if (result.CompletedSynchronously) { return; } SecurityNegotiationAsyncResult self = (SecurityNegotiationAsyncResult)result.AsyncState; bool completeSelf = false; Exception completionException = null; try { self.tokenProvider.EndInitializeChannelFactories(result); completeSelf = self.OnChannelFactoriesInitialized(); if (completeSelf) { self.OnNegotiationComplete(); } } #pragma warning suppress 56500 // covered by FxCOP catch (Exception e) { if (Fx.IsFatal(e)) throw; completeSelf = true; completionException = self.OnAsyncNegotiationFailure(e); } if (completeSelf) { self.Complete(false, completionException); } } bool OnChannelFactoriesInitialized() { this.rstChannel = this.tokenProvider.CreateClientChannel(negotiationState.RemoteAddress, this.via); this.nextOutgoingMessage = null; return this.OnRequestChannelCreated(); } bool OnRequestChannelCreated() { IAsyncResult result = rstChannel.BeginOpen(timeoutHelper.RemainingTime(), openChannelCallback, this); if (!result.CompletedSynchronously) { return false; } rstChannel.EndOpen(result); return this.OnRequestChannelOpened(); } static void OpenChannelCallback(IAsyncResult result) { if (result.CompletedSynchronously) { return; } SecurityNegotiationAsyncResult self = (SecurityNegotiationAsyncResult)result.AsyncState; bool completeSelf = false; Exception completionException = null; try { self.rstChannel.EndOpen(result); completeSelf = self.OnRequestChannelOpened(); if (completeSelf) { self.OnNegotiationComplete(); } } #pragma warning suppress 56500 // covered by FxCOP catch (Exception e) { if (Fx.IsFatal(e)) throw; completeSelf = true; completionException = self.OnAsyncNegotiationFailure(e); } if (completeSelf) { self.Complete(false, completionException); } } bool OnRequestChannelOpened() { return this.SendRequest(); } bool SendRequest() { if (this.nextOutgoingMessage == null) { return this.DoNegotiation(null); } else { this.tokenProvider.PrepareRequest(this.nextOutgoingMessage); bool closeMessage = true; Message incomingMessage = null; IAsyncResult result = null; try { result = this.rstChannel.BeginRequest(this.nextOutgoingMessage, timeoutHelper.RemainingTime(), sendRequestCallback, this); if (!result.CompletedSynchronously) { closeMessage = false; return false; } incomingMessage = rstChannel.EndRequest(result); } finally { if (closeMessage && this.nextOutgoingMessage != null) { this.nextOutgoingMessage.Close(); } } using (incomingMessage) { if (incomingMessage == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.FailToRecieveReplyFromNegotiation))); } return this.DoNegotiation(incomingMessage); } } } static void SendRequestCallback(IAsyncResult result) { if (result.CompletedSynchronously) { return; } SecurityNegotiationAsyncResult self = (SecurityNegotiationAsyncResult)result.AsyncState; bool completeSelf = false; Exception completionException = null; try { Message incomingMessage = null; try { incomingMessage = self.rstChannel.EndRequest(result); } finally { if (self.nextOutgoingMessage != null) { self.nextOutgoingMessage.Close(); } } using (incomingMessage) { if (incomingMessage == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.FailToRecieveReplyFromNegotiation))); } completeSelf = self.DoNegotiation(incomingMessage); } if (completeSelf) { self.OnNegotiationComplete(); } } #pragma warning suppress 56500 // covered by FxCOP catch (Exception e) { if (Fx.IsFatal(e)) throw; completeSelf = true; completionException = self.OnAsyncNegotiationFailure(e); } if (completeSelf) { self.Complete(false, completionException); } } bool DoNegotiation(Message incomingMessage) { this.nextOutgoingMessage = this.tokenProvider.GetNextOutgoingMessage(incomingMessage, this.negotiationState); if (this.nextOutgoingMessage != null) { return SendRequest(); } else { if (!negotiationState.IsNegotiationCompleted) { throw TraceUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.NoNegotiationMessageToSend)), incomingMessage); } return this.CloseRequestChannel(); } } bool CloseRequestChannel() { IAsyncResult result = rstChannel.BeginClose(timeoutHelper.RemainingTime(), closeChannelCallback, this); if (!result.CompletedSynchronously) { return false; } rstChannel.EndClose(result); return true; } static void CloseChannelCallback(IAsyncResult result) { if (result.CompletedSynchronously) { return; } SecurityNegotiationAsyncResult self = (SecurityNegotiationAsyncResult)result.AsyncState; bool completeSelf = false; Exception completionException = null; try { self.rstChannel.EndClose(result); self.OnNegotiationComplete(); completeSelf = true; } #pragma warning suppress 56500 // covered by FxCOP catch (Exception e) { if (Fx.IsFatal(e)) throw; completeSelf = true; completionException = self.OnAsyncNegotiationFailure(e); } if (completeSelf) { self.Complete(false, completionException); } } void Cleanup() { this.tokenProvider.Cleanup(this.rstChannel, this.negotiationState); this.rstChannel = null; this.negotiationState = null; } Exception OnAsyncNegotiationFailure(Exception e) { EndpointAddress pinnedEpr = null; try { pinnedEpr = (this.negotiationState == null) ? null : this.negotiationState.RemoteAddress; Cleanup(); } catch (CommunicationException ex) { DiagnosticUtility.TraceHandledException(ex, TraceEventType.Information); } return IssuanceTokenProviderBase.WrapExceptionIfRequired(e, pinnedEpr, this.issuer); } Exception OnSyncNegotiationFailure(Exception e) { EndpointAddress pinnedTarget = (this.negotiationState == null) ? null : this.negotiationState.RemoteAddress; return IssuanceTokenProviderBase.WrapExceptionIfRequired(e, pinnedTarget, this.issuer); } void OnNegotiationComplete() { using (negotiationState) { SecurityToken token = negotiationState.ServiceToken; this.tokenProvider.ValidateAndCacheServiceToken(negotiationState); this.serviceToken = token; } } public static SecurityToken End(IAsyncResult result) { SecurityNegotiationAsyncResult self = AsyncResult.End(result); return self.serviceToken; } } } }