From: Scott Cantor Date: Fri, 2 Mar 2012 18:18:07 +0000 (+0000) Subject: https://issues.shibboleth.net/jira/browse/SSPCPP-398 X-Git-Tag: 2.5.0~166 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=shibboleth%2Fcpp-sp.git;a=commitdiff_plain;h=e3b022349431a31bf9b86efdea2e859a250c1416 https://issues.shibboleth.net/jira/browse/SSPCPP-398 --- diff --git a/schemas/shibboleth-2.0-native-sp-config.xsd b/schemas/shibboleth-2.0-native-sp-config.xsd index 31a2441..c7ec932 100644 --- a/schemas/shibboleth-2.0-native-sp-config.xsd +++ b/schemas/shibboleth-2.0-native-sp-config.xsd @@ -402,7 +402,6 @@ - @@ -429,7 +428,6 @@ - @@ -437,10 +435,12 @@ + + diff --git a/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp b/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp index 56cedf4..dc5570e 100644 --- a/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp +++ b/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp @@ -533,16 +533,26 @@ void QueryResolver::SAML2Query(QueryContext& ctx) const m_log.warn("simple resolver only supports one assertion in the query response"); } - CredentialResolver* cr=application.getCredentialResolver(); + CredentialResolver* cr = application.getCredentialResolver(); if (!cr) { m_log.warn("found encrypted assertion, but no CredentialResolver was available"); throw FatalProfileException("Assertion was encrypted, but no decryption credentials are available."); } + // With this flag on, we block unauthenticated ciphertext when decrypting, + // unless the protocol was authenticated. + pair authenticatedCipher = application.getBool("requireAuthenticatedCipher"); + if (policy->isAuthenticated()) + authenticatedCipher.second = false; + // Attempt to decrypt it. try { Locker credlocker(cr); - auto_ptr tokenwrapper(encassertions.front()->decrypt(*cr, relyingParty->getXMLString("entityID").second, &mcc)); + auto_ptr tokenwrapper( + encassertions.front()->decrypt( + *cr, relyingParty->getXMLString("entityID").second, &mcc, authenticatedCipher.first && authenticatedCipher.second + ) + ); newtoken = dynamic_cast(tokenwrapper.get()); if (newtoken) { tokenwrapper.release(); diff --git a/shibsp/attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp b/shibsp/attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp index b4aef5b..7e2d1d0 100644 --- a/shibsp/attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp +++ b/shibsp/attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp @@ -418,10 +418,20 @@ void SimpleAggregationResolver::doQuery(SimpleAggregationContext& ctx, const cha throw FatalProfileException("Assertion was encrypted, but no decryption credentials are available."); } + // With this flag on, we block unauthenticated ciphertext when decrypting, + // unless the protocol was authenticated. + pair authenticatedCipher = application.getBool("requireAuthenticatedCipher"); + if (policy->isAuthenticated()) + authenticatedCipher.second = false; + // Attempt to decrypt it. try { Locker credlocker(cr); - auto_ptr tokenwrapper(encassertions.front()->decrypt(*cr, relyingParty->getXMLString("entityID").second, &mcc)); + auto_ptr tokenwrapper( + encassertions.front()->decrypt( + *cr, relyingParty->getXMLString("entityID").second, &mcc, authenticatedCipher.first && authenticatedCipher.second + ) + ); newtoken = dynamic_cast(tokenwrapper.get()); if (newtoken) { tokenwrapper.release(); diff --git a/shibsp/handler/impl/SAML2Consumer.cpp b/shibsp/handler/impl/SAML2Consumer.cpp index 5dec9e4..be11ccd 100644 --- a/shibsp/handler/impl/SAML2Consumer.cpp +++ b/shibsp/handler/impl/SAML2Consumer.cpp @@ -168,12 +168,19 @@ void SAML2Consumer::implementProtocol( // And also track "owned" tokens that we decrypt here. vector< boost::shared_ptr > ownedtokens; + // With this flag on, we block unauthenticated ciphertext when decrypting, + // unless the protocol was authenticated. + pair requireAuthenticatedCipher = application.getBool("requireAuthenticatedCipher"); + if (alreadySecured) + requireAuthenticatedCipher.second = false; + // With this flag on, we ignore any unsigned assertions. const EntityDescriptor* entity = nullptr; - pair flag = make_pair(false,false); + pair requireSignedAssertions = make_pair(false,false); if (alreadySecured && policy.getIssuerMetadata()) { entity = dynamic_cast(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. @@ -195,7 +202,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. @@ -217,8 +224,8 @@ void SAML2Consumer::implementProtocol( // If we hadn't established Issuer yet, redo the signedAssertions check. if (!entity && policy.getIssuerMetadata()) { entity = dynamic_cast(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."); } @@ -266,7 +273,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"); @@ -279,7 +286,14 @@ void SAML2Consumer::implementProtocol( scoped_ptr mcc( policy.getIssuerMetadata() ? new MetadataCredentialCriteria(*policy.getIssuerMetadata()) : nullptr ); - boost::shared_ptr wrapper(ea->decrypt(*cr, application.getRelyingParty(entity)->getXMLString("entityID").second, mcc.get())); + boost::shared_ptr wrapper( + ea->decrypt( + *cr, + application.getRelyingParty(entity)->getXMLString("entityID").second, + mcc.get(), + requireAuthenticatedCipher.first && requireAuthenticatedCipher.second + ) + ); decrypted = dynamic_pointer_cast(wrapper); if (decrypted) { ownedtokens.push_back(decrypted); @@ -294,6 +308,10 @@ void SAML2Consumer::implementProtocol( continue; try { + // Skip unsigned assertion? + if (!decrypted->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. policy.setAuthenticated(false); policy.reset(true); @@ -316,8 +334,8 @@ void SAML2Consumer::implementProtocol( // If we hadn't established Issuer yet, redo the signedAssertions check. if (!entity && policy.getIssuerMetadata()) { entity = dynamic_cast(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."); }