#pragma warning( disable : 4250 )\r
#endif\r
\r
+ class SHIBSP_DLLLOCAL TokenValidator : public Validator\r
+ {\r
+ public:\r
+ TokenValidator(const Application& app, time_t ts=0, const RoleDescriptor* role=NULL) : m_app(app), m_ts(ts), m_role(role) {}\r
+ void validate(const XMLObject*) const;\r
+\r
+ private:\r
+ const Application& m_app;\r
+ time_t m_ts;\r
+ const RoleDescriptor* m_role;\r
+ };\r
+\r
static vector<const Handler*> g_noHandlers;\r
\r
// Application configuration wrapper\r
const char* getHash() const {return m_hash.c_str();}\r
MetadataProvider* getMetadataProvider() const;\r
TrustEngine* getTrustEngine() const;\r
- const vector<const XMLCh*>& getAudiences() const;\r
const PropertySet* getCredentialUse(const EntityDescriptor* provider) const;\r
\r
const Handler* getDefaultSessionInitiator() const;\r
const Handler* getAssertionConsumerServiceByIndex(unsigned short index) const;\r
const vector<const Handler*>& getAssertionConsumerServicesByBinding(const XMLCh* binding) const;\r
const Handler* getHandler(const char* path) const;\r
- \r
+\r
+ const vector<const XMLCh*>& getAudiences() const;\r
+ Validator* getTokenValidator(time_t ts=0, const opensaml::saml2md::RoleDescriptor* role=NULL) const {\r
+ return new TokenValidator(*this, ts, role);\r
+ }\r
+\r
+ void validator(const XMLObject* xmlObject) const;\r
+\r
// Provides filter to exclude special config elements.\r
short acceptNode(const DOMNode* node) const;\r
\r
}\r
};\r
\r
+void TokenValidator::validate(const XMLObject* xmlObject) const\r
+{\r
+#ifdef _DEBUG\r
+ xmltooling::NDC ndc("validate");\r
+#endif\r
+ Category& log=Category::getInstance(SHIBSP_LOGCAT".Application");\r
+\r
+ const opensaml::RootObject* root = NULL;\r
+ const opensaml::saml2::Assertion* token2 = dynamic_cast<const opensaml::saml2::Assertion*>(xmlObject);\r
+ if (token2) {\r
+ const opensaml::saml2::Conditions* conds = token2->getConditions();\r
+ // First verify the time conditions, using the specified timestamp, if non-zero.\r
+ if (m_ts>0 && conds) {\r
+ unsigned int skew = XMLToolingConfig::getConfig().clock_skew_secs;\r
+ time_t t=conds->getNotBeforeEpoch();\r
+ if (m_ts+skew < t)\r
+ throw ValidationException("Assertion is not yet valid.");\r
+ t=conds->getNotOnOrAfterEpoch();\r
+ if (t <= m_ts-skew)\r
+ throw ValidationException("Assertion is no longer valid.");\r
+ }\r
+\r
+ // Now we process conditions. Only audience restrictions at the moment.\r
+ const vector<opensaml::saml2::Condition*>& convec = conds->getConditions();\r
+ for (vector<opensaml::saml2::Condition*>::const_iterator c = convec.begin(); c!=convec.end(); ++c) {\r
+ const opensaml::saml2::AudienceRestriction* ac=dynamic_cast<const opensaml::saml2::AudienceRestriction*>(*c);\r
+ if (!ac) {\r
+ log.error("unrecognized Condition in assertion (%s)",\r
+ (*c)->getSchemaType() ? (*c)->getSchemaType()->toString().c_str() : (*c)->getElementQName().toString().c_str());\r
+ throw ValidationException("Assertion contains an unrecognized condition.");\r
+ }\r
+\r
+ bool found = false;\r
+ const vector<opensaml::saml2::Audience*>& auds1 = ac->getAudiences();\r
+ const vector<const XMLCh*>& auds2 = m_app.getAudiences();\r
+ for (vector<opensaml::saml2::Audience*>::const_iterator a = auds1.begin(); !found && a!=auds1.end(); ++a) {\r
+ for (vector<const XMLCh*>::const_iterator a2 = auds2.begin(); !found && a2!=auds2.end(); ++a2) {\r
+ found = XMLString::equals((*a)->getAudienceURI(), *a2);\r
+ }\r
+ }\r
+\r
+ if (!found) {\r
+ ostringstream os;\r
+ os << *ac;\r
+ log.error("unacceptable AudienceRestriction in assertion (%s)", os.str().c_str());\r
+ throw ValidationException("Assertion contains an unacceptable AudienceRestriction.");\r
+ }\r
+ }\r
+\r
+ root = token2;\r
+ }\r
+ else {\r
+ const opensaml::saml1::Assertion* token1 = dynamic_cast<const opensaml::saml1::Assertion*>(xmlObject);\r
+ if (token1) {\r
+ const opensaml::saml1::Conditions* conds = token1->getConditions();\r
+ // First verify the time conditions, using the specified timestamp, if non-zero.\r
+ if (m_ts>0 && conds) {\r
+ unsigned int skew = XMLToolingConfig::getConfig().clock_skew_secs;\r
+ time_t t=conds->getNotBeforeEpoch();\r
+ if (m_ts+skew < t)\r
+ throw ValidationException("Assertion is not yet valid.");\r
+ t=conds->getNotOnOrAfterEpoch();\r
+ if (t <= m_ts-skew)\r
+ throw ValidationException("Assertion is no longer valid.");\r
+ }\r
+\r
+ // Now we process conditions. Only audience restrictions at the moment.\r
+ const vector<opensaml::saml1::Condition*>& convec = conds->getConditions();\r
+ for (vector<opensaml::saml1::Condition*>::const_iterator c = convec.begin(); c!=convec.end(); ++c) {\r
+ const opensaml::saml1::AudienceRestrictionCondition* ac=dynamic_cast<const opensaml::saml1::AudienceRestrictionCondition*>(*c);\r
+ if (!ac) {\r
+ log.error("unrecognized Condition in assertion (%s)",\r
+ (*c)->getSchemaType() ? (*c)->getSchemaType()->toString().c_str() : (*c)->getElementQName().toString().c_str());\r
+ throw ValidationException("Assertion contains an unrecognized condition.");\r
+ }\r
+\r
+ bool found = false;\r
+ const vector<opensaml::saml1::Audience*>& auds1 = ac->getAudiences();\r
+ const vector<const XMLCh*>& auds2 = m_app.getAudiences();\r
+ for (vector<opensaml::saml1::Audience*>::const_iterator a = auds1.begin(); !found && a!=auds1.end(); ++a) {\r
+ for (vector<const XMLCh*>::const_iterator a2 = auds2.begin(); !found && a2!=auds2.end(); ++a2) {\r
+ found = XMLString::equals((*a)->getAudienceURI(), *a2);\r
+ }\r
+ }\r
+\r
+ if (!found) {\r
+ ostringstream os;\r
+ os << *ac;\r
+ log.error("unacceptable AudienceRestrictionCondition in assertion (%s)", os.str().c_str());\r
+ throw ValidationException("Assertion contains an unacceptable AudienceRestrictionCondition.");\r
+ }\r
+ }\r
+\r
+ root = token1;\r
+ }\r
+ else {\r
+ throw ValidationException("Unknown object type passed to token validator.");\r
+ }\r
+ }\r
+\r
+ if (!m_role || !m_app.getTrustEngine()) {\r
+ log.warn("no issuer role or TrustEngine provided, so no signature validation performed");\r
+ return;\r
+ }\r
+\r
+ const PropertySet* policy=m_app.getServiceProvider().getPolicySettings(m_app.getString("policyId").second);\r
+ pair<bool,bool> signedAssertions=policy ? policy->getBool("signedAssertions") : make_pair(false,false);\r
+\r
+ if (root->getSignature()) {\r
+ if (!m_app.getTrustEngine()->validate(*(root->getSignature()),*m_role))\r
+ throw ValidationException("Assertion signature did not validate.");\r
+ }\r
+ else if (signedAssertions.first && signedAssertions.second)\r
+ throw ValidationException("Assertion was unsigned, violating policy.");\r
+}\r
+\r
XMLApplication::XMLApplication(\r
const ServiceProvider* sp,\r
const DOMElement* e,\r
// Always include our own providerId as an audience.\r
m_audiences.push_back(getXMLString("providerId").second);\r
\r
- if (conf.isEnabled(SPConfig::AttributeResolver)) {\r
+ if (conf.isEnabled(SPConfig::AttributeResolution)) {\r
// TODO\r
}\r
\r