2016-08-03 10:59:49 +00:00
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace System.ServiceModel.Channels
{
using System.Collections.ObjectModel ;
using System.Diagnostics ;
using System.IdentityModel.Policy ;
using System.IdentityModel.Selectors ;
using System.IdentityModel.Tokens ;
using System.IO ;
using System.Net.Security ;
using System.Runtime ;
using System.Runtime.Diagnostics ;
using System.Security.Authentication ;
using System.Security.Authentication.ExtendedProtection ;
using System.Security.Cryptography.X509Certificates ;
using System.ServiceModel ;
using System.ServiceModel.Description ;
using System.ServiceModel.Diagnostics ;
using System.ServiceModel.Diagnostics.Application ;
using System.ServiceModel.Security ;
using System.ServiceModel.Security.Tokens ;
class SslStreamSecurityUpgradeProvider : StreamSecurityUpgradeProvider , IStreamUpgradeChannelBindingProvider
{
SecurityTokenAuthenticator clientCertificateAuthenticator ;
SecurityTokenManager clientSecurityTokenManager ;
SecurityTokenProvider serverTokenProvider ;
EndpointIdentity identity ;
IdentityVerifier identityVerifier ;
X509Certificate2 serverCertificate ;
bool requireClientCertificate ;
string scheme ;
bool enableChannelBinding ;
SslProtocols sslProtocols ;
SslStreamSecurityUpgradeProvider ( IDefaultCommunicationTimeouts timeouts , SecurityTokenManager clientSecurityTokenManager , bool requireClientCertificate , string scheme , IdentityVerifier identityVerifier , SslProtocols sslProtocols )
: base ( timeouts )
{
this . identityVerifier = identityVerifier ;
this . scheme = scheme ;
this . clientSecurityTokenManager = clientSecurityTokenManager ;
this . requireClientCertificate = requireClientCertificate ;
this . sslProtocols = sslProtocols ;
}
SslStreamSecurityUpgradeProvider ( IDefaultCommunicationTimeouts timeouts , SecurityTokenProvider serverTokenProvider , bool requireClientCertificate , SecurityTokenAuthenticator clientCertificateAuthenticator , string scheme , IdentityVerifier identityVerifier , SslProtocols sslProtocols )
: base ( timeouts )
{
this . serverTokenProvider = serverTokenProvider ;
this . requireClientCertificate = requireClientCertificate ;
this . clientCertificateAuthenticator = clientCertificateAuthenticator ;
this . identityVerifier = identityVerifier ;
this . scheme = scheme ;
this . sslProtocols = sslProtocols ;
}
public static SslStreamSecurityUpgradeProvider CreateClientProvider (
SslStreamSecurityBindingElement bindingElement , BindingContext context )
{
SecurityCredentialsManager credentialProvider = context . BindingParameters . Find < SecurityCredentialsManager > ( ) ;
if ( credentialProvider = = null )
{
credentialProvider = ClientCredentials . CreateDefaultCredentials ( ) ;
}
SecurityTokenManager tokenManager = credentialProvider . CreateSecurityTokenManager ( ) ;
return new SslStreamSecurityUpgradeProvider ( context . Binding , tokenManager , bindingElement . RequireClientCertificate , context . Binding . Scheme , bindingElement . IdentityVerifier , bindingElement . SslProtocols ) ;
}
public static SslStreamSecurityUpgradeProvider CreateServerProvider (
SslStreamSecurityBindingElement bindingElement , BindingContext context )
{
SecurityCredentialsManager credentialProvider =
context . BindingParameters . Find < SecurityCredentialsManager > ( ) ;
if ( credentialProvider = = null )
{
credentialProvider = ServiceCredentials . CreateDefaultCredentials ( ) ;
}
Uri listenUri = TransportSecurityHelpers . GetListenUri ( context . ListenUriBaseAddress , context . ListenUriRelativeAddress ) ;
SecurityTokenManager tokenManager = credentialProvider . CreateSecurityTokenManager ( ) ;
RecipientServiceModelSecurityTokenRequirement serverCertRequirement = new RecipientServiceModelSecurityTokenRequirement ( ) ;
serverCertRequirement . TokenType = SecurityTokenTypes . X509Certificate ;
serverCertRequirement . RequireCryptographicToken = true ;
serverCertRequirement . KeyUsage = SecurityKeyUsage . Exchange ;
serverCertRequirement . TransportScheme = context . Binding . Scheme ;
serverCertRequirement . ListenUri = listenUri ;
SecurityTokenProvider tokenProvider = tokenManager . CreateSecurityTokenProvider ( serverCertRequirement ) ;
if ( tokenProvider = = null )
{
throw DiagnosticUtility . ExceptionUtility . ThrowHelperError ( new InvalidOperationException ( SR . GetString ( SR . ClientCredentialsUnableToCreateLocalTokenProvider , serverCertRequirement ) ) ) ;
}
SecurityTokenAuthenticator certificateAuthenticator =
TransportSecurityHelpers . GetCertificateTokenAuthenticator ( tokenManager , context . Binding . Scheme , listenUri ) ;
return new SslStreamSecurityUpgradeProvider ( context . Binding , tokenProvider , bindingElement . RequireClientCertificate ,
certificateAuthenticator , context . Binding . Scheme , bindingElement . IdentityVerifier , bindingElement . SslProtocols ) ;
}
public override EndpointIdentity Identity
{
get
{
if ( ( this . identity = = null ) & & ( this . serverCertificate ! = null ) )
{
this . identity = SecurityUtils . GetServiceCertificateIdentity ( this . serverCertificate ) ;
}
return this . identity ;
}
}
public IdentityVerifier IdentityVerifier
{
get
{
return this . identityVerifier ;
}
}
public bool RequireClientCertificate
{
get
{
return this . requireClientCertificate ;
}
}
public X509Certificate2 ServerCertificate
{
get
{
return this . serverCertificate ;
}
}
public SecurityTokenAuthenticator ClientCertificateAuthenticator
{
get
{
if ( this . clientCertificateAuthenticator = = null )
{
this . clientCertificateAuthenticator = new X509SecurityTokenAuthenticator ( X509ClientCertificateAuthentication . DefaultCertificateValidator ) ;
}
return this . clientCertificateAuthenticator ;
}
}
public SecurityTokenManager ClientSecurityTokenManager
{
get
{
return this . clientSecurityTokenManager ;
}
}
public string Scheme
{
get { return this . scheme ; }
}
public SslProtocols SslProtocols
{
get { return this . sslProtocols ; }
}
public override T GetProperty < T > ( )
{
if ( typeof ( T ) = = typeof ( IChannelBindingProvider ) | | typeof ( T ) = = typeof ( IStreamUpgradeChannelBindingProvider ) )
{
return ( T ) ( object ) this ;
}
return base . GetProperty < T > ( ) ;
}
ChannelBinding IStreamUpgradeChannelBindingProvider . GetChannelBinding ( StreamUpgradeInitiator upgradeInitiator , ChannelBindingKind kind )
{
if ( upgradeInitiator = = null )
{
throw DiagnosticUtility . ExceptionUtility . ThrowHelperArgumentNull ( "upgradeInitiator" ) ;
}
SslStreamSecurityUpgradeInitiator sslUpgradeInitiator = upgradeInitiator as SslStreamSecurityUpgradeInitiator ;
if ( sslUpgradeInitiator = = null )
{
throw DiagnosticUtility . ExceptionUtility . ThrowHelperArgument ( "upgradeInitiator" , SR . GetString ( SR . UnsupportedUpgradeInitiator , upgradeInitiator . GetType ( ) ) ) ;
}
if ( kind ! = ChannelBindingKind . Endpoint )
{
throw DiagnosticUtility . ExceptionUtility . ThrowHelperArgument ( "kind" , SR . GetString ( SR . StreamUpgradeUnsupportedChannelBindingKind , this . GetType ( ) , kind ) ) ;
}
return sslUpgradeInitiator . ChannelBinding ;
}
ChannelBinding IStreamUpgradeChannelBindingProvider . GetChannelBinding ( StreamUpgradeAcceptor upgradeAcceptor , ChannelBindingKind kind )
{
if ( upgradeAcceptor = = null )
{
throw DiagnosticUtility . ExceptionUtility . ThrowHelperArgumentNull ( "upgradeAcceptor" ) ;
}
SslStreamSecurityUpgradeAcceptor sslupgradeAcceptor = upgradeAcceptor as SslStreamSecurityUpgradeAcceptor ;
if ( sslupgradeAcceptor = = null )
{
throw DiagnosticUtility . ExceptionUtility . ThrowHelperArgument ( "upgradeAcceptor" , SR . GetString ( SR . UnsupportedUpgradeAcceptor , upgradeAcceptor . GetType ( ) ) ) ;
}
if ( kind ! = ChannelBindingKind . Endpoint )
{
throw DiagnosticUtility . ExceptionUtility . ThrowHelperArgument ( "kind" , SR . GetString ( SR . StreamUpgradeUnsupportedChannelBindingKind , this . GetType ( ) , kind ) ) ;
}
return sslupgradeAcceptor . ChannelBinding ;
}
void IChannelBindingProvider . EnableChannelBindingSupport ( )
{
this . enableChannelBinding = true ;
}
bool IChannelBindingProvider . IsChannelBindingSupportEnabled
{
get
{
return this . enableChannelBinding ;
}
}
public override StreamUpgradeAcceptor CreateUpgradeAcceptor ( )
{
ThrowIfDisposedOrNotOpen ( ) ;
return new SslStreamSecurityUpgradeAcceptor ( this ) ;
}
public override StreamUpgradeInitiator CreateUpgradeInitiator ( EndpointAddress remoteAddress , Uri via )
{
ThrowIfDisposedOrNotOpen ( ) ;
return new SslStreamSecurityUpgradeInitiator ( this , remoteAddress , via ) ;
}
protected override void OnAbort ( )
{
if ( this . clientCertificateAuthenticator ! = null )
{
SecurityUtils . AbortTokenAuthenticatorIfRequired ( this . clientCertificateAuthenticator ) ;
}
CleanupServerCertificate ( ) ;
}
protected override void OnClose ( TimeSpan timeout )
{
if ( this . clientCertificateAuthenticator ! = null )
{
SecurityUtils . CloseTokenAuthenticatorIfRequired ( this . clientCertificateAuthenticator , timeout ) ;
}
CleanupServerCertificate ( ) ;
}
protected override IAsyncResult OnBeginClose ( TimeSpan timeout , AsyncCallback callback , object state )
{
return SecurityUtils . BeginCloseTokenAuthenticatorIfRequired ( this . clientCertificateAuthenticator , timeout , callback , state ) ;
}
protected override void OnEndClose ( IAsyncResult result )
{
SecurityUtils . EndCloseTokenAuthenticatorIfRequired ( result ) ;
CleanupServerCertificate ( ) ;
}
void SetupServerCertificate ( SecurityToken token )
{
X509SecurityToken x509Token = token as X509SecurityToken ;
if ( x509Token = = null )
{
SecurityUtils . AbortTokenProviderIfRequired ( this . serverTokenProvider ) ;
throw DiagnosticUtility . ExceptionUtility . ThrowHelperError ( new InvalidOperationException ( SR . GetString (
SR . InvalidTokenProvided , this . serverTokenProvider . GetType ( ) , typeof ( X509SecurityToken ) ) ) ) ;
}
this . serverCertificate = new X509Certificate2 ( x509Token . Certificate ) ;
}
void CleanupServerCertificate ( )
{
if ( this . serverCertificate ! = null )
{
SecurityUtils . ResetCertificate ( this . serverCertificate ) ;
this . serverCertificate = null ;
}
}
protected override void OnOpen ( TimeSpan timeout )
{
TimeoutHelper timeoutHelper = new TimeoutHelper ( timeout ) ;
SecurityUtils . OpenTokenAuthenticatorIfRequired ( this . ClientCertificateAuthenticator , timeoutHelper . RemainingTime ( ) ) ;
if ( this . serverTokenProvider ! = null )
{
SecurityUtils . OpenTokenProviderIfRequired ( this . serverTokenProvider , timeoutHelper . RemainingTime ( ) ) ;
SecurityToken token = this . serverTokenProvider . GetToken ( timeout ) ;
SetupServerCertificate ( token ) ;
SecurityUtils . CloseTokenProviderIfRequired ( this . serverTokenProvider , timeoutHelper . RemainingTime ( ) ) ;
this . serverTokenProvider = null ;
}
}
protected override IAsyncResult OnBeginOpen ( TimeSpan timeout , AsyncCallback callback , object state )
{
return new OpenAsyncResult ( this , timeout , callback , state ) ;
}
protected override void OnEndOpen ( IAsyncResult result )
{
OpenAsyncResult . End ( result ) ;
}
class OpenAsyncResult : AsyncResult
{
SslStreamSecurityUpgradeProvider parent ;
TimeoutHelper timeoutHelper ;
AsyncCallback onOpenTokenAuthenticator ;
AsyncCallback onOpenTokenProvider ;
AsyncCallback onGetToken ;
AsyncCallback onCloseTokenProvider ;
public OpenAsyncResult ( SslStreamSecurityUpgradeProvider parent , TimeSpan timeout ,
AsyncCallback callback , object state )
: base ( callback , state )
{
this . parent = parent ;
this . timeoutHelper = new TimeoutHelper ( timeout ) ;
// since we're at channel.Open and not per-message, minimize our statics overhead and leverage GC for our callbacks
this . onOpenTokenAuthenticator = Fx . ThunkCallback ( new AsyncCallback ( OnOpenTokenAuthenticator ) ) ;
IAsyncResult result = SecurityUtils . BeginOpenTokenAuthenticatorIfRequired ( parent . ClientCertificateAuthenticator ,
timeoutHelper . RemainingTime ( ) , onOpenTokenAuthenticator , this ) ;
if ( ! result . CompletedSynchronously )
{
return ;
}
if ( HandleOpenAuthenticatorComplete ( result ) )
{
base . Complete ( true ) ;
}
}
public static void End ( IAsyncResult result )
{
AsyncResult . End < OpenAsyncResult > ( result ) ;
}
bool HandleOpenAuthenticatorComplete ( IAsyncResult result )
{
SecurityUtils . EndOpenTokenAuthenticatorIfRequired ( result ) ;
if ( parent . serverTokenProvider = = null )
{
return true ;
}
this . onOpenTokenProvider = Fx . ThunkCallback ( new AsyncCallback ( OnOpenTokenProvider ) ) ;
IAsyncResult openTokenProviderResult = SecurityUtils . BeginOpenTokenProviderIfRequired (
parent . serverTokenProvider , timeoutHelper . RemainingTime ( ) , onOpenTokenProvider , this ) ;
if ( ! openTokenProviderResult . CompletedSynchronously )
{
return false ;
}
return HandleOpenTokenProviderComplete ( openTokenProviderResult ) ;
}
bool HandleOpenTokenProviderComplete ( IAsyncResult result )
{
SecurityUtils . EndOpenTokenProviderIfRequired ( result ) ;
this . onGetToken = Fx . ThunkCallback ( new AsyncCallback ( OnGetToken ) ) ;
IAsyncResult getTokenResult = parent . serverTokenProvider . BeginGetToken ( timeoutHelper . RemainingTime ( ) ,
onGetToken , this ) ;
if ( ! getTokenResult . CompletedSynchronously )
{
return false ;
}
return HandleGetTokenComplete ( getTokenResult ) ;
}
bool HandleGetTokenComplete ( IAsyncResult result )
{
SecurityToken token = parent . serverTokenProvider . EndGetToken ( result ) ;
parent . SetupServerCertificate ( token ) ;
this . onCloseTokenProvider = Fx . ThunkCallback ( new AsyncCallback ( OnCloseTokenProvider ) ) ;
IAsyncResult closeTokenProviderResult =
SecurityUtils . BeginCloseTokenProviderIfRequired ( parent . serverTokenProvider , timeoutHelper . RemainingTime ( ) ,
onCloseTokenProvider , this ) ;
if ( ! closeTokenProviderResult . CompletedSynchronously )
{
return false ;
}
return HandleCloseTokenProviderComplete ( closeTokenProviderResult ) ;
}
bool HandleCloseTokenProviderComplete ( IAsyncResult result )
{
SecurityUtils . EndCloseTokenProviderIfRequired ( result ) ;
parent . serverTokenProvider = null ;
return true ;
}
void OnOpenTokenAuthenticator ( IAsyncResult result )
{
if ( result . CompletedSynchronously )
{
return ;
}
Exception completionException = null ;
bool completeSelf = false ;
try
{
completeSelf = this . HandleOpenAuthenticatorComplete ( result ) ;
}
2017-08-21 15:34:15 +00:00
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
2016-08-03 10:59:49 +00:00
catch ( Exception e )
{
if ( Fx . IsFatal ( e ) )
{
throw ;
}
completeSelf = true ;
completionException = e ;
}
if ( completeSelf )
{
base . Complete ( false , completionException ) ;
}
}
void OnOpenTokenProvider ( IAsyncResult result )
{
if ( result . CompletedSynchronously )
{
return ;
}
Exception completionException = null ;
bool completeSelf = false ;
try
{
completeSelf = this . HandleOpenTokenProviderComplete ( result ) ;
}
2017-08-21 15:34:15 +00:00
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
2016-08-03 10:59:49 +00:00
catch ( Exception e )
{
if ( Fx . IsFatal ( e ) )
{
throw ;
}
completeSelf = true ;
completionException = e ;
}
if ( completeSelf )
{
base . Complete ( false , completionException ) ;
}
}
void OnGetToken ( IAsyncResult result )
{
if ( result . CompletedSynchronously )
{
return ;
}
Exception completionException = null ;
bool completeSelf = false ;
try
{
completeSelf = this . HandleGetTokenComplete ( result ) ;
}
2017-08-21 15:34:15 +00:00
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
2016-08-03 10:59:49 +00:00
catch ( Exception e )
{
if ( Fx . IsFatal ( e ) )
{
throw ;
}
completeSelf = true ;
completionException = e ;
}
if ( completeSelf )
{
base . Complete ( false , completionException ) ;
}
}
void OnCloseTokenProvider ( IAsyncResult result )
{
if ( result . CompletedSynchronously )
{
return ;
}
Exception completionException = null ;
bool completeSelf = false ;
try
{
completeSelf = this . HandleCloseTokenProviderComplete ( result ) ;
}
2017-08-21 15:34:15 +00:00
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
2016-08-03 10:59:49 +00:00
catch ( Exception e )
{
if ( Fx . IsFatal ( e ) )
{
throw ;
}
completeSelf = true ;
completionException = e ;
}
if ( completeSelf )
{
base . Complete ( false , completionException ) ;
}
}
}
}
class SslStreamSecurityUpgradeAcceptor : StreamSecurityUpgradeAcceptorBase
{
SslStreamSecurityUpgradeProvider parent ;
SecurityMessageProperty clientSecurity ;
// for audit
X509Certificate2 clientCertificate = null ;
ChannelBinding channelBindingToken ;
public SslStreamSecurityUpgradeAcceptor ( SslStreamSecurityUpgradeProvider parent )
: base ( FramingUpgradeString . SslOrTls )
{
this . parent = parent ;
this . clientSecurity = new SecurityMessageProperty ( ) ;
}
internal ChannelBinding ChannelBinding
{
get
{
Fx . Assert ( this . IsChannelBindingSupportEnabled , "A request for the ChannelBinding is not permitted without enabling ChannelBinding first (through the IChannelBindingProvider interface)" ) ;
return this . channelBindingToken ;
}
}
internal bool IsChannelBindingSupportEnabled
{
get
{
return ( ( IChannelBindingProvider ) parent ) . IsChannelBindingSupportEnabled ;
}
}
protected override Stream OnAcceptUpgrade ( Stream stream , out SecurityMessageProperty remoteSecurity )
{
if ( TD . SslOnAcceptUpgradeIsEnabled ( ) )
{
TD . SslOnAcceptUpgrade ( this . EventTraceActivity ) ;
}
SslStream sslStream = new SslStream ( stream , false , this . ValidateRemoteCertificate ) ;
try
{
sslStream . AuthenticateAsServer ( this . parent . ServerCertificate , this . parent . RequireClientCertificate ,
this . parent . SslProtocols , false ) ;
}
catch ( AuthenticationException exception )
{
throw DiagnosticUtility . ExceptionUtility . ThrowHelperError ( new SecurityNegotiationException ( exception . Message ,
exception ) ) ;
}
catch ( IOException ioException )
{
throw DiagnosticUtility . ExceptionUtility . ThrowHelperError ( new SecurityNegotiationException (
SR . GetString ( SR . NegotiationFailedIO , ioException . Message ) , ioException ) ) ;
}
if ( SecurityUtils . ShouldValidateSslCipherStrength ( ) )
{
SecurityUtils . ValidateSslCipherStrength ( sslStream . CipherStrength ) ;
}
remoteSecurity = this . clientSecurity ;
if ( this . IsChannelBindingSupportEnabled )
{
this . channelBindingToken = ChannelBindingUtility . GetToken ( sslStream ) ;
}
return sslStream ;
}
protected override IAsyncResult OnBeginAcceptUpgrade ( Stream stream , AsyncCallback callback , object state )
{
AcceptUpgradeAsyncResult result = new AcceptUpgradeAsyncResult ( this , callback , state ) ;
result . Begin ( stream ) ;
return result ;
}
protected override Stream OnEndAcceptUpgrade ( IAsyncResult result , out SecurityMessageProperty remoteSecurity )
{
return AcceptUpgradeAsyncResult . End ( result , out remoteSecurity , out this . channelBindingToken ) ;
}
// callback from schannel
bool ValidateRemoteCertificate ( object sender , X509Certificate certificate , X509Chain chain ,
SslPolicyErrors sslPolicyErrors )
{
if ( this . parent . RequireClientCertificate )
{
if ( certificate = = null )
{
if ( DiagnosticUtility . ShouldTraceError )
{
TraceUtility . TraceEvent ( TraceEventType . Error , TraceCode . SslClientCertMissing ,
SR . GetString ( SR . TraceCodeSslClientCertMissing ) , this ) ;
}
return false ;
}
// Note: add ref to handle since the caller will reset the cert after the callback return.
X509Certificate2 certificate2 = new X509Certificate2 ( certificate ) ;
this . clientCertificate = certificate2 ;
try
{
SecurityToken token = new X509SecurityToken ( certificate2 , false ) ;
ReadOnlyCollection < IAuthorizationPolicy > authorizationPolicies = this . parent . ClientCertificateAuthenticator . ValidateToken ( token ) ;
this . clientSecurity = new SecurityMessageProperty ( ) ;
this . clientSecurity . TransportToken = new SecurityTokenSpecification ( token , authorizationPolicies ) ;
this . clientSecurity . ServiceSecurityContext = new ServiceSecurityContext ( authorizationPolicies ) ;
}
catch ( SecurityTokenException e )
{
DiagnosticUtility . TraceHandledException ( e , TraceEventType . Information ) ;
return false ;
}
}
return true ;
}
public override SecurityMessageProperty GetRemoteSecurity ( )
{
if ( this . clientSecurity . TransportToken ! = null )
{
return this . clientSecurity ;
}
if ( this . clientCertificate ! = null )
{
SecurityToken token = new X509SecurityToken ( this . clientCertificate ) ;
ReadOnlyCollection < IAuthorizationPolicy > authorizationPolicies = SecurityUtils . NonValidatingX509Authenticator . ValidateToken ( token ) ;
this . clientSecurity = new SecurityMessageProperty ( ) ;
this . clientSecurity . TransportToken = new SecurityTokenSpecification ( token , authorizationPolicies ) ;
this . clientSecurity . ServiceSecurityContext = new ServiceSecurityContext ( authorizationPolicies ) ;
return this . clientSecurity ;
}
return base . GetRemoteSecurity ( ) ;
}
class AcceptUpgradeAsyncResult : StreamSecurityUpgradeAcceptorAsyncResult
{
SslStreamSecurityUpgradeAcceptor acceptor ;
SslStream sslStream ;
ChannelBinding channelBindingToken ;
public AcceptUpgradeAsyncResult ( SslStreamSecurityUpgradeAcceptor acceptor , AsyncCallback callback ,
object state )
: base ( callback , state )
{
this . acceptor = acceptor ;
}
protected override IAsyncResult OnBegin ( Stream stream , AsyncCallback callback )
{
if ( TD . SslOnAcceptUpgradeIsEnabled ( ) )
{
TD . SslOnAcceptUpgrade ( acceptor . EventTraceActivity ) ;
}
this . sslStream = new SslStream ( stream , false , this . acceptor . ValidateRemoteCertificate ) ;
return this . sslStream . BeginAuthenticateAsServer ( this . acceptor . parent . ServerCertificate ,
this . acceptor . parent . RequireClientCertificate , this . acceptor . parent . SslProtocols , false , callback , this ) ;
}
protected override Stream OnCompleteAuthenticateAsServer ( IAsyncResult result )
{
this . sslStream . EndAuthenticateAsServer ( result ) ;
if ( SecurityUtils . ShouldValidateSslCipherStrength ( ) )
{
SecurityUtils . ValidateSslCipherStrength ( sslStream . CipherStrength ) ;
}
if ( this . acceptor . IsChannelBindingSupportEnabled )
{
this . channelBindingToken = ChannelBindingUtility . GetToken ( this . sslStream ) ;
}
return this . sslStream ;
}
protected override SecurityMessageProperty ValidateCreateSecurity ( )
{
return this . acceptor . clientSecurity ;
}
public static Stream End ( IAsyncResult result , out SecurityMessageProperty remoteSecurity , out ChannelBinding channelBinding )
{
Stream stream = StreamSecurityUpgradeAcceptorAsyncResult . End ( result , out remoteSecurity ) ;
channelBinding = ( ( AcceptUpgradeAsyncResult ) result ) . channelBindingToken ;
return stream ;
}
}
}
class SslStreamSecurityUpgradeInitiator : StreamSecurityUpgradeInitiatorBase
{
SslStreamSecurityUpgradeProvider parent ;
SecurityMessageProperty serverSecurity ;
SecurityTokenProvider clientCertificateProvider ;
X509SecurityToken clientToken ;
SecurityTokenAuthenticator serverCertificateAuthenticator ;
ChannelBinding channelBindingToken ;
static LocalCertificateSelectionCallback clientCertificateSelectionCallback ;
public SslStreamSecurityUpgradeInitiator ( SslStreamSecurityUpgradeProvider parent ,
EndpointAddress remoteAddress , Uri via )
: base ( FramingUpgradeString . SslOrTls , remoteAddress , via )
{
this . parent = parent ;
InitiatorServiceModelSecurityTokenRequirement serverCertRequirement = new InitiatorServiceModelSecurityTokenRequirement ( ) ;
serverCertRequirement . TokenType = SecurityTokenTypes . X509Certificate ;
serverCertRequirement . RequireCryptographicToken = true ;
serverCertRequirement . KeyUsage = SecurityKeyUsage . Exchange ;
serverCertRequirement . TargetAddress = remoteAddress ;
serverCertRequirement . Via = via ;
serverCertRequirement . TransportScheme = this . parent . Scheme ;
serverCertRequirement . PreferSslCertificateAuthenticator = true ;
SecurityTokenResolver dummy ;
this . serverCertificateAuthenticator = ( parent . ClientSecurityTokenManager . CreateSecurityTokenAuthenticator ( serverCertRequirement , out dummy ) ) ;
if ( parent . RequireClientCertificate )
{
InitiatorServiceModelSecurityTokenRequirement clientCertRequirement = new InitiatorServiceModelSecurityTokenRequirement ( ) ;
clientCertRequirement . TokenType = SecurityTokenTypes . X509Certificate ;
clientCertRequirement . RequireCryptographicToken = true ;
clientCertRequirement . KeyUsage = SecurityKeyUsage . Signature ;
clientCertRequirement . TargetAddress = remoteAddress ;
clientCertRequirement . Via = via ;
clientCertRequirement . TransportScheme = this . parent . Scheme ;
this . clientCertificateProvider = parent . ClientSecurityTokenManager . CreateSecurityTokenProvider ( clientCertRequirement ) ;
if ( clientCertificateProvider = = null )
{
throw DiagnosticUtility . ExceptionUtility . ThrowHelperError ( new InvalidOperationException ( SR . GetString ( SR . ClientCredentialsUnableToCreateLocalTokenProvider , clientCertRequirement ) ) ) ;
}
}
}
static LocalCertificateSelectionCallback ClientCertificateSelectionCallback
{
get
{
if ( clientCertificateSelectionCallback = = null )
{
clientCertificateSelectionCallback = new LocalCertificateSelectionCallback ( SelectClientCertificate ) ;
}
return clientCertificateSelectionCallback ;
}
}
internal ChannelBinding ChannelBinding
{
get
{
Fx . Assert ( this . IsChannelBindingSupportEnabled , "A request for the ChannelBinding is not permitted without enabling ChannelBinding first (through the IChannelBindingProvider interface)" ) ;
return this . channelBindingToken ;
}
}
internal bool IsChannelBindingSupportEnabled
{
get
{
return ( ( IChannelBindingProvider ) parent ) . IsChannelBindingSupportEnabled ;
}
}
IAsyncResult BaseBeginOpen ( TimeSpan timeout , AsyncCallback callback , object state )
{
return base . BeginOpen ( timeout , callback , state ) ;
}
void BaseEndOpen ( IAsyncResult result )
{
base . EndOpen ( result ) ;
}
internal override IAsyncResult BeginOpen ( TimeSpan timeout , AsyncCallback callback , object state )
{
return new OpenAsyncResult ( this , timeout , callback , state ) ;
}
internal override void EndOpen ( IAsyncResult result )
{
OpenAsyncResult . End ( result ) ;
}
internal override void Open ( TimeSpan timeout )
{
TimeoutHelper timeoutHelper = new TimeoutHelper ( timeout ) ;
base . Open ( timeoutHelper . RemainingTime ( ) ) ;
if ( this . clientCertificateProvider ! = null )
{
SecurityUtils . OpenTokenProviderIfRequired ( this . clientCertificateProvider , timeoutHelper . RemainingTime ( ) ) ;
this . clientToken = ( X509SecurityToken ) this . clientCertificateProvider . GetToken ( timeoutHelper . RemainingTime ( ) ) ;
}
}
IAsyncResult BaseBeginClose ( TimeSpan timeout , AsyncCallback callback , object state )
{
return base . BeginClose ( timeout , callback , state ) ;
}
void BaseEndClose ( IAsyncResult result )
{
base . EndClose ( result ) ;
}
internal override IAsyncResult BeginClose ( TimeSpan timeout , AsyncCallback callback , object state )
{
return new CloseAsyncResult ( this , timeout , callback , state ) ;
}
internal override void EndClose ( IAsyncResult result )
{
CloseAsyncResult . End ( result ) ;
}
internal override void Close ( TimeSpan timeout )
{
TimeoutHelper timeoutHelper = new TimeoutHelper ( timeout ) ;
base . Close ( timeoutHelper . RemainingTime ( ) ) ;
if ( this . clientCertificateProvider ! = null )
{
SecurityUtils . CloseTokenProviderIfRequired ( this . clientCertificateProvider , timeoutHelper . RemainingTime ( ) ) ;
}
}
protected override IAsyncResult OnBeginInitiateUpgrade ( Stream stream , AsyncCallback callback , object state )
{
if ( TD . SslOnInitiateUpgradeIsEnabled ( ) )
{
TD . SslOnInitiateUpgrade ( ) ;
}
InitiateUpgradeAsyncResult result = new InitiateUpgradeAsyncResult ( this , callback , state ) ;
result . Begin ( stream ) ;
return result ;
}
protected override Stream OnEndInitiateUpgrade ( IAsyncResult result ,
out SecurityMessageProperty remoteSecurity )
{
return InitiateUpgradeAsyncResult . End ( result , out remoteSecurity , out this . channelBindingToken ) ;
}
protected override Stream OnInitiateUpgrade ( Stream stream , out SecurityMessageProperty remoteSecurity )
{
if ( TD . SslOnInitiateUpgradeIsEnabled ( ) )
{
TD . SslOnInitiateUpgrade ( ) ;
}
X509CertificateCollection clientCertificates = null ;
LocalCertificateSelectionCallback selectionCallback = null ;
if ( this . clientToken ! = null )
{
clientCertificates = new X509CertificateCollection ( ) ;
clientCertificates . Add ( clientToken . Certificate ) ;
selectionCallback = ClientCertificateSelectionCallback ;
}
SslStream sslStream = new SslStream ( stream , false , this . ValidateRemoteCertificate , selectionCallback ) ;
try
{
sslStream . AuthenticateAsClient ( string . Empty , clientCertificates , this . parent . SslProtocols , false ) ;
}
catch ( SecurityTokenValidationException tokenValidationException )
{
throw DiagnosticUtility . ExceptionUtility . ThrowHelperError ( new SecurityNegotiationException ( tokenValidationException . Message ,
tokenValidationException ) ) ;
}
catch ( AuthenticationException exception )
{
throw DiagnosticUtility . ExceptionUtility . ThrowHelperError ( new SecurityNegotiationException ( exception . Message ,
exception ) ) ;
}
catch ( IOException ioException )
{
throw DiagnosticUtility . ExceptionUtility . ThrowHelperError ( new SecurityNegotiationException (
SR . GetString ( SR . NegotiationFailedIO , ioException . Message ) , ioException ) ) ;
}
if ( SecurityUtils . ShouldValidateSslCipherStrength ( ) )
{
SecurityUtils . ValidateSslCipherStrength ( sslStream . CipherStrength ) ;
}
remoteSecurity = this . serverSecurity ;
if ( this . IsChannelBindingSupportEnabled )
{
this . channelBindingToken = ChannelBindingUtility . GetToken ( sslStream ) ;
}
return sslStream ;
}
static X509Certificate SelectClientCertificate ( object sender , string targetHost ,
X509CertificateCollection localCertificates , X509Certificate remoteCertificate , string [ ] acceptableIssuers )
{
return localCertificates [ 0 ] ;
}
bool ValidateRemoteCertificate ( object sender , X509Certificate certificate , X509Chain chain ,
SslPolicyErrors sslPolicyErrors )
{
// Note: add ref to handle since the caller will reset the cert after the callback return.
X509Certificate2 certificate2 = new X509Certificate2 ( certificate ) ;
SecurityToken token = new X509SecurityToken ( certificate2 , false ) ;
ReadOnlyCollection < IAuthorizationPolicy > authorizationPolicies = this . serverCertificateAuthenticator . ValidateToken ( token ) ;
this . serverSecurity = new SecurityMessageProperty ( ) ;
this . serverSecurity . TransportToken = new SecurityTokenSpecification ( token , authorizationPolicies ) ;
this . serverSecurity . ServiceSecurityContext = new ServiceSecurityContext ( authorizationPolicies ) ;
AuthorizationContext authzContext = this . serverSecurity . ServiceSecurityContext . AuthorizationContext ;
this . parent . IdentityVerifier . EnsureOutgoingIdentity ( this . RemoteAddress , this . Via , authzContext ) ;
return true ;
}
class InitiateUpgradeAsyncResult : StreamSecurityUpgradeInitiatorAsyncResult
{
X509CertificateCollection clientCertificates ;
SslStreamSecurityUpgradeInitiator initiator ;
LocalCertificateSelectionCallback selectionCallback ;
SslStream sslStream ;
ChannelBinding channelBindingToken ;
public InitiateUpgradeAsyncResult ( SslStreamSecurityUpgradeInitiator initiator , AsyncCallback callback ,
object state )
: base ( callback , state )
{
this . initiator = initiator ;
if ( initiator . clientToken ! = null )
{
this . clientCertificates = new X509CertificateCollection ( ) ;
this . clientCertificates . Add ( initiator . clientToken . Certificate ) ;
this . selectionCallback = ClientCertificateSelectionCallback ;
}
}
protected override IAsyncResult OnBeginAuthenticateAsClient ( Stream stream , AsyncCallback callback )
{
this . sslStream = new SslStream ( stream , false , this . initiator . ValidateRemoteCertificate ,
this . selectionCallback ) ;
try
{
return this . sslStream . BeginAuthenticateAsClient ( string . Empty , this . clientCertificates ,
this . initiator . parent . SslProtocols , false , callback , this ) ;
}
catch ( SecurityTokenValidationException tokenValidationException )
{
throw DiagnosticUtility . ExceptionUtility . ThrowHelperError ( new SecurityNegotiationException ( tokenValidationException . Message ,
tokenValidationException ) ) ;
}
}
protected override Stream OnCompleteAuthenticateAsClient ( IAsyncResult result )
{
try
{
this . sslStream . EndAuthenticateAsClient ( result ) ;
}
catch ( SecurityTokenValidationException tokenValidationException )
{
throw DiagnosticUtility . ExceptionUtility . ThrowHelperError ( new SecurityNegotiationException ( tokenValidationException . Message ,
tokenValidationException ) ) ;
}
if ( SecurityUtils . ShouldValidateSslCipherStrength ( ) )
{
SecurityUtils . ValidateSslCipherStrength ( sslStream . CipherStrength ) ;
}
if ( this . initiator . IsChannelBindingSupportEnabled )
{
this . channelBindingToken = ChannelBindingUtility . GetToken ( this . sslStream ) ;
}
return this . sslStream ;
}
protected override SecurityMessageProperty ValidateCreateSecurity ( )
{
return this . initiator . serverSecurity ;
}
public static Stream End ( IAsyncResult result , out SecurityMessageProperty remoteSecurity , out ChannelBinding channelBinding )
{
Stream stream = StreamSecurityUpgradeInitiatorAsyncResult . End ( result , out remoteSecurity ) ;
channelBinding = ( ( InitiateUpgradeAsyncResult ) result ) . channelBindingToken ;
return stream ;
}
}
class OpenAsyncResult : AsyncResult
{
SslStreamSecurityUpgradeInitiator parent ;
TimeoutHelper timeoutHelper ;
AsyncCallback onBaseOpen ;
AsyncCallback onOpenTokenProvider ;
AsyncCallback onGetClientToken ;
public OpenAsyncResult ( SslStreamSecurityUpgradeInitiator parent , TimeSpan timeout ,
AsyncCallback callback , object state )
: base ( callback , state )
{
this . parent = parent ;
TimeoutHelper timeoutHelper = new TimeoutHelper ( timeout ) ;
// since we're at channel.Open and not per-message, minimize our statics overhead and leverage GC for our callback
this . onBaseOpen = Fx . ThunkCallback ( new AsyncCallback ( OnBaseOpen ) ) ;
if ( parent . clientCertificateProvider ! = null )
{
this . onOpenTokenProvider = Fx . ThunkCallback ( new AsyncCallback ( OnOpenTokenProvider ) ) ;
this . onGetClientToken = Fx . ThunkCallback ( new AsyncCallback ( OnGetClientToken ) ) ;
}
IAsyncResult result = parent . BaseBeginOpen ( timeoutHelper . RemainingTime ( ) , onBaseOpen , this ) ;
if ( ! result . CompletedSynchronously )
{
return ;
}
if ( HandleBaseOpenComplete ( result ) )
{
base . Complete ( true ) ;
}
}
public static void End ( IAsyncResult result )
{
AsyncResult . End < OpenAsyncResult > ( result ) ;
}
bool HandleBaseOpenComplete ( IAsyncResult result )
{
parent . BaseEndOpen ( result ) ;
if ( parent . clientCertificateProvider = = null )
{
return true ;
}
IAsyncResult openTokenProviderResult = SecurityUtils . BeginOpenTokenProviderIfRequired (
parent . clientCertificateProvider , timeoutHelper . RemainingTime ( ) , onOpenTokenProvider , this ) ;
if ( ! openTokenProviderResult . CompletedSynchronously )
{
return false ;
}
return HandleOpenTokenProviderComplete ( openTokenProviderResult ) ;
}
bool HandleOpenTokenProviderComplete ( IAsyncResult result )
{
SecurityUtils . EndOpenTokenProviderIfRequired ( result ) ;
IAsyncResult getTokenResult = parent . clientCertificateProvider . BeginGetToken ( timeoutHelper . RemainingTime ( ) ,
onGetClientToken , this ) ;
if ( ! getTokenResult . CompletedSynchronously )
{
return false ;
}
return HandleGetTokenComplete ( getTokenResult ) ;
}
bool HandleGetTokenComplete ( IAsyncResult result )
{
parent . clientToken = ( X509SecurityToken ) parent . clientCertificateProvider . EndGetToken ( result ) ;
return true ;
}
void OnBaseOpen ( IAsyncResult result )
{
if ( result . CompletedSynchronously )
{
return ;
}
Exception completionException = null ;
bool completeSelf = false ;
try
{
completeSelf = this . HandleBaseOpenComplete ( result ) ;
}
2017-08-21 15:34:15 +00:00
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
2016-08-03 10:59:49 +00:00
catch ( Exception e )
{
if ( Fx . IsFatal ( e ) )
{
throw ;
}
completeSelf = true ;
completionException = e ;
}
if ( completeSelf )
{
base . Complete ( false , completionException ) ;
}
}
void OnOpenTokenProvider ( IAsyncResult result )
{
if ( result . CompletedSynchronously )
{
return ;
}
Exception completionException = null ;
bool completeSelf = false ;
try
{
completeSelf = this . HandleOpenTokenProviderComplete ( result ) ;
}
2017-08-21 15:34:15 +00:00
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
2016-08-03 10:59:49 +00:00
catch ( Exception e )
{
if ( Fx . IsFatal ( e ) )
{
throw ;
}
completeSelf = true ;
completionException = e ;
}
if ( completeSelf )
{
base . Complete ( false , completionException ) ;
}
}
void OnGetClientToken ( IAsyncResult result )
{
if ( result . CompletedSynchronously )
{
return ;
}
Exception completionException = null ;
bool completeSelf = false ;
try
{
completeSelf = this . HandleGetTokenComplete ( result ) ;
}
2017-08-21 15:34:15 +00:00
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
2016-08-03 10:59:49 +00:00
catch ( Exception e )
{
if ( Fx . IsFatal ( e ) )
{
throw ;
}
completeSelf = true ;
completionException = e ;
}
if ( completeSelf )
{
base . Complete ( false , completionException ) ;
}
}
}
class CloseAsyncResult : AsyncResult
{
SslStreamSecurityUpgradeInitiator parent ;
TimeoutHelper timeoutHelper ;
AsyncCallback onBaseClose ;
AsyncCallback onCloseTokenProvider ;
public CloseAsyncResult ( SslStreamSecurityUpgradeInitiator parent , TimeSpan timeout ,
AsyncCallback callback , object state )
: base ( callback , state )
{
this . parent = parent ;
TimeoutHelper timeoutHelper = new TimeoutHelper ( timeout ) ;
// since we're at channel.Open and not per-message, minimize our statics overhead and leverage GC for our callback
this . onBaseClose = Fx . ThunkCallback ( new AsyncCallback ( OnBaseClose ) ) ;
if ( parent . clientCertificateProvider ! = null )
{
this . onCloseTokenProvider = Fx . ThunkCallback ( new AsyncCallback ( OnCloseTokenProvider ) ) ;
}
IAsyncResult result = parent . BaseBeginClose ( timeoutHelper . RemainingTime ( ) , onBaseClose , this ) ;
if ( ! result . CompletedSynchronously )
{
return ;
}
if ( HandleBaseCloseComplete ( result ) )
{
base . Complete ( true ) ;
}
}
public static void End ( IAsyncResult result )
{
AsyncResult . End < CloseAsyncResult > ( result ) ;
}
bool HandleBaseCloseComplete ( IAsyncResult result )
{
parent . BaseEndClose ( result ) ;
if ( parent . clientCertificateProvider = = null )
{
return true ;
}
IAsyncResult closeTokenProviderResult = SecurityUtils . BeginCloseTokenProviderIfRequired (
parent . clientCertificateProvider , timeoutHelper . RemainingTime ( ) , onCloseTokenProvider , this ) ;
if ( ! closeTokenProviderResult . CompletedSynchronously )
{
return false ;
}
SecurityUtils . EndCloseTokenProviderIfRequired ( closeTokenProviderResult ) ;
return true ;
}
void OnBaseClose ( IAsyncResult result )
{
if ( result . CompletedSynchronously )
{
return ;
}
Exception completionException = null ;
bool completeSelf = false ;
try
{
completeSelf = this . HandleBaseCloseComplete ( result ) ;
}
2017-08-21 15:34:15 +00:00
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
2016-08-03 10:59:49 +00:00
catch ( Exception e )
{
if ( Fx . IsFatal ( e ) )
{
throw ;
}
completeSelf = true ;
completionException = e ;
}
if ( completeSelf )
{
base . Complete ( false , completionException ) ;
}
}
void OnCloseTokenProvider ( IAsyncResult result )
{
if ( result . CompletedSynchronously )
{
return ;
}
Exception completionException = null ;
try
{
SecurityUtils . EndCloseTokenProviderIfRequired ( result ) ;
}
2017-08-21 15:34:15 +00:00
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
2016-08-03 10:59:49 +00:00
catch ( Exception e )
{
if ( Fx . IsFatal ( e ) )
{
throw ;
}
completionException = e ;
}
base . Complete ( false , completionException ) ;
}
}
}
}