1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4 namespace System.ServiceModel
5 {
6     using System.Collections.ObjectModel;
7     using System.IdentityModel.Tokens;
8     using System.Runtime;
9     using System.Runtime.CompilerServices;
10     using System.ServiceModel.Channels;
11     using System.ServiceModel.Security;
12     using System.ServiceModel.Security.Tokens;
13     using System.Xml;
14     using System.ComponentModel;
15 
16     public sealed class FederatedMessageSecurityOverHttp
17     {
18         internal const bool DefaultNegotiateServiceCredential = true;
19         internal const SecurityKeyType DefaultIssuedKeyType = SecurityKeyType.SymmetricKey;
20         internal const bool DefaultEstablishSecurityContext = true;
21 
22         bool establishSecurityContext;
23         bool negotiateServiceCredential;
24         SecurityAlgorithmSuite algorithmSuite;
25         EndpointAddress issuerAddress;
26         EndpointAddress issuerMetadataAddress;
27         Binding issuerBinding;
28         Collection<ClaimTypeRequirement> claimTypeRequirements;
29         string issuedTokenType;
30         SecurityKeyType issuedKeyType;
31         Collection<XmlElement> tokenRequestParameters;
32 
FederatedMessageSecurityOverHttp()33         public FederatedMessageSecurityOverHttp()
34         {
35             negotiateServiceCredential = DefaultNegotiateServiceCredential;
36             algorithmSuite = SecurityAlgorithmSuite.Default;
37             issuedKeyType = DefaultIssuedKeyType;
38             claimTypeRequirements = new Collection<ClaimTypeRequirement>();
39             tokenRequestParameters = new Collection<XmlElement>();
40             establishSecurityContext = DefaultEstablishSecurityContext;
41         }
42 
43         public bool NegotiateServiceCredential
44         {
45             get { return this.negotiateServiceCredential; }
46             set { this.negotiateServiceCredential = value; }
47         }
48 
49         public SecurityAlgorithmSuite AlgorithmSuite
50         {
51             get { return this.algorithmSuite; }
52             set
53             {
54                 if (value == null)
55                 {
56                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
57                 }
58                 this.algorithmSuite = value;
59             }
60         }
61 
62         public bool EstablishSecurityContext
63         {
64             get
65             {
66                 return this.establishSecurityContext;
67             }
68             set
69             {
70                 this.establishSecurityContext = value;
71             }
72         }
73 
74         [DefaultValue(null)]
75         public EndpointAddress IssuerAddress
76         {
77             get { return this.issuerAddress; }
78             set { this.issuerAddress = value; }
79         }
80 
81         [DefaultValue(null)]
82         public EndpointAddress IssuerMetadataAddress
83         {
84             get { return this.issuerMetadataAddress; }
85             set { this.issuerMetadataAddress = value; }
86         }
87 
88         [DefaultValue(null)]
89         public Binding IssuerBinding
90         {
91             get
92             {
93                 return this.issuerBinding;
94             }
95             set
96             {
97                 this.issuerBinding = value;
98             }
99         }
100 
101         [DefaultValue(null)]
102         public string IssuedTokenType
103         {
104             get { return this.issuedTokenType; }
105             set { this.issuedTokenType = value; }
106         }
107 
108         public SecurityKeyType IssuedKeyType
109         {
110             get { return this.issuedKeyType; }
111             set
112             {
113                 if (!SecurityKeyTypeHelper.IsDefined(value))
114                 {
115                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value"));
116                 }
117                 this.issuedKeyType = value;
118             }
119         }
120 
121         public Collection<ClaimTypeRequirement> ClaimTypeRequirements
122         {
123             get { return this.claimTypeRequirements; }
124         }
125 
126         public Collection<XmlElement> TokenRequestParameters
127         {
128             get { return this.tokenRequestParameters; }
129         }
130 
131         [MethodImpl(MethodImplOptions.NoInlining)]
CreateSecurityBindingElement(bool isSecureTransportMode, bool isReliableSession, MessageSecurityVersion version)132         internal SecurityBindingElement CreateSecurityBindingElement(bool isSecureTransportMode,
133                                                                      bool isReliableSession,
134                                                                      MessageSecurityVersion version)
135         {
136             if ((this.IssuedKeyType == SecurityKeyType.BearerKey) &&
137                (version.TrustVersion == TrustVersion.WSTrustFeb2005))
138             {
139                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.BearerKeyIncompatibleWithWSFederationHttpBinding)));
140             }
141 
142             if (isReliableSession && !this.EstablishSecurityContext)
143             {
144                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SecureConversationRequiredByReliableSession)));
145             }
146 
147             SecurityBindingElement result;
148             bool emitBspAttributes = true;
149             IssuedSecurityTokenParameters issuedParameters = new IssuedSecurityTokenParameters(this.IssuedTokenType, this.IssuerAddress, this.IssuerBinding);
150             issuedParameters.IssuerMetadataAddress = this.issuerMetadataAddress;
151             issuedParameters.KeyType = this.IssuedKeyType;
152             if (this.IssuedKeyType == SecurityKeyType.SymmetricKey)
153             {
154                 issuedParameters.KeySize = this.AlgorithmSuite.DefaultSymmetricKeyLength;
155             }
156             else
157             {
158                 issuedParameters.KeySize = 0;
159             }
160             foreach (ClaimTypeRequirement c in this.claimTypeRequirements)
161             {
162                 issuedParameters.ClaimTypeRequirements.Add(c);
163             }
164             foreach (XmlElement p in this.TokenRequestParameters)
165             {
166                 issuedParameters.AdditionalRequestParameters.Add(p);
167             }
168             WSSecurityTokenSerializer versionSpecificSerializer = new WSSecurityTokenSerializer(version.SecurityVersion,
169                                                                                                 version.TrustVersion,
170                                                                                                 version.SecureConversationVersion,
171                                                                                                 emitBspAttributes,
172                                                                                                 null, null, null);
173             SecurityStandardsManager versionSpecificStandardsManager = new SecurityStandardsManager(version, versionSpecificSerializer);
174             issuedParameters.AddAlgorithmParameters(this.AlgorithmSuite, versionSpecificStandardsManager, this.issuedKeyType);
175 
176             SecurityBindingElement issuedTokenSecurity;
177             if (isSecureTransportMode)
178             {
179                 issuedTokenSecurity = SecurityBindingElement.CreateIssuedTokenOverTransportBindingElement(issuedParameters);
180             }
181             else
182             {
183                 if (negotiateServiceCredential)
184                 {
185                     // We should have passed 'true' as RequireCancelation to be consistent with other standard bindings.
186                     // However, to limit the change for Orcas, we scope down to just newer version of WSSecurityPolicy.
187                     issuedTokenSecurity = SecurityBindingElement.CreateIssuedTokenForSslBindingElement(issuedParameters, version.SecurityPolicyVersion != SecurityPolicyVersion.WSSecurityPolicy11);
188                 }
189                 else
190                 {
191                     issuedTokenSecurity = SecurityBindingElement.CreateIssuedTokenForCertificateBindingElement(issuedParameters);
192                 }
193             }
194 
195             issuedTokenSecurity.MessageSecurityVersion = version;
196             issuedTokenSecurity.DefaultAlgorithmSuite = this.AlgorithmSuite;
197 
198             if (this.EstablishSecurityContext)
199             {
200                 result = SecurityBindingElement.CreateSecureConversationBindingElement(issuedTokenSecurity, true);
201             }
202             else
203             {
204                 result = issuedTokenSecurity;
205             }
206 
207             result.MessageSecurityVersion = version;
208             result.DefaultAlgorithmSuite = this.AlgorithmSuite;
209             result.IncludeTimestamp = true;
210 
211             if (!isReliableSession)
212             {
213                 result.LocalServiceSettings.ReconnectTransportOnFailure = false;
214                 result.LocalClientSettings.ReconnectTransportOnFailure = false;
215             }
216             else
217             {
218                 result.LocalServiceSettings.ReconnectTransportOnFailure = true;
219                 result.LocalClientSettings.ReconnectTransportOnFailure = true;
220             }
221 
222             if (this.establishSecurityContext)
223             {
224                 // issue the transition SCT for a short duration only
225                 issuedTokenSecurity.LocalServiceSettings.IssuedCookieLifetime = SpnegoTokenAuthenticator.defaultServerIssuedTransitionTokenLifetime;
226             }
227 
228             return result;
229         }
230 
TryCreate(SecurityBindingElement sbe, bool isSecureTransportMode, bool isReliableSession, MessageSecurityVersion version, out FederatedMessageSecurityOverHttp messageSecurity)231         internal static bool TryCreate(SecurityBindingElement sbe, bool isSecureTransportMode, bool isReliableSession, MessageSecurityVersion version, out FederatedMessageSecurityOverHttp messageSecurity)
232         {
233             Fx.Assert(null != sbe, string.Empty);
234 
235             messageSecurity = null;
236 
237             // do not check local settings: sbe.LocalServiceSettings and sbe.LocalClientSettings
238 
239             if (!sbe.IncludeTimestamp)
240                 return false;
241 
242             if (sbe.SecurityHeaderLayout != SecurityProtocolFactory.defaultSecurityHeaderLayout)
243                 return false;
244 
245             bool emitBspAttributes = true;
246 
247             // Do not check MessageSecurityVersion: it maybe changed by the wrapper element and gets checked later in the SecuritySection.AreBindingsMatching()
248 
249             SecurityBindingElement bootstrapSecurity;
250 
251             bool establishSecurityContext = SecurityBindingElement.IsSecureConversationBinding(sbe, true, out bootstrapSecurity);
252             bootstrapSecurity = establishSecurityContext ? bootstrapSecurity : sbe;
253 
254             if (isSecureTransportMode && !(bootstrapSecurity is TransportSecurityBindingElement))
255                 return false;
256 
257             bool negotiateServiceCredential = DefaultNegotiateServiceCredential;
258             IssuedSecurityTokenParameters issuedTokenParameters;
259 
260             if (isSecureTransportMode)
261             {
262                 if (!SecurityBindingElement.IsIssuedTokenOverTransportBinding(bootstrapSecurity, out issuedTokenParameters))
263                     return false;
264             }
265             else
266             {
267                 // We should have passed 'true' as RequireCancelation to be consistent with other standard bindings.
268                 // However, to limit the change for Orcas, we scope down to just newer version of WSSecurityPolicy.
269                 if (SecurityBindingElement.IsIssuedTokenForSslBinding(bootstrapSecurity, version.SecurityPolicyVersion != SecurityPolicyVersion.WSSecurityPolicy11, out issuedTokenParameters))
270                     negotiateServiceCredential = true;
271                 else if (SecurityBindingElement.IsIssuedTokenForCertificateBinding(bootstrapSecurity, out issuedTokenParameters))
272                     negotiateServiceCredential = false;
273                 else
274                     return false;
275             }
276 
277             if ((issuedTokenParameters.KeyType == SecurityKeyType.BearerKey) &&
278                (version.TrustVersion == TrustVersion.WSTrustFeb2005))
279             {
280                 return false;
281             }
282 
283             Collection<XmlElement> nonAlgorithmRequestParameters;
284             WSSecurityTokenSerializer versionSpecificSerializer = new WSSecurityTokenSerializer(version.SecurityVersion,
285                                                                                                 version.TrustVersion,
286                                                                                                 version.SecureConversationVersion,
287                                                                                                 emitBspAttributes,
288                                                                                                 null, null, null);
289             SecurityStandardsManager versionSpecificStandardsManager = new SecurityStandardsManager(version, versionSpecificSerializer);
290 
291             if (!issuedTokenParameters.DoAlgorithmsMatch(sbe.DefaultAlgorithmSuite,
292                                                          versionSpecificStandardsManager,
293                                                          out nonAlgorithmRequestParameters))
294             {
295                 return false;
296             }
297             messageSecurity = new FederatedMessageSecurityOverHttp();
298 
299             messageSecurity.AlgorithmSuite = sbe.DefaultAlgorithmSuite;
300             messageSecurity.NegotiateServiceCredential = negotiateServiceCredential;
301             messageSecurity.EstablishSecurityContext = establishSecurityContext;
302             messageSecurity.IssuedTokenType = issuedTokenParameters.TokenType;
303             messageSecurity.IssuerAddress = issuedTokenParameters.IssuerAddress;
304             messageSecurity.IssuerBinding = issuedTokenParameters.IssuerBinding;
305             messageSecurity.IssuerMetadataAddress = issuedTokenParameters.IssuerMetadataAddress;
306             messageSecurity.IssuedKeyType = issuedTokenParameters.KeyType;
307             foreach (ClaimTypeRequirement c in issuedTokenParameters.ClaimTypeRequirements)
308             {
309                 messageSecurity.ClaimTypeRequirements.Add(c);
310             }
311             foreach (XmlElement p in nonAlgorithmRequestParameters)
312             {
313                 messageSecurity.TokenRequestParameters.Add(p);
314             }
315             if (issuedTokenParameters.AlternativeIssuerEndpoints != null && issuedTokenParameters.AlternativeIssuerEndpoints.Count > 0)
316             {
317                 return false;
318             }
319             return true;
320         }
321 
InternalShouldSerialize()322         internal bool InternalShouldSerialize()
323         {
324             return (this.ShouldSerializeAlgorithmSuite()
325                 || this.ShouldSerializeClaimTypeRequirements()
326                 || this.ShouldSerializeNegotiateServiceCredential()
327                 || this.ShouldSerializeEstablishSecurityContext()
328                 || this.ShouldSerializeIssuedKeyType()
329                 || this.ShouldSerializeTokenRequestParameters());
330         }
331 
332         [EditorBrowsable(EditorBrowsableState.Never)]
ShouldSerializeAlgorithmSuite()333         public bool ShouldSerializeAlgorithmSuite()
334         {
335             return (this.AlgorithmSuite != SecurityAlgorithmSuite.Default);
336         }
337 
338         [EditorBrowsable(EditorBrowsableState.Never)]
ShouldSerializeClaimTypeRequirements()339         public bool ShouldSerializeClaimTypeRequirements()
340         {
341             return (this.ClaimTypeRequirements.Count > 0);
342         }
343 
344         [EditorBrowsable(EditorBrowsableState.Never)]
ShouldSerializeNegotiateServiceCredential()345         public bool ShouldSerializeNegotiateServiceCredential()
346         {
347             return (this.NegotiateServiceCredential != DefaultNegotiateServiceCredential);
348         }
349 
350         [EditorBrowsable(EditorBrowsableState.Never)]
ShouldSerializeEstablishSecurityContext()351         public bool ShouldSerializeEstablishSecurityContext()
352         {
353             return (this.EstablishSecurityContext != DefaultEstablishSecurityContext);
354         }
355 
356         [EditorBrowsable(EditorBrowsableState.Never)]
ShouldSerializeIssuedKeyType()357         public bool ShouldSerializeIssuedKeyType()
358         {
359             return (this.IssuedKeyType != DefaultIssuedKeyType);
360         }
361 
362         [EditorBrowsable(EditorBrowsableState.Never)]
ShouldSerializeTokenRequestParameters()363         public bool ShouldSerializeTokenRequestParameters()
364         {
365             return (this.TokenRequestParameters.Count > 0);
366         }
367 
368     }
369 }
370