1 //------------------------------------------------------------ 2 // Copyright (c) Microsoft Corporation. All rights reserved. 3 //------------------------------------------------------------ 4 namespace System.ServiceModel.Channels 5 { 6 using System.Collections.Generic; 7 using System.Collections.ObjectModel; 8 using System.Diagnostics; 9 using System.Globalization; 10 using System.IdentityModel.Policy; 11 using System.IdentityModel.Selectors; 12 using System.IdentityModel.Tokens; 13 using System.IO; 14 using System.Net; 15 using System.Net.Http; 16 using System.Net.WebSockets; 17 using System.Runtime; 18 using System.Runtime.CompilerServices; 19 using System.Runtime.Diagnostics; 20 using System.Security.Authentication.ExtendedProtection; 21 using System.Security.Cryptography.X509Certificates; 22 using System.Security.Principal; 23 using System.ServiceModel; 24 using System.ServiceModel.Activation; 25 using System.ServiceModel.Description; 26 using System.ServiceModel.Diagnostics; 27 using System.ServiceModel.Diagnostics.Application; 28 using System.ServiceModel.Dispatcher; 29 using System.ServiceModel.Security; 30 using System.ServiceModel.Security.Tokens; 31 using System.Threading; 32 using System.Threading.Tasks; 33 using System.Xml; 34 35 abstract class HttpChannelListener : TransportChannelListener, 36 IHttpTransportFactorySettings 37 { 38 AuthenticationSchemes authenticationScheme; 39 bool extractGroupsForWindowsAccounts; 40 EndpointIdentity identity; 41 bool keepAliveEnabled; 42 int maxBufferSize; 43 readonly int maxPendingAccepts; 44 string method; 45 string realm; 46 readonly TimeSpan requestInitializationTimeout; 47 TransferMode transferMode; 48 bool unsafeConnectionNtlmAuthentication; 49 ISecurityCapabilities securityCapabilities; 50 51 SecurityCredentialsManager credentialProvider; 52 SecurityTokenAuthenticator userNameTokenAuthenticator; 53 SecurityTokenAuthenticator windowsTokenAuthenticator; 54 ExtendedProtectionPolicy extendedProtectionPolicy; 55 bool usingDefaultSpnList; 56 HttpAnonymousUriPrefixMatcher anonymousUriPrefixMatcher; 57 58 HttpMessageSettings httpMessageSettings; 59 WebSocketTransportSettings webSocketSettings; 60 61 static UriPrefixTable<ITransportManagerRegistration> transportManagerTable = 62 new UriPrefixTable<ITransportManagerRegistration>(true); 63 HttpChannelListener(HttpTransportBindingElement bindingElement, BindingContext context)64 public HttpChannelListener(HttpTransportBindingElement bindingElement, BindingContext context) 65 : base(bindingElement, context, HttpTransportDefaults.GetDefaultMessageEncoderFactory(), 66 bindingElement.HostNameComparisonMode) 67 { 68 if (bindingElement.TransferMode == TransferMode.Buffered) 69 { 70 if (bindingElement.MaxReceivedMessageSize > int.MaxValue) 71 { 72 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 73 new ArgumentOutOfRangeException("bindingElement.MaxReceivedMessageSize", 74 SR.GetString(SR.MaxReceivedMessageSizeMustBeInIntegerRange))); 75 } 76 77 if (bindingElement.MaxBufferSize != bindingElement.MaxReceivedMessageSize) 78 { 79 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("bindingElement", 80 SR.GetString(SR.MaxBufferSizeMustMatchMaxReceivedMessageSize)); 81 } 82 } 83 else 84 { 85 if (bindingElement.MaxBufferSize > bindingElement.MaxReceivedMessageSize) 86 { 87 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("bindingElement", 88 SR.GetString(SR.MaxBufferSizeMustNotExceedMaxReceivedMessageSize)); 89 } 90 } 91 92 if (bindingElement.AuthenticationScheme.IsSet(AuthenticationSchemes.Basic) && 93 bindingElement.AuthenticationScheme.IsNotSet(AuthenticationSchemes.Digest | AuthenticationSchemes.Ntlm | AuthenticationSchemes.Negotiate) && 94 bindingElement.ExtendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.Always) 95 { 96 //Basic auth + PolicyEnforcement.Always doesn't make sense because basic auth can't support CBT. 97 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.ExtendedProtectionPolicyBasicAuthNotSupported))); 98 } 99 100 this.authenticationScheme = bindingElement.AuthenticationScheme; 101 this.keepAliveEnabled = bindingElement.KeepAliveEnabled; 102 this.InheritBaseAddressSettings = bindingElement.InheritBaseAddressSettings; 103 this.maxBufferSize = bindingElement.MaxBufferSize; 104 this.maxPendingAccepts = HttpTransportDefaults.GetEffectiveMaxPendingAccepts(bindingElement.MaxPendingAccepts); 105 this.method = bindingElement.Method; 106 this.realm = bindingElement.Realm; 107 this.requestInitializationTimeout = bindingElement.RequestInitializationTimeout; 108 this.transferMode = bindingElement.TransferMode; 109 this.unsafeConnectionNtlmAuthentication = bindingElement.UnsafeConnectionNtlmAuthentication; 110 this.credentialProvider = context.BindingParameters.Find<SecurityCredentialsManager>(); 111 this.securityCapabilities = bindingElement.GetProperty<ISecurityCapabilities>(context); 112 this.extendedProtectionPolicy = GetPolicyWithDefaultSpnCollection(bindingElement.ExtendedProtectionPolicy, this.authenticationScheme, this.HostNameComparisonModeInternal, base.Uri, out this.usingDefaultSpnList); 113 114 this.webSocketSettings = WebSocketHelper.GetRuntimeWebSocketSettings(bindingElement.WebSocketSettings); 115 116 if (bindingElement.AnonymousUriPrefixMatcher != null) 117 { 118 this.anonymousUriPrefixMatcher = new HttpAnonymousUriPrefixMatcher(bindingElement.AnonymousUriPrefixMatcher); 119 } 120 121 this.httpMessageSettings = context.BindingParameters.Find<HttpMessageSettings>() ?? new HttpMessageSettings(); 122 123 if (this.httpMessageSettings.HttpMessagesSupported && this.MessageVersion != MessageVersion.None) 124 { 125 throw FxTrace.Exception.AsError( 126 new NotSupportedException(SR.GetString( 127 SR.MessageVersionNoneRequiredForHttpMessageSupport, 128 typeof(HttpRequestMessage).Name, 129 typeof(HttpResponseMessage).Name, 130 typeof(HttpMessageSettings).Name, 131 typeof(MessageVersion).Name, 132 typeof(MessageEncodingBindingElement).Name, 133 this.MessageVersion.ToString(), 134 MessageVersion.None.ToString()))); 135 } 136 } 137 138 public TimeSpan RequestInitializationTimeout 139 { 140 get { return this.requestInitializationTimeout; } 141 } 142 143 public WebSocketTransportSettings WebSocketSettings 144 { 145 get { return this.webSocketSettings; } 146 } 147 148 public HttpMessageSettings HttpMessageSettings 149 { 150 get { return this.httpMessageSettings; } 151 } 152 153 public ExtendedProtectionPolicy ExtendedProtectionPolicy 154 { 155 get 156 { 157 return this.extendedProtectionPolicy; 158 } 159 } 160 161 public virtual bool IsChannelBindingSupportEnabled 162 { 163 get 164 { 165 return false; 166 } 167 } 168 169 public abstract bool UseWebSocketTransport { get; } 170 171 internal HttpAnonymousUriPrefixMatcher AnonymousUriPrefixMatcher 172 { 173 get 174 { 175 return this.anonymousUriPrefixMatcher; 176 } 177 } 178 179 protected SecurityTokenAuthenticator UserNameTokenAuthenticator 180 { 181 get { return this.userNameTokenAuthenticator; } 182 } 183 ApplyHostedContext(string virtualPath, bool isMetadataListener)184 internal override void ApplyHostedContext(string virtualPath, bool isMetadataListener) 185 { 186 base.ApplyHostedContext(virtualPath, isMetadataListener); 187 AspNetEnvironment.Current.ValidateHttpSettings(virtualPath, isMetadataListener, this.usingDefaultSpnList, ref this.authenticationScheme, ref this.extendedProtectionPolicy, ref this.realm); 188 } 189 190 public AuthenticationSchemes AuthenticationScheme 191 { 192 get 193 { 194 return this.authenticationScheme; 195 } 196 } 197 198 public bool KeepAliveEnabled 199 { 200 get 201 { 202 return this.keepAliveEnabled; 203 } 204 } 205 206 public bool ExtractGroupsForWindowsAccounts 207 { 208 get 209 { 210 return this.extractGroupsForWindowsAccounts; 211 } 212 } 213 214 public HostNameComparisonMode HostNameComparisonMode 215 { 216 get 217 { 218 return this.HostNameComparisonModeInternal; 219 } 220 } 221 222 //Returns true if one of the non-anonymous authentication schemes is set on this.AuthenticationScheme 223 protected bool IsAuthenticationSupported 224 { 225 get 226 { 227 return this.authenticationScheme != AuthenticationSchemes.Anonymous; 228 } 229 } 230 231 bool IsAuthenticationRequired 232 { 233 get 234 { 235 return this.AuthenticationScheme.IsNotSet(AuthenticationSchemes.Anonymous); 236 } 237 } 238 239 public int MaxBufferSize 240 { 241 get 242 { 243 return this.maxBufferSize; 244 } 245 } 246 247 public int MaxPendingAccepts 248 { 249 get { return this.maxPendingAccepts; } 250 } 251 252 public virtual string Method 253 { 254 get 255 { 256 return this.method; 257 } 258 } 259 260 public TransferMode TransferMode 261 { 262 get 263 { 264 return transferMode; 265 } 266 } 267 268 public string Realm 269 { 270 get { return this.realm; } 271 } 272 273 int IHttpTransportFactorySettings.MaxBufferSize 274 { 275 get { return MaxBufferSize; } 276 } 277 278 TransferMode IHttpTransportFactorySettings.TransferMode 279 { 280 get { return TransferMode; } 281 } 282 283 public override string Scheme 284 { 285 get { return Uri.UriSchemeHttp; } 286 } 287 288 internal static UriPrefixTable<ITransportManagerRegistration> StaticTransportManagerTable 289 { 290 get 291 { 292 return transportManagerTable; 293 } 294 } 295 296 public bool UnsafeConnectionNtlmAuthentication 297 { 298 get 299 { 300 return this.unsafeConnectionNtlmAuthentication; 301 } 302 } 303 304 internal override UriPrefixTable<ITransportManagerRegistration> TransportManagerTable 305 { 306 get 307 { 308 return transportManagerTable; 309 } 310 } 311 CreateTransportManagerRegistration(Uri listenUri)312 internal override ITransportManagerRegistration CreateTransportManagerRegistration(Uri listenUri) 313 { 314 return new SharedHttpTransportManager(listenUri, this); 315 } 316 GetAuthType(HttpListenerContext listenerContext)317 string GetAuthType(HttpListenerContext listenerContext) 318 { 319 string authType = null; 320 IPrincipal principal = listenerContext.User; 321 if ((principal != null) && (principal.Identity != null)) 322 { 323 authType = principal.Identity.AuthenticationType; 324 } 325 return authType; 326 } 327 GetAuthType(IHttpAuthenticationContext authenticationContext)328 protected string GetAuthType(IHttpAuthenticationContext authenticationContext) 329 { 330 string authType = null; 331 if (authenticationContext.LogonUserIdentity != null) 332 { 333 authType = authenticationContext.LogonUserIdentity.AuthenticationType; 334 } 335 return authType; 336 } 337 IsAuthSchemeValid(string authType)338 bool IsAuthSchemeValid(string authType) 339 { 340 return AuthenticationSchemesHelper.DoesAuthTypeMatch(this.authenticationScheme, authType); 341 } 342 GetMaxBufferSize()343 internal override int GetMaxBufferSize() 344 { 345 return MaxBufferSize; 346 } 347 GetProperty()348 public override T GetProperty<T>() 349 { 350 if (typeof(T) == typeof(EndpointIdentity)) 351 { 352 return (T)(object)(this.identity); 353 } 354 else if (typeof(T) == typeof(ILogonTokenCacheManager)) 355 { 356 object cacheManager = (object)GetIdentityModelProperty<T>(); 357 if (cacheManager != null) 358 { 359 return (T)cacheManager; 360 } 361 } 362 else if (typeof(T) == typeof(ISecurityCapabilities)) 363 { 364 return (T)(object)this.securityCapabilities; 365 } 366 else if (typeof(T) == typeof(ExtendedProtectionPolicy)) 367 { 368 return (T)(object)this.extendedProtectionPolicy; 369 } 370 371 return base.GetProperty<T>(); 372 } 373 374 [MethodImpl(MethodImplOptions.NoInlining)] GetIdentityModelProperty()375 T GetIdentityModelProperty<T>() 376 { 377 if (typeof(T) == typeof(EndpointIdentity)) 378 { 379 if (this.identity == null) 380 { 381 if (this.authenticationScheme.IsSet(AuthenticationSchemes.Negotiate) || 382 this.authenticationScheme.IsSet(AuthenticationSchemes.Ntlm)) 383 { 384 this.identity = SecurityUtils.CreateWindowsIdentity(); 385 } 386 } 387 388 return (T)(object)this.identity; 389 } 390 else if (typeof(T) == typeof(ILogonTokenCacheManager) 391 && (this.userNameTokenAuthenticator != null)) 392 { 393 ILogonTokenCacheManager retVal = this.userNameTokenAuthenticator as ILogonTokenCacheManager; 394 395 if (retVal != null) 396 { 397 return (T)(object)retVal; 398 } 399 } 400 401 return default(T); 402 } 403 BeginHttpContextReceived( HttpRequestContext context, Action acceptorCallback, AsyncCallback callback, object state)404 internal abstract IAsyncResult BeginHttpContextReceived( 405 HttpRequestContext context, 406 Action acceptorCallback, 407 AsyncCallback callback, 408 object state); 409 EndHttpContextReceived(IAsyncResult result)410 internal abstract bool EndHttpContextReceived(IAsyncResult result); 411 412 [MethodImpl(MethodImplOptions.NoInlining)] InitializeSecurityTokenAuthenticator()413 void InitializeSecurityTokenAuthenticator() 414 { 415 Fx.Assert(this.IsAuthenticationSupported, "SecurityTokenAuthenticator should only be initialized when authentication is supported."); 416 ServiceCredentials serviceCredentials = this.credentialProvider as ServiceCredentials; 417 418 if (serviceCredentials != null) 419 { 420 if (this.AuthenticationScheme == AuthenticationSchemes.Basic) 421 { 422 // when Basic authentiction is enabled - but Digest and Windows are disabled use the UsernameAuthenticationSetting 423 this.extractGroupsForWindowsAccounts = serviceCredentials.UserNameAuthentication.IncludeWindowsGroups; 424 } 425 else 426 { 427 if (this.AuthenticationScheme.IsSet(AuthenticationSchemes.Basic) && 428 serviceCredentials.UserNameAuthentication.IncludeWindowsGroups != serviceCredentials.WindowsAuthentication.IncludeWindowsGroups) 429 { 430 // Ensure there are no inconsistencies when Basic and (Digest and/or Ntlm and/or Negotiate) are both enabled 431 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SecurityTokenProviderIncludeWindowsGroupsInconsistent, 432 (AuthenticationSchemes)authenticationScheme - AuthenticationSchemes.Basic, 433 serviceCredentials.UserNameAuthentication.IncludeWindowsGroups, 434 serviceCredentials.WindowsAuthentication.IncludeWindowsGroups))); 435 } 436 437 this.extractGroupsForWindowsAccounts = serviceCredentials.WindowsAuthentication.IncludeWindowsGroups; 438 } 439 440 // we will only support custom and windows validation modes, if anything else is specified, we'll fall back to windows user name. 441 if (serviceCredentials.UserNameAuthentication.UserNamePasswordValidationMode == UserNamePasswordValidationMode.Custom) 442 { 443 this.userNameTokenAuthenticator = new CustomUserNameSecurityTokenAuthenticator(serviceCredentials.UserNameAuthentication.GetUserNamePasswordValidator()); 444 } 445 else 446 { 447 if (serviceCredentials.UserNameAuthentication.CacheLogonTokens) 448 { 449 this.userNameTokenAuthenticator = new WindowsUserNameCachingSecurityTokenAuthenticator(this.extractGroupsForWindowsAccounts, 450 serviceCredentials.UserNameAuthentication.MaxCachedLogonTokens, serviceCredentials.UserNameAuthentication.CachedLogonTokenLifetime); 451 } 452 else 453 { 454 this.userNameTokenAuthenticator = new WindowsUserNameSecurityTokenAuthenticator(this.extractGroupsForWindowsAccounts); 455 } 456 } 457 } 458 else 459 { 460 this.extractGroupsForWindowsAccounts = TransportDefaults.ExtractGroupsForWindowsAccounts; 461 this.userNameTokenAuthenticator = new WindowsUserNameSecurityTokenAuthenticator(this.extractGroupsForWindowsAccounts); 462 } 463 464 this.windowsTokenAuthenticator = new WindowsSecurityTokenAuthenticator(this.extractGroupsForWindowsAccounts); 465 } 466 OnOpened()467 protected override void OnOpened() 468 { 469 base.OnOpened(); 470 471 if (this.IsAuthenticationSupported) 472 { 473 InitializeSecurityTokenAuthenticator(); 474 this.identity = GetIdentityModelProperty<EndpointIdentity>(); 475 } 476 } 477 478 [MethodImpl(MethodImplOptions.NoInlining)] CloseUserNameTokenAuthenticator(TimeSpan timeout)479 protected void CloseUserNameTokenAuthenticator(TimeSpan timeout) 480 { 481 SecurityUtils.CloseTokenAuthenticatorIfRequired(this.userNameTokenAuthenticator, timeout); 482 } 483 484 [MethodImpl(MethodImplOptions.NoInlining)] AbortUserNameTokenAuthenticator()485 protected void AbortUserNameTokenAuthenticator() 486 { 487 SecurityUtils.AbortTokenAuthenticatorIfRequired(this.userNameTokenAuthenticator); 488 } 489 ShouldProcessAuthentication(IHttpAuthenticationContext authenticationContext)490 bool ShouldProcessAuthentication(IHttpAuthenticationContext authenticationContext) 491 { 492 Fx.Assert(authenticationContext != null, "IsAuthenticated should only be called if authenticationContext != null"); 493 Fx.Assert(authenticationContext.LogonUserIdentity != null, "IsAuthenticated should only be called if authenticationContext.LogonUserIdentity != null"); 494 return this.IsAuthenticationRequired || (this.IsAuthenticationSupported && authenticationContext.LogonUserIdentity.IsAuthenticated); 495 } 496 ShouldProcessAuthentication(HttpListenerContext listenerContext)497 bool ShouldProcessAuthentication(HttpListenerContext listenerContext) 498 { 499 Fx.Assert(listenerContext != null, "IsAuthenticated should only be called if listenerContext != null"); 500 Fx.Assert(listenerContext.Request != null, "IsAuthenticated should only be called if listenerContext.Request != null"); 501 return this.IsAuthenticationRequired || (this.IsAuthenticationSupported && listenerContext.Request.IsAuthenticated); 502 } 503 ProcessAuthentication(IHttpAuthenticationContext authenticationContext)504 public virtual SecurityMessageProperty ProcessAuthentication(IHttpAuthenticationContext authenticationContext) 505 { 506 if (this.ShouldProcessAuthentication(authenticationContext)) 507 { 508 SecurityMessageProperty retValue; 509 try 510 { 511 retValue = this.ProcessAuthentication(authenticationContext.LogonUserIdentity, GetAuthType(authenticationContext)); 512 } 513 #pragma warning suppress 56500 // covered by FXCop 514 catch (Exception exception) 515 { 516 if (Fx.IsFatal(exception)) 517 throw; 518 519 // Audit Authentication failure 520 if (AuditLevel.Failure == (this.AuditBehavior.MessageAuthenticationAuditLevel & AuditLevel.Failure)) 521 WriteAuditEvent(AuditLevel.Failure, (authenticationContext.LogonUserIdentity != null) ? authenticationContext.LogonUserIdentity.Name : String.Empty, exception); 522 523 throw; 524 } 525 526 // Audit Authentication success 527 if (AuditLevel.Success == (this.AuditBehavior.MessageAuthenticationAuditLevel & AuditLevel.Success)) 528 WriteAuditEvent(AuditLevel.Success, (authenticationContext.LogonUserIdentity != null) ? authenticationContext.LogonUserIdentity.Name : String.Empty, null); 529 530 return retValue; 531 } 532 else 533 { 534 return null; 535 } 536 } 537 ProcessAuthentication(HttpListenerContext listenerContext)538 public virtual SecurityMessageProperty ProcessAuthentication(HttpListenerContext listenerContext) 539 { 540 if (this.ShouldProcessAuthentication(listenerContext)) 541 { 542 return this.ProcessRequiredAuthentication(listenerContext); 543 } 544 else 545 { 546 return null; 547 } 548 } 549 ProcessRequiredAuthentication(HttpListenerContext listenerContext)550 SecurityMessageProperty ProcessRequiredAuthentication(HttpListenerContext listenerContext) 551 { 552 SecurityMessageProperty retValue; 553 HttpListenerBasicIdentity identity = null; 554 WindowsIdentity wid = null; 555 try 556 { 557 Fx.Assert(listenerContext.User != null, "HttpListener delivered authenticated request without an IPrincipal."); 558 wid = listenerContext.User.Identity as WindowsIdentity; 559 560 if (this.AuthenticationScheme.IsSet(AuthenticationSchemes.Basic) 561 && wid == null) 562 { 563 identity = listenerContext.User.Identity as HttpListenerBasicIdentity; 564 Fx.Assert(identity != null, "HttpListener delivered Basic authenticated request with a non-Basic IIdentity."); 565 retValue = this.ProcessAuthentication(identity); 566 } 567 else 568 { 569 Fx.Assert(wid != null, "HttpListener delivered non-Basic authenticated request with a non-Windows IIdentity."); 570 retValue = this.ProcessAuthentication(wid, GetAuthType(listenerContext)); 571 } 572 } 573 #pragma warning suppress 56500 // covered by FXCop 574 catch (Exception exception) 575 { 576 if (!Fx.IsFatal(exception)) 577 { 578 // Audit Authentication failure 579 if (AuditLevel.Failure == (this.AuditBehavior.MessageAuthenticationAuditLevel & AuditLevel.Failure)) 580 { 581 WriteAuditEvent(AuditLevel.Failure, (identity != null) ? identity.Name : ((wid != null) ? wid.Name : String.Empty), exception); 582 } 583 } 584 throw; 585 } 586 587 // Audit Authentication success 588 if (AuditLevel.Success == (this.AuditBehavior.MessageAuthenticationAuditLevel & AuditLevel.Success)) 589 { 590 WriteAuditEvent(AuditLevel.Success, (identity != null) ? identity.Name : ((wid != null) ? wid.Name : String.Empty), null); 591 } 592 593 return retValue; 594 } 595 TryGetTransportManagerRegistration(HostNameComparisonMode hostNameComparisonMode, out ITransportManagerRegistration registration)596 protected override bool TryGetTransportManagerRegistration(HostNameComparisonMode hostNameComparisonMode, 597 out ITransportManagerRegistration registration) 598 { 599 if (this.TransportManagerTable.TryLookupUri(this.Uri, hostNameComparisonMode, out registration)) 600 { 601 HttpTransportManager httpTransportManager = registration as HttpTransportManager; 602 if (httpTransportManager != null && httpTransportManager.IsHosted) 603 { 604 return true; 605 } 606 // Due to HTTP.SYS behavior, we don't reuse registrations from a higher point in the URI hierarchy. 607 if (registration.ListenUri.Segments.Length >= this.BaseUri.Segments.Length) 608 { 609 return true; 610 } 611 } 612 return false; 613 } 614 WriteAuditEvent(AuditLevel auditLevel, string primaryIdentity, Exception exception)615 protected void WriteAuditEvent(AuditLevel auditLevel, string primaryIdentity, Exception exception) 616 { 617 try 618 { 619 if (auditLevel == AuditLevel.Success) 620 { 621 SecurityAuditHelper.WriteTransportAuthenticationSuccessEvent(this.AuditBehavior.AuditLogLocation, 622 this.AuditBehavior.SuppressAuditFailure, null, this.Uri, primaryIdentity); 623 } 624 else 625 { 626 SecurityAuditHelper.WriteTransportAuthenticationFailureEvent(this.AuditBehavior.AuditLogLocation, 627 this.AuditBehavior.SuppressAuditFailure, null, this.Uri, primaryIdentity, exception); 628 } 629 } 630 #pragma warning suppress 56500 631 catch (Exception auditException) 632 { 633 if (Fx.IsFatal(auditException) || auditLevel == AuditLevel.Success) 634 throw; 635 636 DiagnosticUtility.TraceHandledException(auditException, TraceEventType.Error); 637 } 638 } 639 ProcessAuthentication(HttpListenerBasicIdentity identity)640 SecurityMessageProperty ProcessAuthentication(HttpListenerBasicIdentity identity) 641 { 642 SecurityToken securityToken = new UserNameSecurityToken(identity.Name, identity.Password); 643 ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies = this.userNameTokenAuthenticator.ValidateToken(securityToken); 644 SecurityMessageProperty security = new SecurityMessageProperty(); 645 security.TransportToken = new SecurityTokenSpecification(securityToken, authorizationPolicies); 646 security.ServiceSecurityContext = new ServiceSecurityContext(authorizationPolicies); 647 return security; 648 } 649 ProcessAuthentication(WindowsIdentity identity, string authenticationType)650 SecurityMessageProperty ProcessAuthentication(WindowsIdentity identity, string authenticationType) 651 { 652 SecurityUtils.ValidateAnonymityConstraint(identity, false); 653 SecurityToken securityToken = new WindowsSecurityToken(identity, SecurityUniqueId.Create().Value, authenticationType); 654 ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies = this.windowsTokenAuthenticator.ValidateToken(securityToken); 655 SecurityMessageProperty security = new SecurityMessageProperty(); 656 security.TransportToken = new SecurityTokenSpecification(securityToken, authorizationPolicies); 657 security.ServiceSecurityContext = new ServiceSecurityContext(authorizationPolicies); 658 return security; 659 } 660 ValidateAuthentication(string authType)661 HttpStatusCode ValidateAuthentication(string authType) 662 { 663 if (this.IsAuthSchemeValid(authType)) 664 { 665 return HttpStatusCode.OK; 666 } 667 else 668 { 669 // Audit Authentication failure 670 if (AuditLevel.Failure == (this.AuditBehavior.MessageAuthenticationAuditLevel & AuditLevel.Failure)) 671 { 672 string message = SR.GetString(SR.HttpAuthenticationFailed, this.AuthenticationScheme, HttpStatusCode.Unauthorized); 673 Exception exception = DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(message)); 674 WriteAuditEvent(AuditLevel.Failure, String.Empty, exception); 675 } 676 677 return HttpStatusCode.Unauthorized; 678 } 679 } 680 ValidateAuthentication(IHttpAuthenticationContext authenticationContext)681 public virtual HttpStatusCode ValidateAuthentication(IHttpAuthenticationContext authenticationContext) 682 { 683 HttpStatusCode result = HttpStatusCode.OK; 684 685 if (this.IsAuthenticationSupported) 686 { 687 string authType = GetAuthType(authenticationContext); 688 result = ValidateAuthentication(authType); 689 } 690 691 if (result == HttpStatusCode.OK && 692 authenticationContext.LogonUserIdentity != null && 693 authenticationContext.LogonUserIdentity.IsAuthenticated && 694 this.ExtendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.Always && 695 !authenticationContext.IISSupportsExtendedProtection) 696 { 697 Exception exception = DiagnosticUtility.ExceptionUtility.ThrowHelperError( 698 new PlatformNotSupportedException(SR.GetString(SR.ExtendedProtectionNotSupported))); 699 WriteAuditEvent(AuditLevel.Failure, String.Empty, exception); 700 701 result = HttpStatusCode.Unauthorized; 702 } 703 704 return result; 705 } 706 ValidateAuthentication(HttpListenerContext listenerContext)707 public virtual HttpStatusCode ValidateAuthentication(HttpListenerContext listenerContext) 708 { 709 HttpStatusCode result = HttpStatusCode.OK; 710 711 if (this.IsAuthenticationSupported) 712 { 713 string authType = GetAuthType(listenerContext); 714 result = ValidateAuthentication(authType); 715 } 716 717 return result; 718 } 719 GetPolicyWithDefaultSpnCollection(ExtendedProtectionPolicy policy, AuthenticationSchemes authenticationScheme, HostNameComparisonMode hostNameComparisonMode, Uri listenUri, out bool usingDefaultSpnList)720 static ExtendedProtectionPolicy GetPolicyWithDefaultSpnCollection(ExtendedProtectionPolicy policy, AuthenticationSchemes authenticationScheme, HostNameComparisonMode hostNameComparisonMode, Uri listenUri, out bool usingDefaultSpnList) 721 { 722 if (policy.PolicyEnforcement != PolicyEnforcement.Never && 723 policy.CustomServiceNames == null && //null indicates "use default" 724 policy.CustomChannelBinding == null && //not needed if a channel binding is provided. 725 authenticationScheme != AuthenticationSchemes.Anonymous && //SPN list only needed with authentication (mixed mode uses own default list) 726 string.Equals(listenUri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase))//SPN list not used for HTTPS (CBT is used instead). 727 { 728 usingDefaultSpnList = true; 729 return new ExtendedProtectionPolicy(policy.PolicyEnforcement, policy.ProtectionScenario, GetDefaultSpnList(hostNameComparisonMode, listenUri)); 730 } 731 732 usingDefaultSpnList = false; 733 return policy; 734 } 735 GetDefaultSpnList(HostNameComparisonMode hostNameComparisonMode, Uri listenUri)736 static ServiceNameCollection GetDefaultSpnList(HostNameComparisonMode hostNameComparisonMode, Uri listenUri) 737 { 738 //In 3.5 SP1, we started sending the HOST/xyz format, so we have to accept it for compat reasons. 739 //with this change, we will be changing our client so that it lets System.Net pick the SPN by default 740 //which will usually mean they use the HTTP/xyz format, which is more likely to interop with 741 //other web service stacks that support windows auth... 742 const string hostSpnFormat = "HOST/{0}"; 743 const string httpSpnFormat = "HTTP/{0}"; 744 const string localhost = "localhost"; 745 746 Dictionary<string, string> serviceNames = new Dictionary<string, string>(); 747 748 string hostName = null; 749 string dnsSafeHostName = listenUri.DnsSafeHost; 750 751 switch (hostNameComparisonMode) 752 { 753 case HostNameComparisonMode.Exact: 754 UriHostNameType hostNameType = listenUri.HostNameType; 755 if (hostNameType == UriHostNameType.IPv4 || hostNameType == UriHostNameType.IPv6) 756 { 757 hostName = Dns.GetHostEntry(string.Empty).HostName; 758 AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, hostSpnFormat, hostName)); 759 AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, httpSpnFormat, hostName)); 760 } 761 else 762 { 763 if (listenUri.DnsSafeHost.Contains(".")) 764 { 765 //since we are listening explicitly on the FQDN, we should add only the FQDN SPN 766 AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, hostSpnFormat, dnsSafeHostName)); 767 AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, httpSpnFormat, dnsSafeHostName)); 768 } 769 else 770 { 771 hostName = Dns.GetHostEntry(string.Empty).HostName; 772 //add the short name (from the URI) and the FQDN (from Dns) 773 AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, hostSpnFormat, dnsSafeHostName)); 774 AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, httpSpnFormat, dnsSafeHostName)); 775 AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, hostSpnFormat, hostName)); 776 AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, httpSpnFormat, hostName)); 777 } 778 } 779 break; 780 case HostNameComparisonMode.StrongWildcard: 781 case HostNameComparisonMode.WeakWildcard: 782 hostName = Dns.GetHostEntry(string.Empty).HostName; 783 AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, hostSpnFormat, hostName)); 784 AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, httpSpnFormat, hostName)); 785 break; 786 default: 787 Fx.Assert("Unhandled HostNameComparisonMode: " + hostNameComparisonMode); 788 break; 789 } 790 791 AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, hostSpnFormat, localhost)); 792 AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, httpSpnFormat, localhost)); 793 794 return new ServiceNameCollection(serviceNames.Values); 795 } 796 AddSpn(Dictionary<string, string> list, string value)797 static void AddSpn(Dictionary<string, string> list, string value) 798 { 799 string key = value.ToLowerInvariant(); 800 801 if (!list.ContainsKey(key)) 802 { 803 list.Add(key, value); 804 } 805 } 806 CreateWebSocketChannelAndEnqueue(HttpRequestContext httpRequestContext, HttpPipeline httpPipeline, HttpResponseMessage httpResponseMessage, string subProtocol, Action dequeuedCallback)807 public abstract bool CreateWebSocketChannelAndEnqueue(HttpRequestContext httpRequestContext, HttpPipeline httpPipeline, HttpResponseMessage httpResponseMessage, string subProtocol, Action dequeuedCallback); 808 TakeWebSocketInternalBuffer()809 public abstract byte[] TakeWebSocketInternalBuffer(); ReturnWebSocketInternalBuffer(byte[] buffer)810 public abstract void ReturnWebSocketInternalBuffer(byte[] buffer); 811 812 internal interface IHttpAuthenticationContext 813 { 814 WindowsIdentity LogonUserIdentity { get; } GetClientCertificate(out bool isValidCertificate)815 X509Certificate2 GetClientCertificate(out bool isValidCertificate); 816 bool IISSupportsExtendedProtection { get; } CreateTraceRecord()817 TraceRecord CreateTraceRecord(); 818 } 819 } 820 821 class HttpChannelListener<TChannel> : HttpChannelListener, 822 IChannelListener<TChannel> where TChannel : class, IChannel 823 { 824 InputQueueChannelAcceptor<TChannel> acceptor; 825 bool useWebSocketTransport; 826 CommunicationObjectManager<ServerWebSocketTransportDuplexSessionChannel> webSocketLifetimeManager; 827 TransportIntegrationHandler transportIntegrationHandler; 828 ConnectionBufferPool bufferPool; 829 string currentWebSocketVersion; 830 HttpChannelListener(HttpTransportBindingElement bindingElement, BindingContext context)831 public HttpChannelListener(HttpTransportBindingElement bindingElement, BindingContext context) 832 : base(bindingElement, context) 833 { 834 this.useWebSocketTransport = bindingElement.WebSocketSettings.TransportUsage == WebSocketTransportUsage.Always 835 || (bindingElement.WebSocketSettings.TransportUsage == WebSocketTransportUsage.WhenDuplex && typeof(TChannel) != typeof(IReplyChannel)); 836 if (this.useWebSocketTransport) 837 { 838 if (AspNetEnvironment.Enabled) 839 { 840 AspNetEnvironment env = AspNetEnvironment.Current; 841 842 // When IIS hosted, WebSockets can be used if the pipeline mode is integrated 843 if (!env.UsingIntegratedPipeline) 844 { 845 throw FxTrace.Exception.AsError(new NotSupportedException(SR.GetString(SR.WebSocketsNotSupportedInClassicPipeline))); 846 } 847 } 848 else if (!WebSocketHelper.OSSupportsWebSockets()) 849 { 850 throw FxTrace.Exception.AsError(new PlatformNotSupportedException(SR.GetString(SR.WebSocketsServerSideNotSupported))); 851 } 852 853 this.currentWebSocketVersion = WebSocketHelper.GetCurrentVersion(); 854 this.acceptor = new InputQueueChannelAcceptor<TChannel>(this); 855 int webSocketBufferSize = WebSocketHelper.ComputeServerBufferSize(bindingElement.MaxReceivedMessageSize); 856 this.bufferPool = new ConnectionBufferPool(webSocketBufferSize); 857 this.webSocketLifetimeManager = new CommunicationObjectManager<ServerWebSocketTransportDuplexSessionChannel>(this.ThisLock); 858 } 859 else 860 { 861 this.acceptor = (InputQueueChannelAcceptor<TChannel>)(object)(new TransportReplyChannelAcceptor(this)); 862 } 863 864 this.CreatePipeline(bindingElement.MessageHandlerFactory); 865 } 866 867 public override bool UseWebSocketTransport 868 { 869 get 870 { 871 return this.useWebSocketTransport; 872 } 873 } 874 875 public InputQueueChannelAcceptor<TChannel> Acceptor 876 { 877 get { return this.acceptor; } 878 } 879 880 public override string Method 881 { 882 get 883 { 884 if (this.UseWebSocketTransport) 885 { 886 return WebSocketTransportSettings.WebSocketMethod; 887 } 888 return base.Method; 889 } 890 } 891 AcceptChannel()892 public TChannel AcceptChannel() 893 { 894 return this.AcceptChannel(this.DefaultReceiveTimeout); 895 } 896 BeginAcceptChannel(AsyncCallback callback, object state)897 public IAsyncResult BeginAcceptChannel(AsyncCallback callback, object state) 898 { 899 return this.BeginAcceptChannel(this.DefaultReceiveTimeout, callback, state); 900 } 901 AcceptChannel(TimeSpan timeout)902 public TChannel AcceptChannel(TimeSpan timeout) 903 { 904 base.ThrowIfNotOpened(); 905 return this.Acceptor.AcceptChannel(timeout); 906 } 907 BeginAcceptChannel(TimeSpan timeout, AsyncCallback callback, object state)908 public IAsyncResult BeginAcceptChannel(TimeSpan timeout, AsyncCallback callback, object state) 909 { 910 base.ThrowIfNotOpened(); 911 return this.Acceptor.BeginAcceptChannel(timeout, callback, state); 912 } 913 EndAcceptChannel(IAsyncResult result)914 public TChannel EndAcceptChannel(IAsyncResult result) 915 { 916 base.ThrowPending(); 917 return this.Acceptor.EndAcceptChannel(result); 918 } 919 CreateWebSocketChannelAndEnqueue(HttpRequestContext httpRequestContext, HttpPipeline pipeline, HttpResponseMessage httpResponseMessage, string subProtocol, Action dequeuedCallback)920 public override bool CreateWebSocketChannelAndEnqueue(HttpRequestContext httpRequestContext, HttpPipeline pipeline, HttpResponseMessage httpResponseMessage, string subProtocol, Action dequeuedCallback) 921 { 922 Fx.Assert(this.WebSocketSettings.MaxPendingConnections > 0, "MaxPendingConnections should be positive."); 923 if (this.Acceptor.PendingCount >= this.WebSocketSettings.MaxPendingConnections) 924 { 925 if (TD.MaxPendingConnectionsExceededIsEnabled()) 926 { 927 TD.MaxPendingConnectionsExceeded(SR.GetString(SR.WebSocketMaxPendingConnectionsReached, this.WebSocketSettings.MaxPendingConnections, WebSocketHelper.MaxPendingConnectionsString, WebSocketHelper.WebSocketTransportSettingsString)); 928 } 929 930 if (DiagnosticUtility.ShouldTraceWarning) 931 { 932 TraceUtility.TraceEvent(TraceEventType.Warning, 933 TraceCode.MaxPendingConnectionsReached, SR.GetString(SR.WebSocketMaxPendingConnectionsReached, this.WebSocketSettings.MaxPendingConnections, WebSocketHelper.MaxPendingConnectionsString, WebSocketHelper.WebSocketTransportSettingsString), 934 new StringTraceRecord(WebSocketHelper.MaxPendingConnectionsString, this.WebSocketSettings.MaxPendingConnections.ToString(System.Globalization.CultureInfo.InvariantCulture)), 935 this, 936 null); 937 } 938 939 return false; 940 } 941 942 ServerWebSocketTransportDuplexSessionChannel channel = new ServerWebSocketTransportDuplexSessionChannel(this, 943 new EndpointAddress(this.Uri), this.Uri, this.bufferPool, httpRequestContext, pipeline, httpResponseMessage, subProtocol); 944 httpRequestContext.WebSocketChannel = channel; 945 946 // webSocketLifetimeManager hooks into the channel.Closed event as well and will take care of cleaning itself up OnClosed. 947 // We want to be called before any user-specified close handlers are called. 948 this.webSocketLifetimeManager.Add(channel); 949 this.Acceptor.EnqueueAndDispatch((TChannel)(object)channel, dequeuedCallback, true); 950 return true; 951 } 952 TakeWebSocketInternalBuffer()953 public override byte[] TakeWebSocketInternalBuffer() 954 { 955 Fx.Assert(this.bufferPool != null, "bufferPool should not be null."); 956 return this.bufferPool.Take(); 957 } 958 ReturnWebSocketInternalBuffer(byte[] buffer)959 public override void ReturnWebSocketInternalBuffer(byte[] buffer) 960 { 961 Fx.Assert(this.bufferPool != null, "bufferPool should not be null."); 962 this.bufferPool.Return(buffer); 963 } 964 OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)965 protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state) 966 { 967 return new ChainedOpenAsyncResult(timeout, callback, state, base.OnBeginOpen, base.OnEndOpen, this.Acceptor); 968 } 969 OnOpen(TimeSpan timeout)970 protected override void OnOpen(TimeSpan timeout) 971 { 972 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); 973 base.OnOpen(timeoutHelper.RemainingTime()); 974 this.Acceptor.Open(timeoutHelper.RemainingTime()); 975 } 976 OnEndOpen(IAsyncResult result)977 protected override void OnEndOpen(IAsyncResult result) 978 { 979 ChainedOpenAsyncResult.End(result); 980 } 981 OnClose(TimeSpan timeout)982 protected override void OnClose(TimeSpan timeout) 983 { 984 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); 985 this.Acceptor.Close(timeoutHelper.RemainingTime()); 986 if (this.IsAuthenticationSupported) 987 { 988 CloseUserNameTokenAuthenticator(timeoutHelper.RemainingTime()); 989 } 990 if (this.useWebSocketTransport) 991 { 992 this.webSocketLifetimeManager.Close(timeoutHelper.RemainingTime()); 993 } 994 base.OnClose(timeoutHelper.RemainingTime()); 995 } 996 OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)997 protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state) 998 { 999 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); 1000 ICommunicationObject[] communicationObjects; 1001 ICommunicationObject communicationObject = this.UserNameTokenAuthenticator as ICommunicationObject; 1002 if (communicationObject == null) 1003 { 1004 if (this.IsAuthenticationSupported) 1005 { 1006 CloseUserNameTokenAuthenticator(timeoutHelper.RemainingTime()); 1007 } 1008 communicationObjects = new ICommunicationObject[] { this.Acceptor }; 1009 } 1010 else 1011 { 1012 communicationObjects = new ICommunicationObject[] { this.Acceptor, communicationObject }; 1013 } 1014 1015 if (this.useWebSocketTransport) 1016 { 1017 return new LifetimeWrappedCloseAsyncResult<ServerWebSocketTransportDuplexSessionChannel>( 1018 timeoutHelper.RemainingTime(), 1019 callback, 1020 state, 1021 this.webSocketLifetimeManager, 1022 base.OnBeginClose, 1023 base.OnEndClose, 1024 communicationObjects); 1025 } 1026 else 1027 { 1028 return new ChainedCloseAsyncResult(timeoutHelper.RemainingTime(), callback, state, base.OnBeginClose, base.OnEndClose, communicationObjects); 1029 } 1030 } 1031 OnEndClose(IAsyncResult result)1032 protected override void OnEndClose(IAsyncResult result) 1033 { 1034 if (this.useWebSocketTransport) 1035 { 1036 LifetimeWrappedCloseAsyncResult<ServerWebSocketTransportDuplexSessionChannel>.End(result); 1037 } 1038 else 1039 { 1040 ChainedCloseAsyncResult.End(result); 1041 } 1042 } 1043 OnClosed()1044 protected override void OnClosed() 1045 { 1046 base.OnClosed(); 1047 if (this.bufferPool != null) 1048 { 1049 this.bufferPool.Close(); 1050 } 1051 1052 if (this.transportIntegrationHandler != null) 1053 { 1054 this.transportIntegrationHandler.Dispose(); 1055 } 1056 } 1057 OnAbort()1058 protected override void OnAbort() 1059 { 1060 if (this.IsAuthenticationSupported) 1061 { 1062 AbortUserNameTokenAuthenticator(); 1063 } 1064 1065 this.Acceptor.Abort(); 1066 1067 if (this.useWebSocketTransport) 1068 { 1069 this.webSocketLifetimeManager.Abort(); 1070 } 1071 1072 base.OnAbort(); 1073 } 1074 OnWaitForChannel(TimeSpan timeout)1075 protected override bool OnWaitForChannel(TimeSpan timeout) 1076 { 1077 return Acceptor.WaitForChannel(timeout); 1078 } 1079 OnBeginWaitForChannel(TimeSpan timeout, AsyncCallback callback, object state)1080 protected override IAsyncResult OnBeginWaitForChannel(TimeSpan timeout, AsyncCallback callback, object state) 1081 { 1082 return Acceptor.BeginWaitForChannel(timeout, callback, state); 1083 } 1084 OnEndWaitForChannel(IAsyncResult result)1085 protected override bool OnEndWaitForChannel(IAsyncResult result) 1086 { 1087 return Acceptor.EndWaitForChannel(result); 1088 } 1089 BeginHttpContextReceived(HttpRequestContext context, Action acceptorCallback, AsyncCallback callback, object state)1090 internal override IAsyncResult BeginHttpContextReceived(HttpRequestContext context, 1091 Action acceptorCallback, 1092 AsyncCallback callback, 1093 object state) 1094 { 1095 return new HttpContextReceivedAsyncResult<TChannel>( 1096 context, 1097 acceptorCallback, 1098 this, 1099 callback, 1100 state); 1101 } 1102 EndHttpContextReceived(IAsyncResult result)1103 internal override bool EndHttpContextReceived(IAsyncResult result) 1104 { 1105 return HttpContextReceivedAsyncResult<TChannel>.End(result); 1106 } 1107 CreatePipeline(HttpMessageHandlerFactory httpMessageHandlerFactory)1108 void CreatePipeline(HttpMessageHandlerFactory httpMessageHandlerFactory) 1109 { 1110 HttpMessageHandler innerPipeline; 1111 if (this.UseWebSocketTransport) 1112 { 1113 innerPipeline = new DefaultWebSocketConnectionHandler(this.WebSocketSettings.SubProtocol, this.currentWebSocketVersion, this.MessageVersion, this.MessageEncoderFactory, this.TransferMode); 1114 if (httpMessageHandlerFactory != null) 1115 { 1116 innerPipeline = httpMessageHandlerFactory.Create(innerPipeline); 1117 } 1118 } 1119 else 1120 { 1121 if (httpMessageHandlerFactory == null) 1122 { 1123 return; 1124 } 1125 1126 innerPipeline = httpMessageHandlerFactory.Create(new ChannelModelIntegrationHandler()); 1127 } 1128 1129 if (innerPipeline == null) 1130 { 1131 throw FxTrace.Exception.AsError( 1132 new InvalidOperationException(SR.GetString(SR.HttpMessageHandlerChannelFactoryNullPipeline, 1133 httpMessageHandlerFactory.GetType().Name, typeof(HttpRequestContext).Name))); 1134 } 1135 1136 this.transportIntegrationHandler = new TransportIntegrationHandler(innerPipeline); 1137 } 1138 HandleProcessInboundException(Exception ex, HttpRequestContext context)1139 static void HandleProcessInboundException(Exception ex, HttpRequestContext context) 1140 { 1141 if (Fx.IsFatal(ex)) 1142 { 1143 return; 1144 } 1145 1146 if (ex is ProtocolException) 1147 { 1148 ProtocolException protocolException = (ProtocolException)ex; 1149 HttpStatusCode statusCode = HttpStatusCode.BadRequest; 1150 string statusDescription = string.Empty; 1151 if (protocolException.Data.Contains(HttpChannelUtilities.HttpStatusCodeExceptionKey)) 1152 { 1153 statusCode = (HttpStatusCode)protocolException.Data[HttpChannelUtilities.HttpStatusCodeExceptionKey]; 1154 protocolException.Data.Remove(HttpChannelUtilities.HttpStatusCodeExceptionKey); 1155 } 1156 if (protocolException.Data.Contains(HttpChannelUtilities.HttpStatusDescriptionExceptionKey)) 1157 { 1158 statusDescription = (string)protocolException.Data[HttpChannelUtilities.HttpStatusDescriptionExceptionKey]; 1159 protocolException.Data.Remove(HttpChannelUtilities.HttpStatusDescriptionExceptionKey); 1160 } 1161 context.SendResponseAndClose(statusCode, statusDescription); 1162 1163 } 1164 else 1165 { 1166 try 1167 { 1168 context.SendResponseAndClose(HttpStatusCode.BadRequest); 1169 } 1170 catch (Exception closeException) 1171 { 1172 if (Fx.IsFatal(closeException)) 1173 { 1174 throw; 1175 } 1176 1177 DiagnosticUtility.TraceHandledException(closeException, TraceEventType.Error); 1178 } 1179 } 1180 } 1181 ContextReceiveExceptionHandled(Exception e)1182 static bool ContextReceiveExceptionHandled(Exception e) 1183 { 1184 if (Fx.IsFatal(e)) 1185 { 1186 return false; 1187 } 1188 1189 if (e is CommunicationException) 1190 { 1191 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); 1192 } 1193 else if (e is XmlException) 1194 { 1195 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); 1196 } 1197 else if (e is IOException) 1198 { 1199 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); 1200 } 1201 else if (e is TimeoutException) 1202 { 1203 if (TD.ReceiveTimeoutIsEnabled()) 1204 { 1205 TD.ReceiveTimeout(e.Message); 1206 } 1207 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); 1208 } 1209 else if (e is OperationCanceledException) 1210 { 1211 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); 1212 } 1213 else if (!ExceptionHandler.HandleTransportExceptionHelper(e)) 1214 { 1215 return false; 1216 } 1217 1218 return true; 1219 } 1220 1221 class HttpContextReceivedAsyncResult<TListenerChannel> : TraceAsyncResult where TListenerChannel : class, IChannel 1222 { 1223 static AsyncCallback onProcessInboundRequest = Fx.ThunkCallback(OnProcessInboundRequest); 1224 bool enqueued; 1225 HttpRequestContext context; 1226 Action acceptorCallback; 1227 HttpChannelListener<TListenerChannel> listener; 1228 HttpContextReceivedAsyncResult( HttpRequestContext requestContext, Action acceptorCallback, HttpChannelListener<TListenerChannel> listener, AsyncCallback callback, object state)1229 public HttpContextReceivedAsyncResult( 1230 HttpRequestContext requestContext, 1231 Action acceptorCallback, 1232 HttpChannelListener<TListenerChannel> listener, 1233 AsyncCallback callback, 1234 object state) 1235 : base(callback, state) 1236 { 1237 this.context = requestContext; 1238 this.acceptorCallback = acceptorCallback; 1239 this.listener = listener; 1240 1241 if (this.ProcessHttpContextAsync() == AsyncCompletionResult.Completed) 1242 { 1243 base.Complete(true); 1244 } 1245 } 1246 End(IAsyncResult result)1247 public static bool End(IAsyncResult result) 1248 { 1249 return AsyncResult.End<HttpContextReceivedAsyncResult<TListenerChannel>>(result).enqueued; 1250 } 1251 OnProcessInboundRequest(IAsyncResult result)1252 static void OnProcessInboundRequest(IAsyncResult result) 1253 { 1254 if (result.CompletedSynchronously) 1255 { 1256 return; 1257 } 1258 1259 HttpContextReceivedAsyncResult<TListenerChannel> thisPtr = (HttpContextReceivedAsyncResult<TListenerChannel>)result.AsyncState; 1260 Exception completionException = null; 1261 1262 try 1263 { 1264 thisPtr.HandleProcessInboundRequest(result); 1265 } 1266 catch (Exception ex) 1267 { 1268 if (Fx.IsFatal(ex)) 1269 { 1270 throw; 1271 } 1272 1273 completionException = ex; 1274 } 1275 1276 thisPtr.Complete(false, completionException); 1277 } 1278 ProcessHttpContextAsync()1279 AsyncCompletionResult ProcessHttpContextAsync() 1280 { 1281 bool abort = false; 1282 try 1283 { 1284 this.context.InitializeHttpPipeline(this.listener.transportIntegrationHandler); 1285 if (!this.Authenticate()) 1286 { 1287 return AsyncCompletionResult.Completed; 1288 } 1289 1290 if (listener.UseWebSocketTransport && !context.IsWebSocketRequest) 1291 { 1292 this.context.SendResponseAndClose(HttpStatusCode.BadRequest, SR.GetString(SR.WebSocketEndpointOnlySupportWebSocketError)); 1293 return AsyncCompletionResult.Completed; 1294 } 1295 1296 if (!listener.UseWebSocketTransport && context.IsWebSocketRequest) 1297 { 1298 this.context.SendResponseAndClose(HttpStatusCode.BadRequest, SR.GetString(SR.WebSocketEndpointDoesNotSupportWebSocketError)); 1299 return AsyncCompletionResult.Completed; 1300 } 1301 1302 try 1303 { 1304 IAsyncResult result = context.BeginProcessInboundRequest(listener.Acceptor as ReplyChannelAcceptor, 1305 this.acceptorCallback, 1306 onProcessInboundRequest, 1307 this); 1308 if (result.CompletedSynchronously) 1309 { 1310 this.EndInboundProcessAndEnqueue(result); 1311 return AsyncCompletionResult.Completed; 1312 } 1313 } 1314 catch (Exception ex) 1315 { 1316 HandleProcessInboundException(ex, this.context); 1317 throw; 1318 } 1319 } 1320 catch (Exception ex) 1321 { 1322 // containment -- we abort the context in all error cases, no additional containment action needed 1323 abort = true; 1324 if (!ContextReceiveExceptionHandled(ex)) 1325 { 1326 throw; 1327 } 1328 } 1329 finally 1330 { 1331 if (abort) 1332 { 1333 context.Abort(); 1334 } 1335 } 1336 1337 return abort ? AsyncCompletionResult.Completed : AsyncCompletionResult.Queued; 1338 } 1339 Authenticate()1340 bool Authenticate() 1341 { 1342 if (!this.context.ProcessAuthentication()) 1343 { 1344 if (TD.HttpAuthFailedIsEnabled()) 1345 { 1346 TD.HttpAuthFailed(context.EventTraceActivity); 1347 } 1348 1349 if (DiagnosticUtility.ShouldTraceInformation) 1350 { 1351 TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.HttpAuthFailed, SR.GetString(SR.TraceCodeHttpAuthFailed), this); 1352 } 1353 1354 return false; 1355 } 1356 1357 return true; 1358 } 1359 HandleProcessInboundRequest(IAsyncResult result)1360 void HandleProcessInboundRequest(IAsyncResult result) 1361 { 1362 bool abort = true; 1363 try 1364 { 1365 try 1366 { 1367 this.EndInboundProcessAndEnqueue(result); 1368 abort = false; 1369 } 1370 catch (Exception ex) 1371 { 1372 HandleProcessInboundException(ex, this.context); 1373 throw; 1374 } 1375 } 1376 catch (Exception ex) 1377 { 1378 // containment -- we abort the context in all error cases, no additional containment action needed 1379 if (!ContextReceiveExceptionHandled(ex)) 1380 { 1381 throw; 1382 } 1383 } 1384 finally 1385 { 1386 if (abort) 1387 { 1388 context.Abort(); 1389 } 1390 } 1391 } 1392 EndInboundProcessAndEnqueue(IAsyncResult result)1393 void EndInboundProcessAndEnqueue(IAsyncResult result) 1394 { 1395 Fx.Assert(result != null, "Trying to complete without issuing a BeginProcessInboundRequest."); 1396 context.EndProcessInboundRequest(result); 1397 1398 //We have finally managed to enqueue the message. 1399 this.enqueued = true; 1400 } 1401 } 1402 1403 class LifetimeWrappedCloseAsyncResult<TCommunicationObject> : AsyncResult where TCommunicationObject : CommunicationObject 1404 { 1405 static AsyncCompletion handleLifetimeManagerClose = new AsyncCompletion(HandleLifetimeManagerClose); 1406 static AsyncCompletion handleChannelClose = new AsyncCompletion(HandleChannelClose); 1407 1408 TimeoutHelper timeoutHelper; 1409 1410 ICommunicationObject[] communicationObjects; 1411 CommunicationObjectManager<TCommunicationObject> communicationObjectManager; 1412 ChainedBeginHandler begin1; 1413 ChainedEndHandler end1; 1414 LifetimeWrappedCloseAsyncResult(TimeSpan timeout, AsyncCallback callback, object state, CommunicationObjectManager<TCommunicationObject> communicationObjectManager, ChainedBeginHandler begin1, ChainedEndHandler end1, ICommunicationObject[] communicationObjects)1415 public LifetimeWrappedCloseAsyncResult(TimeSpan timeout, AsyncCallback callback, object state, CommunicationObjectManager<TCommunicationObject> communicationObjectManager, ChainedBeginHandler begin1, ChainedEndHandler end1, ICommunicationObject[] communicationObjects) 1416 : base(callback, state) 1417 { 1418 this.timeoutHelper = new TimeoutHelper(timeout); 1419 this.begin1 = begin1; 1420 this.end1 = end1; 1421 this.communicationObjects = communicationObjects; 1422 this.communicationObjectManager = communicationObjectManager; 1423 1424 IAsyncResult result = communicationObjectManager.BeginClose( 1425 this.timeoutHelper.RemainingTime(), 1426 PrepareAsyncCompletion(handleLifetimeManagerClose), 1427 this); 1428 1429 bool completeSelf = SyncContinue(result); 1430 1431 if (completeSelf) 1432 { 1433 this.Complete(true); 1434 } 1435 } 1436 End(IAsyncResult result)1437 public static void End(IAsyncResult result) 1438 { 1439 AsyncResult.End<LifetimeWrappedCloseAsyncResult<TCommunicationObject>>(result); 1440 } 1441 HandleLifetimeManagerClose(IAsyncResult result)1442 static bool HandleLifetimeManagerClose(IAsyncResult result) 1443 { 1444 LifetimeWrappedCloseAsyncResult<TCommunicationObject> thisPtr = (LifetimeWrappedCloseAsyncResult<TCommunicationObject>)result.AsyncState; 1445 thisPtr.communicationObjectManager.EndClose(result); 1446 1447 // begin second step of the close... 1448 ChainedCloseAsyncResult closeResult = new ChainedCloseAsyncResult( 1449 thisPtr.timeoutHelper.RemainingTime(), 1450 thisPtr.PrepareAsyncCompletion(handleChannelClose), 1451 thisPtr, 1452 thisPtr.begin1, 1453 thisPtr.end1, 1454 thisPtr.communicationObjects); 1455 1456 return thisPtr.SyncContinue(closeResult); 1457 } 1458 HandleChannelClose(IAsyncResult result)1459 static bool HandleChannelClose(IAsyncResult result) 1460 { 1461 ChainedCloseAsyncResult.End(result); 1462 return true; 1463 } 1464 } 1465 } 1466 1467 /// <summary> 1468 /// Handler wrapping the bottom (towards network) of the <see cref="HttpMessageHandler"/> and integrates 1469 /// back into the <see cref="IReplyChannel"/>. 1470 /// </summary> 1471 class TransportIntegrationHandler : DelegatingHandler 1472 { 1473 /// <summary> 1474 /// Initializes a new instance of the <see cref="TransportIntegrationHandler"/> class. 1475 /// </summary> 1476 /// <param name="innerChannel">The inner <see cref="HttpMessageHandler"/> on which we send the <see cref="HttpRequestMessage"/>.</param> TransportIntegrationHandler(HttpMessageHandler innerChannel)1477 public TransportIntegrationHandler(HttpMessageHandler innerChannel) 1478 : base(innerChannel) 1479 { 1480 } 1481 1482 /// <summary> 1483 /// Submits an <see cref="HttpRequestMessage"/> on the inner channel asynchronously. 1484 /// </summary> 1485 /// <param name="request"><see cref="HttpRequestMessage"/> to submit</param> 1486 /// <param name="cancellationToken">Token used to cancel operation.</param> 1487 /// <returns>A <see cref="Task<T>"/> representing the operation.</returns> ProcessPipelineAsync(HttpRequestMessage request, CancellationToken cancellationToken)1488 public Task<HttpResponseMessage> ProcessPipelineAsync(HttpRequestMessage request, CancellationToken cancellationToken) 1489 { 1490 return base.SendAsync(request, cancellationToken).ContinueWith(task => 1491 { 1492 HttpResponseMessage httpResponse; 1493 if (task.IsFaulted) 1494 { 1495 if (Fx.IsFatal(task.Exception)) 1496 { 1497 throw task.Exception; 1498 } 1499 1500 // We must inspect task.Exception -- otherwise it is automatically rethrown. 1501 FxTrace.Exception.AsError<FaultException>(task.Exception); 1502 httpResponse = TraceFaultAndGetResponseMessasge(request); 1503 } 1504 else if (task.IsCanceled) 1505 { 1506 HttpPipeline pipeline = HttpPipeline.GetHttpPipeline(request); 1507 if (TD.HttpPipelineTimeoutExceptionIsEnabled()) 1508 { 1509 TD.HttpPipelineTimeoutException(pipeline != null ? pipeline.EventTraceActivity : null); 1510 } 1511 1512 FxTrace.Exception.AsError(new TimeoutException(SR.GetString(SR.HttpPipelineOperationCanceledError))); 1513 pipeline.Cancel(); 1514 httpResponse = null; 1515 } 1516 else 1517 { 1518 httpResponse = task.Result; 1519 if (httpResponse == null) 1520 { 1521 FxTrace.Exception.AsError(new NotSupportedException(SR.GetString(SR.HttpPipelineNotSupportNullResponseMessage, typeof(DelegatingHandler).Name, typeof(HttpResponseMessage).Name))); 1522 httpResponse = TraceFaultAndGetResponseMessasge(request); 1523 } 1524 } 1525 1526 return httpResponse; 1527 }, 1528 TaskContinuationOptions.ExecuteSynchronously); 1529 } 1530 TraceFaultAndGetResponseMessasge(HttpRequestMessage request)1531 static HttpResponseMessage TraceFaultAndGetResponseMessasge(HttpRequestMessage request) 1532 { 1533 HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.InternalServerError); 1534 response.RequestMessage = request; 1535 1536 if (TD.HttpPipelineFaultedIsEnabled()) 1537 { 1538 HttpPipeline pipeline = HttpPipeline.GetHttpPipeline(request); 1539 TD.HttpPipelineFaulted(pipeline != null ? pipeline.EventTraceActivity : null); 1540 } 1541 1542 return response; 1543 } 1544 } 1545 1546 /// <summary> 1547 /// Handler wrapping the top (towards Channel Model) of the <see cref="HttpMessageHandler"/> and integrates 1548 /// bask into the <see cref="IReplyChannel"/>. 1549 /// </summary> 1550 class ChannelModelIntegrationHandler : HttpMessageHandler 1551 { 1552 /// <summary> 1553 /// Submits an <see cref="HttpRequestMessage"/> on the inner channel asynchronously. 1554 /// </summary> 1555 /// <param name="request"><see cref="HttpRequestMessage"/> to submit</param> 1556 /// <param name="cancellationToken">Token used to cancel operation.</param> 1557 /// <returns>A <see cref="Task<T>"/> representing the operation.</returns> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)1558 protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 1559 { 1560 if (request == null) 1561 { 1562 throw FxTrace.Exception.ArgumentNull("request"); 1563 } 1564 1565 if (cancellationToken == null) 1566 { 1567 throw FxTrace.Exception.ArgumentNull("cancellationToken"); 1568 } 1569 1570 cancellationToken.ThrowIfCancellationRequested(); 1571 1572 HttpChannelUtilities.EnsureHttpRequestMessageContentNotNull(request); 1573 //// We ran up through the pipeline and are now ready to hook back into the WCF channel model 1574 HttpPipeline httpPipeline = HttpPipeline.GetHttpPipeline(request); 1575 1576 return httpPipeline.Dispatch(request); 1577 } 1578 } 1579 } 1580