1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4 
5 namespace System.ServiceModel.Security
6 {
7     using System.Collections.Generic;
8     using System.Collections.ObjectModel;
9     using System.IdentityModel.Policy;
10     using System.IdentityModel.Selectors;
11     using System.IdentityModel.Tokens;
12     using System.IO;
13     using System.Runtime;
14     using System.Security.Authentication.ExtendedProtection;
15     using System.Security.Cryptography;
16     using System.ServiceModel;
17     using System.ServiceModel.Channels;
18     using System.ServiceModel.Diagnostics;
19     using System.ServiceModel.Security.Tokens;
20     using System.Xml;
21 
22     using CanonicalizationDriver = System.IdentityModel.CanonicalizationDriver;
23     using Psha1DerivedKeyGenerator = System.IdentityModel.Psha1DerivedKeyGenerator;
24     using SafeFreeCredentials = System.IdentityModel.SafeFreeCredentials;
25 
26     abstract class SspiNegotiationTokenProvider : NegotiationTokenProvider<SspiNegotiationTokenProviderState>
27     {
28         bool negotiateTokenOnOpen;
29         SecurityBindingElement securityBindingElement;
30 
SspiNegotiationTokenProvider()31         protected SspiNegotiationTokenProvider()
32             : this(null)
33         {
34         }
35 
SspiNegotiationTokenProvider(SecurityBindingElement securityBindingElement)36         protected SspiNegotiationTokenProvider(SecurityBindingElement securityBindingElement)
37             : base()
38         {
39             this.securityBindingElement = securityBindingElement;
40         }
41 
42         public bool NegotiateTokenOnOpen
43         {
44             get
45             {
46                 return this.negotiateTokenOnOpen;
47             }
48             set
49             {
50                 this.CommunicationObject.ThrowIfDisposedOrImmutable();
51                 this.negotiateTokenOnOpen = value;
52             }
53         }
54 
55         // SspiNegotiationTokenProvider abstract methods
ValidateSspiNegotiation(ISspiNegotiation sspiNegotiation)56         protected abstract ReadOnlyCollection<IAuthorizationPolicy> ValidateSspiNegotiation(ISspiNegotiation sspiNegotiation);
57         public abstract XmlDictionaryString NegotiationValueType { get; }
58 
OnOpen(TimeSpan timeout)59         public override void OnOpen(TimeSpan timeout)
60         {
61             TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
62             this.EnsureEndpointAddressDoesNotRequireEncryption(this.TargetAddress);
63             base.OnOpen(timeoutHelper.RemainingTime());
64             if (this.negotiateTokenOnOpen)
65             {
66                 this.DoNegotiation(timeoutHelper.RemainingTime());
67             }
68         }
69 
GetNegotiationChannelFactory(IChannelFactory<IRequestChannel> transportChannelFactory, ChannelBuilder channelBuilder)70         protected override IChannelFactory<IRequestChannel> GetNegotiationChannelFactory(IChannelFactory<IRequestChannel> transportChannelFactory, ChannelBuilder channelBuilder)
71         {
72             return transportChannelFactory;
73         }
74 
75         // helper methods
ValidateIncomingBinaryNegotiation(BinaryNegotiation incomingNego)76         void ValidateIncomingBinaryNegotiation(BinaryNegotiation incomingNego)
77         {
78             incomingNego.Validate(NegotiationValueType);
79         }
80 
AddToDigest(HashAlgorithm negotiationDigest, Stream stream)81         static void AddToDigest(HashAlgorithm negotiationDigest, Stream stream)
82         {
83             stream.Flush();
84             stream.Seek(0, SeekOrigin.Begin);
85             CanonicalizationDriver canonicalizer = new CanonicalizationDriver();
86             canonicalizer.SetInput(stream);
87             byte[] canonicalizedData = canonicalizer.GetBytes();
88             lock (negotiationDigest)
89             {
90                 negotiationDigest.TransformBlock(canonicalizedData, 0, canonicalizedData.Length, canonicalizedData, 0);
91             }
92         }
93 
AddToDigest(SspiNegotiationTokenProviderState sspiState, RequestSecurityToken rst)94         static void AddToDigest(SspiNegotiationTokenProviderState sspiState, RequestSecurityToken rst)
95         {
96             MemoryStream stream = new MemoryStream();
97             XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream);
98             rst.WriteTo(writer);
99             writer.Flush();
100             AddToDigest(sspiState.NegotiationDigest, stream);
101         }
102 
AddToDigest(SspiNegotiationTokenProviderState sspiState, RequestSecurityTokenResponse rstr, bool wasReceived, bool isFinalRstr)103         void AddToDigest(SspiNegotiationTokenProviderState sspiState, RequestSecurityTokenResponse rstr, bool wasReceived, bool isFinalRstr)
104         {
105             MemoryStream stream = new MemoryStream();
106             XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream);
107             if (!wasReceived)
108             {
109                 rstr.WriteTo(writer);
110             }
111             else
112             {
113                 if (!isFinalRstr)
114                 {
115                     rstr.RequestSecurityTokenResponseXml.WriteTo(writer);
116                 }
117                 else
118                 {
119                     XmlElement rstrClone = (XmlElement) rstr.RequestSecurityTokenResponseXml.CloneNode(true);
120                     List<XmlNode> nodesToRemove = new List<XmlNode>(2);
121                     for (int i = 0; i < rstrClone.ChildNodes.Count; ++i)
122                     {
123                         XmlNode child = (rstrClone.ChildNodes[i]);
124                         if (this.StandardsManager.TrustDriver.IsRequestedSecurityTokenElement(child.LocalName, child.NamespaceURI))
125                         {
126                             nodesToRemove.Add(child);
127                         }
128                         else if (this.StandardsManager.TrustDriver.IsRequestedProofTokenElement(child.LocalName, child.NamespaceURI))
129                         {
130                             nodesToRemove.Add(child);
131                         }
132                     }
133                     for (int i = 0; i < nodesToRemove.Count; ++i)
134                     {
135                         rstrClone.RemoveChild(nodesToRemove[i]);
136                     }
137                     rstrClone.WriteTo(writer);
138                 }
139             }
140             writer.Flush();
141             AddToDigest(sspiState.NegotiationDigest, stream);
142         }
143 
IsCorrectAuthenticator(SspiNegotiationTokenProviderState sspiState, byte[] proofKey, byte[] serverAuthenticator)144         static bool IsCorrectAuthenticator(SspiNegotiationTokenProviderState sspiState, byte[] proofKey, byte[] serverAuthenticator)
145         {
146             byte[] negotiationHash;
147             lock (sspiState.NegotiationDigest)
148             {
149                 sspiState.NegotiationDigest.TransformFinalBlock(CryptoHelper.EmptyBuffer, 0, 0);
150                 negotiationHash = sspiState.NegotiationDigest.Hash;
151             }
152             Psha1DerivedKeyGenerator generator = new Psha1DerivedKeyGenerator(proofKey);
153             byte[] clientAuthenticator = generator.GenerateDerivedKey(SecurityUtils.CombinedHashLabel, negotiationHash, SecurityNegotiationConstants.NegotiationAuthenticatorSize, 0);
154             if (clientAuthenticator.Length != serverAuthenticator.Length)
155             {
156                 return false;
157             }
158             for (int i = 0; i < clientAuthenticator.Length; ++i)
159             {
160                 if (clientAuthenticator[i] != serverAuthenticator[i])
161                 {
162                     return false;
163                 }
164             }
165             return true;
166         }
167 
PrepareRstr( SspiNegotiationTokenProviderState sspiState, byte[] outgoingBlob )168         BodyWriter PrepareRstr( SspiNegotiationTokenProviderState sspiState, byte[] outgoingBlob )
169         {
170             RequestSecurityTokenResponse rstr = new RequestSecurityTokenResponse(this.StandardsManager);
171             rstr.Context = sspiState.Context;
172             rstr.SetBinaryNegotiation(new BinaryNegotiation(NegotiationValueType, outgoingBlob));
173             rstr.MakeReadOnly();
174             AddToDigest(sspiState, rstr, false, false);
175             return rstr;
176         }
177 
GetFirstOutgoingMessageBody( SspiNegotiationTokenProviderState sspiState, out MessageProperties messageProperties )178         protected override BodyWriter GetFirstOutgoingMessageBody( SspiNegotiationTokenProviderState sspiState, out MessageProperties messageProperties )
179         {
180             messageProperties = null;
181 
182             // both message logging and Visual Studio trigger message serialization and hence can cause
183             // premature invocation of OnGetBinaryNegotiation(); flag this RST as streamed to block
184             // serialization of its body and hence premature calls to InitializeSecurityContext()
185 
186             RequestSecurityToken rst = new RequestSecurityToken(this.StandardsManager, false);
187             rst.Context = sspiState.Context;
188             rst.TokenType = this.StandardsManager.SecureConversationDriver.TokenTypeUri;
189             rst.KeySize = this.SecurityAlgorithmSuite.DefaultSymmetricKeyLength;
190 
191             // delay GetOutgoingBlob()'s first call to InitializeSecurityContext() until a channel binding
192             // is available
193 
194             rst.OnGetBinaryNegotiation = (new GetOutgoingBlobProxy(sspiState, this, rst)).GetOutgoingBlob;
195 
196             return rst;
197         }
198 
CreateClientChannel( EndpointAddress target, Uri via )199         protected override IRequestChannel CreateClientChannel( EndpointAddress target, Uri via )
200         {
201             IRequestChannel rstChannel = base.CreateClientChannel(target, via);
202 
203             if (!SecurityUtils.IsChannelBindingDisabled && (this.securityBindingElement is TransportSecurityBindingElement))
204             {
205                 // enable channel binding on this side channel
206                 IChannelBindingProvider cbp = rstChannel.GetProperty<IChannelBindingProvider>();
207                 if (cbp != null)
208                 {
209                     cbp.EnableChannelBindingSupport();
210                 }
211             }
212 
213             return rstChannel;
214         }
215 
216         /// <summary>
217         /// Proxy helps in implementating the delay of obtaining the binary data till later in the stack until the
218         /// ChannelBinding is obtained from the message.
219         /// </summary>
220         class GetOutgoingBlobProxy
221         {
222             RequestSecurityToken _rst;
223             SspiNegotiationTokenProvider _sspiProvider;
224             SspiNegotiationTokenProviderState _sspiState;
225 
GetOutgoingBlobProxy( SspiNegotiationTokenProviderState sspiState, SspiNegotiationTokenProvider sspiProvider, RequestSecurityToken rst )226             public GetOutgoingBlobProxy( SspiNegotiationTokenProviderState sspiState, SspiNegotiationTokenProvider sspiProvider, RequestSecurityToken rst )
227             {
228                 _sspiState = sspiState;
229                 _sspiProvider = sspiProvider;
230                 _rst = rst;
231             }
232 
GetOutgoingBlob( ChannelBinding channelBinding )233             public void GetOutgoingBlob( ChannelBinding channelBinding )
234             {
235                 byte[] outgoingBlob = _sspiState.SspiNegotiation.GetOutgoingBlob(null, channelBinding, null);
236 
237                 if (outgoingBlob == null && _sspiState.SspiNegotiation.IsCompleted == false)
238                 {
239                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.NoBinaryNegoToSend)));
240                 }
241 
242                 _rst.SetBinaryNegotiation(new BinaryNegotiation(_sspiProvider.NegotiationValueType, outgoingBlob));
243                 SspiNegotiationTokenProvider.AddToDigest(_sspiState, _rst);
244                 _rst.MakeReadOnly();
245             }
246         }
247 
GetNextOutgoingMessageBody(Message incomingMessage, SspiNegotiationTokenProviderState sspiState)248         protected override BodyWriter GetNextOutgoingMessageBody(Message incomingMessage, SspiNegotiationTokenProviderState sspiState)
249         {
250             try
251             {
252                 ThrowIfFault(incomingMessage, this.TargetAddress);
253             }
254             catch (FaultException fault)
255             {
256                 if (fault.Code.IsSenderFault)
257                 {
258                     if (fault.Code.SubCode.Name == TrustApr2004Strings.FailedAuthenticationFaultCode || fault.Code.SubCode.Name == TrustFeb2005Strings.FailedAuthenticationFaultCode)
259                         throw TraceUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.AuthenticationOfClientFailed), fault), incomingMessage);
260 
261                     throw TraceUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.FailedSspiNegotiation), fault), incomingMessage);
262                 }
263                 else
264                 {
265                     throw;
266                 }
267             }
268             RequestSecurityTokenResponse negotiationRstr = null;
269             RequestSecurityTokenResponse authenticatorRstr = null;
270             XmlDictionaryReader bodyReader = incomingMessage.GetReaderAtBodyContents();
271             using (bodyReader)
272             {
273                 if (this.StandardsManager.TrustDriver.IsAtRequestSecurityTokenResponseCollection(bodyReader))
274                 {
275                     RequestSecurityTokenResponseCollection rstrCollection = this.StandardsManager.TrustDriver.CreateRequestSecurityTokenResponseCollection(bodyReader);
276                     using (IEnumerator<RequestSecurityTokenResponse> enumerator = rstrCollection.RstrCollection.GetEnumerator())
277                     {
278                         enumerator.MoveNext();
279                         negotiationRstr = enumerator.Current;
280                         if (enumerator.MoveNext())
281                         {
282                             authenticatorRstr = enumerator.Current;
283                         }
284                     }
285                     if (authenticatorRstr == null)
286                     {
287                         throw TraceUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.AuthenticatorNotPresentInRSTRCollection)), incomingMessage);
288                     }
289                     else if (authenticatorRstr.Context != negotiationRstr.Context)
290                     {
291                         throw TraceUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.RSTRAuthenticatorHasBadContext)), incomingMessage);
292                     }
293                     AddToDigest(sspiState, negotiationRstr, true, true);
294                 }
295                 else if (this.StandardsManager.TrustDriver.IsAtRequestSecurityTokenResponse(bodyReader))
296                 {
297                     negotiationRstr = RequestSecurityTokenResponse.CreateFrom(this.StandardsManager, bodyReader);
298                     AddToDigest(sspiState, negotiationRstr, true, false);
299                 }
300                 else
301                 {
302                     this.StandardsManager.TrustDriver.OnRSTRorRSTRCMissingException();
303                 }
304                 incomingMessage.ReadFromBodyContentsToEnd(bodyReader);
305             }
306             if (negotiationRstr.Context != sspiState.Context)
307             {
308                 throw TraceUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.BadSecurityNegotiationContext)), incomingMessage);
309             }
310             BinaryNegotiation incomingBinaryNego = negotiationRstr.GetBinaryNegotiation();
311             byte[] incomingBlob;
312             if (incomingBinaryNego != null)
313             {
314                 ValidateIncomingBinaryNegotiation(incomingBinaryNego);
315                 incomingBlob = incomingBinaryNego.GetNegotiationData();
316             }
317             else
318             {
319                 incomingBlob = null;
320             }
321             BodyWriter nextMessageBody;
322             if (incomingBlob == null && sspiState.SspiNegotiation.IsCompleted == false)
323             {
324                 throw TraceUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.NoBinaryNegoToReceive)), incomingMessage);
325             }
326             else if (incomingBlob == null && sspiState.SspiNegotiation.IsCompleted == true)
327             {
328                 // the incoming RSTR must have the negotiated token
329                 OnNegotiationComplete(sspiState, negotiationRstr, authenticatorRstr);
330                 nextMessageBody = null;
331             }
332             else
333             {
334                 // we got an incoming blob. Process it and see if there is an outgoing blob
335                 byte[] outgoingBlob = sspiState.SspiNegotiation.GetOutgoingBlob(incomingBlob,
336                                                             SecurityUtils.GetChannelBindingFromMessage(incomingMessage),
337                                                             null);
338 
339                 if (outgoingBlob == null && sspiState.SspiNegotiation.IsCompleted == false)
340                 {
341                     throw TraceUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.NoBinaryNegoToSend)), incomingMessage);
342                 }
343                 else if (outgoingBlob == null && sspiState.SspiNegotiation.IsCompleted == true)
344                 {
345                     // the incoming RSTR had the last blob. It must have the token too
346                     this.OnNegotiationComplete(sspiState, negotiationRstr, authenticatorRstr);
347                     nextMessageBody = null;
348                 }
349                 else
350                 {
351                     nextMessageBody = PrepareRstr(sspiState, outgoingBlob);
352                 }
353             }
354             return nextMessageBody;
355         }
356 
OnNegotiationComplete(SspiNegotiationTokenProviderState sspiState, RequestSecurityTokenResponse negotiationRstr, RequestSecurityTokenResponse authenticatorRstr)357         void OnNegotiationComplete(SspiNegotiationTokenProviderState sspiState, RequestSecurityTokenResponse negotiationRstr, RequestSecurityTokenResponse authenticatorRstr)
358         {
359             ISspiNegotiation sspiNegotiation = sspiState.SspiNegotiation;
360             ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies = ValidateSspiNegotiation(sspiNegotiation);
361             // the negotiation has completed successfully - the service token needs to be extracted from the
362             // negotiationRstr
363             SecurityTokenResolver tokenResolver = new SspiSecurityTokenResolver(sspiNegotiation);
364             GenericXmlSecurityToken serviceToken = negotiationRstr.GetIssuedToken(tokenResolver, EmptyReadOnlyCollection<SecurityTokenAuthenticator>.Instance,
365                 SecurityKeyEntropyMode.ServerEntropy, null, this.SecurityContextTokenUri, authorizationPolicies, 0, false);
366             if (serviceToken == null)
367             {
368                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.NoServiceTokenReceived)));
369             }
370             WrappedKeySecurityToken wrappedToken = (serviceToken.ProofToken as WrappedKeySecurityToken);
371             if (wrappedToken == null || wrappedToken.WrappingAlgorithm != sspiNegotiation.KeyEncryptionAlgorithm)
372             {
373                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.ProofTokenWasNotWrappedCorrectly)));
374             }
375             byte[] proofKey = wrappedToken.GetWrappedKey();
376             if (authenticatorRstr == null)
377             {
378                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.RSTRAuthenticatorNotPresent)));
379             }
380             byte[] serverAuthenticator = authenticatorRstr.GetAuthenticator();
381             if (serverAuthenticator == null)
382             {
383                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.RSTRAuthenticatorNotPresent)));
384             }
385             if (!IsCorrectAuthenticator(sspiState, proofKey, serverAuthenticator))
386             {
387                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.RSTRAuthenticatorIncorrect)));
388             }
389             sspiState.SetServiceToken(serviceToken);
390         }
391 
392         class SspiSecurityTokenResolver : SecurityTokenResolver, ISspiNegotiationInfo
393         {
394             ISspiNegotiation sspiNegotiation;
395 
SspiSecurityTokenResolver(ISspiNegotiation sspiNegotiation)396             public SspiSecurityTokenResolver(ISspiNegotiation sspiNegotiation)
397             {
398                 this.sspiNegotiation = sspiNegotiation;
399             }
400 
401             public ISspiNegotiation SspiNegotiation
402             {
403                 get { return this.sspiNegotiation; }
404             }
405 
TryResolveTokenCore(SecurityKeyIdentifier keyIdentifier, out SecurityToken token)406             protected override bool TryResolveTokenCore(SecurityKeyIdentifier keyIdentifier, out SecurityToken token)
407             {
408                 token = null;
409                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
410             }
411 
TryResolveTokenCore(SecurityKeyIdentifierClause keyIdentifierClause, out SecurityToken token)412             protected override bool TryResolveTokenCore(SecurityKeyIdentifierClause keyIdentifierClause, out SecurityToken token)
413             {
414                 token = null;
415                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
416             }
417 
TryResolveSecurityKeyCore(SecurityKeyIdentifierClause keyIdentifierClause, out SecurityKey key)418             protected override bool TryResolveSecurityKeyCore(SecurityKeyIdentifierClause keyIdentifierClause, out SecurityKey key)
419             {
420                 key = null;
421                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
422             }
423         }
424     }
425 
426     class SspiIssuanceChannelParameter
427     {
428         bool getTokenOnOpen;
429         SafeFreeCredentials credentialsHandle;
430 
SspiIssuanceChannelParameter(bool getTokenOnOpen, SafeFreeCredentials credentialsHandle)431         public SspiIssuanceChannelParameter(bool getTokenOnOpen, SafeFreeCredentials credentialsHandle)
432         {
433             this.getTokenOnOpen = getTokenOnOpen;
434             this.credentialsHandle = credentialsHandle;
435         }
436 
437         public bool GetTokenOnOpen
438         {
439             get { return this.getTokenOnOpen; }
440         }
441 
442         public SafeFreeCredentials CredentialsHandle
443         {
444             get { return this.credentialsHandle; }
445         }
446     }
447 
448 }
449