//----------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- namespace System.IdentityModel.Selectors { using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Text; using System.IdentityModel.Claims; using System.IdentityModel.Tokens; using System.IdentityModel.Policy; using System.Security.Principal; using System.Security; public class SamlSecurityTokenAuthenticator : SecurityTokenAuthenticator { List supportingAuthenticators; Collection allowedAudienceUris; AudienceUriMode audienceUriMode; TimeSpan maxClockSkew; public SamlSecurityTokenAuthenticator(IList supportingAuthenticators) : this(supportingAuthenticators, TimeSpan.Zero) { } public SamlSecurityTokenAuthenticator(IList supportingAuthenticators, TimeSpan maxClockSkew) { if (supportingAuthenticators == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("supportingAuthenticators"); this.supportingAuthenticators = new List(supportingAuthenticators.Count); for (int i = 0; i < supportingAuthenticators.Count; ++i) { this.supportingAuthenticators.Add(supportingAuthenticators[i]); } this.maxClockSkew = maxClockSkew; this.audienceUriMode = AudienceUriMode.Always; this.allowedAudienceUris = new Collection(); } public AudienceUriMode AudienceUriMode { get { return this.audienceUriMode; } set { AudienceUriModeValidationHelper.Validate(audienceUriMode); this.audienceUriMode = value; } } public IList AllowedAudienceUris { get { return this.allowedAudienceUris; } } protected override bool CanValidateTokenCore(SecurityToken token) { return token is SamlSecurityToken; } protected override ReadOnlyCollection ValidateTokenCore(SecurityToken token) { if (token == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token"); SamlSecurityToken samlToken = token as SamlSecurityToken; if (samlToken == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.SamlTokenAuthenticatorCanOnlyProcessSamlTokens, token.GetType().ToString()))); if (samlToken.Assertion.Signature == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.SamlTokenMissingSignature))); if (!this.IsCurrentlyTimeEffective(samlToken)) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.SAMLTokenTimeInvalid, DateTime.UtcNow.ToUniversalTime(), samlToken.ValidFrom.ToString(), samlToken.ValidTo.ToString()))); if (samlToken.Assertion.SigningToken == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.SamlSigningTokenMissing))); // Build the Issuer ClaimSet for this Saml token. ClaimSet issuer = null; bool canBeValidated = false; for (int i = 0; i < this.supportingAuthenticators.Count; ++i) { canBeValidated = this.supportingAuthenticators[i].CanValidateToken(samlToken.Assertion.SigningToken); if (canBeValidated) break; } if (!canBeValidated) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.SamlInvalidSigningToken))); issuer = ResolveClaimSet(samlToken.Assertion.SigningToken) ?? ClaimSet.Anonymous; List policies = new List(); for (int i = 0; i < samlToken.Assertion.Statements.Count; ++i) { policies.Add(samlToken.Assertion.Statements[i].CreatePolicy(issuer, this)); } // Check AudienceUri if required // AudienceUriMode != Never - don't need to check can only be one of three // AudienceUriMode == Always // AudienceUriMode == BearerKey and there are no proof keys // if ((this.audienceUriMode == AudienceUriMode.Always) || (this.audienceUriMode == AudienceUriMode.BearerKeyOnly) && (samlToken.SecurityKeys.Count < 1)) { // throws if not found. bool foundAudienceCondition = false; if (this.allowedAudienceUris == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.SAMLAudienceUrisNotFound))); } for (int i = 0; i < samlToken.Assertion.Conditions.Conditions.Count; i++) { SamlAudienceRestrictionCondition audienceCondition = samlToken.Assertion.Conditions.Conditions[i] as SamlAudienceRestrictionCondition; if (audienceCondition == null) continue; foundAudienceCondition = true; if (!ValidateAudienceRestriction(audienceCondition)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.SAMLAudienceUriValidationFailed))); } } if (!foundAudienceCondition) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.SAMLAudienceUriValidationFailed))); } return policies.AsReadOnly(); } protected virtual bool ValidateAudienceRestriction(SamlAudienceRestrictionCondition audienceRestrictionCondition) { for (int i = 0; i < audienceRestrictionCondition.Audiences.Count; i++) { if (audienceRestrictionCondition.Audiences[i] == null) continue; for (int j = 0; j < this.allowedAudienceUris.Count; j++) { if (StringComparer.Ordinal.Compare(audienceRestrictionCondition.Audiences[i].AbsoluteUri, this.allowedAudienceUris[j]) == 0) return true; else if (Uri.IsWellFormedUriString(this.allowedAudienceUris[j], UriKind.Absolute)) { Uri uri = new Uri(this.allowedAudienceUris[j]); if (audienceRestrictionCondition.Audiences[i].Equals(uri)) return true; } } } return false; } public virtual ClaimSet ResolveClaimSet(SecurityToken token) { if (token == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token"); for (int i = 0; i < this.supportingAuthenticators.Count; ++i) { if (this.supportingAuthenticators[i].CanValidateToken(token)) { ReadOnlyCollection authorizationPolicies = this.supportingAuthenticators[i].ValidateToken(token); AuthorizationContext authContext = AuthorizationContext.CreateDefaultAuthorizationContext(authorizationPolicies); if (authContext.ClaimSets.Count > 0) { return authContext.ClaimSets[0]; } } } return null; } public virtual ClaimSet ResolveClaimSet(SecurityKeyIdentifier keyIdentifier) { if (keyIdentifier == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("keyIdentifier"); RsaKeyIdentifierClause rsaKeyIdentifierClause; EncryptedKeyIdentifierClause encryptedKeyIdentifierClause; if (keyIdentifier.TryFind(out rsaKeyIdentifierClause)) { return new DefaultClaimSet(new Claim(ClaimTypes.Rsa, rsaKeyIdentifierClause.Rsa, Rights.PossessProperty)); } else if (keyIdentifier.TryFind(out encryptedKeyIdentifierClause)) { return new DefaultClaimSet(Claim.CreateHashClaim(encryptedKeyIdentifierClause.GetBuffer())); } return null; } public virtual IIdentity ResolveIdentity(SecurityToken token) { if (token == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token"); for (int i = 0; i < this.supportingAuthenticators.Count; ++i) { if (this.supportingAuthenticators[i].CanValidateToken(token)) { ReadOnlyCollection authorizationPolicies = this.supportingAuthenticators[i].ValidateToken(token); if (authorizationPolicies != null && authorizationPolicies.Count != 0) { for (int j = 0; j < authorizationPolicies.Count; ++j) { IAuthorizationPolicy policy = authorizationPolicies[j]; if (policy is UnconditionalPolicy) { return ((UnconditionalPolicy)policy).PrimaryIdentity; } } } } } return null; } public virtual IIdentity ResolveIdentity(SecurityKeyIdentifier keyIdentifier) { if (keyIdentifier == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("keyIdentifier"); RsaKeyIdentifierClause rsaKeyIdentifierClause; if (keyIdentifier.TryFind(out rsaKeyIdentifierClause)) { return SecurityUtils.CreateIdentity(rsaKeyIdentifierClause.Rsa.ToXmlString(false), this.GetType().Name); } return null; } bool IsCurrentlyTimeEffective(SamlSecurityToken token) { if (token.Assertion.Conditions != null) { return SecurityUtils.IsCurrentlyTimeEffective(token.Assertion.Conditions.NotBefore, token.Assertion.Conditions.NotOnOrAfter, this.maxClockSkew); } // If SAML Condition is not present then the assertion is valid at any given time. return true; } } }