Category& m_log;
string m_policyId;
+ bool m_subjectMatch;
vector<AttributeDesignator*> m_SAML1Designators;
vector<saml2::Attribute*> m_SAML2Designators;
};
return new QueryResolver(e);
}
- static const XMLCh _policyId[] = UNICODE_LITERAL_8(p,o,l,i,c,y,I,d);
+ static const XMLCh policyId[] = UNICODE_LITERAL_8(p,o,l,i,c,y,I,d);
+ static const XMLCh subjectMatch[] = UNICODE_LITERAL_12(s,u,b,j,e,c,t,M,a,t,c,h);
};
-QueryResolver::QueryResolver(const DOMElement* e) : m_log(Category::getInstance(SHIBSP_LOGCAT".AttributeResolver.Query"))
+QueryResolver::QueryResolver(const DOMElement* e) : m_log(Category::getInstance(SHIBSP_LOGCAT".AttributeResolver.Query")), m_subjectMatch(false)
{
#ifdef _DEBUG
xmltooling::NDC ndc("QueryResolver");
#endif
- const XMLCh* pid = e ? e->getAttributeNS(NULL, _policyId) : NULL;
+ const XMLCh* pid = e ? e->getAttributeNS(NULL, policyId) : NULL;
if (pid && *pid) {
auto_ptr_char temp(pid);
m_policyId = temp.get();
}
+ pid = e ? e->getAttributeNS(NULL, subjectMatch) : NULL;
+ if (pid && (*pid == chLatin_t || *pid == chDigit_1))
+ m_subjectMatch = true;
DOMElement* child = XMLHelper::getFirstChildElement(e);
while (child) {
AttributeExtractor* extractor = application.getAttributeExtractor();
if (extractor) {
Locker extlocker(extractor);
- extractor->extractAttributes(application, AA, *newtoken, ctx.getResolvedAttributes());
+ const vector<saml1::AttributeStatement*>& statements = const_cast<const saml1::Assertion*>(newtoken)->getAttributeStatements();
+ for (vector<saml1::AttributeStatement*>::const_iterator s = statements.begin(); s!=statements.end(); ++s) {
+ if (m_subjectMatch) {
+ // Check for subject match.
+ const NameIdentifier* respName = (*s)->getSubject() ? (*s)->getSubject()->getNameIdentifier() : NULL;
+ if (!respName || !XMLString::equals(respName->getName(), ctx.getNameID()->getName()) ||
+ !XMLString::equals(respName->getFormat(), ctx.getNameID()->getFormat()) ||
+ !XMLString::equals(respName->getNameQualifier(), ctx.getNameID()->getNameQualifier())) {
+ if (respName)
+ m_log.warnStream() << "ignoring AttributeStatement without strongly matching NameIdentifier in Subject: " <<
+ *respName << logging::eol;
+ else
+ m_log.warn("ignoring AttributeStatement without NameIdentifier in Subject");
+ continue;
+ }
+ }
+ extractor->extractAttributes(application, AA, *(*s), ctx.getResolvedAttributes());
+ }
}
AttributeFilter* filter = application.getAttributeFilter();
// Encrypt the NameID?
if (encryption.first && (!strcmp(encryption.second, "true") || !strcmp(encryption.second, "back"))) {
auto_ptr<EncryptedID> encrypted(EncryptedIDBuilder::buildEncryptedID());
- MetadataCredentialCriteria mcc(*AA);
encrypted->encrypt(
*ctx.getNameID(),
*(application.getMetadataProvider()),
// Attempt to decrypt it.
try {
Locker credlocker(cr);
- auto_ptr<MetadataCredentialCriteria> mcc(
- policy.getIssuerMetadata() ? new MetadataCredentialCriteria(*policy.getIssuerMetadata()) : NULL
- );
- auto_ptr<XMLObject> tokenwrapper(
- encassertions.front()->decrypt(*cr, application.getRelyingParty(ctx.getEntityDescriptor())->getXMLString("entityID").second, mcc.get())
- );
+ auto_ptr<XMLObject> tokenwrapper(encassertions.front()->decrypt(*cr, relyingParty->getXMLString("entityID").second, &mcc));
newtoken = dynamic_cast<saml2::Assertion*>(tokenwrapper.get());
if (newtoken) {
tokenwrapper.release();
// Now we can check the security status of the policy.
if (!policy.isAuthenticated())
throw SecurityPolicyException("Security of SAML 2.0 query result not established.");
+
+ if (m_subjectMatch) {
+ // Check for subject match.
+ bool ownedName = false;
+ NameID* respName = newtoken->getSubject() ? newtoken->getSubject()->getNameID() : NULL;
+ if (!respName) {
+ // Check for encryption.
+ EncryptedID* encname = newtoken->getSubject() ? newtoken->getSubject()->getEncryptedID() : NULL;
+ if (encname) {
+ CredentialResolver* cr=application.getCredentialResolver();
+ if (!cr)
+ m_log.warn("found EncryptedID, but no CredentialResolver was available");
+ else {
+ Locker credlocker(cr);
+ auto_ptr<XMLObject> decryptedID(encname->decrypt(*cr, relyingParty->getXMLString("entityID").second, &mcc));
+ respName = dynamic_cast<NameID*>(decryptedID.get());
+ if (respName) {
+ ownedName = true;
+ decryptedID.release();
+ if (m_log.isDebugEnabled())
+ m_log.debugStream() << "decrypted NameID: " << *respName << logging::eol;
+ }
+ }
+ }
+ }
+
+ auto_ptr<NameID> nameIDwrapper(ownedName ? respName : NULL);
+
+ if (!respName || !XMLString::equals(respName->getName(), ctx.getNameID()->getName()) ||
+ !XMLString::equals(respName->getFormat(), ctx.getNameID()->getFormat()) ||
+ !XMLString::equals(respName->getNameQualifier(), ctx.getNameID()->getNameQualifier()) ||
+ !XMLString::equals(respName->getSPNameQualifier(), ctx.getNameID()->getSPNameQualifier())) {
+ if (respName)
+ m_log.warnStream() << "ignoring Assertion without strongly matching NameID in Subject: " <<
+ *respName << logging::eol;
+ else
+ m_log.warn("ignoring Assertion without NameID in Subject");
+ if (!wrapper.get())
+ delete newtoken;
+ return true;
+ }
+ }
}
catch (exception& ex) {
m_log.error("assertion failed policy validation: %s", ex.what());
m_log.debug("attempting SAML 1.x attribute query");
SAML1Query(qctx);
}
- else
- m_log.warn("SSO protocol does not allow for attribute query");
+ else {
+ m_log.info("SSO protocol does not allow for attribute query");
+ }
}
- else
+ else {
m_log.warn("can't attempt attribute query, either no NameID or no metadata to use");
+ }
}