SSPCPP-616 - clean up concatenated string literals
[shibboleth/cpp-sp.git] / shibsp / handler / impl / SAML2Consumer.cpp
index 98f9522..d7d8fa9 100644 (file)
@@ -33,7 +33,6 @@
 # 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>
@@ -45,6 +44,7 @@
 # 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;
@@ -72,7 +72,7 @@ namespace shibsp {
     {
     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));
@@ -169,12 +169,19 @@ void SAML2Consumer::implementProtocol(
     // 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.
@@ -196,7 +203,7 @@ void SAML2Consumer::implementProtocol(
             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.
@@ -218,8 +225,8 @@ void SAML2Consumer::implementProtocol(
             // 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.");
             }
 
@@ -267,7 +274,7 @@ void SAML2Consumer::implementProtocol(
     }
 
     // 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");
 
@@ -280,7 +287,14 @@ void SAML2Consumer::implementProtocol(
             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);
@@ -289,12 +303,19 @@ void SAML2Consumer::implementProtocol(
             }
         }
         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);
@@ -317,8 +338,8 @@ void SAML2Consumer::implementProtocol(
             // 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.");
             }
 
@@ -384,7 +405,7 @@ void SAML2Consumer::implementProtocol(
                     }
                 }
                 catch (std::exception& ex) {
-                    m_log.error(ex.what());
+                    m_log.error("failed to decrypt NameID: %s", ex.what());
                 }
             }
         }
@@ -414,8 +435,10 @@ void SAML2Consumer::implementProtocol(
     scoped_ptr<ResolutionContext> ctx(
         resolveAttributes(
             application,
+            &httpRequest,
             policy.getIssuerMetadata(),
             samlconstants::SAML20P_NS,
+            response,
             nullptr,
             nullptr,
             ssoName,