From daf3f79d9624614fb13ca7f618c9fe5742392a3e Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Wed, 7 Mar 2007 04:25:35 +0000 Subject: [PATCH] Basic assertion validator. --- .cdtproject | 16 +++-- saml/Makefile.am | 12 ++++ saml/binding/impl/XMLSigningRule.cpp | 2 +- saml/saml.vcproj | 50 +++++++++++++++ saml/saml1/profile/AssertionValidator.cpp | 96 ++++++++++++++++++++++++++++ saml/saml1/profile/AssertionValidator.h | 77 ++++++++++++++++++++++ saml/saml2/profile/AssertionValidator.cpp | 94 +++++++++++++++++++++++++++ saml/saml2/profile/AssertionValidator.h | 77 ++++++++++++++++++++++ saml/signature/SignatureProfileValidator.cpp | 9 ++- saml/signature/SignatureProfileValidator.h | 7 ++ 10 files changed, 430 insertions(+), 10 deletions(-) create mode 100644 saml/saml1/profile/AssertionValidator.cpp create mode 100644 saml/saml1/profile/AssertionValidator.h create mode 100644 saml/saml2/profile/AssertionValidator.cpp create mode 100644 saml/saml2/profile/AssertionValidator.h diff --git a/.cdtproject b/.cdtproject index bef0879..5bc0b69 100644 --- a/.cdtproject +++ b/.cdtproject @@ -1,9 +1,9 @@ - + - + @@ -54,19 +54,21 @@ - + - - - - + + + + + + diff --git a/saml/Makefile.am b/saml/Makefile.am index 8183f96..9a7a5da 100644 --- a/saml/Makefile.am +++ b/saml/Makefile.am @@ -18,12 +18,16 @@ saml1bindincludedir = $(includedir)/saml/saml1/binding saml1coreincludedir = $(includedir)/saml/saml1/core +saml1profincludedir = $(includedir)/saml/saml1/profile + saml2bindincludedir = $(includedir)/saml/saml2/binding saml2coreincludedir = $(includedir)/saml/saml2/core saml2mdincludedir = $(includedir)/saml/saml2/metadata +saml2profincludedir = $(includedir)/saml/saml2/profile + libsamlinclude_HEADERS = \ Assertion.h \ base.h \ @@ -68,6 +72,9 @@ saml1bindinclude_HEADERS = \ saml1/binding/SAMLArtifactType0002.h \ saml1/binding/SAML1SOAPClient.h +saml1profinclude_HEADERS = \ + saml1/profile/AssertionValidator.h + saml2coreinclude_HEADERS = \ saml2/core/Assertions.h \ saml2/core/Protocols.h @@ -88,6 +95,9 @@ saml2mdinclude_HEADERS = \ saml2/metadata/MetadataProvider.h \ saml2/metadata/ObservableMetadataProvider.h +saml2profinclude_HEADERS = \ + saml2/profile/AssertionValidator.h + noinst_HEADERS = \ internal.h @@ -119,6 +129,7 @@ libsaml_la_SOURCES = \ saml1/binding/impl/SAML1SOAPEncoder.cpp \ saml1/binding/impl/SAML1SOAPClient.cpp \ saml1/binding/impl/SAML1MessageRule.cpp \ + saml1/profile/AssertionValidator.cpp \ saml2/core/impl/Assertions20Impl.cpp \ saml2/core/impl/Assertions20SchemaValidators.cpp \ saml2/core/impl/Protocols20Impl.cpp \ @@ -146,6 +157,7 @@ libsaml_la_SOURCES = \ saml2/binding/impl/SAML2SOAPEncoder.cpp \ saml2/binding/impl/SAML2SOAPClient.cpp \ saml2/binding/impl/SAML2MessageRule.cpp \ + saml2/profile/AssertionValidator.cpp \ encryption/EncryptedKeyResolver.cpp \ signature/ContentReference.cpp \ signature/SignatureProfileValidator.cpp \ diff --git a/saml/binding/impl/XMLSigningRule.cpp b/saml/binding/impl/XMLSigningRule.cpp index ab68b22..fab79bc 100644 --- a/saml/binding/impl/XMLSigningRule.cpp +++ b/saml/binding/impl/XMLSigningRule.cpp @@ -90,7 +90,7 @@ void XMLSigningRule::evaluate(const XMLObject& message, const GenericRequest* re log.debug("validating signature profile"); try { SignatureProfileValidator sigval; - sigval.validate(signable->getSignature()); + sigval.validateSignature(*(signable->getSignature())); } catch (ValidationException& ve) { log.error("signature profile failed to validate: %s", ve.what()); diff --git a/saml/saml.vcproj b/saml/saml.vcproj index 107ae93..ec74261 100644 --- a/saml/saml.vcproj +++ b/saml/saml.vcproj @@ -276,6 +276,14 @@ + + + + + + + + + + + + + + + + + + + + + + +#include + +using namespace opensaml::saml1; +using namespace xmltooling; +using namespace log4cpp; +using namespace std; + +void AssertionValidator::validate(const xmltooling::XMLObject* xmlObject) const +{ + const Assertion* a=dynamic_cast(xmlObject); + if (!a) + throw ValidationException("Validator only applies to SAML 1.x Assertion objects."); + validateAssertion(*a); +} + +void AssertionValidator::validateAssertion(const Assertion& assertion) const +{ +#ifdef _DEBUG + xmltooling::NDC ndc("validate"); +#endif + + const Conditions* conds = assertion.getConditions(); + // First verify the time conditions, using the specified timestamp, if non-zero. + if (m_ts>0 && conds) { + unsigned int skew = XMLToolingConfig::getConfig().clock_skew_secs; + time_t t=conds->getNotBeforeEpoch(); + if (m_ts+skew < t) + throw ValidationException("Assertion is not yet valid."); + t=conds->getNotOnOrAfterEpoch(); + if (t <= m_ts-skew) + throw ValidationException("Assertion is no longer valid."); + } + + // Now we process conditions. Only audience restrictions at the moment. + const vector& convec = conds->getConditions(); + for (vector::const_iterator c = convec.begin(); c!=convec.end(); ++c) { + if (!validateCondition(*c)) { + Category::getInstance(SAML_LOGCAT".AssertionValidator").error("unrecognized Condition in assertion (%s)", + (*c)->getSchemaType() ? (*c)->getSchemaType()->toString().c_str() : (*c)->getElementQName().toString().c_str()); + throw ValidationException("Assertion contains an unrecognized condition."); + } + } +} + +bool AssertionValidator::validateCondition(const Condition* condition) const +{ + const AudienceRestrictionCondition* ac=dynamic_cast(condition); + if (!ac) + return false; + + bool found = false; + const vector& auds1 = ac->getAudiences(); + for (vector::const_iterator a = auds1.begin(); !found && a!=auds1.end(); ++a) { + for (vector::const_iterator a2 = m_audiences.begin(); !found && a2!=m_audiences.end(); ++a2) { + found = XMLString::equals((*a)->getAudienceURI(), *a2); + } + } + + if (!found) { + ostringstream os; + os << *ac; + Category::getInstance(SAML_LOGCAT".AssertionValidator").error( + "unacceptable AudienceRestrictionCondition in assertion (%s)", os.str().c_str() + ); + throw ValidationException("Assertion contains an unacceptable AudienceRestrictionCondition."); + } + + return found; +} diff --git a/saml/saml1/profile/AssertionValidator.h b/saml/saml1/profile/AssertionValidator.h new file mode 100644 index 0000000..68c29c4 --- /dev/null +++ b/saml/saml1/profile/AssertionValidator.h @@ -0,0 +1,77 @@ +/* + * Copyright 2001-2007 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file saml/saml1/profile/AssertionValidator.h + * + * SAML 1.x basic assertion validator + */ + +#ifndef __saml1_assval_h__ +#define __saml1_assval_h__ + +#include +#include + +namespace opensaml { + namespace saml1 { + + class SAML_API Assertion; + class SAML_API Condition; + + /** + * SAML 1.x basic assertion validator provides time and audience condition checking. + */ + class SAML_API AssertionValidator : public virtual xmltooling::Validator + { + public: + /** + * Constructor + * + * @param audiences set of audience values representing recipient + * @param ts timestamp to evaluate assertion conditions, or 0 to bypass check + */ + AssertionValidator(const std::vector& audiences, time_t ts=0) : m_ts(ts), m_audiences(audiences) {} + virtual ~AssertionValidator() {} + + void validate(const xmltooling::XMLObject* xmlObject) const; + + /** + * Type-safe validation method. + * + * @param assertion assertion to validate + */ + virtual void validateAssertion(const Assertion& assertion) const; + + /** + * Condition validation. + * + *

Base class version only understands AudienceRestrictionConditions. + * + * @param condition condition to validate + * @return true iff condition was understood + */ + virtual bool validateCondition(const Condition* condition) const; + + private: + time_t m_ts; + const std::vector& m_audiences; + }; + + }; +}; + +#endif /* __saml1_assval_h__ */ diff --git a/saml/saml2/profile/AssertionValidator.cpp b/saml/saml2/profile/AssertionValidator.cpp new file mode 100644 index 0000000..a7c7eee --- /dev/null +++ b/saml/saml2/profile/AssertionValidator.cpp @@ -0,0 +1,94 @@ +/* + * Copyright 2001-2007 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * AssertionValidator.cpp + * + * SAML 2.0 basic assertion validator + */ + +#include "internal.h" +#include "saml2/core/Assertions.h" +#include "saml2/profile/AssertionValidator.h" + +#include +#include + +using namespace opensaml::saml2; +using namespace xmltooling; +using namespace log4cpp; +using namespace std; + +void AssertionValidator::validate(const xmltooling::XMLObject* xmlObject) const +{ + const Assertion* a=dynamic_cast(xmlObject); + if (!a) + throw ValidationException("Validator only applies to SAML 2.0 Assertion objects."); + validateAssertion(*a); +} + +void AssertionValidator::validateAssertion(const Assertion& assertion) const +{ +#ifdef _DEBUG + xmltooling::NDC ndc("validate"); +#endif + + const Conditions* conds = assertion.getConditions(); + // First verify the time conditions, using the specified timestamp, if non-zero. + if (m_ts>0 && conds) { + unsigned int skew = XMLToolingConfig::getConfig().clock_skew_secs; + time_t t=conds->getNotBeforeEpoch(); + if (m_ts+skew < t) + throw ValidationException("Assertion is not yet valid."); + t=conds->getNotOnOrAfterEpoch(); + if (t <= m_ts-skew) + throw ValidationException("Assertion is no longer valid."); + } + + // Now we process conditions. Only audience restrictions at the moment. + const vector& convec = conds->getConditions(); + for (vector::const_iterator c = convec.begin(); c!=convec.end(); ++c) { + if (!validateCondition(*c)) { + Category::getInstance(SAML_LOGCAT".AssertionValidator").error("unrecognized Condition in assertion (%s)", + (*c)->getSchemaType() ? (*c)->getSchemaType()->toString().c_str() : (*c)->getElementQName().toString().c_str()); + throw ValidationException("Assertion contains an unrecognized condition."); + } + } +} + +bool AssertionValidator::validateCondition(const Condition* condition) const +{ + const AudienceRestriction* ac=dynamic_cast(condition); + if (!ac) + return false; + + bool found = false; + const vector& auds1 = ac->getAudiences(); + for (vector::const_iterator a = auds1.begin(); !found && a!=auds1.end(); ++a) { + for (vector::const_iterator a2 = m_audiences.begin(); !found && a2!=m_audiences.end(); ++a2) { + found = XMLString::equals((*a)->getAudienceURI(), *a2); + } + } + + if (!found) { + ostringstream os; + os << *ac; + Category::getInstance(SAML_LOGCAT".AssertionValidator").error("unacceptable AudienceRestriction in assertion (%s)", os.str().c_str()); + throw ValidationException("Assertion contains an unacceptable AudienceRestriction."); + } + + return found; +} diff --git a/saml/saml2/profile/AssertionValidator.h b/saml/saml2/profile/AssertionValidator.h new file mode 100644 index 0000000..0c03284 --- /dev/null +++ b/saml/saml2/profile/AssertionValidator.h @@ -0,0 +1,77 @@ +/* + * Copyright 2001-2007 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file saml/saml2/profile/AssertionValidator.h + * + * SAML 2.0 basic assertion validator + */ + +#ifndef __saml2_assval_h__ +#define __saml2_assval_h__ + +#include +#include + +namespace opensaml { + namespace saml2 { + + class SAML_API Assertion; + class SAML_API Condition; + + /** + * SAML 2.0 basic assertion validator provides time and audience condition checking. + */ + class SAML_API AssertionValidator : public virtual xmltooling::Validator + { + public: + /** + * Constructor + * + * @param audiences set of audience values representing recipient + * @param ts timestamp to evaluate assertion conditions, or 0 to bypass check + */ + AssertionValidator(const std::vector& audiences, time_t ts=0) : m_ts(ts), m_audiences(audiences) {} + virtual ~AssertionValidator() {} + + void validate(const xmltooling::XMLObject* xmlObject) const; + + /** + * Type-safe validation method. + * + * @param assertion assertion to validate + */ + virtual void validateAssertion(const Assertion& assertion) const; + + /** + * Condition validation. + * + *

