0abdbe5a7d
Former-commit-id: 7467d4b717762eeaf652d77f1486dd11ffb1ff1f
353 lines
15 KiB
C#
353 lines
15 KiB
C#
//
|
|
// ServiceCredentialsSecurityTokenManager.cs
|
|
//
|
|
// Author:
|
|
// Atsushi Enomoto <atsushi@ximian.com>
|
|
//
|
|
// Copyright (C) 2006 Novell, Inc. http://www.novell.com
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
using System;
|
|
using System.Net.Security;
|
|
using System.IdentityModel.Selectors;
|
|
using System.IdentityModel.Tokens;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
using System.ServiceModel;
|
|
using System.ServiceModel.Channels;
|
|
using System.ServiceModel.Description;
|
|
using System.ServiceModel.Security.Tokens;
|
|
|
|
using ReqType = System.ServiceModel.Security.Tokens.ServiceModelSecurityTokenRequirement;
|
|
|
|
namespace System.ServiceModel.Security
|
|
{
|
|
public class ServiceCredentialsSecurityTokenManager : SecurityTokenManager, IEndpointIdentityProvider
|
|
{
|
|
ServiceCredentials credentials;
|
|
|
|
public ServiceCredentialsSecurityTokenManager (
|
|
ServiceCredentials parent)
|
|
{
|
|
this.credentials = parent;
|
|
}
|
|
|
|
public ServiceCredentials ServiceCredentials {
|
|
get { return credentials; }
|
|
}
|
|
|
|
[MonoTODO]
|
|
public virtual EndpointIdentity GetIdentityOfSelf (
|
|
SecurityTokenRequirement tokenRequirement)
|
|
{
|
|
throw new NotImplementedException ();
|
|
}
|
|
|
|
[MonoTODO]
|
|
public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator (
|
|
SecurityTokenRequirement tokenRequirement,
|
|
out SecurityTokenResolver outOfBandTokenResolver)
|
|
{
|
|
outOfBandTokenResolver = null;
|
|
if (tokenRequirement.TokenType == SecurityTokenTypes.UserName)
|
|
return CreateUserNameAuthenticator (tokenRequirement);
|
|
if (tokenRequirement.TokenType == SecurityTokenTypes.X509Certificate)
|
|
return CreateX509Authenticator (tokenRequirement);
|
|
if (tokenRequirement.TokenType == SecurityTokenTypes.Rsa)
|
|
return new RsaSecurityTokenAuthenticator ();
|
|
if (tokenRequirement.TokenType == ServiceModelSecurityTokenTypes.SecureConversation) {
|
|
SecurityBindingElement binding;
|
|
if (!tokenRequirement.TryGetProperty<SecurityBindingElement> (ReqType.SecurityBindingElementProperty, out binding))
|
|
throw new ArgumentException ("SecurityBindingElement is required in the security token requirement");
|
|
SecureConversationSecurityTokenParameters issuedParams;
|
|
if (!tokenRequirement.TryGetProperty<SecureConversationSecurityTokenParameters> (ReqType.IssuedSecurityTokenParametersProperty, out issuedParams))
|
|
throw new ArgumentException ("IssuedSecurityTokenParameters are required in the security token requirement");
|
|
BindingContext issuerBC;
|
|
if (!tokenRequirement.TryGetProperty<BindingContext> (ReqType.IssuerBindingContextProperty, out issuerBC))
|
|
throw new ArgumentException ("IssuerBindingContext is required in the security token requirement");
|
|
SecurityTokenVersion secVer;
|
|
if (!tokenRequirement.TryGetProperty<SecurityTokenVersion> (ReqType.MessageSecurityVersionProperty, out secVer))
|
|
throw new ArgumentException ("MessageSecurityVersion property (of type SecurityTokenVersion) is required in the security token requirement");
|
|
|
|
// FIXME: get parameters from somewhere
|
|
SecurityContextSecurityTokenResolver resolver =
|
|
new SecurityContextSecurityTokenResolver (0x1000, true);
|
|
outOfBandTokenResolver = resolver;
|
|
SecurityContextSecurityTokenAuthenticator sc =
|
|
new SecurityContextSecurityTokenAuthenticator ();
|
|
return new SecureConversationSecurityTokenAuthenticator (tokenRequirement, sc, resolver);
|
|
}
|
|
throw new NotImplementedException ("Not implemented token type: " + tokenRequirement.TokenType);
|
|
}
|
|
|
|
UserNameSecurityTokenAuthenticator CreateUserNameAuthenticator (SecurityTokenRequirement requirement)
|
|
{
|
|
UserNamePasswordServiceCredential c = ServiceCredentials.UserNameAuthentication;
|
|
switch (c.UserNamePasswordValidationMode) {
|
|
case UserNamePasswordValidationMode.MembershipProvider:
|
|
if (c.MembershipProvider == null)
|
|
throw new InvalidOperationException ("For MembershipProvider validation mode, MembershipProvider is required to create a user name token authenticator.");
|
|
return new CustomUserNameSecurityTokenAuthenticator (UserNamePasswordValidator.CreateMembershipProviderValidator (c.MembershipProvider));
|
|
case UserNamePasswordValidationMode.Windows:
|
|
return new WindowsUserNameSecurityTokenAuthenticator (c.IncludeWindowsGroups);
|
|
default:
|
|
if (c.CustomUserNamePasswordValidator == null)
|
|
throw new InvalidOperationException ("For Custom validation mode, CustomUserNamePasswordValidator is required to create a user name token authenticator.");
|
|
return new CustomUserNameSecurityTokenAuthenticator (c.CustomUserNamePasswordValidator);
|
|
}
|
|
}
|
|
|
|
X509SecurityTokenAuthenticator CreateX509Authenticator (SecurityTokenRequirement requirement)
|
|
{
|
|
X509CertificateInitiatorServiceCredential c = ServiceCredentials.ClientCertificate;
|
|
switch (c.Authentication.CertificateValidationMode) {
|
|
case X509CertificateValidationMode.Custom:
|
|
if (c.Authentication.CustomCertificateValidator == null)
|
|
throw new InvalidOperationException ("For Custom certificate validation mode, CustomCertificateValidator is required to create a token authenticator for X509 certificate.");
|
|
return new X509SecurityTokenAuthenticator (c.Authentication.CustomCertificateValidator);
|
|
case X509CertificateValidationMode.None:
|
|
return new X509SecurityTokenAuthenticator (X509CertificateValidator.None);
|
|
case X509CertificateValidationMode.PeerOrChainTrust:
|
|
return new X509SecurityTokenAuthenticator (X509CertificateValidator.PeerOrChainTrust);
|
|
case X509CertificateValidationMode.ChainTrust:
|
|
return new X509SecurityTokenAuthenticator (X509CertificateValidator.ChainTrust);
|
|
default:
|
|
return new X509SecurityTokenAuthenticator (X509CertificateValidator.PeerTrust);
|
|
}
|
|
}
|
|
|
|
void InitializeAuthenticatorCommunicationObject (AuthenticatorCommunicationObject p, SecurityTokenRequirement r)
|
|
{
|
|
p.ListenUri = r.GetProperty<Uri> (ReqType.ListenUriProperty);
|
|
|
|
// FIXME: use it somewhere, probably to build
|
|
// IssuerBinding. However, there is also IssuerBinding
|
|
// property. SecureConversationSecurityBindingElement
|
|
// as well.
|
|
SecurityBindingElement sbe =
|
|
r.GetProperty<SecurityBindingElement> (ReqType.SecurityBindingElementProperty);
|
|
p.SecurityBindingElement = sbe;
|
|
|
|
/*
|
|
// I doubt the binding is acquired this way ...
|
|
Binding binding;
|
|
if (!r.TryGetProperty<Binding> (ReqType.IssuerBindingProperty, out binding))
|
|
binding = new CustomBinding (
|
|
new TextMessageEncodingBindingElement (),
|
|
new HttpTransportBindingElement ());
|
|
p.IssuerBinding = binding;
|
|
|
|
// not sure if it is used only for this purpose though ...
|
|
BindingContext ctx = r.GetProperty<BindingContext> (ReqType.IssuerBindingContextProperty);
|
|
foreach (IEndpointBehavior b in ctx.BindingParameters.FindAll<IEndpointBehavior> ())
|
|
p.IssuerChannelBehaviors.Add (b);
|
|
*/
|
|
|
|
SecurityTokenVersion ver =
|
|
r.GetProperty<SecurityTokenVersion> (ReqType.MessageSecurityVersionProperty);
|
|
p.SecurityTokenSerializer =
|
|
CreateSecurityTokenSerializer (ver);
|
|
|
|
/*
|
|
// seems like they are optional here ... (but possibly
|
|
// used later)
|
|
EndpointAddress address;
|
|
if (!r.TryGetProperty<EndpointAddress> (ReqType.IssuerAddressProperty, out address))
|
|
address = p.TargetAddress;
|
|
p.IssuerAddress = address;
|
|
*/
|
|
|
|
// It is somehow not checked as mandatory ...
|
|
SecurityAlgorithmSuite suite = null;
|
|
r.TryGetProperty<SecurityAlgorithmSuite> (ReqType.SecurityAlgorithmSuiteProperty, out suite);
|
|
p.SecurityAlgorithmSuite = suite;
|
|
}
|
|
|
|
#region CreateSecurityTokenProvider()
|
|
|
|
[MonoTODO]
|
|
public override SecurityTokenProvider CreateSecurityTokenProvider (SecurityTokenRequirement requirement)
|
|
{
|
|
if (IsIssuedSecurityTokenRequirement (requirement))
|
|
return CreateIssuedTokenProvider (requirement);
|
|
|
|
// not supported: UserName, Rsa, AnonymousSslnego, SecureConv
|
|
|
|
// huh, they are not constants but properties.
|
|
if (requirement.TokenType == SecurityTokenTypes.X509Certificate)
|
|
return CreateX509SecurityTokenProvider (requirement);
|
|
else if (requirement.TokenType == ServiceModelSecurityTokenTypes.MutualSslnego) {
|
|
// FIXME: implement
|
|
throw new NotImplementedException ();
|
|
} else if (requirement.TokenType == ServiceModelSecurityTokenTypes.SecurityContext) {
|
|
// FIXME: implement
|
|
throw new NotImplementedException ();
|
|
} else if (requirement.TokenType == ServiceModelSecurityTokenTypes.AnonymousSslnego) {
|
|
throw new NotSupportedException (String.Format ("Token type '{0}' is not supported", requirement.TokenType));
|
|
} else if (requirement.TokenType == ServiceModelSecurityTokenTypes.Spnego) {
|
|
// FIXME: implement
|
|
throw new NotImplementedException ();
|
|
} else if (requirement.TokenType == ServiceModelSecurityTokenTypes.SspiCredential) {
|
|
// FIXME: implement
|
|
throw new NotImplementedException ();
|
|
} else if (requirement.TokenType == SecurityTokenTypes.Saml) {
|
|
// FIXME: implement
|
|
throw new NotImplementedException ();
|
|
} else if (requirement.TokenType == SecurityTokenTypes.Kerberos) {
|
|
// FIXME: implement
|
|
throw new NotImplementedException ();
|
|
}
|
|
throw new NotSupportedException (String.Format ("Securirty token requirement '{0}' is not supported", requirement));
|
|
}
|
|
|
|
X509SecurityTokenProvider CreateX509SecurityTokenProvider (SecurityTokenRequirement requirement)
|
|
{
|
|
bool isInitiator;
|
|
requirement.TryGetProperty<bool> (ReqType.IsInitiatorProperty, out isInitiator);
|
|
// when it is initiator, then it is for MutualCertificateDuplex.
|
|
X509Certificate2 cert;
|
|
if (isInitiator) {
|
|
cert = credentials.ClientCertificate.Certificate;
|
|
if (cert == null)
|
|
throw new InvalidOperationException ("Client certificate is not provided in ServiceCredentials.");
|
|
if (cert.PrivateKey == null)
|
|
throw new ArgumentException ("Client certificate for MutualCertificateDuplex does not have a private key which is required for key exchange.");
|
|
} else {
|
|
cert = credentials.ServiceCertificate.Certificate;
|
|
if (cert == null)
|
|
throw new InvalidOperationException ("Service certificate is not provided in ServiceCredentials.");
|
|
if (cert.PrivateKey == null)
|
|
throw new ArgumentException ("Service certificate does not have a private key which is required for key exchange.");
|
|
}
|
|
X509SecurityTokenProvider p =
|
|
new X509SecurityTokenProvider (cert);
|
|
return p;
|
|
}
|
|
|
|
IssuedSecurityTokenProvider CreateIssuedProviderBase (SecurityTokenRequirement r)
|
|
{
|
|
IssuedSecurityTokenProvider p =
|
|
new IssuedSecurityTokenProvider ();
|
|
|
|
p.TargetAddress = r.GetProperty<EndpointAddress> (ReqType.TargetAddressProperty);
|
|
|
|
// FIXME: use it somewhere, probably to build
|
|
// IssuerBinding. However, there is also IssuerBinding
|
|
// property. SecureConversationSecurityBindingElement
|
|
// as well.
|
|
SecurityBindingElement sbe =
|
|
r.GetProperty<SecurityBindingElement> (ReqType.SecurityBindingElementProperty);
|
|
|
|
// I doubt the binding is acquired this way ...
|
|
Binding binding;
|
|
if (!r.TryGetProperty<Binding> (ReqType.IssuerBindingProperty, out binding))
|
|
binding = new CustomBinding (sbe,
|
|
new TextMessageEncodingBindingElement (),
|
|
new HttpTransportBindingElement ());
|
|
p.IssuerBinding = binding;
|
|
|
|
// not sure if it is used only for this purpose though ...
|
|
BindingContext ctx = r.GetProperty<BindingContext> (ReqType.IssuerBindingContextProperty);
|
|
foreach (IEndpointBehavior b in ctx.BindingParameters.FindAll<IEndpointBehavior> ())
|
|
p.IssuerChannelBehaviors.Add (b);
|
|
|
|
SecurityTokenVersion ver =
|
|
r.GetProperty<SecurityTokenVersion> (ReqType.MessageSecurityVersionProperty);
|
|
p.SecurityTokenSerializer =
|
|
CreateSecurityTokenSerializer (ver);
|
|
|
|
// seems like they are optional here ... (but possibly
|
|
// used later)
|
|
EndpointAddress address;
|
|
if (!r.TryGetProperty<EndpointAddress> (ReqType.IssuerAddressProperty, out address))
|
|
address = p.TargetAddress;
|
|
p.IssuerAddress = address;
|
|
|
|
// It is somehow not checked as mandatory ...
|
|
SecurityAlgorithmSuite suite = null;
|
|
r.TryGetProperty<SecurityAlgorithmSuite> (ReqType.SecurityAlgorithmSuiteProperty, out suite);
|
|
p.SecurityAlgorithmSuite = suite;
|
|
|
|
return p;
|
|
}
|
|
|
|
// FIXME: it is far from done.
|
|
SecurityTokenProvider CreateSecureConversationProvider (SecurityTokenRequirement r)
|
|
{
|
|
IssuedSecurityTokenProvider p =
|
|
CreateIssuedProviderBase (r);
|
|
|
|
// FIXME: use it somewhere.
|
|
int keySize = r.KeySize;
|
|
|
|
return p;
|
|
}
|
|
|
|
IssuedSecurityTokenProvider CreateIssuedTokenProvider (SecurityTokenRequirement requirement)
|
|
{
|
|
IssuedSecurityTokenProvider p =
|
|
new IssuedSecurityTokenProvider ();
|
|
// FIXME: fill properties
|
|
EndpointAddress address;
|
|
if (requirement.TryGetProperty<EndpointAddress> (ReqType.IssuerAddressProperty, out address))
|
|
p.IssuerAddress = address;
|
|
if (requirement.TryGetProperty<EndpointAddress> (ReqType.TargetAddressProperty, out address))
|
|
p.TargetAddress = address;
|
|
Binding binding;
|
|
if (requirement.TryGetProperty<Binding> (ReqType.IssuerBindingProperty, out binding))
|
|
p.IssuerBinding = binding;
|
|
MessageSecurityVersion ver;
|
|
if (requirement.TryGetProperty<MessageSecurityVersion> (ReqType.MessageSecurityVersionProperty, out ver))
|
|
p.SecurityTokenSerializer = CreateSecurityTokenSerializer (ver.SecurityTokenVersion);
|
|
SecurityAlgorithmSuite suite;
|
|
if (requirement.TryGetProperty<SecurityAlgorithmSuite> (ReqType.SecurityAlgorithmSuiteProperty, out suite))
|
|
p.SecurityAlgorithmSuite = suite;
|
|
return p;
|
|
}
|
|
|
|
#endregion
|
|
|
|
[MonoTODO ("pass correct arguments to WSSecurityTokenSerializer..ctor()")]
|
|
public override SecurityTokenSerializer CreateSecurityTokenSerializer (SecurityTokenVersion version)
|
|
{
|
|
bool bsp = version.GetSecuritySpecifications ().Contains (Constants.WSBasicSecurityProfileCore1);
|
|
SecurityVersion ver =
|
|
version.GetSecuritySpecifications ().Contains (Constants.Wss11Namespace) ?
|
|
SecurityVersion.WSSecurity11 :
|
|
SecurityVersion.WSSecurity10;
|
|
|
|
// FIXME: pass correct arguments.
|
|
return new WSSecurityTokenSerializer (ver, bsp, null,
|
|
ServiceCredentials.SecureConversationAuthentication.SecurityStateEncoder,
|
|
Type.EmptyTypes,
|
|
int.MaxValue, int.MaxValue, int.MaxValue);
|
|
}
|
|
|
|
protected internal bool IsIssuedSecurityTokenRequirement (
|
|
SecurityTokenRequirement requirement)
|
|
{
|
|
SecurityTokenParameters ret;
|
|
if (!requirement.TryGetProperty<SecurityTokenParameters> (ServiceModelSecurityTokenRequirement.IssuedSecurityTokenParametersProperty, out ret))
|
|
return false;
|
|
return ret is IssuedSecurityTokenParameters;
|
|
}
|
|
}
|
|
}
|