Adjust logging/error-handling, schema fixes, failed message handling.
[shibboleth/sp.git] / shibsp / attribute / resolver / impl / SimpleAttributeResolver.cpp
1 /*\r
2  *  Copyright 2001-2007 Internet2\r
3  * \r
4  * Licensed under the Apache License, Version 2.0 (the "License");\r
5  * you may not use this file except in compliance with the License.\r
6  * You may obtain a copy of the License at\r
7  *\r
8  *     http://www.apache.org/licenses/LICENSE-2.0\r
9  *\r
10  * Unless required by applicable law or agreed to in writing, software\r
11  * distributed under the License is distributed on an "AS IS" BASIS,\r
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  * See the License for the specific language governing permissions and\r
14  * limitations under the License.\r
15  */\r
16 \r
17 /**\r
18  * SimpleAttributeResolver.cpp\r
19  * \r
20  * AttributeResolver based on a simple mapping of SAML information.\r
21  */\r
22 \r
23 #include "internal.h"\r
24 #include "Application.h"\r
25 #include "ServiceProvider.h"\r
26 #include "SessionCache.h"\r
27 #include "attribute/AttributeDecoder.h"\r
28 #include "attribute/resolver/AttributeResolver.h"\r
29 #include "attribute/resolver/ResolutionContext.h"\r
30 #include "binding/SOAPClient.h"\r
31 #include "util/SPConstants.h"\r
32 \r
33 \r
34 #include <log4cpp/Category.hh>\r
35 #include <saml/binding/SecurityPolicy.h>\r
36 #include <saml/saml1/binding/SAML1SOAPClient.h>\r
37 #include <saml/saml1/core/Assertions.h>\r
38 #include <saml/saml1/core/Protocols.h>\r
39 #include <saml/saml1/profile/AssertionValidator.h>\r
40 #include <saml/saml2/binding/SAML2SOAPClient.h>\r
41 #include <saml/saml2/core/Protocols.h>\r
42 #include <saml/saml2/metadata/Metadata.h>\r
43 #include <saml/saml2/metadata/MetadataProvider.h>\r
44 #include <saml/saml2/profile/AssertionValidator.h>\r
45 #include <xmltooling/util/NDC.h>\r
46 #include <xmltooling/util/ReloadableXMLFile.h>\r
47 #include <xmltooling/util/XMLHelper.h>\r
48 #include <xercesc/util/XMLUniDefs.hpp>\r
49 \r
50 using namespace shibsp;\r
51 using namespace opensaml::saml1;\r
52 using namespace opensaml::saml1p;\r
53 using namespace opensaml::saml2;\r
54 using namespace opensaml::saml2p;\r
55 using namespace opensaml::saml2md;\r
56 using namespace opensaml;\r
57 using namespace xmltooling;\r
58 using namespace log4cpp;\r
59 using namespace std;\r
60 \r
61 namespace shibsp {\r
62 \r
63     class SHIBSP_DLLLOCAL SimpleContext : public ResolutionContext\r
64     {\r
65     public:\r
66         SimpleContext(const Application& application, const Session& session)\r
67             : m_app(application), m_session(&session), m_client_addr(NULL), m_metadata(NULL), m_entity(NULL),\r
68                 m_nameid(session.getNameID()), m_tokens(NULL) {\r
69         }\r
70         \r
71         SimpleContext(\r
72             const Application& application,\r
73             const char* client_addr,\r
74             const EntityDescriptor* issuer,\r
75             const NameID& nameid,\r
76             const vector<const opensaml::Assertion*>* tokens=NULL\r
77             ) : m_app(application), m_session(NULL), m_client_addr(client_addr), m_metadata(NULL), m_entity(issuer),\r
78                 m_nameid(nameid), m_tokens(tokens) {\r
79         }\r
80         \r
81         ~SimpleContext() {\r
82             if (m_metadata)\r
83                 m_metadata->unlock();\r
84             for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<shibsp::Attribute>());\r
85             for_each(m_assertions.begin(), m_assertions.end(), xmltooling::cleanup<opensaml::Assertion>());\r
86         }\r
87     \r
88         const Application& getApplication() const {\r
89             return m_app;\r
90         }\r
91         const char* getClientAddress() const {\r
92             return m_session ? m_session->getClientAddress() : m_client_addr;\r
93         }\r
94         const EntityDescriptor* getEntityDescriptor() const {\r
95             if (m_entity)\r
96                 return m_entity;\r
97             if (m_session && m_session->getEntityID()) {\r
98                 m_metadata = m_app.getMetadataProvider();\r
99                 if (m_metadata) {\r
100                     m_metadata->lock();\r
101                     return m_entity = m_metadata->getEntityDescriptor(m_session->getEntityID());\r
102                 }\r
103             }\r
104             return NULL;\r
105         }\r
106         const NameID& getNameID() const {\r
107             return m_nameid;\r
108         }\r
109         const vector<const opensaml::Assertion*>* getTokens() const {\r
110             return m_tokens;\r
111         }\r
112         const Session* getSession() const {\r
113             return m_session;\r
114         }        \r
115         vector<shibsp::Attribute*>& getResolvedAttributes() {\r
116             return m_attributes;\r
117         }\r
118         vector<opensaml::Assertion*>& getResolvedAssertions() {\r
119             return m_assertions;\r
120         }\r
121 \r
122     private:\r
123         const Application& m_app;\r
124         const Session* m_session;\r
125         const char* m_client_addr;\r
126         mutable MetadataProvider* m_metadata;\r
127         mutable const EntityDescriptor* m_entity;\r
128         const NameID& m_nameid;\r
129         const vector<const opensaml::Assertion*>* m_tokens;\r
130         vector<shibsp::Attribute*> m_attributes;\r
131         vector<opensaml::Assertion*> m_assertions;\r
132     };\r
133     \r
134 #if defined (_MSC_VER)\r
135     #pragma warning( push )\r
136     #pragma warning( disable : 4250 )\r
137 #endif\r
138 \r
139     class SimpleResolverImpl\r
140     {\r
141     public:\r
142         SimpleResolverImpl(const DOMElement* e);\r
143         ~SimpleResolverImpl() {\r
144             for_each(m_decoderMap.begin(), m_decoderMap.end(), cleanup_pair<string,AttributeDecoder>());\r
145             if (m_document)\r
146                 m_document->release();\r
147         }\r
148 \r
149         void setDocument(DOMDocument* doc) {\r
150             m_document = doc;\r
151         }\r
152 \r
153         void query(\r
154             ResolutionContext& ctx, const NameIdentifier& nameid, const vector<const char*>* attributes=NULL\r
155             ) const;\r
156         void query(\r
157             ResolutionContext& ctx, const NameID& nameid, const vector<const char*>* attributes=NULL\r
158             ) const;\r
159         void resolve(\r
160             ResolutionContext& ctx, const saml1::Assertion* token, const vector<const char*>* attributes=NULL\r
161             ) const;\r
162         void resolve(\r
163             ResolutionContext& ctx, const saml2::Assertion* token, const vector<const char*>* attributes=NULL\r
164             ) const;\r
165 \r
166         bool m_allowQuery;\r
167     private:\r
168         DOMDocument* m_document;\r
169         map<string,AttributeDecoder*> m_decoderMap;\r
170 #ifdef HAVE_GOOD_STL\r
171         map< pair<xstring,xstring>,pair<const AttributeDecoder*,string> > m_attrMap;\r
172 #else\r
173         map< pair<string,string>,pair<const AttributeDecoder*,string> > m_attrMap;\r
174 #endif\r
175     };\r
176     \r
177     class SimpleResolver : public AttributeResolver, public ReloadableXMLFile\r
178     {\r
179     public:\r
180         SimpleResolver(const DOMElement* e) : ReloadableXMLFile(e), m_impl(NULL) {\r
181             load();\r
182         }\r
183         ~SimpleResolver() {\r
184             delete m_impl;\r
185         }\r
186         \r
187         ResolutionContext* createResolutionContext(\r
188             const Application& application,\r
189             const char* client_addr,\r
190             const EntityDescriptor* issuer,\r
191             const NameID& nameid,\r
192             const vector<const opensaml::Assertion*>* tokens=NULL\r
193             ) const {\r
194             return new SimpleContext(application,client_addr,issuer,nameid,tokens);\r
195         }\r
196 \r
197         ResolutionContext* createResolutionContext(const Application& application, const Session& session) const {\r
198             return new SimpleContext(application,session);\r
199         }\r
200         \r
201         void resolveAttributes(ResolutionContext& ctx, const vector<const char*>* attributes=NULL) const;\r
202 \r
203     protected:\r
204         pair<bool,DOMElement*> load();\r
205         SimpleResolverImpl* m_impl;\r
206     };\r
207 \r
208 #if defined (_MSC_VER)\r
209     #pragma warning( pop )\r
210 #endif\r
211 \r
212     AttributeResolver* SHIBSP_DLLLOCAL SimpleAttributeResolverFactory(const DOMElement* const & e)\r
213     {\r
214         return new SimpleResolver(e);\r
215     }\r
216     \r
217     static const XMLCh SIMPLE_NS[] = {\r
218         chLatin_u, chLatin_r, chLatin_n, chColon, chLatin_m, chLatin_a, chLatin_c, chLatin_e, chColon,\r
219         chLatin_s, chLatin_h, chLatin_i, chLatin_b, chLatin_b, chLatin_o, chLatin_l, chLatin_e, chLatin_t, chLatin_h, chColon,\r
220         chDigit_2, chPeriod, chDigit_0, chColon,\r
221         chLatin_r, chLatin_e, chLatin_s, chLatin_o, chLatin_l, chLatin_v, chLatin_e, chLatin_r, chColon,\r
222         chLatin_s, chLatin_i, chLatin_m, chLatin_p, chLatin_l, chLatin_e, chNull\r
223     };\r
224     static const XMLCh _AttributeDecoder[] =    UNICODE_LITERAL_16(A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r);\r
225     static const XMLCh _AttributeResolver[] =   UNICODE_LITERAL_17(A,t,t,r,i,b,u,t,e,R,e,s,o,l,v,e,r);\r
226     static const XMLCh allowQuery[] =           UNICODE_LITERAL_10(a,l,l,o,w,Q,u,e,r,y);\r
227     static const XMLCh decoderId[] =            UNICODE_LITERAL_9(d,e,c,o,d,e,r,I,d);\r
228     static const XMLCh _id[] =                  UNICODE_LITERAL_2(i,d);\r
229     static const XMLCh _type[] =                UNICODE_LITERAL_4(t,y,p,e);\r
230 };\r
231 \r
232 SimpleResolverImpl::SimpleResolverImpl(const DOMElement* e) : m_document(NULL), m_allowQuery(true)\r
233 {\r
234 #ifdef _DEBUG\r
235     xmltooling::NDC ndc("SimpleResolverImpl");\r
236 #endif\r
237     Category& log=Category::getInstance(SHIBSP_LOGCAT".AttributeResolver");\r
238     \r
239     if (!XMLHelper::isNodeNamed(e, SIMPLE_NS, _AttributeResolver))\r
240         throw ConfigurationException("Simple resolver requires resolver:AttributeResolver at root of configuration.");\r
241     \r
242     const XMLCh* flag = e->getAttributeNS(NULL,allowQuery);\r
243     if (flag && (*flag==chLatin_f || *flag==chDigit_0)) {\r
244         log.info("SAML attribute queries disabled");\r
245         m_allowQuery = false;\r
246     }\r
247 \r
248     DOMElement* child = XMLHelper::getFirstChildElement(e, SIMPLE_NS, _AttributeDecoder);\r
249     while (child) {\r
250         auto_ptr_char id(child->getAttributeNS(NULL, _id));\r
251         auto_ptr_char type(child->getAttributeNS(NULL, _type));\r
252         try {\r
253             log.info("building AttributeDecoder (%s) of type %s", id.get(), type.get());\r
254             m_decoderMap[id.get()] = SPConfig::getConfig().AttributeDecoderManager.newPlugin(type.get(), child);\r
255         }\r
256         catch (exception& ex) {\r
257             log.error("error building AttributeDecoder (%s): %s", id.get(), ex.what());\r
258         }\r
259         child = XMLHelper::getNextSiblingElement(child, SIMPLE_NS, _AttributeDecoder);\r
260     }\r
261     \r
262     child = XMLHelper::getFirstChildElement(e, samlconstants::SAML20_NS, saml2::Attribute::LOCAL_NAME);\r
263     while (child) {\r
264         // Check for missing Name.\r
265         const XMLCh* name = child->getAttributeNS(NULL, saml2::Attribute::NAME_ATTRIB_NAME);\r
266         if (!name || !*name) {\r
267             log.warn("skipping saml:Attribute declared with no Name");\r
268             child = XMLHelper::getNextSiblingElement(child, samlconstants::SAML20_NS, saml2::Attribute::LOCAL_NAME);\r
269             continue;\r
270         }\r
271 \r
272         const AttributeDecoder* decoder=NULL;\r
273         auto_ptr_char id(child->getAttributeNS(NULL, saml2::Attribute::FRIENDLYNAME_ATTRIB_NAME));\r
274         auto_ptr_char d(child->getAttributeNS(SIMPLE_NS, decoderId));\r
275         if (!id.get() || !*id.get() || !d.get() || !*d.get() || !(decoder=m_decoderMap[d.get()])) {\r
276             log.warn("skipping saml:Attribute declared with no FriendlyName or resolvable AttributeDecoder");\r
277             child = XMLHelper::getNextSiblingElement(child, samlconstants::SAML20_NS, saml2::Attribute::LOCAL_NAME);\r
278             continue;\r
279         }\r
280         \r
281         // Empty NameFormat implies the usual Shib URI naming defaults.\r
282         const XMLCh* format = child->getAttributeNS(NULL, saml2::Attribute::NAMEFORMAT_ATTRIB_NAME);\r
283         if (!format || XMLString::equals(format, shibspconstants::SHIB1_ATTRIBUTE_NAMESPACE_URI) ||\r
284                 XMLString::equals(format, saml2::Attribute::URI_REFERENCE))\r
285             format = &chNull;  // ignore default Format/Namespace values\r
286 \r
287         // Fetch/create the map entry and see if it's a duplicate rule.\r
288 #ifdef HAVE_GOOD_STL\r
289         pair<const AttributeDecoder*,string>& decl = m_attrMap[make_pair(name,format)];\r
290 #else\r
291         auto_ptr_char n(name);\r
292         auto_ptr_char f(format);\r
293         pair<const AttributeDecoder*,string>& decl = m_attrMap[make_pair(n.get(),f.get())];\r
294 #endif\r
295         if (decl.first) {\r
296             log.warn("skipping duplicate saml:Attribute declaration (same Name and NameFormat)");\r
297             child = XMLHelper::getNextSiblingElement(child, samlconstants::SAML20_NS, saml2::Attribute::LOCAL_NAME);\r
298             continue;\r
299         }\r
300 \r
301         if (log.isInfoEnabled()) {\r
302 #ifdef HAVE_GOOD_STL\r
303             auto_ptr_char n(name);\r
304             auto_ptr_char f(format);\r
305 #endif\r
306             log.info("creating declaration for Attribute %s%s%s", n.get(), *f.get() ? ", Format/Namespace:" : "", f.get());\r
307         }\r
308         \r
309         decl.first = decoder;\r
310         decl.second = id.get();\r
311         \r
312         child = XMLHelper::getNextSiblingElement(child, samlconstants::SAML20_NS, saml2::Attribute::LOCAL_NAME);\r
313     }\r
314 }\r
315 \r
316 void SimpleResolverImpl::resolve(\r
317     ResolutionContext& ctx, const saml1::Assertion* token, const vector<const char*>* attributes\r
318     ) const\r
319 {\r
320     set<string> aset;\r
321     if (attributes)\r
322         for(vector<const char*>::const_iterator i=attributes->begin(); i!=attributes->end(); ++i)\r
323             aset.insert(*i);\r
324 \r
325     vector<shibsp::Attribute*>& resolved = ctx.getResolvedAttributes();\r
326 \r
327     auto_ptr_char assertingParty(ctx.getEntityDescriptor() ? ctx.getEntityDescriptor()->getEntityID() : NULL);\r
328     const char* relyingParty = ctx.getApplication().getString("providerId").second;\r
329 \r
330 #ifdef HAVE_GOOD_STL\r
331     map< pair<xstring,xstring>,pair<const AttributeDecoder*,string> >::const_iterator rule;\r
332 #else\r
333     map< pair<string,string>,pair<const AttributeDecoder*,string> >::const_iterator rule;\r
334 #endif\r
335 \r
336     // Check the NameID based on the format.\r
337     const XMLCh* name;\r
338     const XMLCh* format = ctx.getNameID().getFormat();\r
339     if (!format) {\r
340         format = NameID::UNSPECIFIED;\r
341 #ifdef HAVE_GOOD_STL\r
342         if ((rule=m_attrMap.find(make_pair(format,xstring()))) != m_attrMap.end()) {\r
343 #else\r
344         auto_ptr_char temp(format);\r
345         if ((rule=m_attrMap.find(make_pair(temp.get(),string()))) != m_attrMap.end()) {\r
346 #endif\r
347             if (aset.empty() || aset.count(rule->second.second)) {\r
348                 resolved.push_back(\r
349                     rule->second.first->decode(\r
350                         rule->second.second.c_str(), &ctx.getNameID(), assertingParty.get(), relyingParty\r
351                         )\r
352                     );\r
353             }\r
354         }\r
355     }\r
356 \r
357     const vector<saml1::AttributeStatement*>& statements = token->getAttributeStatements();\r
358     for (vector<saml1::AttributeStatement*>::const_iterator s = statements.begin(); s!=statements.end(); ++s) {\r
359         const vector<saml1::Attribute*>& attrs = const_cast<const saml1::AttributeStatement*>(*s)->getAttributes();\r
360         for (vector<saml1::Attribute*>::const_iterator a = attrs.begin(); a!=attrs.end(); ++a) {\r
361             name = (*a)->getAttributeName();\r
362             format = (*a)->getAttributeNamespace();\r
363             if (!name || !*name)\r
364                 continue;\r
365             if (!format)\r
366                 format = &chNull;\r
367 #ifdef HAVE_GOOD_STL\r
368             if ((rule=m_attrMap.find(make_pair(name,format))) != m_attrMap.end()) {\r
369 #else\r
370             auto_ptr_char temp1(name);\r
371             auto_ptr_char temp2(format);\r
372             if ((rule=m_attrMap.find(make_pair(temp1.get(),temp2.get()))) != m_attrMap.end()) {\r
373 #endif\r
374                 if (aset.empty() || aset.count(rule->second.second)) {\r
375                     resolved.push_back(\r
376                         rule->second.first->decode(rule->second.second.c_str(), *a, assertingParty.get(), relyingParty)\r
377                         );\r
378                 }\r
379             }\r
380         }\r
381     }\r
382 }\r
383 \r
384 void SimpleResolverImpl::resolve(\r
385     ResolutionContext& ctx, const saml2::Assertion* token, const vector<const char*>* attributes\r
386     ) const\r
387 {\r
388     set<string> aset;\r
389     if (attributes)\r
390         for(vector<const char*>::const_iterator i=attributes->begin(); i!=attributes->end(); ++i)\r
391             aset.insert(*i);\r
392 \r
393     vector<shibsp::Attribute*>& resolved = ctx.getResolvedAttributes();\r
394 \r
395     auto_ptr_char assertingParty(ctx.getEntityDescriptor() ? ctx.getEntityDescriptor()->getEntityID() : NULL);\r
396     const char* relyingParty = ctx.getApplication().getString("providerId").second;\r
397 \r
398 #ifdef HAVE_GOOD_STL\r
399     map< pair<xstring,xstring>,pair<const AttributeDecoder*,string> >::const_iterator rule;\r
400 #else\r
401     map< pair<string,string>,pair<const AttributeDecoder*,string> >::const_iterator rule;\r
402 #endif\r
403 \r
404     // Check the NameID based on the format.\r
405     const XMLCh* name;\r
406     const XMLCh* format = ctx.getNameID().getFormat();\r
407     if (!format) {\r
408         format = NameID::UNSPECIFIED;\r
409 #ifdef HAVE_GOOD_STL\r
410         if ((rule=m_attrMap.find(make_pair(format,xstring()))) != m_attrMap.end()) {\r
411 #else\r
412         auto_ptr_char temp(format);\r
413         if ((rule=m_attrMap.find(make_pair(temp.get(),string()))) != m_attrMap.end()) {\r
414 #endif\r
415             if (aset.empty() || aset.count(rule->second.second)) {\r
416                 resolved.push_back(\r
417                     rule->second.first->decode(\r
418                         rule->second.second.c_str(), &ctx.getNameID(), assertingParty.get(), relyingParty\r
419                         )\r
420                     );\r
421             }\r
422         }\r
423     }\r
424 \r
425     const vector<saml2::AttributeStatement*>& statements = token->getAttributeStatements();\r
426     for (vector<saml2::AttributeStatement*>::const_iterator s = statements.begin(); s!=statements.end(); ++s) {\r
427         const vector<saml2::Attribute*>& attrs = const_cast<const saml2::AttributeStatement*>(*s)->getAttributes();\r
428         for (vector<saml2::Attribute*>::const_iterator a = attrs.begin(); a!=attrs.end(); ++a) {\r
429             name = (*a)->getName();\r
430             format = (*a)->getNameFormat();\r
431             if (!name || !*name)\r
432                 continue;\r
433             if (!format)\r
434                 format = &chNull;\r
435 #ifdef HAVE_GOOD_STL\r
436             if ((rule=m_attrMap.find(make_pair(name,format))) != m_attrMap.end()) {\r
437 #else\r
438             auto_ptr_char temp1(name);\r
439             auto_ptr_char temp2(format);\r
440             if ((rule=m_attrMap.find(make_pair(temp1.get(),temp2.get()))) != m_attrMap.end()) {\r
441 #endif\r
442                 if (aset.empty() || aset.count(rule->second.second)) {\r
443                     resolved.push_back(\r
444                         rule->second.first->decode(rule->second.second.c_str(), *a, assertingParty.get(), relyingParty)\r
445                         );\r
446                 }\r
447             }\r
448         }\r
449     }\r
450 }\r
451 \r
452 void SimpleResolverImpl::query(ResolutionContext& ctx, const NameIdentifier& nameid, const vector<const char*>* attributes) const\r
453 {\r
454 #ifdef _DEBUG\r
455     xmltooling::NDC ndc("query");\r
456 #endif\r
457     Category& log=Category::getInstance(SHIBSP_LOGCAT".AttributeResolver");\r
458 \r
459     const EntityDescriptor* entity = ctx.getEntityDescriptor();\r
460     if (!entity) {\r
461         log.debug("no issuer information available, skipping query");\r
462         return;\r
463     }\r
464 \r
465     int version = 1;\r
466     const AttributeAuthorityDescriptor* AA = entity->getAttributeAuthorityDescriptor(samlconstants::SAML11_PROTOCOL_ENUM);\r
467     if (!AA) {\r
468         AA = entity->getAttributeAuthorityDescriptor(samlconstants::SAML10_PROTOCOL_ENUM);\r
469         version = 0;\r
470     }\r
471     if (!AA) {\r
472         log.info("no SAML 1.x AttributeAuthority role found in metadata");\r
473         return;\r
474     }\r
475 \r
476     SecurityPolicy policy;\r
477     shibsp::SOAPClient soaper(ctx.getApplication(),policy);\r
478     const PropertySet* policySettings = ctx.getApplication().getServiceProvider().getPolicySettings(ctx.getApplication().getString("policyId").second);\r
479     pair<bool,bool> signedAssertions = policySettings->getBool("signedAssertions");\r
480 \r
481     auto_ptr_XMLCh binding(samlconstants::SAML1_BINDING_SOAP);\r
482     saml1p::Response* response=NULL;\r
483     const vector<AttributeService*>& endpoints=AA->getAttributeServices();\r
484     for (vector<AttributeService*>::const_iterator ep=endpoints.begin(); !response && ep!=endpoints.end(); ++ep) {\r
485         try {\r
486             if (!XMLString::equals((*ep)->getBinding(),binding.get()))\r
487                 continue;\r
488             auto_ptr_char loc((*ep)->getLocation());\r
489             auto_ptr_XMLCh issuer(ctx.getApplication().getString("providerId").second);\r
490             saml1::Subject* subject = saml1::SubjectBuilder::buildSubject();\r
491             subject->setNameIdentifier(nameid.cloneNameIdentifier());\r
492             saml1p::AttributeQuery* query = saml1p::AttributeQueryBuilder::buildAttributeQuery();\r
493             query->setSubject(subject);\r
494             Request* request = RequestBuilder::buildRequest();\r
495             request->setAttributeQuery(query);\r
496             query->setResource(issuer.get());\r
497             request->setMinorVersion(version);\r
498             SAML1SOAPClient client(soaper);\r
499             client.sendSAML(request, *AA, loc.get());\r
500             response = client.receiveSAML();\r
501         }\r
502         catch (exception& ex) {\r
503             log.error("exception making SAML query: %s", ex.what());\r
504             soaper.reset();\r
505         }\r
506     }\r
507 \r
508     if (!response) {\r
509         log.error("unable to successfully query for attributes");\r
510         return;\r
511     }\r
512 \r
513     const vector<saml1::Assertion*>& assertions = const_cast<const saml1p::Response*>(response)->getAssertions();\r
514     if (assertions.size()>1)\r
515         log.warn("simple resolver only supports one assertion in the query response");\r
516 \r
517     auto_ptr<saml1p::Response> wrapper(response);\r
518     saml1::Assertion* newtoken = assertions.front();\r
519 \r
520     if (!newtoken->getSignature() && signedAssertions.first && signedAssertions.second) {\r
521         log.error("assertion unsigned, rejecting it based on signedAssertions policy");\r
522         return;\r
523     }\r
524 \r
525     try {\r
526         policy.evaluate(*newtoken);\r
527         if (!policy.isSecure())\r
528             throw SecurityPolicyException("Security of SAML 1.x query result not established.");\r
529         saml1::AssertionValidator tokval(ctx.getApplication().getAudiences(), time(NULL));\r
530         tokval.validateAssertion(*newtoken);\r
531     }\r
532     catch (exception& ex) {\r
533         log.error("assertion failed policy/validation: %s", ex.what());\r
534     }\r
535     newtoken->detach();\r
536     wrapper.release();\r
537     ctx.getResolvedAssertions().push_back(newtoken);\r
538     resolve(ctx, newtoken, attributes);\r
539 }\r
540 \r
541 void SimpleResolverImpl::query(ResolutionContext& ctx, const NameID& nameid, const vector<const char*>* attributes) const\r
542 {\r
543 #ifdef _DEBUG\r
544     xmltooling::NDC ndc("query");\r
545 #endif\r
546     Category& log=Category::getInstance(SHIBSP_LOGCAT".AttributeResolver");\r
547 \r
548     const EntityDescriptor* entity = ctx.getEntityDescriptor();\r
549     if (!entity) {\r
550         log.debug("no issuer information available, skipping query");\r
551         return;\r
552     }\r
553     const AttributeAuthorityDescriptor* AA = entity->getAttributeAuthorityDescriptor(samlconstants::SAML20P_NS);\r
554     if (!AA) {\r
555         log.info("no SAML 2 AttributeAuthority role found in metadata");\r
556         return;\r
557     }\r
558 \r
559     SecurityPolicy policy;\r
560     shibsp::SOAPClient soaper(ctx.getApplication(),policy);\r
561     const PropertySet* policySettings = ctx.getApplication().getServiceProvider().getPolicySettings(ctx.getApplication().getString("policyId").second);\r
562     pair<bool,bool> signedAssertions = policySettings->getBool("signedAssertions");\r
563 \r
564     auto_ptr_XMLCh binding(samlconstants::SAML20_BINDING_SOAP);\r
565     saml2p::StatusResponseType* srt=NULL;\r
566     const vector<AttributeService*>& endpoints=AA->getAttributeServices();\r
567     for (vector<AttributeService*>::const_iterator ep=endpoints.begin(); !srt && ep!=endpoints.end(); ++ep) {\r
568         try {\r
569             if (!XMLString::equals((*ep)->getBinding(),binding.get()))\r
570                 continue;\r
571             auto_ptr_char loc((*ep)->getLocation());\r
572             auto_ptr_XMLCh issuer(ctx.getApplication().getString("providerId").second);\r
573             saml2::Subject* subject = saml2::SubjectBuilder::buildSubject();\r
574             subject->setNameID(nameid.cloneNameID());\r
575             saml2p::AttributeQuery* query = saml2p::AttributeQueryBuilder::buildAttributeQuery();\r
576             query->setSubject(subject);\r
577             Issuer* iss = IssuerBuilder::buildIssuer();\r
578             query->setIssuer(iss);\r
579             iss->setName(issuer.get());\r
580             SAML2SOAPClient client(soaper);\r
581             client.sendSAML(query, *AA, loc.get());\r
582             srt = client.receiveSAML();\r
583         }\r
584         catch (exception& ex) {\r
585             log.error("exception making SAML query: %s", ex.what());\r
586             soaper.reset();\r
587         }\r
588     }\r
589 \r
590     if (!srt) {\r
591         log.error("unable to successfully query for attributes");\r
592         return;\r
593     }\r
594     saml2p::Response* response = dynamic_cast<saml2p::Response*>(srt);\r
595     if (!response) {\r
596         delete srt;\r
597         log.error("message was not a samlp:Response");\r
598         return;\r
599     }\r
600 \r
601     const vector<saml2::Assertion*>& assertions = const_cast<const saml2p::Response*>(response)->getAssertions();\r
602     if (assertions.size()>1)\r
603         log.warn("simple resolver only supports one assertion in the query response");\r
604 \r
605     auto_ptr<saml2p::Response> wrapper(response);\r
606     saml2::Assertion* newtoken = assertions.front();\r
607 \r
608     if (!newtoken->getSignature() && signedAssertions.first && signedAssertions.second) {\r
609         log.error("assertion unsigned, rejecting it based on signedAssertions policy");\r
610         return;\r
611     }\r
612 \r
613     try {\r
614         policy.evaluate(*newtoken);\r
615         if (!policy.isSecure())\r
616             throw SecurityPolicyException("Security of SAML 2.0 query result not established.");\r
617         saml2::AssertionValidator tokval(ctx.getApplication().getAudiences(), time(NULL));\r
618         tokval.validateAssertion(*newtoken);\r
619     }\r
620     catch (exception& ex) {\r
621         log.error("assertion failed policy/validation: %s", ex.what());\r
622     }\r
623     newtoken->detach();\r
624     wrapper.release();\r
625     ctx.getResolvedAssertions().push_back(newtoken);\r
626     resolve(ctx, newtoken, attributes);\r
627 }\r
628 \r
629 void SimpleResolver::resolveAttributes(ResolutionContext& ctx, const vector<const char*>* attributes) const\r
630 {\r
631 #ifdef _DEBUG\r
632     xmltooling::NDC ndc("resolveAttributes");\r
633 #endif\r
634     Category& log=Category::getInstance(SHIBSP_LOGCAT".AttributeResolver");\r
635     \r
636     log.debug("examining tokens to resolve");\r
637 \r
638     bool query = m_impl->m_allowQuery;\r
639     const saml1::Assertion* token1;\r
640     const saml2::Assertion* token2;\r
641     if (ctx.getTokens()) {\r
642         for (vector<const opensaml::Assertion*>::const_iterator t = ctx.getTokens()->begin(); t!=ctx.getTokens()->end(); ++t) {\r
643             token2 = dynamic_cast<const saml2::Assertion*>(*t);\r
644             if (token2 && !token2->getAttributeStatements().empty()) {\r
645                 log.debug("resolving SAML 2 token with an AttributeStatement");\r
646                 m_impl->resolve(ctx, token2, attributes);\r
647                 query = false;\r
648             }\r
649             else {\r
650                 token1 = dynamic_cast<const saml1::Assertion*>(*t);\r
651                 if (token1 && !token1->getAttributeStatements().empty()) {\r
652                     log.debug("resolving SAML 1 token with an AttributeStatement");\r
653                     m_impl->resolve(ctx, token1, attributes);\r
654                     query = false;\r
655                 }\r
656             }\r
657         }\r
658     }\r
659 \r
660     if (query) {\r
661         if (token1 && !token1->getAuthenticationStatements().empty()) {\r
662             log.debug("attempting SAML 1.x attribute query");\r
663             return m_impl->query(ctx, *(token1->getAuthenticationStatements().front()->getSubject()->getNameIdentifier()), attributes);\r
664         }\r
665         log.debug("attempting SAML 2.0 attribute query");\r
666         m_impl->query(ctx, ctx.getNameID(), attributes);\r
667     }\r
668 }\r
669 \r
670 pair<bool,DOMElement*> SimpleResolver::load()\r
671 {\r
672     // Load from source using base class.\r
673     pair<bool,DOMElement*> raw = ReloadableXMLFile::load();\r
674     \r
675     // If we own it, wrap it.\r
676     XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : NULL);\r
677 \r
678     SimpleResolverImpl* impl = new SimpleResolverImpl(raw.second);\r
679     \r
680     // If we held the document, transfer it to the impl. If we didn't, it's a no-op.\r
681     impl->setDocument(docjanitor.release());\r
682 \r
683     delete m_impl;\r
684     m_impl = impl;\r
685 \r
686     return make_pair(false,(DOMElement*)NULL);\r
687 }\r