e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
591 lines
21 KiB
C#
591 lines
21 KiB
C#
//------------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//------------------------------------------------------------
|
|
|
|
namespace System.ServiceModel.Security
|
|
{
|
|
using System;
|
|
using System.ServiceModel.Channels;
|
|
using System.ServiceModel.Description;
|
|
using System.ServiceModel;
|
|
using System.Security.Cryptography.Xml;
|
|
using System.IdentityModel.Claims;
|
|
using System.IdentityModel.Policy;
|
|
using System.IdentityModel.Tokens;
|
|
using System.IdentityModel.Selectors;
|
|
using System.ServiceModel.Security.Tokens;
|
|
using System.Runtime.Serialization;
|
|
using System.Xml.Serialization;
|
|
using System.Xml.Schema;
|
|
using System.Xml;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.IO;
|
|
using System.ServiceModel.Security;
|
|
using System.Globalization;
|
|
using System.ServiceModel.Dispatcher;
|
|
using System.Security.Authentication.ExtendedProtection;
|
|
|
|
|
|
class RequestSecurityToken : BodyWriter
|
|
{
|
|
string context;
|
|
string tokenType;
|
|
string requestType;
|
|
SecurityToken entropyToken;
|
|
BinaryNegotiation negotiationData;
|
|
XmlElement rstXml;
|
|
IList<XmlElement> requestProperties;
|
|
byte[] cachedWriteBuffer;
|
|
int cachedWriteBufferLength;
|
|
int keySize;
|
|
Message message;
|
|
SecurityKeyIdentifierClause renewTarget;
|
|
SecurityKeyIdentifierClause closeTarget;
|
|
OnGetBinaryNegotiationCallback onGetBinaryNegotiation;
|
|
SecurityStandardsManager standardsManager;
|
|
bool isReceiver;
|
|
bool isReadOnly;
|
|
object appliesTo;
|
|
DataContractSerializer appliesToSerializer;
|
|
Type appliesToType;
|
|
|
|
object thisLock = new Object();
|
|
|
|
public RequestSecurityToken()
|
|
: this(SecurityStandardsManager.DefaultInstance)
|
|
{
|
|
}
|
|
|
|
public RequestSecurityToken(MessageSecurityVersion messageSecurityVersion, SecurityTokenSerializer securityTokenSerializer)
|
|
: this(SecurityUtils.CreateSecurityStandardsManager(messageSecurityVersion, securityTokenSerializer))
|
|
{
|
|
}
|
|
|
|
|
|
public RequestSecurityToken(MessageSecurityVersion messageSecurityVersion,
|
|
SecurityTokenSerializer securityTokenSerializer,
|
|
XmlElement requestSecurityTokenXml,
|
|
string context,
|
|
string tokenType,
|
|
string requestType,
|
|
int keySize,
|
|
SecurityKeyIdentifierClause renewTarget,
|
|
SecurityKeyIdentifierClause closeTarget)
|
|
: this(SecurityUtils.CreateSecurityStandardsManager(messageSecurityVersion, securityTokenSerializer),
|
|
requestSecurityTokenXml,
|
|
context,
|
|
tokenType,
|
|
requestType,
|
|
keySize,
|
|
renewTarget,
|
|
closeTarget)
|
|
{
|
|
}
|
|
|
|
public RequestSecurityToken(XmlElement requestSecurityTokenXml,
|
|
string context,
|
|
string tokenType,
|
|
string requestType,
|
|
int keySize,
|
|
SecurityKeyIdentifierClause renewTarget,
|
|
SecurityKeyIdentifierClause closeTarget)
|
|
: this(SecurityStandardsManager.DefaultInstance,
|
|
requestSecurityTokenXml,
|
|
context,
|
|
tokenType,
|
|
requestType,
|
|
keySize,
|
|
renewTarget,
|
|
closeTarget)
|
|
{
|
|
}
|
|
|
|
internal RequestSecurityToken(SecurityStandardsManager standardsManager,
|
|
XmlElement rstXml,
|
|
string context,
|
|
string tokenType,
|
|
string requestType,
|
|
int keySize,
|
|
SecurityKeyIdentifierClause renewTarget,
|
|
SecurityKeyIdentifierClause closeTarget)
|
|
: base(true)
|
|
{
|
|
if (standardsManager == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("standardsManager"));
|
|
}
|
|
this.standardsManager = standardsManager;
|
|
if (rstXml == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("rstXml");
|
|
this.rstXml = rstXml;
|
|
this.context = context;
|
|
this.tokenType = tokenType;
|
|
this.keySize = keySize;
|
|
this.requestType = requestType;
|
|
this.renewTarget = renewTarget;
|
|
this.closeTarget = closeTarget;
|
|
this.isReceiver = true;
|
|
this.isReadOnly = true;
|
|
}
|
|
|
|
internal RequestSecurityToken(SecurityStandardsManager standardsManager)
|
|
: this(standardsManager, true)
|
|
{
|
|
// no op
|
|
}
|
|
|
|
internal RequestSecurityToken(SecurityStandardsManager standardsManager, bool isBuffered)
|
|
: base(isBuffered)
|
|
{
|
|
if (standardsManager == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("standardsManager"));
|
|
}
|
|
this.standardsManager = standardsManager;
|
|
this.requestType = this.standardsManager.TrustDriver.RequestTypeIssue;
|
|
this.requestProperties = null;
|
|
this.isReceiver = false;
|
|
this.isReadOnly = false;
|
|
}
|
|
|
|
public ChannelBinding GetChannelBinding()
|
|
{
|
|
if (this.message == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
ChannelBindingMessageProperty channelBindingMessageProperty = null;
|
|
ChannelBindingMessageProperty.TryGet( this.message, out channelBindingMessageProperty );
|
|
ChannelBinding channelBinding = null;
|
|
|
|
if ( channelBindingMessageProperty != null )
|
|
{
|
|
channelBinding = channelBindingMessageProperty.ChannelBinding;
|
|
}
|
|
|
|
return channelBinding;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Will hold a reference to the outbound message from which we will fish the ChannelBinding out of.
|
|
/// </summary>
|
|
public Message Message
|
|
{
|
|
get { return message; }
|
|
set { message = value; }
|
|
}
|
|
|
|
|
|
public string Context
|
|
{
|
|
get
|
|
{
|
|
return this.context;
|
|
}
|
|
set
|
|
{
|
|
if (this.IsReadOnly)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ObjectIsReadOnly)));
|
|
this.context = value;
|
|
}
|
|
}
|
|
|
|
public string TokenType
|
|
{
|
|
get
|
|
{
|
|
return this.tokenType;
|
|
}
|
|
set
|
|
{
|
|
if (this.IsReadOnly)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ObjectIsReadOnly)));
|
|
this.tokenType = value;
|
|
}
|
|
}
|
|
|
|
public int KeySize
|
|
{
|
|
get
|
|
{
|
|
return this.keySize;
|
|
}
|
|
set
|
|
{
|
|
if (this.IsReadOnly)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ObjectIsReadOnly)));
|
|
if (value < 0)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", SR.GetString(SR.ValueMustBeNonNegative)));
|
|
this.keySize = value;
|
|
}
|
|
}
|
|
|
|
public bool IsReadOnly
|
|
{
|
|
get
|
|
{
|
|
return this.isReadOnly;
|
|
}
|
|
}
|
|
|
|
public delegate void OnGetBinaryNegotiationCallback( ChannelBinding channelBinding );
|
|
public OnGetBinaryNegotiationCallback OnGetBinaryNegotiation
|
|
{
|
|
get
|
|
{
|
|
return this.onGetBinaryNegotiation;
|
|
}
|
|
set
|
|
{
|
|
if (this.IsReadOnly)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ObjectIsReadOnly)));
|
|
}
|
|
this.onGetBinaryNegotiation = value;
|
|
}
|
|
}
|
|
|
|
public IEnumerable<XmlElement> RequestProperties
|
|
{
|
|
get
|
|
{
|
|
if (this.isReceiver)
|
|
{
|
|
// PreSharp Bug: Property get methods should not throw exceptions.
|
|
#pragma warning suppress 56503
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ItemNotAvailableInDeserializedRST, "RequestProperties")));
|
|
}
|
|
return this.requestProperties;
|
|
}
|
|
set
|
|
{
|
|
if (this.IsReadOnly)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ObjectIsReadOnly)));
|
|
if (value != null)
|
|
{
|
|
int index = 0;
|
|
Collection<XmlElement> coll = new Collection<XmlElement>();
|
|
foreach (XmlElement property in value)
|
|
{
|
|
if (property == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(String.Format(CultureInfo.InvariantCulture, "value[{0}]", index)));
|
|
coll.Add(property);
|
|
++index;
|
|
}
|
|
this.requestProperties = coll;
|
|
}
|
|
else
|
|
{
|
|
this.requestProperties = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
public string RequestType
|
|
{
|
|
get
|
|
{
|
|
return this.requestType;
|
|
}
|
|
set
|
|
{
|
|
if (this.IsReadOnly)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ObjectIsReadOnly)));
|
|
if (value == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
|
|
this.requestType = value;
|
|
}
|
|
}
|
|
|
|
public SecurityKeyIdentifierClause RenewTarget
|
|
{
|
|
get
|
|
{
|
|
return this.renewTarget;
|
|
}
|
|
set
|
|
{
|
|
if (this.IsReadOnly)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ObjectIsReadOnly)));
|
|
this.renewTarget = value;
|
|
}
|
|
}
|
|
|
|
public SecurityKeyIdentifierClause CloseTarget
|
|
{
|
|
get
|
|
{
|
|
return this.closeTarget;
|
|
}
|
|
set
|
|
{
|
|
if (this.IsReadOnly)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ObjectIsReadOnly)));
|
|
this.closeTarget = value;
|
|
}
|
|
}
|
|
|
|
public XmlElement RequestSecurityTokenXml
|
|
{
|
|
get
|
|
{
|
|
if (!this.isReceiver)
|
|
{
|
|
// PreSharp Bug: Property get methods should not throw exceptions.
|
|
#pragma warning suppress 56503
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ItemAvailableInDeserializedRSTOnly, "RequestSecurityTokenXml")));
|
|
}
|
|
return this.rstXml;
|
|
}
|
|
}
|
|
|
|
internal SecurityStandardsManager StandardsManager
|
|
{
|
|
get
|
|
{
|
|
return this.standardsManager;
|
|
}
|
|
set
|
|
{
|
|
if (this.IsReadOnly)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ObjectIsReadOnly)));
|
|
if (value == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("value"));
|
|
}
|
|
this.standardsManager = value;
|
|
}
|
|
}
|
|
|
|
internal bool IsReceiver
|
|
{
|
|
get
|
|
{
|
|
return this.isReceiver;
|
|
}
|
|
}
|
|
|
|
internal object AppliesTo
|
|
{
|
|
get
|
|
{
|
|
if (this.isReceiver)
|
|
{
|
|
// PreSharp Bug: Property get methods should not throw exceptions.
|
|
#pragma warning suppress 56503
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ItemNotAvailableInDeserializedRST, "AppliesTo")));
|
|
}
|
|
return this.appliesTo;
|
|
}
|
|
}
|
|
|
|
internal DataContractSerializer AppliesToSerializer
|
|
{
|
|
get
|
|
{
|
|
if (this.isReceiver)
|
|
{
|
|
// PreSharp Bug: Property get methods should not throw exceptions.
|
|
#pragma warning suppress 56503
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ItemNotAvailableInDeserializedRST, "AppliesToSerializer")));
|
|
}
|
|
return this.appliesToSerializer;
|
|
}
|
|
}
|
|
|
|
internal Type AppliesToType
|
|
{
|
|
get
|
|
{
|
|
if (this.isReceiver)
|
|
{
|
|
// PreSharp Bug: Property get methods should not throw exceptions.
|
|
#pragma warning suppress 56503
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ItemNotAvailableInDeserializedRST, "AppliesToType")));
|
|
}
|
|
return this.appliesToType;
|
|
}
|
|
}
|
|
|
|
protected Object ThisLock
|
|
{
|
|
get
|
|
{
|
|
return this.thisLock;
|
|
}
|
|
}
|
|
|
|
internal void SetBinaryNegotiation(BinaryNegotiation negotiation)
|
|
{
|
|
if (negotiation == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("negotiation");
|
|
if (this.IsReadOnly)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ObjectIsReadOnly)));
|
|
this.negotiationData = negotiation;
|
|
}
|
|
|
|
internal BinaryNegotiation GetBinaryNegotiation()
|
|
{
|
|
if (this.isReceiver)
|
|
{
|
|
return this.standardsManager.TrustDriver.GetBinaryNegotiation(this);
|
|
}
|
|
else if (this.negotiationData == null && this.onGetBinaryNegotiation != null)
|
|
{
|
|
this.onGetBinaryNegotiation(this.GetChannelBinding());
|
|
}
|
|
return this.negotiationData;
|
|
}
|
|
|
|
public SecurityToken GetRequestorEntropy()
|
|
{
|
|
return this.GetRequestorEntropy(null);
|
|
}
|
|
|
|
internal SecurityToken GetRequestorEntropy(SecurityTokenResolver resolver)
|
|
{
|
|
if (this.isReceiver)
|
|
{
|
|
return this.standardsManager.TrustDriver.GetEntropy(this, resolver);
|
|
}
|
|
else
|
|
return this.entropyToken;
|
|
}
|
|
|
|
public void SetRequestorEntropy(byte[] entropy)
|
|
{
|
|
if (this.IsReadOnly)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ObjectIsReadOnly)));
|
|
this.entropyToken = (entropy != null) ? new NonceToken(entropy) : null;
|
|
}
|
|
|
|
internal void SetRequestorEntropy(WrappedKeySecurityToken entropyToken)
|
|
{
|
|
if (this.IsReadOnly)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ObjectIsReadOnly)));
|
|
this.entropyToken = entropyToken;
|
|
}
|
|
|
|
public void SetAppliesTo<T>(T appliesTo, DataContractSerializer serializer)
|
|
{
|
|
if (this.IsReadOnly)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ObjectIsReadOnly)));
|
|
if (appliesTo != null && serializer == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serializer");
|
|
}
|
|
this.appliesTo = appliesTo;
|
|
this.appliesToSerializer = serializer;
|
|
this.appliesToType = typeof(T);
|
|
}
|
|
|
|
public void GetAppliesToQName(out string localName, out string namespaceUri)
|
|
{
|
|
if (!this.isReceiver)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ItemAvailableInDeserializedRSTOnly, "MatchesAppliesTo")));
|
|
this.standardsManager.TrustDriver.GetAppliesToQName(this, out localName, out namespaceUri);
|
|
}
|
|
|
|
public T GetAppliesTo<T>()
|
|
{
|
|
return this.GetAppliesTo<T>(DataContractSerializerDefaults.CreateSerializer(typeof(T), DataContractSerializerDefaults.MaxItemsInObjectGraph));
|
|
}
|
|
|
|
public T GetAppliesTo<T>(XmlObjectSerializer serializer)
|
|
{
|
|
if (this.isReceiver)
|
|
{
|
|
if (serializer == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serializer");
|
|
}
|
|
return this.standardsManager.TrustDriver.GetAppliesTo<T>(this, serializer);
|
|
}
|
|
else
|
|
{
|
|
return (T)this.appliesTo;
|
|
}
|
|
}
|
|
|
|
void OnWriteTo(XmlWriter writer)
|
|
{
|
|
if (this.isReceiver)
|
|
{
|
|
this.rstXml.WriteTo(writer);
|
|
}
|
|
else
|
|
{
|
|
this.standardsManager.TrustDriver.WriteRequestSecurityToken(this, writer);
|
|
}
|
|
}
|
|
|
|
public void WriteTo(XmlWriter writer)
|
|
{
|
|
if (writer == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
|
|
if (this.IsReadOnly)
|
|
{
|
|
// cache the serialized bytes to ensure repeatability
|
|
if (this.cachedWriteBuffer == null)
|
|
{
|
|
MemoryStream stream = new MemoryStream();
|
|
using (XmlDictionaryWriter binaryWriter = XmlDictionaryWriter.CreateBinaryWriter(stream, XD.Dictionary))
|
|
{
|
|
this.OnWriteTo(binaryWriter);
|
|
binaryWriter.Flush();
|
|
stream.Flush();
|
|
stream.Seek(0, SeekOrigin.Begin);
|
|
this.cachedWriteBuffer = stream.GetBuffer();
|
|
this.cachedWriteBufferLength = (int)stream.Length;
|
|
}
|
|
}
|
|
writer.WriteNode(XmlDictionaryReader.CreateBinaryReader(this.cachedWriteBuffer, 0, this.cachedWriteBufferLength, XD.Dictionary, XmlDictionaryReaderQuotas.Max), false);
|
|
}
|
|
else
|
|
this.OnWriteTo(writer);
|
|
}
|
|
|
|
public static RequestSecurityToken CreateFrom(XmlReader reader)
|
|
{
|
|
return CreateFrom(SecurityStandardsManager.DefaultInstance, reader);
|
|
}
|
|
|
|
public static RequestSecurityToken CreateFrom(XmlReader reader, MessageSecurityVersion messageSecurityVersion, SecurityTokenSerializer securityTokenSerializer)
|
|
{
|
|
return CreateFrom(SecurityUtils.CreateSecurityStandardsManager(messageSecurityVersion, securityTokenSerializer), reader);
|
|
}
|
|
|
|
internal static RequestSecurityToken CreateFrom(SecurityStandardsManager standardsManager, XmlReader reader)
|
|
{
|
|
return standardsManager.TrustDriver.CreateRequestSecurityToken(reader);
|
|
}
|
|
|
|
public void MakeReadOnly()
|
|
{
|
|
if (!this.isReadOnly)
|
|
{
|
|
this.isReadOnly = true;
|
|
if (this.requestProperties != null)
|
|
{
|
|
this.requestProperties = new ReadOnlyCollection<XmlElement>(this.requestProperties);
|
|
}
|
|
this.OnMakeReadOnly();
|
|
}
|
|
}
|
|
|
|
internal protected virtual void OnWriteCustomAttributes(XmlWriter writer) { }
|
|
|
|
internal protected virtual void OnWriteCustomElements(XmlWriter writer) { }
|
|
|
|
internal protected virtual void OnMakeReadOnly() { }
|
|
|
|
protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
|
|
{
|
|
WriteTo(writer);
|
|
}
|
|
}
|
|
}
|