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.IdentityModel.Claims; 10 using System.IdentityModel.Policy; 11 using System.IdentityModel.Selectors; 12 using System.IdentityModel.Tokens; 13 using System.Runtime; 14 using System.Runtime.CompilerServices; 15 using System.Runtime.Serialization; 16 using System.Security.Cryptography; 17 using System.Security.Cryptography.X509Certificates; 18 using System.ServiceModel.Diagnostics; 19 using System.ServiceModel.Security; 20 using System.ServiceModel.Security.Tokens; 21 using System.Text; 22 using System.Xml; 23 24 class PeerSecurityHelpers 25 { ComputeHash(X509Certificate2 cert, string pwd)26 public static byte[] ComputeHash(X509Certificate2 cert, string pwd) 27 { 28 RSACryptoServiceProvider keyProv = cert.PublicKey.Key as RSACryptoServiceProvider; 29 Fx.Assert(keyProv != null, "Remote Peer's credentials are invalid!"); 30 byte[] key = keyProv.ExportCspBlob(false); 31 return ComputeHash(key, pwd); 32 } 33 ComputeHash(Claim claim, string pwd)34 public static byte[] ComputeHash(Claim claim, string pwd) 35 { 36 RSACryptoServiceProvider provider = claim.Resource as RSACryptoServiceProvider; 37 if (provider == null) 38 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("claim"); 39 using (provider) 40 { 41 byte[] keyBlob = provider.ExportCspBlob(false); 42 if (keyBlob == null) 43 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("key"); 44 return ComputeHash(keyBlob, pwd); 45 } 46 } 47 ComputeHash(byte[] message, string pwd)48 public static byte[] ComputeHash(byte[] message, string pwd) 49 { 50 byte[] returnValue = null; 51 RuntimeHelpers.PrepareConstrainedRegions(); 52 byte[] pwdBytes = null; 53 byte[] pwdHash = null; 54 byte[] tempBuffer = null; 55 try 56 { 57 pwdBytes = UnicodeEncoding.Unicode.GetBytes(pwd.Trim()); 58 using (HMACSHA256 algo = new HMACSHA256(pwdBytes)) 59 { 60 using (SHA256Managed sha = new SHA256Managed()) 61 { 62 pwdHash = sha.ComputeHash(pwdBytes); 63 tempBuffer = DiagnosticUtility.Utility.AllocateByteArray(checked(message.Length + pwdHash.Length)); 64 Array.Copy(pwdHash, tempBuffer, pwdHash.Length); 65 Array.Copy(message, 0, tempBuffer, pwdHash.Length, message.Length); 66 67 returnValue = algo.ComputeHash(tempBuffer); 68 } 69 } 70 } 71 finally 72 { 73 ArrayClear(pwdBytes); 74 ArrayClear(pwdHash); 75 ArrayClear(tempBuffer); 76 } 77 return returnValue; 78 } 79 ArrayClear(byte[] buffer)80 static void ArrayClear(byte[] buffer) 81 { 82 if (buffer != null) 83 Array.Clear(buffer, 0, buffer.Length); 84 } 85 Authenticate(Claim claim, string password, byte[] authenticator)86 public static bool Authenticate(Claim claim, string password, byte[] authenticator) 87 { 88 bool returnValue = false; 89 if (authenticator == null) 90 return false; 91 byte[] hash = null; 92 RuntimeHelpers.PrepareConstrainedRegions(); 93 try 94 { 95 hash = ComputeHash(claim, password); 96 if (hash.Length == authenticator.Length) 97 { 98 for (int i = 0; i < hash.Length; i++) 99 { 100 if (hash[i] != authenticator[i]) 101 { 102 returnValue = false; 103 break; 104 } 105 } 106 returnValue = true; 107 } 108 } 109 finally 110 { 111 ArrayClear(hash); 112 } 113 114 return returnValue; 115 } 116 AuthenticateRequest(Claim claim, string password, Message message)117 public static bool AuthenticateRequest(Claim claim, string password, Message message) 118 { 119 PeerHashToken request = PeerRequestSecurityToken.CreateHashTokenFrom(message); 120 return request.Validate(claim, password); 121 } 122 AuthenticateResponse(Claim claim, string password, Message message)123 public static bool AuthenticateResponse(Claim claim, string password, Message message) 124 { 125 PeerHashToken request = PeerRequestSecurityTokenResponse.CreateHashTokenFrom(message); 126 return request.Validate(claim, password); 127 } 128 129 } 130 131 132 internal class PeerIdentityClaim 133 { 134 const string resourceValue = "peer"; 135 const string resourceRight = "peer"; 136 public const string PeerClaimType = PeerStrings.Namespace + "/peer"; Claim()137 static internal Claim Claim() 138 { 139 return new Claim(PeerClaimType, resourceValue, resourceRight); 140 } IsMatch(EndpointIdentity identity)141 static internal bool IsMatch(EndpointIdentity identity) 142 { 143 return identity.IdentityClaim.ClaimType == PeerClaimType; 144 } 145 } 146 147 class PeerDoNothingSecurityProtocol : SecurityProtocol 148 { PeerDoNothingSecurityProtocol(SecurityProtocolFactory factory)149 public PeerDoNothingSecurityProtocol(SecurityProtocolFactory factory) : base(factory, null, null) { } SecureOutgoingMessage(ref Message message, TimeSpan timeout)150 public override void SecureOutgoingMessage(ref Message message, TimeSpan timeout) 151 { 152 } VerifyIncomingMessage(ref Message request, TimeSpan timeout)153 public override void VerifyIncomingMessage(ref Message request, TimeSpan timeout) 154 { 155 try 156 { 157 int i = request.Headers.FindHeader(SecurityJan2004Strings.Security, SecurityJan2004Strings.Namespace); 158 if (i >= 0) 159 { 160 request.Headers.AddUnderstood(i); 161 } 162 } 163 catch (MessageHeaderException e) 164 { 165 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); 166 } 167 catch (XmlException e) 168 { 169 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); 170 } 171 catch (SerializationException e) 172 { 173 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); 174 } 175 176 } 177 OnAbort()178 public override void OnAbort() 179 { 180 } 181 OnClose(TimeSpan timeout)182 public override void OnClose(TimeSpan timeout) 183 { 184 } 185 OnOpen(TimeSpan timeout)186 public override void OnOpen(TimeSpan timeout) 187 { 188 } 189 } 190 191 class PeerDoNothingSecurityProtocolFactory : SecurityProtocolFactory 192 { OnCreateSecurityProtocol(EndpointAddress target, Uri via, object listenerSecurityState, TimeSpan timeout)193 protected override SecurityProtocol OnCreateSecurityProtocol(EndpointAddress target, Uri via, object listenerSecurityState, TimeSpan timeout) 194 { 195 return new PeerDoNothingSecurityProtocol(this); 196 } 197 OnAbort()198 public override void OnAbort() 199 { 200 } 201 202 OnOpen(TimeSpan timeout)203 public override void OnOpen(TimeSpan timeout) 204 { 205 } 206 OnClose(TimeSpan timeout)207 public override void OnClose(TimeSpan timeout) 208 { 209 } 210 } 211 212 class PeerIdentityVerifier : IdentityVerifier 213 { PeerIdentityVerifier()214 public PeerIdentityVerifier() : base() { } CheckAccess(EndpointIdentity identity, AuthorizationContext authContext)215 public override bool CheckAccess(EndpointIdentity identity, AuthorizationContext authContext) 216 { 217 return true; 218 } TryGetIdentity(EndpointAddress reference, out EndpointIdentity identity)219 public override bool TryGetIdentity(EndpointAddress reference, out EndpointIdentity identity) 220 { 221 if (reference == null) 222 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reference"); 223 224 identity = reference.Identity; 225 if (identity == null) 226 { 227 identity = new PeerEndpointIdentity(); 228 } 229 return true; 230 } 231 } 232 233 class PeerEndpointIdentity : EndpointIdentity 234 { PeerEndpointIdentity()235 public PeerEndpointIdentity() 236 : base() 237 { 238 base.Initialize(PeerIdentityClaim.Claim()); 239 } 240 } 241 242 class PeerX509TokenProvider : X509SecurityTokenProvider 243 { 244 X509CertificateValidator validator; PeerX509TokenProvider(X509CertificateValidator validator, X509Certificate2 credential)245 public PeerX509TokenProvider(X509CertificateValidator validator, X509Certificate2 credential) 246 : base(credential) 247 { 248 this.validator = validator; 249 } 250 GetTokenCore(TimeSpan timeout)251 protected override SecurityToken GetTokenCore(TimeSpan timeout) 252 { 253 X509SecurityToken token = (X509SecurityToken)base.GetTokenCore(timeout); 254 if (validator != null) 255 { 256 validator.Validate(token.Certificate); 257 } 258 return token; 259 } 260 } 261 262 class PeerCertificateClientCredentials : SecurityCredentialsManager 263 { 264 X509Certificate2 selfCertificate; 265 X509CertificateValidator certificateValidator; 266 PeerCertificateClientCredentials(X509Certificate2 selfCertificate, X509CertificateValidator validator)267 public PeerCertificateClientCredentials(X509Certificate2 selfCertificate, X509CertificateValidator validator) 268 { 269 this.selfCertificate = selfCertificate; 270 this.certificateValidator = validator; 271 } 272 CreateSecurityTokenManager()273 public override SecurityTokenManager CreateSecurityTokenManager() 274 { 275 return new PeerCertificateClientCredentialsSecurityTokenManager(this); 276 } 277 278 class PeerCertificateClientCredentialsSecurityTokenManager : SecurityTokenManager 279 { 280 PeerCertificateClientCredentials creds; 281 PeerCertificateClientCredentialsSecurityTokenManager(PeerCertificateClientCredentials creds)282 public PeerCertificateClientCredentialsSecurityTokenManager(PeerCertificateClientCredentials creds) 283 { 284 this.creds = creds; 285 } 286 CreateSecurityTokenSerializer(SecurityTokenVersion version)287 public override SecurityTokenSerializer CreateSecurityTokenSerializer(SecurityTokenVersion version) 288 { 289 MessageSecurityTokenVersion messageVersion = (MessageSecurityTokenVersion)version; 290 return new WSSecurityTokenSerializer(messageVersion.SecurityVersion, messageVersion.TrustVersion, messageVersion.SecureConversationVersion, messageVersion.EmitBspRequiredAttributes, null, null, null); 291 } 292 CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver)293 public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver) 294 { 295 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException()); 296 } 297 CreateSecurityTokenProvider(SecurityTokenRequirement requirement)298 public override SecurityTokenProvider CreateSecurityTokenProvider(SecurityTokenRequirement requirement) 299 { 300 if (requirement == null) 301 { 302 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("requirement"); 303 } 304 if (requirement.TokenType == SecurityTokenTypes.X509Certificate && requirement.KeyUsage == SecurityKeyUsage.Signature) 305 { 306 return new PeerX509TokenProvider(this.creds.certificateValidator, this.creds.selfCertificate); 307 } 308 else 309 { 310 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException()); 311 } 312 } 313 } 314 } 315 316 internal class PeerHashToken : SecurityToken 317 { 318 string id = SecurityUniqueId.Create().Value; 319 Uri status; 320 bool isValid; 321 ReadOnlyCollection<SecurityKey> keys; 322 internal const string TokenTypeString = PeerStrings.Namespace + "/peerhashtoken"; 323 internal const string RequestTypeString = "http://schemas.xmlsoap.org/ws/2005/02/trust/Validate"; 324 internal const string Action = "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Validate"; 325 public const string PeerNamespace = PeerStrings.Namespace; 326 public const string PeerTokenElementName = "PeerHashToken"; 327 public const string PeerAuthenticatorElementName = "Authenticator"; 328 public const string PeerPrefix = "peer"; 329 static PeerHashToken invalid = new PeerHashToken(); 330 331 byte[] authenticator; 332 DateTime effectiveTime = DateTime.UtcNow; 333 DateTime expirationTime = DateTime.UtcNow.AddHours(10); 334 PeerHashToken()335 PeerHashToken() 336 { 337 CheckValidity(); 338 } 339 PeerHashToken(byte[] authenticator)340 public PeerHashToken(byte[] authenticator) 341 { 342 this.authenticator = authenticator; 343 CheckValidity(); 344 } 345 PeerHashToken(X509Certificate2 certificate, string password)346 public PeerHashToken(X509Certificate2 certificate, string password) 347 { 348 this.authenticator = PeerSecurityHelpers.ComputeHash(certificate, password); 349 CheckValidity(); 350 } 351 PeerHashToken(Claim claim, string password)352 public PeerHashToken(Claim claim, string password) 353 { 354 this.authenticator = PeerSecurityHelpers.ComputeHash(claim, password); 355 CheckValidity(); 356 } 357 358 public override string Id 359 { 360 get { return this.id; } 361 } 362 363 public override DateTime ValidFrom 364 { 365 get { return this.effectiveTime; } 366 } 367 368 public override DateTime ValidTo 369 { 370 get { return this.expirationTime; } 371 } 372 373 public static PeerHashToken Invalid 374 { 375 get 376 { 377 return invalid; 378 } 379 } 380 381 public override ReadOnlyCollection<SecurityKey> SecurityKeys 382 { 383 get 384 { 385 if (null == this.keys) 386 { 387 this.keys = new ReadOnlyCollection<SecurityKey>(new List<SecurityKey>()); 388 } 389 return this.keys; 390 } 391 } 392 393 public Uri Status 394 { 395 get 396 { 397 return this.status; 398 } 399 } 400 401 public bool IsValid 402 { 403 get 404 { 405 return this.isValid; 406 } 407 } 408 Validate(Claim claim, string password)409 public bool Validate(Claim claim, string password) 410 { 411 if (!(this.authenticator != null)) 412 { 413 throw Fx.AssertAndThrow("Incorrect initialization"); 414 } 415 bool result = PeerSecurityHelpers.Authenticate(claim, password, this.authenticator); 416 return result; 417 } 418 CheckValidity()419 void CheckValidity() 420 { 421 isValid = this.authenticator != null; 422 status = new Uri(isValid ? PeerRequestSecurityTokenResponse.ValidString : PeerRequestSecurityTokenResponse.InvalidString); 423 } 424 Write(XmlWriter writer)425 public void Write(XmlWriter writer) 426 { 427 writer.WriteStartElement(PeerPrefix, PeerTokenElementName, PeerNamespace); 428 writer.WriteStartElement(PeerPrefix, PeerAuthenticatorElementName, PeerNamespace); 429 writer.WriteString(Convert.ToBase64String(this.authenticator)); 430 writer.WriteEndElement(); 431 writer.WriteEndElement(); 432 } 433 CreateFrom(XmlElement child)434 internal static PeerHashToken CreateFrom(XmlElement child) 435 { 436 byte[] auth = null; 437 foreach (XmlNode node in child.ChildNodes) 438 { 439 XmlElement element = (XmlElement)node; 440 441 if (element == null || !PeerRequestSecurityToken.CompareWithNS(element.LocalName, element.NamespaceURI, PeerTokenElementName, PeerNamespace)) 442 continue; 443 if (element.ChildNodes.Count != 1) 444 break; 445 XmlElement authElement = element.ChildNodes[0] as XmlElement; 446 if (authElement == null || !PeerRequestSecurityToken.CompareWithNS(authElement.LocalName, authElement.NamespaceURI, PeerAuthenticatorElementName, PeerNamespace)) 447 break; 448 try 449 { 450 auth = Convert.FromBase64String(XmlHelper.ReadTextElementAsTrimmedString(authElement)); 451 break; 452 } 453 catch (ArgumentNullException e) 454 { 455 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); 456 } 457 catch (FormatException e) 458 { 459 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); 460 } 461 } 462 return new PeerHashToken(auth); 463 } 464 Equals(object token)465 public override bool Equals(object token) 466 { 467 PeerHashToken that = token as PeerHashToken; 468 if (that == null) 469 return false; 470 if (Object.ReferenceEquals(that, this)) 471 return true; 472 if (this.authenticator != null && that.authenticator != null && this.authenticator.Length == that.authenticator.Length) 473 { 474 for (int i = 0; i < this.authenticator.Length; i++) 475 { 476 if (this.authenticator[i] != that.authenticator[i]) 477 return false; 478 } 479 return true; 480 } 481 return false; 482 } 483 GetHashCode()484 public override int GetHashCode() 485 { 486 return isValid ? this.authenticator.GetHashCode() : 0; 487 } 488 } 489 490 class PeerSecurityTokenSerializer : WSSecurityTokenSerializer 491 { CreateKeyIdentifierClauseFromTokenXml(XmlElement element, SecurityTokenReferenceStyle tokenReferenceStyle)492 public override SecurityKeyIdentifierClause CreateKeyIdentifierClauseFromTokenXml(XmlElement element, SecurityTokenReferenceStyle tokenReferenceStyle) 493 { 494 return null; 495 } 496 } 497 498 internal class PeerRequestSecurityToken : RequestSecurityToken 499 { 500 PeerHashToken token; 501 public const string TrustNamespace = TrustFeb2005Strings.Namespace; 502 public const string PeerNamespace = PeerStrings.Namespace; 503 public const string RequestElementName = "RequestSecurityToken"; 504 public const string RequestedSecurityTokenElementName = "RequestedSecurityToken"; 505 public const string PeerHashTokenElementName = "PeerHashToken"; 506 PeerRequestSecurityToken(PeerHashToken token)507 public PeerRequestSecurityToken(PeerHashToken token) 508 : base() 509 { 510 this.token = token; 511 this.TokenType = PeerHashToken.TokenTypeString; 512 this.RequestType = PeerHashToken.RequestTypeString; 513 } 514 515 public PeerHashToken Token 516 { 517 get 518 { 519 return this.token; 520 } 521 } 522 CreateHashTokenFrom(Message message)523 public static PeerHashToken CreateHashTokenFrom(Message message) 524 { 525 PeerHashToken token = PeerHashToken.Invalid; 526 XmlReader reader = message.GetReaderAtBodyContents(); 527 RequestSecurityToken rst = RequestSecurityToken.CreateFrom(reader); 528 XmlElement rstXml = rst.RequestSecurityTokenXml; 529 if (rstXml != null) 530 { 531 532 //find the wrapper element 533 foreach (XmlNode node in rst.RequestSecurityTokenXml.ChildNodes) 534 { 535 XmlElement element = (XmlElement)node; 536 if (element == null || !PeerRequestSecurityToken.CompareWithNS(element.LocalName, element.NamespaceURI, PeerRequestSecurityToken.RequestedSecurityTokenElementName, TrustFeb2005Strings.Namespace)) 537 continue; 538 token = PeerHashToken.CreateFrom(element); 539 } 540 } 541 return token; 542 } 543 CreateFrom(X509Certificate2 credential, string password)544 public PeerRequestSecurityToken CreateFrom(X509Certificate2 credential, string password) 545 { 546 PeerHashToken token = new PeerHashToken(credential, password); 547 return new PeerRequestSecurityToken(token); 548 } 549 550 OnWriteCustomElements(XmlWriter writer)551 internal protected override void OnWriteCustomElements(XmlWriter writer) 552 { 553 if (!(token != null && token.IsValid)) 554 { 555 throw Fx.AssertAndThrow("Could not construct a valid RST without token!"); 556 } 557 string wstprefix = writer.LookupPrefix(TrustNamespace); 558 559 writer.WriteStartElement(wstprefix, TrustFeb2005Strings.RequestedSecurityToken, TrustFeb2005Strings.Namespace); 560 token.Write(writer); 561 writer.WriteEndElement(); 562 } 563 OnMakeReadOnly()564 internal protected override void OnMakeReadOnly() { } CompareWithNS(string first, string firstNS, string second, string secondNS)565 internal static bool CompareWithNS(string first, string firstNS, string second, string secondNS) 566 { 567 return ((String.Compare(first, second, StringComparison.Ordinal) == 0) 568 && (String.Compare(firstNS, secondNS, StringComparison.OrdinalIgnoreCase) == 0)); 569 } 570 } 571 572 class PeerRequestSecurityTokenResponse : RequestSecurityTokenResponse 573 { 574 public const string Action = "http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Validate"; 575 public const string ValidString = "http://schemas.xmlsoap.org/ws/2005/02/trust/status/valid"; 576 public const string InvalidString = "http://schemas.xmlsoap.org/ws/2005/02/trust/status/invalid"; 577 public const string StatusString = "Status"; 578 public const string CodeString = "Code"; 579 580 PeerHashToken token; 581 bool isValid = false; 582 PeerRequestSecurityTokenResponse()583 public PeerRequestSecurityTokenResponse() 584 : this(null) 585 { 586 } 587 PeerRequestSecurityTokenResponse(PeerHashToken token)588 public PeerRequestSecurityTokenResponse(PeerHashToken token) 589 { 590 this.token = token; 591 this.isValid = (token != null && token.IsValid); 592 } 593 594 public PeerHashToken Token 595 { 596 get 597 { 598 if (!(this.isValid)) 599 { 600 throw Fx.AssertAndThrow("should not be called when the token is invalid!"); 601 } 602 return this.token; 603 } 604 } 605 606 public bool IsValid 607 { 608 get 609 { 610 return this.isValid; 611 } 612 } 613 CreateHashTokenFrom(Message message)614 public static PeerHashToken CreateHashTokenFrom(Message message) 615 { 616 PeerHashToken token = PeerHashToken.Invalid; 617 RequestSecurityTokenResponse response = RequestSecurityTokenResponse.CreateFrom(message.GetReaderAtBodyContents(), MessageSecurityVersion.Default, new PeerSecurityTokenSerializer()); 618 if (String.Compare(response.TokenType, PeerHashToken.TokenTypeString, StringComparison.OrdinalIgnoreCase) != 0) 619 { 620 return token; 621 } 622 XmlElement responseXml = response.RequestSecurityTokenResponseXml; 623 if (responseXml != null) 624 { 625 foreach (XmlElement child in responseXml.ChildNodes) 626 { 627 if (PeerRequestSecurityToken.CompareWithNS(child.LocalName, child.NamespaceURI, StatusString, TrustFeb2005Strings.Namespace)) 628 { 629 if (child.ChildNodes.Count == 1) 630 { 631 XmlElement desc = (child.ChildNodes[0] as XmlElement); 632 if (PeerRequestSecurityToken.CompareWithNS(desc.LocalName, desc.NamespaceURI, CodeString, TrustFeb2005Strings.Namespace)) 633 { 634 string code = XmlHelper.ReadTextElementAsTrimmedString(desc); 635 if (String.Compare(code, ValidString, StringComparison.OrdinalIgnoreCase) != 0) 636 break; 637 } 638 } 639 } 640 else if (PeerRequestSecurityToken.CompareWithNS(child.LocalName, child.NamespaceURI, TrustFeb2005Strings.RequestedSecurityToken, TrustFeb2005Strings.Namespace)) 641 { 642 token = PeerHashToken.CreateFrom(child); 643 break; 644 } 645 } 646 } 647 return token; 648 } 649 CreateFrom(X509Certificate2 credential, string password)650 public static RequestSecurityTokenResponse CreateFrom(X509Certificate2 credential, string password) 651 { 652 PeerHashToken token = new PeerHashToken(credential, password); 653 return new PeerRequestSecurityTokenResponse(token); 654 } 655 OnWriteCustomElements(XmlWriter writer)656 internal protected override void OnWriteCustomElements(XmlWriter writer) 657 { 658 string wstprefix = writer.LookupPrefix(TrustFeb2005Strings.Namespace); 659 660 writer.WriteStartElement(wstprefix, TrustFeb2005Strings.TokenType, TrustFeb2005Strings.Namespace); 661 writer.WriteString(PeerHashToken.TokenTypeString); 662 writer.WriteEndElement(); 663 664 writer.WriteStartElement(wstprefix, StatusString, TrustFeb2005Strings.Namespace); 665 writer.WriteStartElement(wstprefix, CodeString, TrustFeb2005Strings.Namespace); 666 if (!this.IsValid) 667 writer.WriteString(InvalidString); 668 else 669 writer.WriteString(ValidString); 670 writer.WriteEndElement(); 671 writer.WriteEndElement(); 672 if (this.IsValid) 673 { 674 writer.WriteStartElement(wstprefix, PeerRequestSecurityToken.RequestedSecurityTokenElementName, TrustFeb2005Strings.Namespace); 675 this.token.Write(writer); 676 writer.WriteEndElement(); 677 } 678 } 679 } 680 681 class PeerChannelAuthenticatorExtension : IExtension<IPeerNeighbor> 682 { 683 IPeerNeighbor host; 684 PeerSecurityManager securityManager; 685 PeerAuthState state; 686 EventArgs originalArgs; 687 EventHandler onSucceeded; 688 IOThreadTimer timer = null; 689 object thisLock = new object(); 690 static TimeSpan Timeout = new TimeSpan(0, 2, 0); 691 string meshId; 692 693 enum PeerAuthState 694 { 695 Created, 696 Authenticated, 697 Failed 698 } 699 PeerChannelAuthenticatorExtension(PeerSecurityManager securityManager, EventHandler onSucceeded, EventArgs args, string meshId)700 public PeerChannelAuthenticatorExtension(PeerSecurityManager securityManager, EventHandler onSucceeded, EventArgs args, string meshId) 701 { 702 this.securityManager = securityManager; 703 this.state = PeerAuthState.Created; 704 this.originalArgs = args; 705 this.onSucceeded = onSucceeded; 706 this.meshId = meshId; 707 } 708 709 object ThisLock 710 { 711 get 712 { 713 return this.thisLock; 714 } 715 } 716 Attach(IPeerNeighbor host)717 public void Attach(IPeerNeighbor host) 718 { 719 Fx.AssertAndThrow(this.securityManager.AuthenticationMode == PeerAuthenticationMode.Password, "Invalid AuthenticationMode!"); 720 Fx.AssertAndThrow(host != null, "unrecognized host!"); 721 this.host = host; 722 this.timer = new IOThreadTimer(new Action<object>(OnTimeout), null, true); 723 this.timer.Set(Timeout); 724 } 725 OnNeighborClosed(IPeerNeighbor neighbor)726 static public void OnNeighborClosed(IPeerNeighbor neighbor) 727 { 728 Fx.Assert(neighbor != null, "Neighbor must have a value"); 729 PeerChannelAuthenticatorExtension ext = neighbor.Extensions.Find<PeerChannelAuthenticatorExtension>(); 730 if (ext != null) neighbor.Extensions.Remove(ext); 731 } 732 Detach(IPeerNeighbor host)733 public void Detach(IPeerNeighbor host) 734 { 735 736 Fx.Assert(host != null, "unrecognized host!"); 737 if (host.State < PeerNeighborState.Authenticated) 738 { 739 OnFailed(host); 740 } 741 this.host = null; 742 this.timer.Cancel(); 743 } 744 OnTimeout(object state)745 void OnTimeout(object state) 746 { 747 IPeerNeighbor neighbor = host; 748 if (neighbor == null) 749 return; 750 if (neighbor.State < PeerNeighborState.Authenticated) 751 { 752 OnFailed(neighbor); 753 } 754 } 755 InitiateHandShake()756 public void InitiateHandShake() 757 { 758 IPeerNeighbor neighbor = host; 759 Message reply = null; 760 761 Fx.Assert(host != null, "Cannot initiate security handshake without a host!"); 762 763 //send the RST message. 764 using (OperationContextScope scope = new OperationContextScope(new OperationContext((ServiceHostBase)null))) 765 { 766 PeerHashToken token = this.securityManager.GetSelfToken(); 767 Message request = Message.CreateMessage(MessageVersion.Soap12WSAddressing10, TrustFeb2005Strings.RequestSecurityToken, new PeerRequestSecurityToken(token)); 768 bool fatal = false; 769 try 770 { 771 reply = neighbor.RequestSecurityToken(request); 772 773 if (!(reply != null)) 774 { 775 throw Fx.AssertAndThrow("SecurityHandshake return empty message!"); 776 } 777 ProcessRstr(neighbor, reply, PeerSecurityManager.FindClaim(ServiceSecurityContext.Current)); 778 } 779 catch (Exception e) 780 { 781 if (Fx.IsFatal(e)) 782 { 783 fatal = true; 784 throw; 785 } 786 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); 787 this.state = PeerAuthState.Failed; 788 if (DiagnosticUtility.ShouldTraceError) 789 { 790 ServiceSecurityContext context = ServiceSecurityContext.Current; 791 ClaimSet claimSet = null; 792 if (context != null && context.AuthorizationContext != null && context.AuthorizationContext.ClaimSets != null && context.AuthorizationContext.ClaimSets.Count > 0) 793 claimSet = context.AuthorizationContext.ClaimSets[0]; 794 PeerAuthenticationFailureTraceRecord record = new PeerAuthenticationFailureTraceRecord( 795 meshId, 796 neighbor.ListenAddress.EndpointAddress.ToString(), 797 claimSet, 798 e); 799 TraceUtility.TraceEvent(TraceEventType.Error, 800 TraceCode.PeerNodeAuthenticationFailure, SR.GetString(SR.TraceCodePeerNodeAuthenticationFailure), 801 record, this, null); 802 } 803 neighbor.Abort(PeerCloseReason.AuthenticationFailure, PeerCloseInitiator.LocalNode); 804 } 805 finally 806 { 807 if (!fatal) 808 request.Close(); 809 } 810 } 811 } 812 ProcessRst(Message message, Claim claim)813 public Message ProcessRst(Message message, Claim claim) 814 { 815 IPeerNeighbor neighbor = host; 816 PeerRequestSecurityTokenResponse response = null; 817 Message reply = null; 818 819 lock (ThisLock) 820 { 821 if (this.state != PeerAuthState.Created || neighbor == null || neighbor.IsInitiator || neighbor.State != PeerNeighborState.Opened) 822 { 823 OnFailed(neighbor); 824 return null; 825 } 826 } 827 828 try 829 { 830 PeerHashToken receivedToken = PeerRequestSecurityToken.CreateHashTokenFrom(message); 831 PeerHashToken expectedToken = securityManager.GetExpectedTokenForClaim(claim); 832 833 if (!expectedToken.Equals(receivedToken)) 834 { 835 OnFailed(neighbor); 836 } 837 else 838 { 839 this.state = PeerAuthState.Authenticated; 840 PeerHashToken selfToken = securityManager.GetSelfToken(); 841 response = new PeerRequestSecurityTokenResponse(selfToken); 842 reply = Message.CreateMessage(MessageVersion.Soap12WSAddressing10, TrustFeb2005Strings.RequestSecurityTokenResponse, response); 843 OnAuthenticated(); 844 } 845 } 846 catch (Exception e) 847 { 848 if (Fx.IsFatal(e)) throw; 849 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); 850 OnFailed(neighbor); 851 } 852 return reply; 853 } 854 ProcessRstr(IPeerNeighbor neighbor, Message message, Claim claim)855 public void ProcessRstr(IPeerNeighbor neighbor, Message message, Claim claim) 856 { 857 PeerHashToken receivedToken = PeerRequestSecurityTokenResponse.CreateHashTokenFrom(message); 858 859 if (!receivedToken.IsValid) 860 { 861 OnFailed(neighbor); 862 } 863 else 864 { 865 PeerHashToken expectedToken = securityManager.GetExpectedTokenForClaim(claim); 866 if (!expectedToken.Equals(receivedToken)) 867 OnFailed(neighbor); 868 else 869 OnAuthenticated(); 870 } 871 } 872 OnAuthenticated()873 public void OnAuthenticated() 874 { 875 IPeerNeighbor neighbor = null; 876 lock (ThisLock) 877 { 878 this.timer.Cancel(); 879 neighbor = this.host; 880 this.state = PeerAuthState.Authenticated; 881 } 882 if (neighbor == null) 883 return; 884 neighbor.TrySetState(PeerNeighborState.Authenticated); 885 onSucceeded(neighbor, originalArgs); 886 } 887 OnFailed(IPeerNeighbor neighbor)888 void OnFailed(IPeerNeighbor neighbor) 889 { 890 lock (ThisLock) 891 { 892 this.state = PeerAuthState.Failed; 893 this.timer.Cancel(); 894 this.host = null; 895 } 896 if (DiagnosticUtility.ShouldTraceError) 897 { 898 PeerAuthenticationFailureTraceRecord record = null; 899 String remoteUri = ""; 900 PeerNodeAddress remoteAddress = neighbor.ListenAddress; 901 if (remoteAddress != null) 902 { 903 remoteUri = remoteAddress.EndpointAddress.ToString(); 904 } 905 OperationContext opContext = OperationContext.Current; 906 if (opContext != null) 907 { 908 remoteUri = opContext.IncomingMessageProperties.Via.ToString(); 909 ServiceSecurityContext secContext = opContext.ServiceSecurityContext; 910 if (secContext != null) 911 { 912 record = new PeerAuthenticationFailureTraceRecord( 913 meshId, 914 remoteUri, 915 secContext.AuthorizationContext.ClaimSets[0], null); 916 917 if (DiagnosticUtility.ShouldTraceError) 918 { 919 TraceUtility.TraceEvent( 920 TraceEventType.Error, 921 TraceCode.PeerNodeAuthenticationFailure, 922 SR.GetString(SR.TraceCodePeerNodeAuthenticationFailure), 923 record, 924 this, 925 null); 926 } 927 } 928 } 929 else 930 { 931 record = new PeerAuthenticationFailureTraceRecord(meshId, remoteUri); 932 if (DiagnosticUtility.ShouldTraceError) 933 { 934 TraceUtility.TraceEvent(TraceEventType.Error, 935 TraceCode.PeerNodeAuthenticationTimeout, 936 SR.GetString(SR.TraceCodePeerNodeAuthenticationTimeout), 937 record, 938 this, 939 null); 940 } 941 } 942 } 943 neighbor.Abort(PeerCloseReason.AuthenticationFailure, PeerCloseInitiator.LocalNode); 944 } 945 } 946 } 947 948 namespace System.ServiceModel.Channels 949 { 950 internal enum PeerAuthenticationMode 951 { 952 None = 0, 953 Password = 1, 954 MutualCertificate = 2 955 } 956 } 957 958 959 960 961