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