//------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ namespace System.IdentityModel.Tokens { using System.Collections.Generic; using System.Diagnostics; using System.IdentityModel.Configuration; using System.IdentityModel.Diagnostics; using System.IdentityModel.Selectors; using System.Security.Claims; using System.Security.Cryptography.X509Certificates; using System.ServiceModel.Security; using System.Text; using System.Xml; /// /// Extends SecurityTokenRequirement by adding new properties which are /// useful for issued tokens. /// public class SamlSecurityTokenRequirement { // // The below defaults will only be used if some verification properties are set in config and others are not // static X509RevocationMode DefaultRevocationMode = X509RevocationMode.Online; static X509CertificateValidationMode DefaultValidationMode = X509CertificateValidationMode.PeerOrChainTrust; static StoreLocation DefaultStoreLocation = StoreLocation.LocalMachine; string _nameClaimType = ClaimsIdentity.DefaultNameClaimType; string _roleClaimType = ClaimTypes.Role; bool _mapToWindows; X509CertificateValidator _certificateValidator; /// /// Creates an instance of /// public SamlSecurityTokenRequirement() { } /// /// Creates an instance of /// The XmlElement from which the instance is to be loaded. /// public SamlSecurityTokenRequirement(XmlElement element) { if (element == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("element"); } if (element.LocalName != ConfigurationStrings.SamlSecurityTokenRequirement) { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID7000, ConfigurationStrings.SamlSecurityTokenRequirement, element.LocalName)); } bool foundCustomX509Validator = false; X509RevocationMode revocationMode = DefaultRevocationMode; X509CertificateValidationMode certificateValidationMode = DefaultValidationMode; StoreLocation trustedStoreLocation = DefaultStoreLocation; string customValidator = null; foreach (XmlAttribute attribute in element.Attributes) { if (StringComparer.OrdinalIgnoreCase.Equals(attribute.LocalName, ConfigurationStrings.MapToWindows)) { bool outMapToWindows = false; if (!bool.TryParse(attribute.Value, out outMapToWindows)) { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID7022, attribute.Value)); } this.MapToWindows = outMapToWindows; } else if (StringComparer.OrdinalIgnoreCase.Equals(attribute.LocalName, ConfigurationStrings.IssuerCertificateValidator)) { customValidator = attribute.Value.ToString(); } else if (StringComparer.OrdinalIgnoreCase.Equals(attribute.LocalName, ConfigurationStrings.IssuerCertificateRevocationMode)) { foundCustomX509Validator = true; string revocationModeString = attribute.Value.ToString(); if (StringComparer.OrdinalIgnoreCase.Equals(revocationModeString, ConfigurationStrings.X509RevocationModeNoCheck)) { revocationMode = X509RevocationMode.NoCheck; } else if (StringComparer.OrdinalIgnoreCase.Equals(revocationModeString, ConfigurationStrings.X509RevocationModeOffline)) { revocationMode = X509RevocationMode.Offline; } else if (StringComparer.OrdinalIgnoreCase.Equals(revocationModeString, ConfigurationStrings.X509RevocationModeOnline)) { revocationMode = X509RevocationMode.Online; } else { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID7011, attribute.LocalName, element.LocalName))); } } else if (StringComparer.OrdinalIgnoreCase.Equals(attribute.LocalName, ConfigurationStrings.IssuerCertificateValidationMode)) { foundCustomX509Validator = true; string validationModeString = attribute.Value.ToString(); if (StringComparer.OrdinalIgnoreCase.Equals(validationModeString, ConfigurationStrings.X509CertificateValidationModeChainTrust)) { certificateValidationMode = X509CertificateValidationMode.ChainTrust; } else if (StringComparer.OrdinalIgnoreCase.Equals(validationModeString, ConfigurationStrings.X509CertificateValidationModePeerOrChainTrust)) { certificateValidationMode = X509CertificateValidationMode.PeerOrChainTrust; } else if (StringComparer.OrdinalIgnoreCase.Equals(validationModeString, ConfigurationStrings.X509CertificateValidationModePeerTrust)) { certificateValidationMode = X509CertificateValidationMode.PeerTrust; } else if (StringComparer.OrdinalIgnoreCase.Equals(validationModeString, ConfigurationStrings.X509CertificateValidationModeNone)) { certificateValidationMode = X509CertificateValidationMode.None; } else if (StringComparer.OrdinalIgnoreCase.Equals(validationModeString, ConfigurationStrings.X509CertificateValidationModeCustom)) { certificateValidationMode = X509CertificateValidationMode.Custom; } else { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID7011, attribute.LocalName, element.LocalName))); } } else if (StringComparer.OrdinalIgnoreCase.Equals(attribute.LocalName, ConfigurationStrings.IssuerCertificateTrustedStoreLocation)) { foundCustomX509Validator = true; string trustedStoreLocationString = attribute.Value.ToString(); if (StringComparer.OrdinalIgnoreCase.Equals(trustedStoreLocationString, ConfigurationStrings.X509TrustedStoreLocationCurrentUser)) { trustedStoreLocation = StoreLocation.CurrentUser; } else if (StringComparer.OrdinalIgnoreCase.Equals(trustedStoreLocationString, ConfigurationStrings.X509TrustedStoreLocationLocalMachine)) { trustedStoreLocation = StoreLocation.LocalMachine; } else { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID7011, attribute.LocalName, element.LocalName))); } } else { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID7004, attribute.LocalName, element.LocalName))); } } List configElements = XmlUtil.GetXmlElements(element.ChildNodes); foreach (XmlElement childElement in configElements) { if (StringComparer.Ordinal.Equals(childElement.LocalName, ConfigurationStrings.NameClaimType)) { if (childElement.Attributes.Count != 1 || !StringComparer.Ordinal.Equals(childElement.Attributes[0].LocalName, ConfigurationStrings.Value)) { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID7001, String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}/{1}", element.LocalName, childElement.LocalName), ConfigurationStrings.Value)); } this.NameClaimType = childElement.Attributes[0].Value; } else if (StringComparer.Ordinal.Equals(childElement.LocalName, ConfigurationStrings.RoleClaimType)) { if (childElement.Attributes.Count != 1 || !StringComparer.Ordinal.Equals(childElement.Attributes[0].LocalName, ConfigurationStrings.Value)) { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID7001, String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}/{1}", element.LocalName, childElement.LocalName), ConfigurationStrings.Value)); } this.RoleClaimType = childElement.Attributes[0].Value; } else { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID7002, childElement.LocalName, ConfigurationStrings.SamlSecurityTokenRequirement)); } } if (certificateValidationMode == X509CertificateValidationMode.Custom) { if (string.IsNullOrEmpty(customValidator)) { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID7028)); } Type customValidatorType = Type.GetType(customValidator, true); if (customValidatorType == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("value", SR.GetString(SR.ID7007, customValidatorType)); } _certificateValidator = CustomTypeElement.Resolve(new CustomTypeElement(customValidatorType)); } else if (foundCustomX509Validator) { _certificateValidator = X509Util.CreateCertificateValidator(certificateValidationMode, revocationMode, trustedStoreLocation); } } /// /// Gets/sets the X509CertificateValidator associated with this token requirement /// public X509CertificateValidator CertificateValidator { get { return _certificateValidator; } set { if (value == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); } _certificateValidator = value; } } /// /// Gets or sets the Claim Type that will be used to generate the /// FederatedIdentity.Name property. /// public string NameClaimType { get { return _nameClaimType; } set { _nameClaimType = value; } } /// /// Gets the Claim Types that are used to generate the /// FederatedIdentity.Roles property. /// public string RoleClaimType { get { return _roleClaimType; } set { _roleClaimType = value; } } /// /// Determines if the token handler will attempt to map the SAML identity to a /// Windows identity via the unique principal name (UPN) claim. /// public bool MapToWindows { get { return _mapToWindows; } set { _mapToWindows = value; } } /// /// Checks if Audience Enforcement checks are required for the given token /// based on this SamlSecurityTokenRequirement settings. /// /// /// The defining the audience requirement. /// /// The Security token to be tested for Audience /// Enforcement. /// True if Audience Enforcement should be applied. /// The input argument 'token' is null. public virtual bool ShouldEnforceAudienceRestriction(AudienceUriMode audienceUriMode, SecurityToken token) { if (null == token) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token"); } // // Use AudienceUriMode to determine whether the audience // should be enforced // switch (audienceUriMode) { case AudienceUriMode.Always: return true; case AudienceUriMode.Never: return false; case AudienceUriMode.BearerKeyOnly: #pragma warning suppress 56506 return (null == token.SecurityKeys || 0 == token.SecurityKeys.Count); default: throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4025, audienceUriMode))); } } /// /// Checks the given list of Audience URIs with the AllowedAudienceUri list. /// /// Collection of AudienceUris. /// Collection of audience URIs the token applies to. /// The input argument 'allowedAudienceUris' is null. /// The input argument 'tokenAudiences' is null. /// Either the input argument 'tokenAudiences' or the configured /// 'AudienceUris' collection is empty. public virtual void ValidateAudienceRestriction(IList allowedAudienceUris, IList tokenAudiences) { if (null == allowedAudienceUris) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("allowedAudienceUris"); } if (null == tokenAudiences) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenAudiences"); } if (0 == tokenAudiences.Count) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new AudienceUriValidationFailedException( SR.GetString(SR.ID1036))); } if (0 == allowedAudienceUris.Count) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new AudienceUriValidationFailedException( SR.GetString(SR.ID1043))); } bool found = false; foreach (Uri audience in tokenAudiences) { if (audience != null) { // Strip off any query string or fragment. This is necessary because the // CardSpace uses the raw Request-URI to form the audience when issuing // tokens for personal cards, but we clearly don't want things like the // ReturnUrl parameter affecting the audience matching. Uri audienceLeftPart; if (audience.IsAbsoluteUri) { audienceLeftPart = new Uri(audience.GetLeftPart(UriPartial.Path)); } else { Uri baseUri = new Uri("http://www.example.com"); Uri resolved = new Uri(baseUri, audience); audienceLeftPart = baseUri.MakeRelativeUri(new Uri(resolved.GetLeftPart(UriPartial.Path))); } if (allowedAudienceUris.Contains(audienceLeftPart)) { found = true; break; } } } if (!found) { #pragma warning suppress 56506 if (1 == tokenAudiences.Count || null != tokenAudiences[0]) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new AudienceUriValidationFailedException( SR.GetString(SR.ID1038, tokenAudiences[0].OriginalString))); } else { StringBuilder sb = new StringBuilder(SR.GetString(SR.ID8007)); bool first = true; foreach (Uri a in tokenAudiences) { if (a != null) { if (first) { first = false; } else { sb.Append(", "); } sb.Append(a.OriginalString); } } TraceUtility.TraceString(TraceEventType.Error, sb.ToString()); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new AudienceUriValidationFailedException(SR.GetString(SR.ID1037))); } } } } }