977 lines
47 KiB
C#
977 lines
47 KiB
C#
//----------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//------------------------------------------------------------
|
|
|
|
namespace System.ServiceModel.Security
|
|
{
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Diagnostics;
|
|
using System.IdentityModel.Policy;
|
|
using System.IdentityModel.Selectors;
|
|
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.Security.Tokens;
|
|
|
|
// See SecurityProtocolFactory for contracts on subclasses etc
|
|
|
|
// SecureOutgoingMessage and VerifyIncomingMessage take message as
|
|
// ref parameters (instead of taking a message and returning a
|
|
// message) to reduce the likelihood that a caller will forget to
|
|
// do the rest of the processing with the modified message object.
|
|
// Especially, on the sender-side, not sending the modified
|
|
// message will result in sending it with an unencrypted body.
|
|
// Correspondingly, the async versions have out parameters instead
|
|
// of simple return values.
|
|
abstract class SecurityProtocol : ISecurityCommunicationObject
|
|
{
|
|
static ReadOnlyCollection<SupportingTokenProviderSpecification> emptyTokenProviders;
|
|
|
|
ICollection<SupportingTokenProviderSpecification> channelSupportingTokenProviderSpecification;
|
|
Dictionary<string, ICollection<SupportingTokenProviderSpecification>> scopedSupportingTokenProviderSpecification;
|
|
Dictionary<string, Collection<SupportingTokenProviderSpecification>> mergedSupportingTokenProvidersMap;
|
|
SecurityProtocolFactory factory;
|
|
EndpointAddress target;
|
|
Uri via;
|
|
WrapperSecurityCommunicationObject communicationObject;
|
|
ChannelParameterCollection channelParameters;
|
|
|
|
protected SecurityProtocol(SecurityProtocolFactory factory, EndpointAddress target, Uri via)
|
|
{
|
|
this.factory = factory;
|
|
this.target = target;
|
|
this.via = via;
|
|
this.communicationObject = new WrapperSecurityCommunicationObject(this);
|
|
}
|
|
|
|
protected WrapperSecurityCommunicationObject CommunicationObject
|
|
{
|
|
get { return this.communicationObject; }
|
|
}
|
|
|
|
public SecurityProtocolFactory SecurityProtocolFactory
|
|
{
|
|
get { return this.factory; }
|
|
}
|
|
|
|
public EndpointAddress Target
|
|
{
|
|
get { return this.target; }
|
|
}
|
|
|
|
public Uri Via
|
|
{
|
|
get { return this.via; }
|
|
}
|
|
|
|
public ICollection<SupportingTokenProviderSpecification> ChannelSupportingTokenProviderSpecification
|
|
{
|
|
get
|
|
{
|
|
return this.channelSupportingTokenProviderSpecification;
|
|
}
|
|
}
|
|
|
|
public Dictionary<string, ICollection<SupportingTokenProviderSpecification>> ScopedSupportingTokenProviderSpecification
|
|
{
|
|
get
|
|
{
|
|
return this.scopedSupportingTokenProviderSpecification;
|
|
}
|
|
}
|
|
|
|
static ReadOnlyCollection<SupportingTokenProviderSpecification> EmptyTokenProviders
|
|
{
|
|
get
|
|
{
|
|
if (emptyTokenProviders == null)
|
|
{
|
|
emptyTokenProviders = new ReadOnlyCollection<SupportingTokenProviderSpecification>(new List<SupportingTokenProviderSpecification>());
|
|
}
|
|
return emptyTokenProviders;
|
|
}
|
|
}
|
|
|
|
public ChannelParameterCollection ChannelParameters
|
|
{
|
|
get
|
|
{
|
|
return this.channelParameters;
|
|
}
|
|
set
|
|
{
|
|
this.communicationObject.ThrowIfDisposedOrImmutable();
|
|
this.channelParameters = value;
|
|
}
|
|
}
|
|
|
|
// ISecurityCommunicationObject members
|
|
public TimeSpan DefaultOpenTimeout
|
|
{
|
|
get { return ServiceDefaults.OpenTimeout; }
|
|
}
|
|
|
|
public TimeSpan DefaultCloseTimeout
|
|
{
|
|
get { return ServiceDefaults.CloseTimeout; }
|
|
}
|
|
|
|
public IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
return new OperationWithTimeoutAsyncResult(new OperationWithTimeoutCallback(this.OnClose), timeout, callback, state);
|
|
}
|
|
|
|
public IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
return new OperationWithTimeoutAsyncResult(new OperationWithTimeoutCallback(this.OnOpen), timeout, callback, state);
|
|
}
|
|
|
|
public void OnClosed()
|
|
{
|
|
}
|
|
|
|
public void OnClosing()
|
|
{
|
|
}
|
|
|
|
public void OnEndClose(IAsyncResult result)
|
|
{
|
|
OperationWithTimeoutAsyncResult.End(result);
|
|
}
|
|
|
|
public void OnEndOpen(IAsyncResult result)
|
|
{
|
|
OperationWithTimeoutAsyncResult.End(result);
|
|
}
|
|
|
|
public void OnFaulted()
|
|
{
|
|
}
|
|
|
|
public void OnOpened()
|
|
{
|
|
}
|
|
|
|
public void OnOpening()
|
|
{
|
|
}
|
|
|
|
internal IList<SupportingTokenProviderSpecification> GetSupportingTokenProviders(string action)
|
|
{
|
|
if (this.mergedSupportingTokenProvidersMap != null && this.mergedSupportingTokenProvidersMap.Count > 0)
|
|
{
|
|
if (action != null && this.mergedSupportingTokenProvidersMap.ContainsKey(action))
|
|
{
|
|
return this.mergedSupportingTokenProvidersMap[action];
|
|
}
|
|
else if (this.mergedSupportingTokenProvidersMap.ContainsKey(MessageHeaders.WildcardAction))
|
|
{
|
|
return this.mergedSupportingTokenProvidersMap[MessageHeaders.WildcardAction];
|
|
}
|
|
}
|
|
// return null if the token providers list is empty - this gets a perf benefit since calling Count is expensive for an empty
|
|
// ReadOnlyCollection
|
|
return (this.channelSupportingTokenProviderSpecification == EmptyTokenProviders) ? null : (IList<SupportingTokenProviderSpecification>)this.channelSupportingTokenProviderSpecification;
|
|
}
|
|
|
|
protected InitiatorServiceModelSecurityTokenRequirement CreateInitiatorSecurityTokenRequirement()
|
|
{
|
|
InitiatorServiceModelSecurityTokenRequirement requirement = new InitiatorServiceModelSecurityTokenRequirement();
|
|
requirement.TargetAddress = this.Target;
|
|
requirement.Via = this.via;
|
|
requirement.SecurityBindingElement = this.factory.SecurityBindingElement;
|
|
requirement.SecurityAlgorithmSuite = this.factory.OutgoingAlgorithmSuite;
|
|
requirement.MessageSecurityVersion = this.factory.MessageSecurityVersion.SecurityTokenVersion;
|
|
if (this.factory.PrivacyNoticeUri != null)
|
|
{
|
|
requirement.Properties[ServiceModelSecurityTokenRequirement.PrivacyNoticeUriProperty] = this.factory.PrivacyNoticeUri;
|
|
}
|
|
if (this.channelParameters != null)
|
|
{
|
|
requirement.Properties[ServiceModelSecurityTokenRequirement.ChannelParametersCollectionProperty] = this.channelParameters;
|
|
}
|
|
|
|
requirement.Properties[ServiceModelSecurityTokenRequirement.PrivacyNoticeVersionProperty] = this.factory.PrivacyNoticeVersion;
|
|
|
|
return requirement;
|
|
}
|
|
|
|
InitiatorServiceModelSecurityTokenRequirement CreateInitiatorSecurityTokenRequirement(SecurityTokenParameters parameters, SecurityTokenAttachmentMode attachmentMode)
|
|
{
|
|
InitiatorServiceModelSecurityTokenRequirement requirement = CreateInitiatorSecurityTokenRequirement();
|
|
parameters.InitializeSecurityTokenRequirement(requirement);
|
|
requirement.KeyUsage = SecurityKeyUsage.Signature;
|
|
requirement.Properties[ServiceModelSecurityTokenRequirement.MessageDirectionProperty] = MessageDirection.Output;
|
|
requirement.Properties[ServiceModelSecurityTokenRequirement.SupportingTokenAttachmentModeProperty] = attachmentMode;
|
|
return requirement;
|
|
}
|
|
|
|
void AddSupportingTokenProviders(SupportingTokenParameters supportingTokenParameters, bool isOptional, IList<SupportingTokenProviderSpecification> providerSpecList)
|
|
{
|
|
for (int i = 0; i < supportingTokenParameters.Endorsing.Count; ++i)
|
|
{
|
|
SecurityTokenRequirement requirement = this.CreateInitiatorSecurityTokenRequirement(supportingTokenParameters.Endorsing[i], SecurityTokenAttachmentMode.Endorsing);
|
|
try
|
|
{
|
|
if (isOptional)
|
|
{
|
|
requirement.IsOptionalToken = true;
|
|
}
|
|
System.IdentityModel.Selectors.SecurityTokenProvider provider = this.factory.SecurityTokenManager.CreateSecurityTokenProvider(requirement);
|
|
if (provider == null)
|
|
{
|
|
continue;
|
|
}
|
|
SupportingTokenProviderSpecification providerSpec = new SupportingTokenProviderSpecification(provider, SecurityTokenAttachmentMode.Endorsing, supportingTokenParameters.Endorsing[i]);
|
|
providerSpecList.Add(providerSpec);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (!isOptional || Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
for (int i = 0; i < supportingTokenParameters.SignedEndorsing.Count; ++i)
|
|
{
|
|
SecurityTokenRequirement requirement = this.CreateInitiatorSecurityTokenRequirement(supportingTokenParameters.SignedEndorsing[i], SecurityTokenAttachmentMode.SignedEndorsing);
|
|
try
|
|
{
|
|
if (isOptional)
|
|
{
|
|
requirement.IsOptionalToken = true;
|
|
}
|
|
System.IdentityModel.Selectors.SecurityTokenProvider provider = this.factory.SecurityTokenManager.CreateSecurityTokenProvider(requirement);
|
|
if (provider == null)
|
|
{
|
|
continue;
|
|
}
|
|
SupportingTokenProviderSpecification providerSpec = new SupportingTokenProviderSpecification(provider, SecurityTokenAttachmentMode.SignedEndorsing, supportingTokenParameters.SignedEndorsing[i]);
|
|
providerSpecList.Add(providerSpec);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (!isOptional || Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
for (int i = 0; i < supportingTokenParameters.SignedEncrypted.Count; ++i)
|
|
{
|
|
SecurityTokenRequirement requirement = this.CreateInitiatorSecurityTokenRequirement(supportingTokenParameters.SignedEncrypted[i], SecurityTokenAttachmentMode.SignedEncrypted);
|
|
try
|
|
{
|
|
if (isOptional)
|
|
{
|
|
requirement.IsOptionalToken = true;
|
|
}
|
|
System.IdentityModel.Selectors.SecurityTokenProvider provider = this.factory.SecurityTokenManager.CreateSecurityTokenProvider(requirement);
|
|
if (provider == null)
|
|
{
|
|
continue;
|
|
}
|
|
SupportingTokenProviderSpecification providerSpec = new SupportingTokenProviderSpecification(provider, SecurityTokenAttachmentMode.SignedEncrypted, supportingTokenParameters.SignedEncrypted[i]);
|
|
providerSpecList.Add(providerSpec);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (!isOptional || Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
for (int i = 0; i < supportingTokenParameters.Signed.Count; ++i)
|
|
{
|
|
SecurityTokenRequirement requirement = this.CreateInitiatorSecurityTokenRequirement(supportingTokenParameters.Signed[i], SecurityTokenAttachmentMode.Signed);
|
|
try
|
|
{
|
|
if (isOptional)
|
|
{
|
|
requirement.IsOptionalToken = true;
|
|
}
|
|
System.IdentityModel.Selectors.SecurityTokenProvider provider = this.factory.SecurityTokenManager.CreateSecurityTokenProvider(requirement);
|
|
if (provider == null)
|
|
{
|
|
continue;
|
|
}
|
|
SupportingTokenProviderSpecification providerSpec = new SupportingTokenProviderSpecification(provider, SecurityTokenAttachmentMode.Signed, supportingTokenParameters.Signed[i]);
|
|
providerSpecList.Add(providerSpec);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (!isOptional || Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MergeSupportingTokenProviders(TimeSpan timeout)
|
|
{
|
|
if (this.ScopedSupportingTokenProviderSpecification.Count == 0)
|
|
{
|
|
this.mergedSupportingTokenProvidersMap = null;
|
|
}
|
|
else
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
this.factory.ExpectSupportingTokens = true;
|
|
this.mergedSupportingTokenProvidersMap = new Dictionary<string, Collection<SupportingTokenProviderSpecification>>();
|
|
foreach (string action in this.ScopedSupportingTokenProviderSpecification.Keys)
|
|
{
|
|
ICollection<SupportingTokenProviderSpecification> scopedProviders = this.ScopedSupportingTokenProviderSpecification[action];
|
|
if (scopedProviders == null || scopedProviders.Count == 0)
|
|
{
|
|
continue;
|
|
}
|
|
Collection<SupportingTokenProviderSpecification> mergedProviders = new Collection<SupportingTokenProviderSpecification>();
|
|
foreach (SupportingTokenProviderSpecification spec in this.channelSupportingTokenProviderSpecification)
|
|
{
|
|
mergedProviders.Add(spec);
|
|
}
|
|
foreach (SupportingTokenProviderSpecification spec in scopedProviders)
|
|
{
|
|
SecurityUtils.OpenTokenProviderIfRequired(spec.TokenProvider, timeoutHelper.RemainingTime());
|
|
if (spec.SecurityTokenAttachmentMode == SecurityTokenAttachmentMode.Endorsing || spec.SecurityTokenAttachmentMode == SecurityTokenAttachmentMode.SignedEndorsing)
|
|
{
|
|
if (spec.TokenParameters.RequireDerivedKeys && !spec.TokenParameters.HasAsymmetricKey)
|
|
{
|
|
this.factory.ExpectKeyDerivation = true;
|
|
}
|
|
}
|
|
mergedProviders.Add(spec);
|
|
}
|
|
this.mergedSupportingTokenProvidersMap.Add(action, mergedProviders);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Open(TimeSpan timeout)
|
|
{
|
|
this.communicationObject.Open(timeout);
|
|
}
|
|
|
|
public IAsyncResult BeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
return this.communicationObject.BeginOpen(timeout, callback, state);
|
|
}
|
|
|
|
public void EndOpen(IAsyncResult result)
|
|
{
|
|
this.communicationObject.EndOpen(result);
|
|
}
|
|
|
|
public virtual void OnOpen(TimeSpan timeout)
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
if (this.factory.ActAsInitiator)
|
|
{
|
|
this.channelSupportingTokenProviderSpecification = new Collection<SupportingTokenProviderSpecification>();
|
|
this.scopedSupportingTokenProviderSpecification = new Dictionary<string, ICollection<SupportingTokenProviderSpecification>>();
|
|
|
|
AddSupportingTokenProviders(this.factory.SecurityBindingElement.EndpointSupportingTokenParameters, false, (IList<SupportingTokenProviderSpecification>)this.channelSupportingTokenProviderSpecification);
|
|
AddSupportingTokenProviders(this.factory.SecurityBindingElement.OptionalEndpointSupportingTokenParameters, true, (IList<SupportingTokenProviderSpecification>)this.channelSupportingTokenProviderSpecification);
|
|
foreach (string action in this.factory.SecurityBindingElement.OperationSupportingTokenParameters.Keys)
|
|
{
|
|
Collection<SupportingTokenProviderSpecification> providerSpecList = new Collection<SupportingTokenProviderSpecification>();
|
|
AddSupportingTokenProviders(this.factory.SecurityBindingElement.OperationSupportingTokenParameters[action], false, providerSpecList);
|
|
this.scopedSupportingTokenProviderSpecification.Add(action, providerSpecList);
|
|
}
|
|
foreach (string action in this.factory.SecurityBindingElement.OptionalOperationSupportingTokenParameters.Keys)
|
|
{
|
|
Collection<SupportingTokenProviderSpecification> providerSpecList;
|
|
ICollection<SupportingTokenProviderSpecification> existingList;
|
|
if (this.scopedSupportingTokenProviderSpecification.TryGetValue(action, out existingList))
|
|
{
|
|
providerSpecList = ((Collection<SupportingTokenProviderSpecification>)existingList);
|
|
}
|
|
else
|
|
{
|
|
providerSpecList = new Collection<SupportingTokenProviderSpecification>();
|
|
this.scopedSupportingTokenProviderSpecification.Add(action, providerSpecList);
|
|
}
|
|
this.AddSupportingTokenProviders(this.factory.SecurityBindingElement.OptionalOperationSupportingTokenParameters[action], true, providerSpecList);
|
|
}
|
|
|
|
if (!this.channelSupportingTokenProviderSpecification.IsReadOnly)
|
|
{
|
|
if (this.channelSupportingTokenProviderSpecification.Count == 0)
|
|
{
|
|
this.channelSupportingTokenProviderSpecification = EmptyTokenProviders;
|
|
}
|
|
else
|
|
{
|
|
this.factory.ExpectSupportingTokens = true;
|
|
foreach (SupportingTokenProviderSpecification tokenProviderSpec in this.channelSupportingTokenProviderSpecification)
|
|
{
|
|
SecurityUtils.OpenTokenProviderIfRequired(tokenProviderSpec.TokenProvider, timeoutHelper.RemainingTime());
|
|
if (tokenProviderSpec.SecurityTokenAttachmentMode == SecurityTokenAttachmentMode.Endorsing || tokenProviderSpec.SecurityTokenAttachmentMode == SecurityTokenAttachmentMode.SignedEndorsing)
|
|
{
|
|
if (tokenProviderSpec.TokenParameters.RequireDerivedKeys && !tokenProviderSpec.TokenParameters.HasAsymmetricKey)
|
|
{
|
|
this.factory.ExpectKeyDerivation = true;
|
|
}
|
|
}
|
|
}
|
|
this.channelSupportingTokenProviderSpecification =
|
|
new ReadOnlyCollection<SupportingTokenProviderSpecification>((Collection<SupportingTokenProviderSpecification>)this.channelSupportingTokenProviderSpecification);
|
|
}
|
|
}
|
|
// create a merged map of the per operation supporting tokens
|
|
MergeSupportingTokenProviders(timeoutHelper.RemainingTime());
|
|
}
|
|
}
|
|
|
|
public void Close(bool aborted, TimeSpan timeout)
|
|
{
|
|
if (aborted)
|
|
{
|
|
this.communicationObject.Abort();
|
|
}
|
|
else
|
|
{
|
|
this.communicationObject.Close(timeout);
|
|
}
|
|
}
|
|
|
|
public virtual void OnAbort()
|
|
{
|
|
if (this.factory.ActAsInitiator)
|
|
{
|
|
foreach (SupportingTokenProviderSpecification spec in this.channelSupportingTokenProviderSpecification)
|
|
{
|
|
SecurityUtils.AbortTokenProviderIfRequired(spec.TokenProvider);
|
|
}
|
|
foreach (string action in this.scopedSupportingTokenProviderSpecification.Keys)
|
|
{
|
|
ICollection<SupportingTokenProviderSpecification> supportingProviders = this.scopedSupportingTokenProviderSpecification[action];
|
|
foreach (SupportingTokenProviderSpecification spec in supportingProviders)
|
|
{
|
|
SecurityUtils.AbortTokenProviderIfRequired(spec.TokenProvider);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public virtual void OnClose(TimeSpan timeout)
|
|
{
|
|
if (this.factory.ActAsInitiator)
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
foreach (SupportingTokenProviderSpecification spec in this.channelSupportingTokenProviderSpecification)
|
|
{
|
|
SecurityUtils.CloseTokenProviderIfRequired(spec.TokenProvider, timeoutHelper.RemainingTime());
|
|
}
|
|
foreach (string action in this.scopedSupportingTokenProviderSpecification.Keys)
|
|
{
|
|
ICollection<SupportingTokenProviderSpecification> supportingProviders = this.scopedSupportingTokenProviderSpecification[action];
|
|
foreach (SupportingTokenProviderSpecification spec in supportingProviders)
|
|
{
|
|
SecurityUtils.CloseTokenProviderIfRequired(spec.TokenProvider, timeoutHelper.RemainingTime());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public IAsyncResult BeginClose(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
return this.communicationObject.BeginClose(timeout, callback, state);
|
|
}
|
|
|
|
public void EndClose(IAsyncResult result)
|
|
{
|
|
this.communicationObject.EndClose(result);
|
|
}
|
|
|
|
static void SetSecurityHeaderId(SendSecurityHeader securityHeader, Message message)
|
|
{
|
|
SecurityMessageProperty messageProperty = message.Properties.Security;
|
|
if (messageProperty != null)
|
|
{
|
|
securityHeader.IdPrefix = messageProperty.SenderIdPrefix;
|
|
}
|
|
}
|
|
|
|
void AddSupportingTokenSpecification(SecurityMessageProperty security, IList<SecurityToken> tokens, SecurityTokenAttachmentMode attachmentMode, IDictionary<SecurityToken, ReadOnlyCollection<IAuthorizationPolicy>> tokenPoliciesMapping)
|
|
{
|
|
if (tokens == null || tokens.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
for (int i = 0; i < tokens.Count; ++i)
|
|
{
|
|
security.IncomingSupportingTokens.Add(new SupportingTokenSpecification(tokens[i], tokenPoliciesMapping[tokens[i]], attachmentMode));
|
|
}
|
|
}
|
|
|
|
protected void AddSupportingTokenSpecification(SecurityMessageProperty security, IList<SecurityToken> basicTokens, IList<SecurityToken> endorsingTokens, IList<SecurityToken> signedEndorsingTokens, IList<SecurityToken> signedTokens, IDictionary<SecurityToken, ReadOnlyCollection<IAuthorizationPolicy>> tokenPoliciesMapping)
|
|
{
|
|
AddSupportingTokenSpecification(security, basicTokens, SecurityTokenAttachmentMode.SignedEncrypted, tokenPoliciesMapping);
|
|
AddSupportingTokenSpecification(security, endorsingTokens, SecurityTokenAttachmentMode.Endorsing, tokenPoliciesMapping);
|
|
AddSupportingTokenSpecification(security, signedEndorsingTokens, SecurityTokenAttachmentMode.SignedEndorsing, tokenPoliciesMapping);
|
|
AddSupportingTokenSpecification(security, signedTokens, SecurityTokenAttachmentMode.Signed, tokenPoliciesMapping);
|
|
}
|
|
|
|
protected SendSecurityHeader CreateSendSecurityHeader(Message message, string actor, SecurityProtocolFactory factory)
|
|
{
|
|
return CreateSendSecurityHeader(message, actor, factory, true);
|
|
}
|
|
|
|
protected SendSecurityHeader CreateSendSecurityHeaderForTransportProtocol(Message message, string actor, SecurityProtocolFactory factory)
|
|
{
|
|
return CreateSendSecurityHeader(message, actor, factory, false);
|
|
}
|
|
|
|
SendSecurityHeader CreateSendSecurityHeader(Message message, string actor, SecurityProtocolFactory factory, bool requireMessageProtection)
|
|
{
|
|
MessageDirection transferDirection = factory.ActAsInitiator ? MessageDirection.Input : MessageDirection.Output;
|
|
SendSecurityHeader sendSecurityHeader = factory.StandardsManager.CreateSendSecurityHeader(
|
|
message,
|
|
actor, true, false,
|
|
factory.OutgoingAlgorithmSuite, transferDirection);
|
|
sendSecurityHeader.Layout = factory.SecurityHeaderLayout;
|
|
sendSecurityHeader.RequireMessageProtection = requireMessageProtection;
|
|
SetSecurityHeaderId(sendSecurityHeader, message);
|
|
if (factory.AddTimestamp)
|
|
{
|
|
sendSecurityHeader.AddTimestamp(factory.TimestampValidityDuration);
|
|
}
|
|
|
|
sendSecurityHeader.StreamBufferManager = factory.StreamBufferManager;
|
|
return sendSecurityHeader;
|
|
}
|
|
|
|
internal void AddMessageSupportingTokens(Message message, ref IList<SupportingTokenSpecification> supportingTokens)
|
|
{
|
|
SecurityMessageProperty supportingTokensProperty = message.Properties.Security;
|
|
if (supportingTokensProperty != null && supportingTokensProperty.HasOutgoingSupportingTokens)
|
|
{
|
|
if (supportingTokens == null)
|
|
{
|
|
supportingTokens = new Collection<SupportingTokenSpecification>();
|
|
}
|
|
for (int i = 0; i < supportingTokensProperty.OutgoingSupportingTokens.Count; ++i)
|
|
{
|
|
SupportingTokenSpecification spec = supportingTokensProperty.OutgoingSupportingTokens[i];
|
|
if (spec.SecurityTokenParameters == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.SenderSideSupportingTokensMustSpecifySecurityTokenParameters)));
|
|
}
|
|
supportingTokens.Add(spec);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal bool TryGetSupportingTokens(SecurityProtocolFactory factory, EndpointAddress target, Uri via, Message message, TimeSpan timeout, bool isBlockingCall, out IList<SupportingTokenSpecification> supportingTokens)
|
|
{
|
|
if (!factory.ActAsInitiator)
|
|
{
|
|
supportingTokens = null;
|
|
return true;
|
|
}
|
|
if (message == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message");
|
|
}
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
supportingTokens = null;
|
|
IList<SupportingTokenProviderSpecification> supportingTokenProviders = this.GetSupportingTokenProviders(message.Headers.Action);
|
|
if (supportingTokenProviders != null && supportingTokenProviders.Count > 0)
|
|
{
|
|
// dont do anything if blocking is not allowed
|
|
if (!isBlockingCall)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
supportingTokens = new Collection<SupportingTokenSpecification>();
|
|
for (int i = 0; i < supportingTokenProviders.Count; ++i)
|
|
{
|
|
SupportingTokenProviderSpecification spec = supportingTokenProviders[i];
|
|
SecurityToken supportingToken;
|
|
// The ProviderBackedSecurityToken was added in Win7 to allow KerberosRequestorSecurity
|
|
// to pass a channel binding to InitializeSecurityContext.
|
|
if ((this is TransportSecurityProtocol) && (spec.TokenParameters is KerberosSecurityTokenParameters))
|
|
{
|
|
supportingToken = new ProviderBackedSecurityToken(spec.TokenProvider, timeoutHelper.RemainingTime());
|
|
}
|
|
else
|
|
{
|
|
supportingToken = spec.TokenProvider.GetToken(timeoutHelper.RemainingTime());
|
|
}
|
|
|
|
supportingTokens.Add(new SupportingTokenSpecification(supportingToken, EmptyReadOnlyCollection<IAuthorizationPolicy>.Instance, spec.SecurityTokenAttachmentMode, spec.TokenParameters));
|
|
}
|
|
}
|
|
// add any runtime supporting tokens
|
|
AddMessageSupportingTokens(message, ref supportingTokens);
|
|
|
|
return true;
|
|
}
|
|
|
|
protected IList<SupportingTokenAuthenticatorSpecification> GetSupportingTokenAuthenticatorsAndSetExpectationFlags(SecurityProtocolFactory factory, Message message,
|
|
ReceiveSecurityHeader securityHeader)
|
|
{
|
|
if (factory.ActAsInitiator)
|
|
{
|
|
return null;
|
|
}
|
|
if (message == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message");
|
|
}
|
|
bool expectBasicTokens;
|
|
bool expectSignedTokens;
|
|
bool expectEndorsingTokens;
|
|
IList<SupportingTokenAuthenticatorSpecification> authenticators = factory.GetSupportingTokenAuthenticators(message.Headers.Action,
|
|
out expectSignedTokens, out expectBasicTokens, out expectEndorsingTokens);
|
|
securityHeader.ExpectBasicTokens = expectBasicTokens;
|
|
securityHeader.ExpectEndorsingTokens = expectEndorsingTokens;
|
|
securityHeader.ExpectSignedTokens = expectSignedTokens;
|
|
return authenticators;
|
|
}
|
|
|
|
|
|
protected ReadOnlyCollection<SecurityTokenResolver> MergeOutOfBandResolvers(IList<SupportingTokenAuthenticatorSpecification> supportingAuthenticators, ReadOnlyCollection<SecurityTokenResolver> primaryResolvers)
|
|
{
|
|
Collection<SecurityTokenResolver> outOfBandResolvers = null;
|
|
if (supportingAuthenticators != null && supportingAuthenticators.Count > 0)
|
|
{
|
|
for (int i = 0; i < supportingAuthenticators.Count; ++i)
|
|
{
|
|
if (supportingAuthenticators[i].TokenResolver != null)
|
|
{
|
|
outOfBandResolvers = outOfBandResolvers ?? new Collection<SecurityTokenResolver>();
|
|
outOfBandResolvers.Add(supportingAuthenticators[i].TokenResolver);
|
|
}
|
|
}
|
|
}
|
|
if (outOfBandResolvers != null)
|
|
{
|
|
if (primaryResolvers != null)
|
|
{
|
|
for (int i = 0; i < primaryResolvers.Count; ++i)
|
|
{
|
|
outOfBandResolvers.Insert(0, primaryResolvers[i]);
|
|
}
|
|
}
|
|
return new ReadOnlyCollection<SecurityTokenResolver>(outOfBandResolvers);
|
|
}
|
|
else
|
|
{
|
|
return primaryResolvers ?? EmptyReadOnlyCollection<SecurityTokenResolver>.Instance;
|
|
}
|
|
}
|
|
|
|
|
|
protected void AddSupportingTokens(SendSecurityHeader securityHeader, IList<SupportingTokenSpecification> supportingTokens)
|
|
{
|
|
if (supportingTokens != null)
|
|
{
|
|
for (int i = 0; i < supportingTokens.Count; ++i)
|
|
{
|
|
SecurityToken token = supportingTokens[i].SecurityToken;
|
|
SecurityTokenParameters tokenParameters = supportingTokens[i].SecurityTokenParameters;
|
|
switch (supportingTokens[i].SecurityTokenAttachmentMode)
|
|
{
|
|
case SecurityTokenAttachmentMode.Signed:
|
|
securityHeader.AddSignedSupportingToken(token, tokenParameters);
|
|
break;
|
|
case SecurityTokenAttachmentMode.Endorsing:
|
|
securityHeader.AddEndorsingSupportingToken(token, tokenParameters);
|
|
break;
|
|
case SecurityTokenAttachmentMode.SignedEncrypted:
|
|
securityHeader.AddBasicSupportingToken(token, tokenParameters);
|
|
break;
|
|
case SecurityTokenAttachmentMode.SignedEndorsing:
|
|
securityHeader.AddSignedEndorsingSupportingToken(token, tokenParameters);
|
|
break;
|
|
default:
|
|
Fx.Assert("Unknown token attachment mode " + supportingTokens[i].SecurityTokenAttachmentMode.ToString());
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.UnknownTokenAttachmentMode, supportingTokens[i].SecurityTokenAttachmentMode.ToString())));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public virtual IAsyncResult BeginSecureOutgoingMessage(Message message, TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
SecureOutgoingMessage(ref message, timeout);
|
|
return new CompletedAsyncResult<Message>(message, callback, state);
|
|
}
|
|
|
|
public virtual IAsyncResult BeginSecureOutgoingMessage(Message message, TimeSpan timeout, SecurityProtocolCorrelationState correlationState, AsyncCallback callback, object state)
|
|
{
|
|
SecurityProtocolCorrelationState newCorrelationState = SecureOutgoingMessage(ref message, timeout, correlationState);
|
|
return new CompletedAsyncResult<Message, SecurityProtocolCorrelationState>(message, newCorrelationState, callback, state);
|
|
}
|
|
|
|
public virtual IAsyncResult BeginVerifyIncomingMessage(Message message, TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
VerifyIncomingMessage(ref message, timeout);
|
|
return new CompletedAsyncResult<Message>(message, callback, state);
|
|
}
|
|
|
|
public virtual IAsyncResult BeginVerifyIncomingMessage(Message message, TimeSpan timeout, SecurityProtocolCorrelationState[] correlationStates, AsyncCallback callback, object state)
|
|
{
|
|
SecurityProtocolCorrelationState newCorrelationState = VerifyIncomingMessage(ref message, timeout, correlationStates);
|
|
return new CompletedAsyncResult<Message, SecurityProtocolCorrelationState>(message, newCorrelationState, callback, state);
|
|
}
|
|
|
|
public virtual void EndSecureOutgoingMessage(IAsyncResult result, out Message message)
|
|
{
|
|
message = CompletedAsyncResult<Message>.End(result);
|
|
}
|
|
|
|
public virtual void EndSecureOutgoingMessage(IAsyncResult result, out Message message, out SecurityProtocolCorrelationState newCorrelationState)
|
|
{
|
|
message = CompletedAsyncResult<Message, SecurityProtocolCorrelationState>.End(result, out newCorrelationState);
|
|
}
|
|
|
|
public virtual void EndVerifyIncomingMessage(IAsyncResult result, out Message message)
|
|
{
|
|
message = CompletedAsyncResult<Message>.End(result);
|
|
}
|
|
|
|
public virtual void EndVerifyIncomingMessage(IAsyncResult result, out Message message, out SecurityProtocolCorrelationState newCorrelationState)
|
|
{
|
|
message = CompletedAsyncResult<Message, SecurityProtocolCorrelationState>.End(result, out newCorrelationState);
|
|
}
|
|
|
|
internal static SecurityToken GetToken(SecurityTokenProvider provider, EndpointAddress target, TimeSpan timeout)
|
|
{
|
|
if (provider == null)
|
|
{
|
|
// should this be an ArgumentNullException ?
|
|
// throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("provider"));
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.TokenProviderCannotGetTokensForTarget, target)));
|
|
}
|
|
|
|
SecurityToken token = null;
|
|
|
|
try
|
|
{
|
|
token = provider.GetToken(timeout);
|
|
}
|
|
catch (SecurityTokenException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.TokenProviderCannotGetTokensForTarget, target), exception));
|
|
}
|
|
catch (SecurityNegotiationException sne)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.TokenProviderCannotGetTokensForTarget, target), sne));
|
|
}
|
|
|
|
return token;
|
|
}
|
|
|
|
public abstract void SecureOutgoingMessage(ref Message message, TimeSpan timeout);
|
|
|
|
// subclasses that offer correlation should override this version
|
|
public virtual SecurityProtocolCorrelationState SecureOutgoingMessage(ref Message message, TimeSpan timeout, SecurityProtocolCorrelationState correlationState)
|
|
{
|
|
SecureOutgoingMessage(ref message, timeout);
|
|
return null;
|
|
}
|
|
|
|
protected virtual void OnOutgoingMessageSecured(Message securedMessage)
|
|
{
|
|
SecurityTraceRecordHelper.TraceOutgoingMessageSecured(this, securedMessage);
|
|
}
|
|
|
|
protected virtual void OnSecureOutgoingMessageFailure(Message message)
|
|
{
|
|
SecurityTraceRecordHelper.TraceSecureOutgoingMessageFailure(this, message);
|
|
}
|
|
|
|
public abstract void VerifyIncomingMessage(ref Message message, TimeSpan timeout);
|
|
|
|
// subclasses that offer correlation should override this version
|
|
public virtual SecurityProtocolCorrelationState VerifyIncomingMessage(ref Message message, TimeSpan timeout, params SecurityProtocolCorrelationState[] correlationStates)
|
|
{
|
|
VerifyIncomingMessage(ref message, timeout);
|
|
|
|
return null;
|
|
}
|
|
|
|
protected virtual void OnIncomingMessageVerified(Message verifiedMessage)
|
|
{
|
|
SecurityTraceRecordHelper.TraceIncomingMessageVerified(this, verifiedMessage);
|
|
|
|
if (AuditLevel.Success == (this.factory.MessageAuthenticationAuditLevel & AuditLevel.Success))
|
|
{
|
|
SecurityAuditHelper.WriteMessageAuthenticationSuccessEvent(this.factory.AuditLogLocation,
|
|
this.factory.SuppressAuditFailure, verifiedMessage, verifiedMessage.Headers.To, verifiedMessage.Headers.Action,
|
|
SecurityUtils.GetIdentityNamesFromContext(verifiedMessage.Properties.Security.ServiceSecurityContext.AuthorizationContext));
|
|
}
|
|
}
|
|
|
|
protected virtual void OnVerifyIncomingMessageFailure(Message message, Exception exception)
|
|
{
|
|
SecurityTraceRecordHelper.TraceVerifyIncomingMessageFailure(this, message);
|
|
if (PerformanceCounters.PerformanceCountersEnabled && null != this.factory.ListenUri) //service side
|
|
{
|
|
if ((exception.GetType() == typeof(MessageSecurityException) || exception.GetType().IsSubclassOf(typeof(MessageSecurityException)))
|
|
|| (exception.GetType() == typeof(SecurityTokenException) || exception.GetType().IsSubclassOf(typeof(SecurityTokenException))))
|
|
{
|
|
PerformanceCounters.AuthenticationFailed(message, this.factory.ListenUri);
|
|
}
|
|
}
|
|
|
|
if (AuditLevel.Failure == (this.factory.MessageAuthenticationAuditLevel & AuditLevel.Failure))
|
|
{
|
|
try
|
|
{
|
|
SecurityMessageProperty security = message.Properties.Security;
|
|
string primaryIdentity;
|
|
if (security != null && security.ServiceSecurityContext != null)
|
|
primaryIdentity = SecurityUtils.GetIdentityNamesFromContext(security.ServiceSecurityContext.AuthorizationContext);
|
|
else
|
|
primaryIdentity = SecurityUtils.AnonymousIdentity.Name;
|
|
|
|
SecurityAuditHelper.WriteMessageAuthenticationFailureEvent(this.factory.AuditLogLocation,
|
|
this.factory.SuppressAuditFailure, message, message.Headers.To, message.Headers.Action, primaryIdentity, exception);
|
|
}
|
|
#pragma warning suppress 56500
|
|
catch (Exception auditException)
|
|
{
|
|
if (Fx.IsFatal(auditException))
|
|
throw;
|
|
|
|
DiagnosticUtility.TraceHandledException(auditException, TraceEventType.Error);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected abstract class GetSupportingTokensAsyncResult : AsyncResult
|
|
{
|
|
static AsyncCallback getSupportingTokensCallback = Fx.ThunkCallback(new AsyncCallback(GetSupportingTokenCallback));
|
|
SecurityProtocol binding;
|
|
Message message;
|
|
IList<SupportingTokenSpecification> supportingTokens;
|
|
int currentTokenProviderIndex = 0;
|
|
IList<SupportingTokenProviderSpecification> supportingTokenProviders;
|
|
TimeoutHelper timeoutHelper;
|
|
|
|
public GetSupportingTokensAsyncResult(Message m, SecurityProtocol binding, TimeSpan timeout, AsyncCallback callback, object state)
|
|
: base(callback, state)
|
|
{
|
|
this.message = m;
|
|
this.binding = binding;
|
|
this.timeoutHelper = new TimeoutHelper(timeout);
|
|
}
|
|
|
|
protected IList<SupportingTokenSpecification> SupportingTokens
|
|
{
|
|
get { return this.supportingTokens; }
|
|
}
|
|
|
|
protected abstract bool OnGetSupportingTokensDone(TimeSpan timeout);
|
|
|
|
static void GetSupportingTokenCallback(IAsyncResult result)
|
|
{
|
|
if (result.CompletedSynchronously)
|
|
{
|
|
return;
|
|
}
|
|
GetSupportingTokensAsyncResult self = (GetSupportingTokensAsyncResult)result.AsyncState;
|
|
bool completeSelf;
|
|
Exception completionException = null;
|
|
try
|
|
{
|
|
self.AddSupportingToken(result);
|
|
completeSelf = self.AddSupportingTokens();
|
|
}
|
|
#pragma warning suppress 56500 // covered by FxCOP
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
|
|
completeSelf = true;
|
|
completionException = e;
|
|
}
|
|
if (completeSelf)
|
|
{
|
|
self.Complete(false, completionException);
|
|
}
|
|
}
|
|
|
|
void AddSupportingToken(IAsyncResult result)
|
|
{
|
|
SupportingTokenProviderSpecification spec = supportingTokenProviders[this.currentTokenProviderIndex];
|
|
SecurityTokenProvider.SecurityTokenAsyncResult securityTokenAsyncResult = result as SecurityTokenProvider.SecurityTokenAsyncResult;
|
|
if (securityTokenAsyncResult != null)
|
|
{
|
|
this.supportingTokens.Add(new SupportingTokenSpecification(SecurityTokenProvider.SecurityTokenAsyncResult.End(result), EmptyReadOnlyCollection<IAuthorizationPolicy>.Instance, spec.SecurityTokenAttachmentMode, spec.TokenParameters));
|
|
}
|
|
else
|
|
{
|
|
this.supportingTokens.Add(new SupportingTokenSpecification(spec.TokenProvider.EndGetToken(result), EmptyReadOnlyCollection<IAuthorizationPolicy>.Instance, spec.SecurityTokenAttachmentMode, spec.TokenParameters));
|
|
}
|
|
|
|
++this.currentTokenProviderIndex;
|
|
}
|
|
|
|
bool AddSupportingTokens()
|
|
{
|
|
while (this.currentTokenProviderIndex < supportingTokenProviders.Count)
|
|
{
|
|
SupportingTokenProviderSpecification spec = supportingTokenProviders[this.currentTokenProviderIndex];
|
|
IAsyncResult result = null;
|
|
if ((this.binding is TransportSecurityProtocol) && (spec.TokenParameters is KerberosSecurityTokenParameters))
|
|
{
|
|
result = new SecurityTokenProvider.SecurityTokenAsyncResult(new ProviderBackedSecurityToken(spec.TokenProvider, timeoutHelper.RemainingTime()), null, this);
|
|
}
|
|
else
|
|
{
|
|
result = spec.TokenProvider.BeginGetToken(timeoutHelper.RemainingTime(), getSupportingTokensCallback, this);
|
|
}
|
|
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
return false;
|
|
}
|
|
this.AddSupportingToken(result);
|
|
}
|
|
this.binding.AddMessageSupportingTokens(message, ref this.supportingTokens);
|
|
return this.OnGetSupportingTokensDone(timeoutHelper.RemainingTime());
|
|
}
|
|
|
|
protected void Start()
|
|
{
|
|
bool completeSelf;
|
|
if (this.binding.TryGetSupportingTokens(this.binding.SecurityProtocolFactory, this.binding.Target, this.binding.Via, this.message, timeoutHelper.RemainingTime(), false, out supportingTokens))
|
|
{
|
|
completeSelf = this.OnGetSupportingTokensDone(timeoutHelper.RemainingTime());
|
|
}
|
|
else
|
|
{
|
|
this.supportingTokens = new Collection<SupportingTokenSpecification>();
|
|
this.supportingTokenProviders = this.binding.GetSupportingTokenProviders(message.Headers.Action);
|
|
if (!(this.supportingTokenProviders != null && this.supportingTokenProviders.Count > 0))
|
|
{
|
|
Fx.Assert("There must be at least 1 supporting token provider");
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException("There must be at least 1 supporting token provider"));
|
|
}
|
|
completeSelf = this.AddSupportingTokens();
|
|
}
|
|
if (completeSelf)
|
|
{
|
|
base.Complete(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|