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