X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=shibsp%2Fattribute%2Fresolver%2Fimpl%2FQueryAttributeResolver.cpp;h=c75b2e5394096bbc296c86ebe08191c07868c4c0;hb=d59a95f8f078ebd56693036acc92be89d3138536;hp=738d73ba510e519be23d6318842d28194573ebac;hpb=d2b54916e13bac5fbb0c3e8d48d90622430685cc;p=shibboleth%2Fsp.git diff --git a/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp b/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp index 738d73b..c75b2e5 100644 --- a/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp +++ b/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp @@ -33,7 +33,6 @@ #include "binding/SOAPClient.h" #include "util/SPConstants.h" -#include #include #include #include @@ -57,7 +56,6 @@ using namespace opensaml::saml2p; using namespace opensaml::saml2md; using namespace opensaml; using namespace xmltooling; -using namespace log4cpp; using namespace std; namespace shibsp { @@ -80,7 +78,7 @@ namespace shibsp { const XMLCh* authncontext_class=NULL, const XMLCh* authncontext_decl=NULL, const vector* tokens=NULL, - const multimap* attributes=NULL + const vector* attributes=NULL ) : m_query(true), m_app(application), m_session(NULL), m_metadata(NULL), m_entity(issuer), m_protocol(protocol), m_nameid(nameid), m_class(authncontext_class), m_decl(authncontext_decl) { @@ -108,7 +106,7 @@ namespace shibsp { } if (m_metadata) m_metadata->unlock(); - for_each(m_attributes.begin(), m_attributes.end(), cleanup_pair()); + for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup()); for_each(m_assertions.begin(), m_assertions.end(), xmltooling::cleanup()); } @@ -123,10 +121,10 @@ namespace shibsp { if (m_entity) return m_entity; if (m_session && m_session->getEntityID()) { - m_metadata = m_app.getMetadataProvider(); + m_metadata = m_app.getMetadataProvider(false); if (m_metadata) { m_metadata->lock(); - return m_entity = m_metadata->getEntityDescriptor(m_session->getEntityID()); + return m_entity = m_metadata->getEntityDescriptor(MetadataProvider::Criteria(m_session->getEntityID())).first; } } return NULL; @@ -146,7 +144,7 @@ namespace shibsp { const Session* getSession() const { return m_session; } - multimap& getResolvedAttributes() { + vector& getResolvedAttributes() { return m_attributes; } vector& getResolvedAssertions() { @@ -163,7 +161,7 @@ namespace shibsp { const NameID* m_nameid; const XMLCh* m_class; const XMLCh* m_decl; - multimap m_attributes; + vector m_attributes; vector m_assertions; }; @@ -187,7 +185,7 @@ namespace shibsp { const XMLCh* authncontext_class=NULL, const XMLCh* authncontext_decl=NULL, const vector* tokens=NULL, - const multimap* attributes=NULL + const vector* attributes=NULL ) const { return new QueryContext(application,issuer,protocol,nameid,authncontext_class,authncontext_decl,tokens,attributes); } @@ -218,11 +216,6 @@ namespace shibsp { }; -void SHIBSP_API shibsp::registerAttributeResolvers() -{ - SPConfig::getConfig().AttributeResolverManager.registerFactory(QUERY_ATTRIBUTE_RESOLVER, QueryResolverFactory); -} - QueryResolver::QueryResolver(const DOMElement* e) : m_log(Category::getInstance(SHIBSP_LOGCAT".AttributeResolver")) { #ifdef _DEBUG @@ -262,23 +255,19 @@ bool QueryResolver::SAML1Query(QueryContext& ctx) const xmltooling::NDC ndc("query"); #endif - int version = 1; - const AttributeAuthorityDescriptor* AA = ctx.getEntityDescriptor()->getAttributeAuthorityDescriptor(samlconstants::SAML11_PROTOCOL_ENUM); + int version = XMLString::equals(ctx.getProtocol(), samlconstants::SAML11_PROTOCOL_ENUM) ? 1 : 0; + const AttributeAuthorityDescriptor* AA = + find_if(ctx.getEntityDescriptor()->getAttributeAuthorityDescriptors(), isValidForProtocol(ctx.getProtocol())); if (!AA) { - AA = ctx.getEntityDescriptor()->getAttributeAuthorityDescriptor(samlconstants::SAML10_PROTOCOL_ENUM); - version = 0; - } - if (!AA) { - m_log.debug("no SAML 1.x AttributeAuthority role found in metadata"); + m_log.warn("no SAML 1.%d AttributeAuthority role found in metadata", version); return false; } - shibsp::SecurityPolicy policy(ctx.getApplication()); + const Application& application = ctx.getApplication(); + const PropertySet* relyingParty = application.getRelyingParty(ctx.getEntityDescriptor()); + shibsp::SecurityPolicy policy(application); MetadataCredentialCriteria mcc(*AA); shibsp::SOAPClient soaper(policy); - const PropertySet* policySettings = - ctx.getApplication().getServiceProvider().getPolicySettings(ctx.getApplication().getString("policyId").second); - pair signedAssertions = policySettings->getBool("signedAssertions"); auto_ptr_XMLCh binding(samlconstants::SAML1_BINDING_SOAP); saml1p::Response* response=NULL; @@ -288,7 +277,6 @@ bool QueryResolver::SAML1Query(QueryContext& ctx) const if (!XMLString::equals((*ep)->getBinding(),binding.get())) continue; auto_ptr_char loc((*ep)->getLocation()); - auto_ptr_XMLCh issuer(ctx.getApplication().getString("entityID").second); NameIdentifier* nameid = NameIdentifierBuilder::buildNameIdentifier(); nameid->setName(ctx.getNameID()->getName()); nameid->setFormat(ctx.getNameID()->getFormat()); @@ -297,7 +285,7 @@ bool QueryResolver::SAML1Query(QueryContext& ctx) const subject->setNameIdentifier(nameid); saml1p::AttributeQuery* query = saml1p::AttributeQueryBuilder::buildAttributeQuery(); query->setSubject(subject); - query->setResource(issuer.get()); + query->setResource(relyingParty->getXMLString("entityID").second); for (vector::const_iterator ad = m_SAML1Designators.begin(); ad!=m_SAML1Designators.end(); ++ad) query->getAttributeDesignators().push_back((*ad)->cloneAttributeDesignator()); Request* request = RequestBuilder::buildRequest(); @@ -305,7 +293,7 @@ bool QueryResolver::SAML1Query(QueryContext& ctx) const request->setMinorVersion(version); SAML1SOAPClient client(soaper, false); - client.sendSAML(request, mcc, loc.get()); + client.sendSAML(request, application.getId(), mcc, loc.get()); response = client.receiveSAML(); } catch (exception& ex) { @@ -318,30 +306,46 @@ bool QueryResolver::SAML1Query(QueryContext& ctx) const m_log.error("unable to obtain a SAML response from attribute authority"); return false; } - else if (!response->getStatus() || !response->getStatus()->getStatusCode() || response->getStatus()->getStatusCode()->getValue()==NULL || - *(response->getStatus()->getStatusCode()->getValue()) != saml1p::StatusCode::SUCCESS) { - delete response; - m_log.error("attribute authority returned a SAML error"); - return true; - } + else if (!response->getStatus() || !response->getStatus()->getStatusCode() || response->getStatus()->getStatusCode()->getValue()==NULL || + *(response->getStatus()->getStatusCode()->getValue()) != saml1p::StatusCode::SUCCESS) { + delete response; + m_log.error("attribute authority returned a SAML error"); + return true; + } const vector& assertions = const_cast(response)->getAssertions(); - if (assertions.size()>1) + if (assertions.empty()) { + delete response; + m_log.warn("response from attribute authority was empty"); + return true; + } + else if (assertions.size()>1) m_log.warn("simple resolver only supports one assertion in the query response"); auto_ptr wrapper(response); saml1::Assertion* newtoken = assertions.front(); + pair signedAssertions = relyingParty->getBool("requireSignedAssertions"); if (!newtoken->getSignature() && signedAssertions.first && signedAssertions.second) { m_log.error("assertion unsigned, rejecting it based on signedAssertions policy"); return true; } try { + // We're going to insist that the assertion issuer is the same as the peer. + // Reset the policy's message bits and extract them from the assertion. + policy.reset(true); + policy.setMessageID(newtoken->getAssertionID()); + policy.setIssueInstant(newtoken->getIssueInstantEpoch()); + policy.setIssuer(newtoken->getIssuer()); policy.evaluate(*newtoken); - if (!policy.isSecure()) + + // Now we can check the security status of the policy. + if (!policy.isAuthenticated()) throw SecurityPolicyException("Security of SAML 1.x query result not established."); - saml1::AssertionValidator tokval(ctx.getApplication().getAudiences(), time(NULL)); + + // Lastly, check it over. + saml1::AssertionValidator tokval(relyingParty->getXMLString("entityID").second, application.getAudiences(), time(NULL)); tokval.validateAssertion(*newtoken); } catch (exception& ex) { @@ -355,22 +359,22 @@ bool QueryResolver::SAML1Query(QueryContext& ctx) const // Finally, extract and filter the result. try { - AttributeExtractor* extractor = ctx.getApplication().getAttributeExtractor(); + AttributeExtractor* extractor = application.getAttributeExtractor(); if (extractor) { Locker extlocker(extractor); - extractor->extractAttributes(ctx.getApplication(), AA, *newtoken, ctx.getResolvedAttributes()); + extractor->extractAttributes(application, AA, *newtoken, ctx.getResolvedAttributes()); } - AttributeFilter* filter = ctx.getApplication().getAttributeFilter(); + AttributeFilter* filter = application.getAttributeFilter(); if (filter) { - BasicFilteringContext fc(ctx.getApplication(), ctx.getResolvedAttributes(), AA, ctx.getClassRef(), ctx.getDeclRef()); + BasicFilteringContext fc(application, ctx.getResolvedAttributes(), AA, ctx.getClassRef(), ctx.getDeclRef()); Locker filtlocker(filter); filter->filterAttributes(fc, ctx.getResolvedAttributes()); } } catch (exception& ex) { m_log.error("caught exception extracting/filtering attributes from query result: %s", ex.what()); - for_each(ctx.getResolvedAttributes().begin(), ctx.getResolvedAttributes().end(), cleanup_pair()); + for_each(ctx.getResolvedAttributes().begin(), ctx.getResolvedAttributes().end(), xmltooling::cleanup()); ctx.getResolvedAttributes().clear(); } @@ -383,18 +387,21 @@ bool QueryResolver::SAML2Query(QueryContext& ctx) const xmltooling::NDC ndc("query"); #endif - const AttributeAuthorityDescriptor* AA = ctx.getEntityDescriptor()->getAttributeAuthorityDescriptor(samlconstants::SAML20P_NS); + const AttributeAuthorityDescriptor* AA = + find_if(ctx.getEntityDescriptor()->getAttributeAuthorityDescriptors(), isValidForProtocol(samlconstants::SAML20P_NS)); if (!AA) { - m_log.debug("no SAML 2 AttributeAuthority role found in metadata"); + m_log.warn("no SAML 2 AttributeAuthority role found in metadata"); return false; } - shibsp::SecurityPolicy policy(ctx.getApplication()); + const Application& application = ctx.getApplication(); + shibsp::SecurityPolicy policy(application); MetadataCredentialCriteria mcc(*AA); shibsp::SOAPClient soaper(policy); - const PropertySet* policySettings = - ctx.getApplication().getServiceProvider().getPolicySettings(ctx.getApplication().getString("policyId").second); - pair signedAssertions = policySettings->getBool("signedAssertions"); + + const PropertySet* relyingParty = application.getRelyingParty(ctx.getEntityDescriptor()); + pair signedAssertions = relyingParty->getBool("requireSignedAssertions"); + pair encryption = relyingParty->getString("encryption"); auto_ptr_XMLCh binding(samlconstants::SAML20_BINDING_SOAP); saml2p::StatusResponseType* srt=NULL; @@ -404,19 +411,35 @@ bool QueryResolver::SAML2Query(QueryContext& ctx) const if (!XMLString::equals((*ep)->getBinding(),binding.get())) continue; auto_ptr_char loc((*ep)->getLocation()); - auto_ptr_XMLCh issuer(ctx.getApplication().getString("entityID").second); - saml2::Subject* subject = saml2::SubjectBuilder::buildSubject(); - subject->setNameID(ctx.getNameID()->cloneNameID()); + auto_ptr subject(saml2::SubjectBuilder::buildSubject()); + + // Encrypt the NameID? + if (encryption.first && (!strcmp(encryption.second, "true") || !strcmp(encryption.second, "back"))) { + auto_ptr encrypted(EncryptedIDBuilder::buildEncryptedID()); + MetadataCredentialCriteria mcc(*AA); + encrypted->encrypt( + *ctx.getNameID(), + *(application.getMetadataProvider()), + mcc, + false, + relyingParty->getXMLString("encryptionAlg").second + ); + subject->setEncryptedID(encrypted.release()); + } + else { + subject->setNameID(ctx.getNameID()->cloneNameID()); + } + saml2p::AttributeQuery* query = saml2p::AttributeQueryBuilder::buildAttributeQuery(); - query->setSubject(subject); + query->setSubject(subject.release()); Issuer* iss = IssuerBuilder::buildIssuer(); - iss->setName(issuer.get()); + iss->setName(relyingParty->getXMLString("entityID").second); query->setIssuer(iss); for (vector::const_iterator ad = m_SAML2Designators.begin(); ad!=m_SAML2Designators.end(); ++ad) query->getAttributes().push_back((*ad)->cloneAttribute()); SAML2SOAPClient client(soaper, false); - client.sendSAML(query, mcc, loc.get()); + client.sendSAML(query, application.getId(), mcc, loc.get()); srt = client.receiveSAML(); } catch (exception& ex) { @@ -443,10 +466,15 @@ bool QueryResolver::SAML2Query(QueryContext& ctx) const } const vector& assertions = const_cast(response)->getAssertions(); - if (assertions.size()>1) + if (assertions.empty()) { + delete srt; + m_log.warn("response from attribute authority was empty"); + return true; + } + else if (assertions.size()>1) m_log.warn("simple resolver only supports one assertion in the query response"); - auto_ptr wrapper(response); + auto_ptr wrapper(srt); saml2::Assertion* newtoken = assertions.front(); if (!newtoken->getSignature() && signedAssertions.first && signedAssertions.second) { @@ -455,10 +483,20 @@ bool QueryResolver::SAML2Query(QueryContext& ctx) const } try { + // We're going to insist that the assertion issuer is the same as the peer. + // Reset the policy's message bits and extract them from the assertion. + policy.reset(true); + policy.setMessageID(newtoken->getID()); + policy.setIssueInstant(newtoken->getIssueInstantEpoch()); + policy.setIssuer(newtoken->getIssuer()); policy.evaluate(*newtoken); - if (!policy.isSecure()) + + // Now we can check the security status of the policy. + if (!policy.isAuthenticated()) throw SecurityPolicyException("Security of SAML 2.0 query result not established."); - saml2::AssertionValidator tokval(ctx.getApplication().getAudiences(), time(NULL)); + + // Lastly, check it over. + saml2::AssertionValidator tokval(relyingParty->getXMLString("entityID").second, application.getAudiences(), time(NULL)); tokval.validateAssertion(*newtoken); } catch (exception& ex) { @@ -472,22 +510,22 @@ bool QueryResolver::SAML2Query(QueryContext& ctx) const // Finally, extract and filter the result. try { - AttributeExtractor* extractor = ctx.getApplication().getAttributeExtractor(); + AttributeExtractor* extractor = application.getAttributeExtractor(); if (extractor) { Locker extlocker(extractor); - extractor->extractAttributes(ctx.getApplication(), AA, *newtoken, ctx.getResolvedAttributes()); + extractor->extractAttributes(application, AA, *newtoken, ctx.getResolvedAttributes()); } - AttributeFilter* filter = ctx.getApplication().getAttributeFilter(); + AttributeFilter* filter = application.getAttributeFilter(); if (filter) { - BasicFilteringContext fc(ctx.getApplication(), ctx.getResolvedAttributes(), AA, ctx.getClassRef(), ctx.getDeclRef()); + BasicFilteringContext fc(application, ctx.getResolvedAttributes(), AA, ctx.getClassRef(), ctx.getDeclRef()); Locker filtlocker(filter); filter->filterAttributes(fc, ctx.getResolvedAttributes()); } } catch (exception& ex) { m_log.error("caught exception extracting/filtering attributes from query result: %s", ex.what()); - for_each(ctx.getResolvedAttributes().begin(), ctx.getResolvedAttributes().end(), cleanup_pair()); + for_each(ctx.getResolvedAttributes().begin(), ctx.getResolvedAttributes().end(), xmltooling::cleanup()); ctx.getResolvedAttributes().clear(); } @@ -507,12 +545,18 @@ void QueryResolver::resolveAttributes(ResolutionContext& ctx) const } if (qctx.getNameID() && qctx.getEntityDescriptor()) { - m_log.debug("attempting SAML 2.0 attribute query"); - if (SAML2Query(qctx)) - return; - m_log.debug("attempting SAML 1.x attribute query"); - SAML1Query(qctx); - return; + if (XMLString::equals(qctx.getProtocol(), samlconstants::SAML20P_NS)) { + m_log.debug("attempting SAML 2.0 attribute query"); + SAML2Query(qctx); + } + else if (XMLString::equals(qctx.getProtocol(), samlconstants::SAML11_PROTOCOL_ENUM) || + XMLString::equals(qctx.getProtocol(), samlconstants::SAML10_PROTOCOL_ENUM)) { + m_log.debug("attempting SAML 1.x attribute query"); + SAML1Query(qctx); + } + else + m_log.warn("SSO protocol does not allow for attribute query"); } - m_log.warn("can't attempt attribute query, either no NameID or no metadata to use"); + else + m_log.warn("can't attempt attribute query, either no NameID or no metadata to use"); }