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.IdentityModel.Claims ;
using System.IdentityModel.Policy ;
using System.IdentityModel.Selectors ;
using System.IdentityModel.Tokens ;
using System.Net ;
using System.Net.Security ;
using System.Runtime ;
using System.Runtime.CompilerServices ;
using System.Security.Cryptography.X509Certificates ;
using System.ServiceModel ;
using System.ServiceModel.Description ;
using System.ServiceModel.Security ;
using System.ServiceModel.Security.Tokens ;
class HttpsChannelFactory < TChannel > : HttpChannelFactory < TChannel >
{
bool requireClientCertificate ;
IChannelBindingProvider channelBindingProvider ;
RemoteCertificateValidationCallback remoteCertificateValidationCallback ;
X509CertificateValidator sslCertificateValidator ;
internal HttpsChannelFactory ( HttpsTransportBindingElement httpsBindingElement , BindingContext context )
: base ( httpsBindingElement , context )
{
this . requireClientCertificate = httpsBindingElement . RequireClientCertificate ;
this . channelBindingProvider = new ChannelBindingProviderHelper ( ) ;
ClientCredentials credentials = context . BindingParameters . Find < ClientCredentials > ( ) ;
if ( credentials ! = null & & credentials . ServiceCertificate . SslCertificateAuthentication ! = null )
{
this . sslCertificateValidator = credentials . ServiceCertificate . SslCertificateAuthentication . GetCertificateValidator ( ) ;
this . remoteCertificateValidationCallback = new RemoteCertificateValidationCallback ( RemoteCertificateValidationCallback ) ;
}
}
public override string Scheme
{
get
{
return Uri . UriSchemeHttps ;
}
}
public bool RequireClientCertificate
{
get
{
return this . requireClientCertificate ;
}
}
public override bool IsChannelBindingSupportEnabled
{
get
{
return this . channelBindingProvider . IsChannelBindingSupportEnabled ;
}
}
public override T GetProperty < T > ( )
{
if ( typeof ( T ) = = typeof ( IChannelBindingProvider ) )
{
return ( T ) ( object ) this . channelBindingProvider ;
}
return base . GetProperty < T > ( ) ;
}
internal override SecurityMessageProperty CreateReplySecurityProperty ( HttpWebRequest request ,
HttpWebResponse response )
{
SecurityMessageProperty result = null ;
X509Certificate certificate = request . ServicePoint . Certificate ;
if ( certificate ! = null )
{
X509Certificate2 certificateEx = new X509Certificate2 ( certificate ) ;
SecurityToken token = new X509SecurityToken ( certificateEx , false ) ;
ReadOnlyCollection < IAuthorizationPolicy > authorizationPolicies = SecurityUtils . NonValidatingX509Authenticator . ValidateToken ( token ) ;
result = new SecurityMessageProperty ( ) ;
result . TransportToken = new SecurityTokenSpecification ( token , authorizationPolicies ) ;
result . ServiceSecurityContext = new ServiceSecurityContext ( authorizationPolicies ) ;
}
else
{
result = base . CreateReplySecurityProperty ( request , response ) ;
}
return result ;
}
protected override void ValidateCreateChannelParameters ( EndpointAddress remoteAddress , Uri via )
{
if ( remoteAddress . Identity ! = null )
{
X509CertificateEndpointIdentity certificateIdentity =
remoteAddress . Identity as X509CertificateEndpointIdentity ;
if ( certificateIdentity ! = null )
{
if ( certificateIdentity . Certificates . Count > 1 )
{
throw DiagnosticUtility . ExceptionUtility . ThrowHelperArgument ( "remoteAddress" , SR . GetString (
SR . HttpsIdentityMultipleCerts , remoteAddress . Uri ) ) ;
}
}
EndpointIdentity identity = remoteAddress . Identity ;
bool validIdentity = ( certificateIdentity ! = null )
| | ClaimTypes . Spn . Equals ( identity . IdentityClaim . ClaimType )
| | ClaimTypes . Upn . Equals ( identity . IdentityClaim . ClaimType )
| | ClaimTypes . Dns . Equals ( identity . IdentityClaim . ClaimType ) ;
if ( ! HttpChannelFactory < TChannel > . IsWindowsAuth ( this . AuthenticationScheme )
& & ! validIdentity )
{
throw DiagnosticUtility . ExceptionUtility . ThrowHelperArgument ( "remoteAddress" , SR . GetString (
SR . HttpsExplicitIdentity ) ) ;
}
}
base . ValidateCreateChannelParameters ( remoteAddress , via ) ;
}
protected override TChannel OnCreateChannelCore ( EndpointAddress address , Uri via )
{
ValidateCreateChannelParameters ( address , via ) ;
this . ValidateWebSocketTransportUsage ( ) ;
if ( typeof ( TChannel ) = = typeof ( IRequestChannel ) )
{
return ( TChannel ) ( object ) new HttpsRequestChannel ( ( HttpsChannelFactory < IRequestChannel > ) ( object ) this , address , via , ManualAddressing ) ;
}
else
{
return ( TChannel ) ( object ) new ClientWebSocketTransportDuplexSessionChannel ( ( HttpChannelFactory < IDuplexSessionChannel > ) ( object ) this , this . ClientWebSocketFactory , address , via , this . WebSocketBufferPool ) ;
}
}
protected override bool IsSecurityTokenManagerRequired ( )
{
return this . requireClientCertificate | | base . IsSecurityTokenManagerRequired ( ) ;
}
protected override string OnGetConnectionGroupPrefix ( HttpWebRequest httpWebRequest , SecurityTokenContainer clientCertificateToken )
{
System . Text . StringBuilder inputStringBuilder = new System . Text . StringBuilder ( ) ;
string delimiter = "\0" ; // nonprintable characters are invalid for SSPI Domain/UserName/Password
if ( this . RequireClientCertificate )
{
HttpsChannelFactory < TChannel > . SetCertificate ( httpWebRequest , clientCertificateToken ) ;
X509CertificateCollection certificateCollection = httpWebRequest . ClientCertificates ;
for ( int i = 0 ; i < certificateCollection . Count ; i + + )
{
inputStringBuilder . AppendFormat ( "{0}{1}" , certificateCollection [ i ] . GetCertHashString ( ) , delimiter ) ;
}
}
return inputStringBuilder . ToString ( ) ;
}
void OnOpenCore ( )
{
if ( this . requireClientCertificate & & this . SecurityTokenManager = = null )
{
throw Fx . AssertAndThrow ( "HttpsChannelFactory: SecurityTokenManager is null on open." ) ;
}
}
protected override void OnEndOpen ( IAsyncResult result )
{
base . OnEndOpen ( result ) ;
OnOpenCore ( ) ;
}
protected override void OnOpen ( TimeSpan timeout )
{
base . OnOpen ( timeout ) ;
OnOpenCore ( ) ;
}
internal SecurityTokenProvider CreateAndOpenCertificateTokenProvider ( EndpointAddress target , Uri via , ChannelParameterCollection channelParameters , TimeSpan timeout )
{
if ( ! this . RequireClientCertificate )
{
return null ;
}
SecurityTokenProvider certificateProvider = TransportSecurityHelpers . GetCertificateTokenProvider (
this . SecurityTokenManager , target , via , this . Scheme , channelParameters ) ;
SecurityUtils . OpenTokenProviderIfRequired ( certificateProvider , timeout ) ;
return certificateProvider ;
}
static void SetCertificate ( HttpWebRequest request , SecurityTokenContainer clientCertificateToken )
{
if ( clientCertificateToken ! = null )
{
X509SecurityToken x509Token = ( X509SecurityToken ) clientCertificateToken . Token ;
request . ClientCertificates . Add ( x509Token . Certificate ) ;
}
}
internal SecurityTokenContainer GetCertificateSecurityToken ( SecurityTokenProvider certificateProvider ,
EndpointAddress to , Uri via , ChannelParameterCollection channelParameters , ref TimeoutHelper timeoutHelper )
{
SecurityToken token = null ;
SecurityTokenContainer tokenContainer = null ;
SecurityTokenProvider webRequestCertificateProvider ;
if ( ManualAddressing & & this . RequireClientCertificate )
{
webRequestCertificateProvider = CreateAndOpenCertificateTokenProvider ( to , via , channelParameters , timeoutHelper . RemainingTime ( ) ) ;
}
else
{
webRequestCertificateProvider = certificateProvider ;
}
if ( webRequestCertificateProvider ! = null )
{
token = webRequestCertificateProvider . GetToken ( timeoutHelper . RemainingTime ( ) ) ;
}
if ( ManualAddressing & & this . RequireClientCertificate )
{
SecurityUtils . AbortTokenProviderIfRequired ( webRequestCertificateProvider ) ;
}
if ( token ! = null )
{
tokenContainer = new SecurityTokenContainer ( token ) ;
}
return tokenContainer ;
}
void AddServerCertMappingOrSetRemoteCertificateValidationCallback ( HttpWebRequest request , EndpointAddress to )
{
Fx . Assert ( request ! = null , "request should not be null." ) ;
if ( this . sslCertificateValidator ! = null )
{
request . ServerCertificateValidationCallback = this . remoteCertificateValidationCallback ;
}
else
{
HttpTransportSecurityHelpers . AddServerCertMapping ( request , to ) ;
}
}
[ System . Diagnostics . CodeAnalysis . SuppressMessage ( FxCop . Category . ReliabilityBasic ,
"Reliability104:CaughtAndHandledExceptionsRule" ,
Justification = "The exception being thrown out comes from user code. Any non-fatal exception should be handled and translated into validation failure(return false)." ) ]
bool RemoteCertificateValidationCallback ( object sender , X509Certificate certificate , X509Chain chain , SslPolicyErrors sslPolicyErrors )
{
Fx . Assert ( sender is HttpWebRequest , "sender should be HttpWebRequest" ) ;
Fx . Assert ( this . sslCertificateValidator ! = null , "sslCertificateAuthentidation should not be null." ) ;
try
{
this . sslCertificateValidator . Validate ( new X509Certificate2 ( certificate ) ) ;
return true ;
}
catch ( SecurityTokenValidationException ex )
{
FxTrace . Exception . AsInformation ( ex ) ;
return false ;
}
catch ( Exception ex )
{
if ( Fx . IsFatal ( ex ) )
{
throw ;
}
FxTrace . Exception . AsWarning ( ex ) ;
return false ;
}
}
class HttpsRequestChannel : HttpRequestChannel
{
SecurityTokenProvider certificateProvider ;
HttpsChannelFactory < IRequestChannel > factory ;
public HttpsRequestChannel ( HttpsChannelFactory < IRequestChannel > factory , EndpointAddress to , Uri via , bool manualAddressing )
: base ( factory , to , via , manualAddressing )
{
this . factory = factory ;
}
public new HttpsChannelFactory < IRequestChannel > Factory
{
get { return this . factory ; }
}
void CreateAndOpenTokenProvider ( TimeSpan timeout )
{
if ( ! ManualAddressing & & this . Factory . RequireClientCertificate )
{
this . certificateProvider = Factory . CreateAndOpenCertificateTokenProvider ( this . RemoteAddress , this . Via , this . ChannelParameters , timeout ) ;
}
}
void CloseTokenProvider ( TimeSpan timeout )
{
if ( this . certificateProvider ! = null )
{
SecurityUtils . CloseTokenProviderIfRequired ( this . certificateProvider , timeout ) ;
}
}
void AbortTokenProvider ( )
{
if ( this . certificateProvider ! = null )
{
SecurityUtils . AbortTokenProviderIfRequired ( this . certificateProvider ) ;
}
}
protected override IAsyncResult OnBeginOpen ( TimeSpan timeout , AsyncCallback callback , object state )
{
TimeoutHelper timeoutHelper = new TimeoutHelper ( timeout ) ;
CreateAndOpenTokenProvider ( timeoutHelper . RemainingTime ( ) ) ;
return base . OnBeginOpen ( timeoutHelper . RemainingTime ( ) , callback , state ) ;
}
protected override void OnOpen ( TimeSpan timeout )
{
TimeoutHelper timeoutHelper = new TimeoutHelper ( timeout ) ;
CreateAndOpenTokenProvider ( timeoutHelper . RemainingTime ( ) ) ;
base . OnOpen ( timeoutHelper . RemainingTime ( ) ) ;
}
protected override void OnAbort ( )
{
AbortTokenProvider ( ) ;
base . OnAbort ( ) ;
}
protected override IAsyncResult OnBeginClose ( TimeSpan timeout , AsyncCallback callback , object state )
{
TimeoutHelper timeoutHelper = new TimeoutHelper ( timeout ) ;
CloseTokenProvider ( timeoutHelper . RemainingTime ( ) ) ;
return base . OnBeginClose ( timeoutHelper . RemainingTime ( ) , callback , state ) ;
}
protected override void OnClose ( TimeSpan timeout )
{
TimeoutHelper timeoutHelper = new TimeoutHelper ( timeout ) ;
CloseTokenProvider ( timeoutHelper . RemainingTime ( ) ) ;
base . OnClose ( timeoutHelper . RemainingTime ( ) ) ;
}
public IAsyncResult BeginBaseGetWebRequest ( EndpointAddress to , Uri via , SecurityTokenContainer clientCertificateToken , ref TimeoutHelper timeoutHelper , AsyncCallback callback , object state )
{
return base . BeginGetWebRequest ( to , via , clientCertificateToken , ref timeoutHelper , callback , state ) ;
}
public HttpWebRequest EndBaseGetWebRequest ( IAsyncResult result )
{
return base . EndGetWebRequest ( result ) ;
}
public override HttpWebRequest GetWebRequest ( EndpointAddress to , Uri via , ref TimeoutHelper timeoutHelper )
{
SecurityTokenContainer clientCertificateToken = Factory . GetCertificateSecurityToken ( this . certificateProvider , to , via , this . ChannelParameters , ref timeoutHelper ) ;
HttpWebRequest request = base . GetWebRequest ( to , via , clientCertificateToken , ref timeoutHelper ) ;
this . factory . AddServerCertMappingOrSetRemoteCertificateValidationCallback ( request , to ) ;
return request ;
}
public override IAsyncResult BeginGetWebRequest ( EndpointAddress to , Uri via , ref TimeoutHelper timeoutHelper , AsyncCallback callback , object state )
{
return new GetWebRequestAsyncResult ( this , to , via , ref timeoutHelper , callback , state ) ;
}
public override HttpWebRequest EndGetWebRequest ( IAsyncResult result )
{
return GetWebRequestAsyncResult . End ( result ) ;
}
public override bool WillGetWebRequestCompleteSynchronously ( )
{
if ( ! base . WillGetWebRequestCompleteSynchronously ( ) )
{
return false ;
}
return ( this . certificateProvider = = null & & ! Factory . ManualAddressing ) ;
}
internal override void OnWebRequestCompleted ( HttpWebRequest request )
{
HttpTransportSecurityHelpers . RemoveServerCertMapping ( request ) ;
}
class GetWebRequestAsyncResult : AsyncResult
{
SecurityTokenProvider certificateProvider ;
HttpsChannelFactory < IRequestChannel > factory ;
HttpsRequestChannel httpsChannel ;
HttpWebRequest request ;
EndpointAddress to ;
Uri via ;
TimeoutHelper timeoutHelper ;
SecurityTokenContainer tokenContainer ;
static AsyncCallback onGetBaseWebRequestCallback = Fx . ThunkCallback ( new AsyncCallback ( OnGetBaseWebRequestCallback ) ) ;
static AsyncCallback onGetTokenCallback ;
public GetWebRequestAsyncResult ( HttpsRequestChannel httpsChannel , EndpointAddress to , Uri via ,
ref TimeoutHelper timeoutHelper ,
AsyncCallback callback , object state )
: base ( callback , state )
{
this . httpsChannel = httpsChannel ;
this . to = to ;
this . via = via ;
this . timeoutHelper = timeoutHelper ;
this . factory = httpsChannel . Factory ;
this . certificateProvider = httpsChannel . certificateProvider ;
if ( this . factory . ManualAddressing & & this . factory . RequireClientCertificate )
{
this . certificateProvider =
this . factory . CreateAndOpenCertificateTokenProvider ( to , via , httpsChannel . ChannelParameters , timeoutHelper . RemainingTime ( ) ) ;
}
if ( ! GetToken ( ) )
{
return ;
}
if ( ! GetWebRequest ( ) )
{
return ;
}
base . Complete ( true ) ;
}
bool GetWebRequest ( )
{
IAsyncResult result = this . httpsChannel . BeginBaseGetWebRequest ( to , via , tokenContainer , ref timeoutHelper , onGetBaseWebRequestCallback , this ) ;
if ( ! result . CompletedSynchronously )
{
return false ;
}
this . request = this . httpsChannel . EndBaseGetWebRequest ( result ) ;
this . factory . AddServerCertMappingOrSetRemoteCertificateValidationCallback ( this . request , this . to ) ;
return true ;
}
bool GetToken ( )
{
if ( this . certificateProvider ! = null )
{
if ( onGetTokenCallback = = null )
{
onGetTokenCallback = Fx . ThunkCallback ( new AsyncCallback ( OnGetTokenCallback ) ) ;
}
IAsyncResult result = this . certificateProvider . BeginGetToken (
timeoutHelper . RemainingTime ( ) , onGetTokenCallback , this ) ;
if ( ! result . CompletedSynchronously )
{
return false ;
}
OnGetToken ( result ) ;
}
return true ;
}
static void OnGetBaseWebRequestCallback ( IAsyncResult result )
{
if ( result . CompletedSynchronously )
return ;
GetWebRequestAsyncResult thisPtr = ( GetWebRequestAsyncResult ) result . AsyncState ;
Exception completionException = null ;
try
{
thisPtr . request = thisPtr . httpsChannel . EndBaseGetWebRequest ( result ) ;
thisPtr . factory . AddServerCertMappingOrSetRemoteCertificateValidationCallback ( thisPtr . request , thisPtr . to ) ;
}
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 ;
}
thisPtr . Complete ( false , completionException ) ;
}
static void OnGetTokenCallback ( IAsyncResult result )
{
if ( result . CompletedSynchronously )
return ;
GetWebRequestAsyncResult thisPtr = ( GetWebRequestAsyncResult ) result . AsyncState ;
Exception completionException = null ;
bool completeSelf ;
try
{
thisPtr . OnGetToken ( result ) ;
completeSelf = thisPtr . GetWebRequest ( ) ;
}
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 )
{
thisPtr . Complete ( false , completionException ) ;
}
}
void OnGetToken ( IAsyncResult result )
{
SecurityToken token = this . certificateProvider . EndGetToken ( result ) ;
if ( token ! = null )
{
this . tokenContainer = new SecurityTokenContainer ( token ) ;
}
CloseCertificateProviderIfRequired ( ) ;
}
void CloseCertificateProviderIfRequired ( )
{
if ( this . factory . ManualAddressing & & this . certificateProvider ! = null )
{
SecurityUtils . AbortTokenProviderIfRequired ( this . certificateProvider ) ;
}
}
public static HttpWebRequest End ( IAsyncResult result )
{
GetWebRequestAsyncResult thisPtr = AsyncResult . End < GetWebRequestAsyncResult > ( result ) ;
return thisPtr . request ;
}
}
}
}
}