2 * The Shibboleth License, Version 1.
4 * University Corporation for Advanced Internet Development, Inc.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
11 * Redistributions of source code must retain the above copyright notice, this
12 * list of conditions and the following disclaimer.
14 * Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution, if any, must include
17 * the following acknowledgment: "This product includes software developed by
18 * the University Corporation for Advanced Internet Development
19 * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement
20 * may appear in the software itself, if and wherever such third-party
21 * acknowledgments normally appear.
23 * Neither the name of Shibboleth nor the names of its contributors, nor
24 * Internet2, nor the University Corporation for Advanced Internet Development,
25 * Inc., nor UCAID may be used to endorse or promote products derived from this
26 * software without specific prior written permission. For written permission,
27 * please contact shibboleth@shibboleth.org
29 * Products derived from this software may not be called Shibboleth, Internet2,
30 * UCAID, or the University Corporation for Advanced Internet Development, nor
31 * may Shibboleth appear in their name, without prior written permission of the
32 * University Corporation for Advanced Internet Development.
35 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
36 * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
38 * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK
39 * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE.
40 * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY
41 * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT,
42 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
43 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
44 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
47 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52 * shib-ccache.cpp -- SHAR Credential Cache
54 * Originally from mod_shib
55 * Modified by: Derek Atkins <derek@ihtfp.com>
66 #include <shib/shib-threads.h>
68 #include <log4cpp/Category.hh>
73 #ifdef HAVE_LIBDMALLOCXX
78 using namespace log4cpp;
80 using namespace shibboleth;
81 using namespace shibtarget;
83 static const XMLCh cleanupInterval[] =
84 { chLatin_c, chLatin_l, chLatin_e, chLatin_a, chLatin_n, chLatin_u, chLatin_p,
85 chLatin_I, chLatin_n, chLatin_t, chLatin_e, chLatin_r, chLatin_v, chLatin_a, chLatin_l, chNull
87 static const XMLCh cacheTimeout[] =
88 { chLatin_c, chLatin_a, chLatin_c, chLatin_h, chLatin_e,
89 chLatin_T, chLatin_i, chLatin_m, chLatin_e, chLatin_o, chLatin_u, chLatin_t, chNull
91 static const XMLCh AAConnectTimeout[] =
92 { chLatin_A, chLatin_A, chLatin_C, chLatin_o, chLatin_n, chLatin_n, chLatin_e, chLatin_c, chLatin_t,
93 chLatin_T, chLatin_i, chLatin_m, chLatin_e, chLatin_o, chLatin_u, chLatin_t, chNull
95 static const XMLCh AATimeout[] =
96 { chLatin_A, chLatin_A, chLatin_T, chLatin_i, chLatin_m, chLatin_e, chLatin_o, chLatin_u, chLatin_t, chNull };
98 static const XMLCh defaultLifetime[] =
99 { chLatin_d, chLatin_e, chLatin_f, chLatin_a, chLatin_u, chLatin_l, chLatin_t,
100 chLatin_L, chLatin_i, chLatin_f, chLatin_e, chLatin_t, chLatin_i, chLatin_m, chLatin_e, chNull
102 static const XMLCh retryInterval[] =
103 { chLatin_r, chLatin_e, chLatin_t, chLatin_r, chLatin_y,
104 chLatin_I, chLatin_n, chLatin_t, chLatin_e, chLatin_r, chLatin_v, chLatin_a, chLatin_l, chNull
106 static const XMLCh strictValidity[] =
107 { chLatin_s, chLatin_t, chLatin_r, chLatin_i, chLatin_c, chLatin_t,
108 chLatin_V, chLatin_a, chLatin_l, chLatin_i, chLatin_d, chLatin_i, chLatin_t, chLatin_y, chNull
110 static const XMLCh propagateErrors[] =
111 { chLatin_p, chLatin_r, chLatin_o, chLatin_p, chLatin_a, chLatin_g, chLatin_a, chLatin_t, chLatin_e,
112 chLatin_E, chLatin_r, chLatin_r, chLatin_o, chLatin_r, chLatin_s, chNull
115 class InternalCCache;
116 class InternalCCacheEntry : public ISessionCacheEntry
121 const IApplication* application,
122 const char* client_addr,
124 const char* providerId,
125 SAMLAuthenticationStatement* s,
126 SAMLResponse* r=NULL,
127 const IRoleDescriptor* source=NULL,
131 ~InternalCCacheEntry();
133 void lock() { m_lock->lock(); }
134 void unlock() { m_lock->unlock(); }
136 bool isValid(time_t lifetime, time_t timeout) const;
137 const char* getClientAddress() const { return m_clientAddress.c_str(); }
138 ShibProfile getProfile() const { return m_profile; }
139 const char* getProviderId() const { return m_provider_id.c_str(); }
140 const SAMLAuthenticationStatement* getAuthnStatement() const { return m_auth_statement; }
141 CachedResponse getResponse();
143 void setCache(InternalCCache *cache) { m_cache = cache; }
144 time_t lastAccess() const { return m_lastAccess; }
146 bool checkApplication(const IApplication* application) { return (m_application_id==application->getId()); }
149 void populate(); // wraps process of checking cache, and repopulating if need be
150 bool responseValid(); // checks validity of existing response
151 pair<SAMLResponse*,SAMLResponse*> getNewResponse(); // wraps an actual query
153 SAMLResponse* filter(SAMLResponse* r, const IApplication* application, const IRoleDescriptor* source);
156 string m_application_id;
157 string m_provider_id;
158 string m_clientAddress;
159 time_t m_sessionCreated;
160 time_t m_responseCreated;
161 mutable time_t m_lastAccess;
164 ShibProfile m_profile;
165 SAMLAuthenticationStatement* m_auth_statement;
166 SAMLResponse* m_response_pre;
167 SAMLResponse* m_response_post;
168 InternalCCache *m_cache;
170 log4cpp::Category* log;
174 class InternalCCache : public ISessionCache
177 InternalCCache(const DOMElement* e);
178 virtual ~InternalCCache();
180 void thread_init() {};
181 void thread_end() {};
183 string generateKey() const;
184 ISessionCacheEntry* find(const char* key, const IApplication* application);
187 const IApplication* application,
188 const char* client_addr,
190 const char* providerId,
191 SAMLAuthenticationStatement* s,
192 SAMLResponse* r=NULL,
193 const IRoleDescriptor* source=NULL,
197 void remove(const char* key);
199 InternalCCacheEntry* findi(const char* key);
203 const DOMElement* m_root; // Only valid during initialization
205 map<string,InternalCCacheEntry*> m_hashtable;
207 log4cpp::Category* log;
209 static void* cleanup_fcn(void*); // Assumes an InternalCCache
211 CondWait* shutdown_wait;
212 Thread* cleanup_thread;
214 // extracted config settings
215 unsigned int m_AATimeout,m_AAConnectTimeout;
216 unsigned int m_defaultLifetime,m_retryInterval;
217 bool m_strictValidity,m_propagateErrors;
218 friend class InternalCCacheEntry;
221 IPlugIn* MemoryCacheFactory(const DOMElement* e)
223 return new InternalCCache(e);
226 /******************************************************************************/
227 /* InternalCCache: in memory session cache */
228 /******************************************************************************/
230 InternalCCache::InternalCCache(const DOMElement* e)
231 : m_root(e), m_AATimeout(30), m_AAConnectTimeout(15), m_defaultLifetime(1800), m_retryInterval(300),
232 m_strictValidity(true), m_propagateErrors(false), lock(RWLock::create()),
233 log (&Category::getInstance("shibtarget.InternalCCache"))
235 const XMLCh* tag=m_root->getAttributeNS(NULL,AATimeout);
237 m_AATimeout = XMLString::parseInt(tag);
241 SAMLConfig::getConfig().timeout = m_AATimeout;
243 tag=m_root->getAttributeNS(NULL,AAConnectTimeout);
245 m_AAConnectTimeout = XMLString::parseInt(tag);
246 if (!m_AAConnectTimeout)
247 m_AAConnectTimeout=15;
249 SAMLConfig::getConfig().conn_timeout = m_AAConnectTimeout;
251 tag=m_root->getAttributeNS(NULL,defaultLifetime);
253 m_defaultLifetime = XMLString::parseInt(tag);
254 if (!m_defaultLifetime)
255 m_defaultLifetime=1800;
258 tag=m_root->getAttributeNS(NULL,retryInterval);
260 m_retryInterval = XMLString::parseInt(tag);
261 if (!m_retryInterval)
265 tag=m_root->getAttributeNS(NULL,strictValidity);
266 if (tag && (*tag==chDigit_0 || *tag==chLatin_f))
267 m_strictValidity=false;
269 tag=m_root->getAttributeNS(NULL,propagateErrors);
270 if (tag && (*tag==chDigit_1 || *tag==chLatin_t))
271 m_propagateErrors=true;
273 shutdown_wait = CondWait::create();
275 cleanup_thread = Thread::create(&cleanup_fcn, (void*)this);
278 InternalCCache::~InternalCCache()
280 // Shut down the cleanup thread and let it know...
282 shutdown_wait->signal();
283 cleanup_thread->join(NULL);
285 for (map<string,InternalCCacheEntry*>::iterator i=m_hashtable.begin(); i!=m_hashtable.end(); i++)
288 delete shutdown_wait;
291 string InternalCCache::generateKey() const
298 // assumes a lock is held..
299 InternalCCacheEntry* InternalCCache::findi(const char* key)
301 map<string,InternalCCacheEntry*>::const_iterator i=m_hashtable.find(key);
302 if (i==m_hashtable.end()) {
303 log->debug("No match found");
306 log->debug("Match found");
311 ISessionCacheEntry* InternalCCache::find(const char* key, const IApplication* application)
313 log->debug("searching memory cache for key (%s)", key);
314 ReadLock rwlock(lock);
316 InternalCCacheEntry* entry = findi(key);
319 else if (!entry->checkApplication(application)) {
320 log->crit("An application (%s) attempted to access another application's session!", application->getId());
324 // Lock the "database record" for the caller -- they have to unlock the item.
329 void InternalCCache::insert(
331 const IApplication* application,
332 const char* client_addr,
334 const char* providerId,
335 SAMLAuthenticationStatement* s,
337 const IRoleDescriptor* source,
342 log->debug("caching new entry for application %s: \"%s\"", application->getId(), key);
344 InternalCCacheEntry* entry = new InternalCCacheEntry(
356 entry->setCache(this);
359 m_hashtable[key]=entry;
363 // remove the entry from the database and then destroy the cacheentry
364 void InternalCCache::remove(const char* key)
366 log->debug("removing cache entry with key (%s)", key);
368 // lock the cache for writing, which means we know nobody is sitting in find()
371 // grab the entry from the database.
372 ISessionCacheEntry* entry = findi(key);
379 // ok, remove the entry and lock it
380 m_hashtable.erase(key);
381 dynamic_cast<InternalCCacheEntry*>(entry)->lock();
384 // we can release the entry lock because we know we're not in the cache anymore
387 // Now delete the entry
391 void InternalCCache::cleanup()
393 Mutex* mutex = Mutex::create();
394 saml::NDC ndc("InternalCCache::cleanup()");
397 int timeout_life = 0;
399 // Load our configuration details...
400 const XMLCh* tag=m_root->getAttributeNS(NULL,cleanupInterval);
402 rerun_timer = XMLString::parseInt(tag);
404 tag=m_root->getAttributeNS(NULL,cacheTimeout);
406 timeout_life = XMLString::parseInt(tag);
408 if (rerun_timer <= 0)
409 rerun_timer = 300; // rerun every 5 minutes
411 if (timeout_life <= 0)
412 timeout_life = 28800; // timeout after 8 hours
416 log->debug("Cleanup thread started... Run every %d secs; timeout after %d secs",
417 rerun_timer, timeout_life);
419 while (shutdown == false) {
420 shutdown_wait->timedwait(mutex,rerun_timer);
422 if (shutdown == true)
425 log->info("Cleanup thread running...");
427 // Ok, let's run through the cleanup process and clean out
428 // really old sessions. This is a two-pass process. The
429 // first pass is done holding a read-lock while we iterate over
430 // the database. The second pass doesn't need a lock because
431 // the 'deletes' will lock the database.
433 // Pass 1: iterate over the map and find all entries that have not been
435 vector<string> stale_keys;
436 time_t stale = time(NULL) - timeout_life;
439 for (map<string,InternalCCacheEntry*>::iterator i=m_hashtable.begin();
440 i != m_hashtable.end(); i++)
442 // If the last access was BEFORE the stale timeout...
444 time_t last=i->second->lastAccess();
447 stale_keys.push_back(i->first);
451 log->info("deleting %d old items.", stale_keys.size());
453 // Pass 2: walk through the list of stale entries and remove them from
455 for (vector<string>::iterator j = stale_keys.begin(); j != stale_keys.end(); j++) {
457 // Transaction Logging
458 STConfig& stc=static_cast<STConfig&>(ShibTargetConfig::getConfig());
459 stc.getTransactionLog().infoStream() << "Purged expired session from memory (ID: " << j->c_str() << ")";
460 stc.releaseTransactionLog();
464 log->debug("Cleanup thread finished.");
471 void* InternalCCache::cleanup_fcn(void* cache_p)
473 InternalCCache* cache = reinterpret_cast<InternalCCache*>(cache_p);
475 // First, let's block all signals
476 Thread::mask_all_signals();
478 // Now run the cleanup process.
483 /******************************************************************************/
484 /* InternalCCacheEntry: A Credential Cache Entry */
485 /******************************************************************************/
487 InternalCCacheEntry::InternalCCacheEntry(
489 const IApplication* application,
490 const char* client_addr,
492 const char* providerId,
493 SAMLAuthenticationStatement* s,
495 const IRoleDescriptor* source,
498 ) : m_application_id(application->getId()), m_profile(profile), m_auth_statement(s), m_response_pre(r), m_response_post(NULL),
499 m_responseCreated(r ? time(NULL) : 0), m_lastRetry(0), log(&Category::getInstance("shibtarget::InternalCCacheEntry"))
501 if (!key || !s || !client_addr || !providerId) {
502 log->error("missing required cache entry details");
503 throw SAMLException("InternalCCacheEntry() missing required cache entry details");
507 m_clientAddress = client_addr;
508 m_provider_id = providerId;
509 m_sessionCreated = (created==0) ? time(NULL) : created;
510 m_lastAccess = (accessed==0) ? time(NULL) : accessed;
512 // If pushing attributes, filter the response.
514 log->debug("filtering attribute information");
515 m_response_post=static_cast<SAMLResponse*>(r->clone());
516 filter(m_response_post, application, source);
519 m_lock = Mutex::create();
521 log->info("new session created with session ID (%s)", key);
522 if (log->isDebugEnabled()) {
523 auto_ptr_char h(s->getSubject()->getNameIdentifier()->getName());
524 log->debug("NameID (%s), IdP (%s), Address (%s)", h.get(), providerId, client_addr);
528 InternalCCacheEntry::~InternalCCacheEntry()
530 log->debug("deleting session (ID: %s)", m_id.c_str());
531 delete m_response_pre;
532 delete m_response_post;
533 delete m_auth_statement;
537 bool InternalCCacheEntry::isValid(time_t lifetime, time_t timeout) const
540 saml::NDC ndc("isValid");
543 log->debug("testing session (ID: %s) (lifetime=%ld, timeout=%ld)",
544 m_id.c_str(), lifetime, timeout);
546 time_t now=time(NULL);
547 if (lifetime > 0 && now > m_sessionCreated+lifetime) {
548 log->info("session beyond lifetime (ID: %s)", m_id.c_str());
552 if (timeout > 0 && now-m_lastAccess >= timeout) {
553 log->info("session timed out (ID: %s)", m_id.c_str());
560 ISessionCacheEntry::CachedResponse InternalCCacheEntry::getResponse()
563 saml::NDC ndc("getResponse");
566 return CachedResponse(m_response_pre,m_response_post);
569 bool InternalCCacheEntry::responseValid()
572 saml::NDC ndc("responseValid");
574 log->debug("checking attribute data validity");
575 time_t now=time(NULL) - SAMLConfig::getConfig().clock_skew_secs;
578 Iterator<SAMLAssertion*> iter = m_response_pre->getAssertions();
579 while (iter.hasNext()) {
580 SAMLAssertion* assertion = iter.next();
582 log->debug("testing assertion...");
584 const SAMLDateTime* thistime = assertion->getNotOnOrAfter();
586 // If there is no time, then just continue and ignore this assertion.
592 if (now >= thistime->getEpoch()) {
593 log->debug("nope, not still valid");
598 // If we didn't find any assertions with times, then see if we're
599 // older than the default response lifetime.
601 if ((now - m_responseCreated) > m_cache->m_defaultLifetime) {
602 log->debug("response is beyond default life, so it's invalid");
607 log->debug("yep, response still valid");
611 void InternalCCacheEntry::populate()
614 saml::NDC ndc("populate");
616 log->debug("populating attributes for session (ID: %s)", m_id.c_str());
618 // Do we have any data cached?
619 if (m_response_pre) {
620 // Can we use what we have?
624 // If we're being strict, dump what we have and reset timestamps.
625 if (m_cache->m_strictValidity) {
626 log->info("strictly enforcing attribute validity, dumping expired data");
627 delete m_response_pre;
628 delete m_response_post;
629 m_response_pre=m_response_post=NULL;
636 // Transaction Logging
637 STConfig& stc=static_cast<STConfig&>(ShibTargetConfig::getConfig());
638 stc.getTransactionLog().infoStream() <<
639 "Making attribute query for session (ID: " <<
641 ") on (applicationId: " <<
643 ") for principal from (IdP: " <<
646 stc.releaseTransactionLog();
648 pair<SAMLResponse*,SAMLResponse*> new_responses=getNewResponse();
649 if (new_responses.first) {
650 delete m_response_pre;
651 delete m_response_post;
652 m_response_pre=new_responses.first;
653 m_response_post=new_responses.second;
654 m_responseCreated=time(NULL);
656 log->debug("fetched and stored new response");
657 stc.getTransactionLog().infoStream() << "Successful attribute query for session (ID: " << m_id << ")";
658 stc.releaseTransactionLog();
661 catch (SAMLException& e) {
662 if (typeid(e)==typeid(InvalidHandleException) || m_cache->m_propagateErrors)
664 log->warn("suppressed SAML exception caught while trying to fetch attributes");
667 if (m_cache->m_propagateErrors)
669 log->warn("suppressed unknown exception caught while trying to fetch attributes");
673 pair<SAMLResponse*,SAMLResponse*> InternalCCacheEntry::getNewResponse()
676 saml::NDC ndc("getNewResponse");
679 // The retryInterval determines how often to poll an AA that might be down.
680 time_t now=time(NULL);
681 if ((now - m_lastRetry) < m_cache->m_retryInterval)
682 return pair<SAMLResponse*,SAMLResponse*>(NULL,NULL);
684 log->debug("retry interval exceeded, so trying again");
687 log->info("trying to get new attributes for session (ID=%s)", m_id.c_str());
689 // Caller must be holding the config lock.
690 // Lookup application for session to get providerId and attributes to request.
691 IConfig* conf=ShibTargetConfig::getConfig().getINI();
692 const IApplication* application=conf->getApplication(m_application_id.c_str());
694 log->crit("unable to locate application for session, deleted?");
695 throw SAMLException("Unable to locate application for session, deleted?");
697 pair<bool,const XMLCh*> providerID=application->getXMLString("providerId");
698 if (!providerID.first) {
699 log->crit("unable to determine ProviderID for application, not set?");
700 throw SAMLException("Unable to determine ProviderID for application, not set?");
704 Metadata m(application->getMetadataProviders());
705 const IEntityDescriptor* site=m.lookup(m_provider_id.c_str());
707 log->error("unable to locate identity provider's metadata during attribute query");
708 throw MetadataException("Unable to locate identity provider's metadata during attribute query.");
711 // Try to locate an AA role.
712 const IAttributeAuthorityDescriptor* AA=site->getAttributeAuthorityDescriptor(saml::XML::SAML11_PROTOCOL_ENUM);
714 log->error("unable to locate metadata for identity provider's Attribute Authority");
715 MetadataException ex("Unable to locate metadata for identity provider's Attribute Authority.");
716 annotateException(ex,site);
719 // Get protocol signing policy.
720 const IPropertySet* credUse=application->getCredentialUse(site);
721 pair<bool,bool> signRequest=credUse ? credUse->getBool("signRequest") : make_pair(false,false);
722 pair<bool,bool> signedResponse=credUse ? credUse->getBool("signedResponse") : make_pair(false,false);
724 SAMLResponse* response = NULL;
726 // Build a SAML Request....
727 SAMLAttributeQuery* q=new SAMLAttributeQuery(
728 new SAMLSubject(static_cast<SAMLNameIdentifier*>(m_auth_statement->getSubject()->getNameIdentifier()->clone())),
730 application->getAttributeDesignators().clone()
732 auto_ptr<SAMLRequest> req(new SAMLRequest(q));
734 // Sign it? Highly doubtful we'll ever use this, but just for fun...
735 if (signRequest.first && signRequest.second) {
736 Credentials creds(conf->getCredentialsProviders());
737 const ICredResolver* signingCred=creds.lookup(credUse->getString("Signing").second);
738 req->sign(SIGNATURE_RSA,signingCred->getKey(),signingCred->getCertificates());
741 log->debug("trying to query an AA...");
743 // Call context object
744 ShibHTTPHook::ShibHTTPHookCallContext ctx(credUse ? credUse->getString("TLS").second : NULL,AA);
745 Trust t(application->getTrustProviders());
747 // Use metadata to locate endpoints.
748 Iterator<const IEndpoint*> endpoints=AA->getAttributeServiceManager()->getEndpoints();
749 while (!response && endpoints.hasNext()) {
750 const IEndpoint* ep=endpoints.next();
752 // Get a binding object for this protocol.
753 const SAMLBinding* binding = application->getBinding(ep->getBinding());
755 auto_ptr_char prot(ep->getBinding());
756 log->warn("skipping binding on unsupported protocol (%s)", prot.get());
759 auto_ptr<SAMLResponse> r(binding->send(ep->getLocation(), *(req.get()), &ctx));
760 if (r->isSigned() && !t.validate(application->getRevocationProviders(),AA,*r))
761 throw TrustException("CCacheEntry::getNewResponse() unable to verify signed response");
762 response = r.release();
764 catch (SAMLException& e) {
765 log->error("caught SAML exception during SAML attribute query: %s", e.what());
766 // Check for shib:InvalidHandle error and propagate it out.
767 Iterator<saml::QName> codes=e.getCodes();
768 if (codes.size()>1) {
769 const saml::QName& code=codes[1];
770 if (!XMLString::compareString(code.getNamespaceURI(),shibboleth::Constants::SHIB_NS) &&
771 !XMLString::compareString(code.getLocalName(), shibboleth::Constants::InvalidHandle)) {
773 throw InvalidHandleException(e.what(),params(),codes);
780 if (signedResponse.first && signedResponse.second && !response->isSigned()) {
782 log->error("unsigned response obtained, but we were told it must be signed.");
783 throw TrustException("CCacheEntry::getNewResponse() unable to obtain a signed response");
786 // Run it through the filter.
787 return make_pair(response,filter(response,application,AA));
790 catch (SAMLException& e) {
791 log->error("caught SAML exception during query to AA: %s", e.what());
792 annotateException(e,AA);
795 log->error("no response obtained");
796 SAMLException ex("Unable to obtain attributes from user's identity provider.");
797 annotateException(ex,AA,false);
801 SAMLResponse* InternalCCacheEntry::filter(SAMLResponse* r, const IApplication* application, const IRoleDescriptor* source)
803 const IPropertySet* credUse=application->getCredentialUse(source->getEntityDescriptor());
804 pair<bool,bool> signedAssertions=credUse ? credUse->getBool("signedAssertions") : make_pair(false,false);
805 Trust t(application->getTrustProviders());
807 // Examine each original assertion...
808 Iterator<SAMLAssertion*> assertions=r->getAssertions();
809 for (unsigned long i=0; i < assertions.size();) {
810 // Check signing policy.
811 if (signedAssertions.first && signedAssertions.second && !(assertions[i]->isSigned())) {
812 log->warn("removing unsigned assertion from response, in accordance with signedAssertions policy");
813 r->removeAssertion(i);
817 // Check any conditions.
819 Iterator<SAMLCondition*> conds=assertions[i]->getConditions();
820 while (conds.hasNext()) {
821 SAMLAudienceRestrictionCondition* cond=dynamic_cast<SAMLAudienceRestrictionCondition*>(conds.next());
822 if (!cond || !cond->eval(application->getAudiences())) {
823 log->warn("assertion condition invalid, removing it");
824 r->removeAssertion(i);
832 // Check token signature.
833 if (assertions[i]->isSigned() && !t.validate(application->getRevocationProviders(),source,*(assertions[i]))) {
834 log->warn("signed assertion failed to validate, removing it");
835 r->removeAssertion(i);
841 // Make a copy of whatever's left and process that against the AAP.
842 auto_ptr<SAMLResponse> copy(static_cast<SAMLResponse*>(r->clone()));
845 Iterator<SAMLAssertion*> copies=copy->getAssertions();
846 for (unsigned long j=0; j < copies.size();) {
848 // Finally, filter the content.
849 AAP::apply(application->getAAPProviders(),*(copies[j]),source);
853 catch (SAMLException&) {
854 log->info("no statements remain after AAP, removing assertion");
855 copy->removeAssertion(j);
859 return copy.release();