# include "SessionCache.h"
# include "TransactionLog.h"
# include "attribute/resolver/ResolutionContext.h"
-# include <boost/scoped_ptr.hpp>
# include <boost/iterator/indirect_iterator.hpp>
# include <saml/exceptions.h>
# include <saml/SAMLConfig.h>
# include <xmltooling/XMLToolingConfig.h>
# include <xmltooling/io/HTTPRequest.h>
# include <xmltooling/util/DateTime.h>
+# include <xmltooling/validation/ValidatorSuite.h>
using namespace opensaml::saml2;
using namespace opensaml::saml2p;
using namespace opensaml::saml2md;
{
public:
SAML2Consumer(const DOMElement* e, const char* appId)
- : AssertionConsumerService(e, appId, Category::getInstance(SHIBSP_LOGCAT".SSO.SAML2")) {
+ : AssertionConsumerService(e, appId, Category::getInstance(SHIBSP_LOGCAT ".SSO.SAML2")) {
#ifndef SHIBSP_LITE
if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess))
m_ssoRule.reset(SAMLConfig::getConfig().SecurityPolicyRuleManager.newPlugin(BEARER_POLICY_RULE, e));
// And also track "owned" tokens that we decrypt here.
vector< boost::shared_ptr<saml2::Assertion> > ownedtokens;
+ // With this flag on, we block unauthenticated ciphertext when decrypting,
+ // unless the protocol was authenticated.
+ pair<bool,bool> requireAuthenticatedEncryption = application.getBool("requireAuthenticatedEncryption");
+ if (alreadySecured)
+ requireAuthenticatedEncryption.second = false;
+
// With this flag on, we ignore any unsigned assertions.
const EntityDescriptor* entity = nullptr;
- pair<bool,bool> flag = make_pair(false,false);
+ pair<bool,bool> requireSignedAssertions = make_pair(false,false);
if (alreadySecured && policy.getIssuerMetadata()) {
entity = dynamic_cast<const EntityDescriptor*>(policy.getIssuerMetadata()->getParent());
- flag = application.getRelyingParty(entity)->getBool("requireSignedAssertions");
+ const PropertySet* rp = application.getRelyingParty(entity);
+ requireSignedAssertions = rp->getBool("requireSignedAssertions");
}
// authnskew allows rejection of SSO if AuthnInstant is too old.
a != make_indirect_iterator(assertions.end()); ++a) {
try {
// Skip unsigned assertion?
- if (!a->getSignature() && flag.first && flag.second)
+ if (!a->getSignature() && requireSignedAssertions.first && requireSignedAssertions.second)
throw SecurityPolicyException("The incoming assertion was unsigned, violating local security policy.");
// We clear the security flag, so we can tell whether the token was secured on its own.
// If we hadn't established Issuer yet, redo the signedAssertions check.
if (!entity && policy.getIssuerMetadata()) {
entity = dynamic_cast<const EntityDescriptor*>(policy.getIssuerMetadata()->getParent());
- flag = application.getRelyingParty(entity)->getBool("requireSignedAssertions");
- if (!a->getSignature() && flag.first && flag.second)
+ requireSignedAssertions = application.getRelyingParty(entity)->getBool("requireSignedAssertions");
+ if (!a->getSignature() && requireSignedAssertions.first && requireSignedAssertions.second)
throw SecurityPolicyException("The incoming assertion was unsigned, violating local security policy.");
}
}
// In case we need decryption...
- CredentialResolver* cr=application.getCredentialResolver();
+ CredentialResolver* cr = application.getCredentialResolver();
if (!cr && !encassertions.empty())
m_log.warn("found encrypted assertions, but no CredentialResolver was available");
scoped_ptr<MetadataCredentialCriteria> mcc(
policy.getIssuerMetadata() ? new MetadataCredentialCriteria(*policy.getIssuerMetadata()) : nullptr
);
- boost::shared_ptr<XMLObject> wrapper(ea->decrypt(*cr, application.getRelyingParty(entity)->getXMLString("entityID").second, mcc.get()));
+ boost::shared_ptr<XMLObject> wrapper(
+ ea->decrypt(
+ *cr,
+ application.getRelyingParty(entity)->getXMLString("entityID").second,
+ mcc.get(),
+ requireAuthenticatedEncryption.first && requireAuthenticatedEncryption.second
+ )
+ );
decrypted = dynamic_pointer_cast<saml2::Assertion>(wrapper);
if (decrypted) {
ownedtokens.push_back(decrypted);
}
}
catch (std::exception& ex) {
- m_log.error(ex.what());
+ m_log.error("failed to decrypt assertion: %s", ex.what());
}
if (!decrypted)
continue;
try {
+ // Skip unsigned assertion?
+ if (!decrypted->getSignature() && requireSignedAssertions.first && requireSignedAssertions.second)
+ throw SecurityPolicyException("The incoming assertion was unsigned, violating local security policy.");
+
+ // Run the schema validators against the assertion, since it was hidden by encryption.
+ SchemaValidators.validate(decrypted.get());
+
// We clear the security flag, so we can tell whether the token was secured on its own.
policy.setAuthenticated(false);
policy.reset(true);
// If we hadn't established Issuer yet, redo the signedAssertions check.
if (!entity && policy.getIssuerMetadata()) {
entity = dynamic_cast<const EntityDescriptor*>(policy.getIssuerMetadata()->getParent());
- flag = application.getRelyingParty(entity)->getBool("requireSignedAssertions");
- if (!decrypted->getSignature() && flag.first && flag.second)
+ requireSignedAssertions = application.getRelyingParty(entity)->getBool("requireSignedAssertions");
+ if (!decrypted->getSignature() && requireSignedAssertions.first && requireSignedAssertions.second)
throw SecurityPolicyException("The decrypted assertion was unsigned, violating local security policy.");
}
}
}
catch (std::exception& ex) {
- m_log.error(ex.what());
+ m_log.error("failed to decrypt NameID: %s", ex.what());
}
}
}
scoped_ptr<ResolutionContext> ctx(
resolveAttributes(
application,
+ &httpRequest,
policy.getIssuerMetadata(),
samlconstants::SAML20P_NS,
+ response,
nullptr,
nullptr,
ssoName,