Base class version only understands AudienceRestrictionConditions. + * + * @param condition condition to validate + * @return true iff condition was understood + */ + virtual bool validateCondition(const Condition* condition) const; + + private: + time_t m_ts; + const std::vector& m_audiences; + }; + + }; +}; + +#endif /* __saml2_assval_h__ */ diff --git a/saml/signature/SignatureProfileValidator.cpp b/saml/signature/SignatureProfileValidator.cpp index c05ee7e..b748f3b 100644 --- a/saml/signature/SignatureProfileValidator.cpp +++ b/saml/signature/SignatureProfileValidator.cpp @@ -41,11 +41,16 @@ void SignatureProfileValidator::validate(const XMLObject* xmlObject) const const Signature* sigObj=dynamic_cast(xmlObject); if (!sigObj) throw ValidationException("Validator only applies to Signature objects."); - DSIGSignature* sig=sigObj->getXMLSignature(); + validateSignature(*sigObj); +} + +void SignatureProfileValidator::validateSignature(const Signature& sigObj) const +{ + DSIGSignature* sig=sigObj.getXMLSignature(); if (!sig) throw ValidationException("Signature does not exist yet."); - const SignableObject* signableObj=dynamic_cast(sigObj->getParent()); + const SignableObject* signableObj=dynamic_cast(sigObj.getParent()); if (!signableObj) throw ValidationException("Signature is not a child of a signable SAML object."); diff --git a/saml/signature/SignatureProfileValidator.h b/saml/signature/SignatureProfileValidator.h index 5e27452..6e1a8f2 100644 --- a/saml/signature/SignatureProfileValidator.h +++ b/saml/signature/SignatureProfileValidator.h @@ -39,6 +39,13 @@ namespace opensaml { virtual ~SignatureProfileValidator() {} void validate(const xmltooling::XMLObject* xmlObject) const; + + /** + * Type-safe validation method. + * + * @param signature Signature to validate + */ + void validateSignature(const xmlsignature::Signature& signature) const; }; }; -- 2.1.4