+
+ if (entityID && m_entityAssertions) {
+ const vector<saml2::Assertion*>& asserts = container->getAssertions();
+ for (indirect_iterator<vector<saml2::Assertion*>::const_iterator> assert = make_indirect_iterator(asserts.begin());
+ assert != make_indirect_iterator(asserts.end()); ++assert) {
+ if (!(assert->getSignature())) {
+ if (m_log.isDebugEnabled()) {
+ auto_ptr_char eid(entityID);
+ m_log.debug("skipping unsigned assertion in metadata extension for entity (%s)", eid.get());
+ }
+ continue;
+ }
+ else if (assert->getAttributeStatements().empty()) {
+ if (m_log.isDebugEnabled()) {
+ auto_ptr_char eid(entityID);
+ m_log.debug("skipping assertion with no AttributeStatement in metadata extension for entity (%s)", eid.get());
+ }
+ continue;
+ }
+ else {
+ // Check subject.
+ const NameID* subject = assert->getSubject() ? assert->getSubject()->getNameID() : nullptr;
+ if (!subject ||
+ !XMLString::equals(subject->getFormat(), NameID::ENTITY) ||
+ !XMLString::equals(subject->getName(), entityID)) {
+ if (m_log.isDebugEnabled()) {
+ auto_ptr_char eid(entityID);
+ m_log.debug("skipping assertion with improper Subject in metadata extension for entity (%s)", eid.get());
+ }
+ continue;
+ }
+ }
+
+ try {
+ // Set up and evaluate a policy for an AA asserting attributes to us.
+ shibsp::SecurityPolicy policy(application, &AttributeAuthorityDescriptor::ELEMENT_QNAME, false, m_policyId.c_str());
+ Locker locker(m_metadata.get());
+ if (m_metadata)
+ policy.setMetadataProvider(m_metadata.get());
+ if (m_trust)
+ policy.setTrustEngine(m_trust.get());
+ // Populate recipient as audience.
+ const XMLCh* issuer = assert->getIssuer() ? assert->getIssuer()->getName() : nullptr;
+ policy.getAudiences().push_back(application.getRelyingParty(issuer)->getXMLString("entityID").second);
+
+ // Extract assertion information for policy.
+ policy.setMessageID(assert->getID());
+ policy.setIssueInstant(assert->getIssueInstantEpoch());
+ policy.setIssuer(assert->getIssuer());
+
+ // Look up metadata for issuer.
+ if (policy.getIssuer() && policy.getMetadataProvider()) {
+ if (policy.getIssuer()->getFormat() && !XMLString::equals(policy.getIssuer()->getFormat(), saml2::NameIDType::ENTITY)) {
+ m_log.debug("non-system entity issuer, skipping metadata lookup");
+ }
+ else {
+ m_log.debug("searching metadata for entity assertion issuer...");
+ pair<const EntityDescriptor*,const RoleDescriptor*> lookup;
+ MetadataProvider::Criteria& mc = policy.getMetadataProviderCriteria();
+ mc.entityID_unicode = policy.getIssuer()->getName();
+ mc.role = &AttributeAuthorityDescriptor::ELEMENT_QNAME;
+ mc.protocol = samlconstants::SAML20P_NS;
+ lookup = policy.getMetadataProvider()->getEntityDescriptor(mc);
+ if (!lookup.first) {
+ auto_ptr_char iname(policy.getIssuer()->getName());
+ m_log.debug("no metadata found, can't establish identity of issuer (%s)", iname.get());
+ }
+ else if (!lookup.second) {
+ m_log.debug("unable to find compatible AA role in metadata");
+ }
+ else {
+ policy.setIssuerMetadata(lookup.second);
+ }
+ }
+ }
+
+ // Authenticate the assertion. We have to clone and marshall it to establish the signature for verification.
+ scoped_ptr<saml2::Assertion> tokencopy(assert->cloneAssertion());
+ tokencopy->marshall();
+ policy.evaluate(*tokencopy);
+ if (!policy.isAuthenticated()) {
+ if (m_log.isDebugEnabled()) {
+ auto_ptr_char tempid(tokencopy->getID());
+ auto_ptr_char eid(entityID);
+ m_log.debug(
+ "failed to authenticate assertion (%s) in metadata extension for entity (%s)", tempid.get(), eid.get()
+ );
+ }
+ continue;
+ }
+
+ // Override the asserting/relying party names based on this new issuer.
+ const EntityDescriptor* inlineEntity =
+ policy.getIssuerMetadata() ? dynamic_cast<const EntityDescriptor*>(policy.getIssuerMetadata()->getParent()) : nullptr;
+ auto_ptr_char inlineAssertingParty(inlineEntity ? inlineEntity->getEntityID() : nullptr);
+ relyingParty = application.getRelyingParty(inlineEntity)->getString("entityID").second;
+
+ // Use a private holding area for filtering purposes.
+ ptr_vector<Attribute> holding2;
+ const vector<saml2::Attribute*>& attrs2 =
+ const_cast<const saml2::AttributeStatement*>(tokencopy->getAttributeStatements().front())->getAttributes();
+ for_each(
+ make_indirect_iterator(attrs2.begin()), make_indirect_iterator(attrs2.end()),
+ boost::bind(extractV2Attr, this, boost::ref(application), request, inlineAssertingParty.get(), relyingParty, _1, boost::ref(holding2))
+ );
+
+ // Now we locally filter the attributes so that the actual issuer can be properly set.
+ // If we relied on outside filtering, the attributes couldn't be distinguished from the
+ // ones that come from the user's IdP.
+ if (m_filter && !holding2.empty()) {
+
+ // The filter API uses an unsafe container, so we have to transfer everything into one and back.
+ vector<Attribute*> unsafe_holding2;
+
+ // Use a local exception context since the container is unsafe.
+ try {
+ while (!holding2.empty()) {
+ ptr_vector<Attribute>::auto_type ptr = holding2.pop_back();
+ unsafe_holding2.push_back(ptr.get());
+ ptr.release();
+ }
+ BasicFilteringContext fc(application, unsafe_holding2, policy.getIssuerMetadata());
+ Locker filtlocker(m_filter.get());
+ m_filter->filterAttributes(fc, unsafe_holding2);
+
+ // Transfer back to safe container
+ while (!unsafe_holding2.empty()) {
+ auto_ptr<Attribute> ptr(unsafe_holding2.back());
+ unsafe_holding2.pop_back();
+ holding2.push_back(ptr.get());
+ ptr.release();
+ }
+ }
+ catch (std::exception& ex) {
+ m_log.error("caught exception filtering attributes: %s", ex.what());
+ m_log.error("dumping extracted attributes due to filtering exception");
+ for_each(unsafe_holding2.begin(), unsafe_holding2.end(), xmltooling::cleanup<Attribute>());
+ holding2.clear(); // in case the exception was during transfer between containers
+ }
+ }
+
+ if (!holding2.empty()) {
+ // Copy them over to the main holding tank, which transfers ownership.
+ holding.transfer(holding.end(), holding2);
+ }
+ }
+ catch (std::exception& ex) {
+ // Known exceptions are handled gracefully by skipping the assertion.
+ if (m_log.isDebugEnabled()) {
+ auto_ptr_char tempid(assert->getID());
+ auto_ptr_char eid(entityID);
+ m_log.debug(
+ "exception authenticating assertion (%s) in metadata extension for entity (%s): %s",
+ tempid.get(),
+ eid.get(),
+ ex.what()
+ );
+ }
+ continue;
+ }
+ }
+ }
+
+ if (!holding.empty()) {
+ if (useCache) {
+ locker.release(); // unguard to upgrade lock
+ m_attrLock->unlock();
+ m_attrLock->wrlock();
+ SharedLock locker2(m_attrLock, false); // pop the lock when we're done
+ if (cacheEntry->second.count(container) == 0) {
+ static void (vector<DDF>::* push_back)(DDF const &) = &vector<DDF>::push_back;
+ vector<DDF>& marshalled = cacheEntry->second[container];
+ for_each(
+ holding.begin(), holding.end(),
+ boost::bind(push_back, boost::ref(marshalled), boost::bind(&Attribute::marshall, _1))
+ );
+ }
+ }
+
+ // Copy them to the output parameter, which transfers ownership.
+ attributes.transfer(attributes.end(), holding);
+ }
+
+ // If the lock is held, it's guarded.
+
+ break; // only process a single extension element