Config changes and rework for new credential APIs.
[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     const XMLCh* name;\r
337     const XMLCh* format;\r
338     \r
339     // Check the NameID based on the format.\r
340     if (ctx.getNameID()) {\r
341         format = ctx.getNameID()->getFormat();\r
342         if (!format || !*format)\r
343             format = NameID::UNSPECIFIED;\r
344 #ifdef HAVE_GOOD_STL\r
345         if ((rule=m_attrMap.find(make_pair(format,xstring()))) != m_attrMap.end()) {\r
346 #else\r
347         auto_ptr_char temp(format);\r
348         if ((rule=m_attrMap.find(make_pair(temp.get(),string()))) != m_attrMap.end()) {\r
349 #endif\r
350             if (aset.empty() || aset.count(rule->second.second)) {\r
351                 resolved.push_back(\r
352                     rule->second.first->decode(\r
353                         rule->second.second.c_str(), ctx.getNameID(), assertingParty.get(), relyingParty\r
354                         )\r
355                     );\r
356             }\r
357         }\r
358     }\r
359 \r
360     const vector<saml1::AttributeStatement*>& statements = token->getAttributeStatements();\r
361     for (vector<saml1::AttributeStatement*>::const_iterator s = statements.begin(); s!=statements.end(); ++s) {\r
362         const vector<saml1::Attribute*>& attrs = const_cast<const saml1::AttributeStatement*>(*s)->getAttributes();\r
363         for (vector<saml1::Attribute*>::const_iterator a = attrs.begin(); a!=attrs.end(); ++a) {\r
364             name = (*a)->getAttributeName();\r
365             format = (*a)->getAttributeNamespace();\r
366             if (!name || !*name)\r
367                 continue;\r
368             if (!format || XMLString::equals(format, shibspconstants::SHIB1_ATTRIBUTE_NAMESPACE_URI))\r
369                 format = &chNull;\r
370 #ifdef HAVE_GOOD_STL\r
371             if ((rule=m_attrMap.find(make_pair(name,format))) != m_attrMap.end()) {\r
372 #else\r
373             auto_ptr_char temp1(name);\r
374             auto_ptr_char temp2(format);\r
375             if ((rule=m_attrMap.find(make_pair(temp1.get(),temp2.get()))) != m_attrMap.end()) {\r
376 #endif\r
377                 if (aset.empty() || aset.count(rule->second.second)) {\r
378                     resolved.push_back(\r
379                         rule->second.first->decode(rule->second.second.c_str(), *a, assertingParty.get(), relyingParty)\r
380                         );\r
381                 }\r
382             }\r
383         }\r
384     }\r
385 }\r
386 \r
387 void SimpleResolverImpl::resolve(\r
388     ResolutionContext& ctx, const saml2::Assertion* token, const vector<const char*>* attributes\r
389     ) const\r
390 {\r
391     set<string> aset;\r
392     if (attributes)\r
393         for(vector<const char*>::const_iterator i=attributes->begin(); i!=attributes->end(); ++i)\r
394             aset.insert(*i);\r
395 \r
396     vector<shibsp::Attribute*>& resolved = ctx.getResolvedAttributes();\r
397 \r
398     auto_ptr_char assertingParty(ctx.getEntityDescriptor() ? ctx.getEntityDescriptor()->getEntityID() : NULL);\r
399     const char* relyingParty = ctx.getApplication().getString("providerId").second;\r
400 \r
401 #ifdef HAVE_GOOD_STL\r
402     map< pair<xstring,xstring>,pair<const AttributeDecoder*,string> >::const_iterator rule;\r
403 #else\r
404     map< pair<string,string>,pair<const AttributeDecoder*,string> >::const_iterator rule;\r
405 #endif\r
406 \r
407     const XMLCh* name;\r
408     const XMLCh* format;\r
409     \r
410     // Check the NameID based on the format.\r
411     if (ctx.getNameID()) {\r
412         format = ctx.getNameID()->getFormat();\r
413         if (!format || !*format)\r
414             format = NameID::UNSPECIFIED;\r
415 #ifdef HAVE_GOOD_STL\r
416         if ((rule=m_attrMap.find(make_pair(format,xstring()))) != m_attrMap.end()) {\r
417 #else\r
418         auto_ptr_char temp(format);\r
419         if ((rule=m_attrMap.find(make_pair(temp.get(),string()))) != m_attrMap.end()) {\r
420 #endif\r
421             if (aset.empty() || aset.count(rule->second.second)) {\r
422                 resolved.push_back(\r
423                     rule->second.first->decode(\r
424                         rule->second.second.c_str(), ctx.getNameID(), assertingParty.get(), relyingParty\r
425                         )\r
426                     );\r
427             }\r
428         }\r
429     }\r
430 \r
431     const vector<saml2::AttributeStatement*>& statements = token->getAttributeStatements();\r
432     for (vector<saml2::AttributeStatement*>::const_iterator s = statements.begin(); s!=statements.end(); ++s) {\r
433         const vector<saml2::Attribute*>& attrs = const_cast<const saml2::AttributeStatement*>(*s)->getAttributes();\r
434         for (vector<saml2::Attribute*>::const_iterator a = attrs.begin(); a!=attrs.end(); ++a) {\r
435             name = (*a)->getName();\r
436             format = (*a)->getNameFormat();\r
437             if (!name || !*name)\r
438                 continue;\r
439             if (!format || !*format)\r
440                 format = saml2::Attribute::UNSPECIFIED;\r
441             else if (XMLString::equals(format, saml2::Attribute::URI_REFERENCE))\r
442                 format = &chNull;\r
443 #ifdef HAVE_GOOD_STL\r
444             if ((rule=m_attrMap.find(make_pair(name,format))) != m_attrMap.end()) {\r
445 #else\r
446             auto_ptr_char temp1(name);\r
447             auto_ptr_char temp2(format);\r
448             if ((rule=m_attrMap.find(make_pair(temp1.get(),temp2.get()))) != m_attrMap.end()) {\r
449 #endif\r
450                 if (aset.empty() || aset.count(rule->second.second)) {\r
451                     resolved.push_back(\r
452                         rule->second.first->decode(rule->second.second.c_str(), *a, assertingParty.get(), relyingParty)\r
453                         );\r
454                 }\r
455             }\r
456         }\r
457 \r
458         const vector<saml2::EncryptedAttribute*>& encattrs = const_cast<const saml2::AttributeStatement*>(*s)->getEncryptedAttributes();\r
459         if (!encattrs.empty()) {\r
460             const XMLCh* recipient = ctx.getApplication().getXMLString("providerId").second;\r
461             CredentialResolver* cr = ctx.getApplication().getCredentialResolver();\r
462             if (!cr) {\r
463                 Category::getInstance(SHIBSP_LOGCAT".AttributeResolver").warn(\r
464                     "found encrypted attributes, but no CredentialResolver was available"\r
465                     );\r
466                 return;\r
467             }\r
468 \r
469             // We look up credentials based on the peer who did the encrypting.\r
470             CredentialCriteria cc;\r
471             cc.setPeerName(assertingParty.get());\r
472 \r
473             Locker credlocker(cr);\r
474             for (vector<saml2::EncryptedAttribute*>::const_iterator ea = encattrs.begin(); ea!=encattrs.end(); ++ea) {\r
475                 auto_ptr<XMLObject> decrypted((*ea)->decrypt(*cr, recipient, &cc));\r
476                 const saml2::Attribute* decattr = dynamic_cast<const saml2::Attribute*>(decrypted.get());\r
477                 name = decattr->getName();\r
478                 format = decattr->getNameFormat();\r
479                 if (!name || !*name)\r
480                     continue;\r
481                 if (!format || !*format)\r
482                     format = saml2::Attribute::UNSPECIFIED;\r
483                 else if (XMLString::equals(format, saml2::Attribute::URI_REFERENCE))\r
484                     format = &chNull;\r
485 #ifdef HAVE_GOOD_STL\r
486                 if ((rule=m_attrMap.find(make_pair(name,format))) != m_attrMap.end()) {\r
487 #else\r
488                 auto_ptr_char temp1(name);\r
489                 auto_ptr_char temp2(format);\r
490                 if ((rule=m_attrMap.find(make_pair(temp1.get(),temp2.get()))) != m_attrMap.end()) {\r
491 #endif\r
492                     if (aset.empty() || aset.count(rule->second.second)) {\r
493                         resolved.push_back(\r
494                             rule->second.first->decode(rule->second.second.c_str(), decattr, assertingParty.get(), relyingParty)\r
495                             );\r
496                     }\r
497                 }\r
498             }\r
499         }\r
500     }\r
501 }\r
502 \r
503 void SimpleResolverImpl::query(ResolutionContext& ctx, const NameIdentifier& nameid, const vector<const char*>* attributes) const\r
504 {\r
505 #ifdef _DEBUG\r
506     xmltooling::NDC ndc("query");\r
507 #endif\r
508     Category& log=Category::getInstance(SHIBSP_LOGCAT".AttributeResolver");\r
509 \r
510     const EntityDescriptor* entity = ctx.getEntityDescriptor();\r
511     if (!entity) {\r
512         log.debug("no issuer information available, skipping query");\r
513         return;\r
514     }\r
515 \r
516     int version = 1;\r
517     const AttributeAuthorityDescriptor* AA = entity->getAttributeAuthorityDescriptor(samlconstants::SAML11_PROTOCOL_ENUM);\r
518     if (!AA) {\r
519         AA = entity->getAttributeAuthorityDescriptor(samlconstants::SAML10_PROTOCOL_ENUM);\r
520         version = 0;\r
521     }\r
522     if (!AA) {\r
523         log.info("no SAML 1.x AttributeAuthority role found in metadata");\r
524         return;\r
525     }\r
526 \r
527     SecurityPolicy policy;\r
528     MetadataCredentialCriteria mcc(*AA);\r
529     shibsp::SOAPClient soaper(ctx.getApplication(),policy);\r
530     const PropertySet* policySettings = ctx.getApplication().getServiceProvider().getPolicySettings(ctx.getApplication().getString("policyId").second);\r
531     pair<bool,bool> signedAssertions = policySettings->getBool("signedAssertions");\r
532 \r
533     auto_ptr_XMLCh binding(samlconstants::SAML1_BINDING_SOAP);\r
534     saml1p::Response* response=NULL;\r
535     const vector<AttributeService*>& endpoints=AA->getAttributeServices();\r
536     for (vector<AttributeService*>::const_iterator ep=endpoints.begin(); !response && ep!=endpoints.end(); ++ep) {\r
537         try {\r
538             if (!XMLString::equals((*ep)->getBinding(),binding.get()))\r
539                 continue;\r
540             auto_ptr_char loc((*ep)->getLocation());\r
541             auto_ptr_XMLCh issuer(ctx.getApplication().getString("providerId").second);\r
542             saml1::Subject* subject = saml1::SubjectBuilder::buildSubject();\r
543             subject->setNameIdentifier(nameid.cloneNameIdentifier());\r
544             saml1p::AttributeQuery* query = saml1p::AttributeQueryBuilder::buildAttributeQuery();\r
545             query->setSubject(subject);\r
546             Request* request = RequestBuilder::buildRequest();\r
547             request->setAttributeQuery(query);\r
548             query->setResource(issuer.get());\r
549             request->setMinorVersion(version);\r
550             SAML1SOAPClient client(soaper);\r
551             client.sendSAML(request, mcc, loc.get());\r
552             response = client.receiveSAML();\r
553         }\r
554         catch (exception& ex) {\r
555             log.error("exception making SAML query: %s", ex.what());\r
556             soaper.reset();\r
557         }\r
558     }\r
559 \r
560     if (!response) {\r
561         log.error("unable to successfully query for attributes");\r
562         return;\r
563     }\r
564 \r
565     const vector<saml1::Assertion*>& assertions = const_cast<const saml1p::Response*>(response)->getAssertions();\r
566     if (assertions.size()>1)\r
567         log.warn("simple resolver only supports one assertion in the query response");\r
568 \r
569     auto_ptr<saml1p::Response> wrapper(response);\r
570     saml1::Assertion* newtoken = assertions.front();\r
571 \r
572     if (!newtoken->getSignature() && signedAssertions.first && signedAssertions.second) {\r
573         log.error("assertion unsigned, rejecting it based on signedAssertions policy");\r
574         return;\r
575     }\r
576 \r
577     try {\r
578         policy.evaluate(*newtoken);\r
579         if (!policy.isSecure())\r
580             throw SecurityPolicyException("Security of SAML 1.x query result not established.");\r
581         saml1::AssertionValidator tokval(ctx.getApplication().getAudiences(), time(NULL));\r
582         tokval.validateAssertion(*newtoken);\r
583     }\r
584     catch (exception& ex) {\r
585         log.error("assertion failed policy/validation: %s", ex.what());\r
586     }\r
587     newtoken->detach();\r
588     wrapper.release();\r
589     ctx.getResolvedAssertions().push_back(newtoken);\r
590     resolve(ctx, newtoken, attributes);\r
591 }\r
592 \r
593 void SimpleResolverImpl::query(ResolutionContext& ctx, const NameID& nameid, const vector<const char*>* attributes) const\r
594 {\r
595 #ifdef _DEBUG\r
596     xmltooling::NDC ndc("query");\r
597 #endif\r
598     Category& log=Category::getInstance(SHIBSP_LOGCAT".AttributeResolver");\r
599 \r
600     const EntityDescriptor* entity = ctx.getEntityDescriptor();\r
601     if (!entity) {\r
602         log.debug("no issuer information available, skipping query");\r
603         return;\r
604     }\r
605     const AttributeAuthorityDescriptor* AA = entity->getAttributeAuthorityDescriptor(samlconstants::SAML20P_NS);\r
606     if (!AA) {\r
607         log.info("no SAML 2 AttributeAuthority role found in metadata");\r
608         return;\r
609     }\r
610 \r
611     SecurityPolicy policy;\r
612     MetadataCredentialCriteria mcc(*AA);\r
613     shibsp::SOAPClient soaper(ctx.getApplication(),policy);\r
614     const PropertySet* policySettings = ctx.getApplication().getServiceProvider().getPolicySettings(ctx.getApplication().getString("policyId").second);\r
615     pair<bool,bool> signedAssertions = policySettings->getBool("signedAssertions");\r
616 \r
617     auto_ptr_XMLCh binding(samlconstants::SAML20_BINDING_SOAP);\r
618     saml2p::StatusResponseType* srt=NULL;\r
619     const vector<AttributeService*>& endpoints=AA->getAttributeServices();\r
620     for (vector<AttributeService*>::const_iterator ep=endpoints.begin(); !srt && ep!=endpoints.end(); ++ep) {\r
621         try {\r
622             if (!XMLString::equals((*ep)->getBinding(),binding.get()))\r
623                 continue;\r
624             auto_ptr_char loc((*ep)->getLocation());\r
625             auto_ptr_XMLCh issuer(ctx.getApplication().getString("providerId").second);\r
626             saml2::Subject* subject = saml2::SubjectBuilder::buildSubject();\r
627             subject->setNameID(nameid.cloneNameID());\r
628             saml2p::AttributeQuery* query = saml2p::AttributeQueryBuilder::buildAttributeQuery();\r
629             query->setSubject(subject);\r
630             Issuer* iss = IssuerBuilder::buildIssuer();\r
631             query->setIssuer(iss);\r
632             iss->setName(issuer.get());\r
633             SAML2SOAPClient client(soaper);\r
634             client.sendSAML(query, mcc, loc.get());\r
635             srt = client.receiveSAML();\r
636         }\r
637         catch (exception& ex) {\r
638             log.error("exception making SAML query: %s", ex.what());\r
639             soaper.reset();\r
640         }\r
641     }\r
642 \r
643     if (!srt) {\r
644         log.error("unable to successfully query for attributes");\r
645         return;\r
646     }\r
647     saml2p::Response* response = dynamic_cast<saml2p::Response*>(srt);\r
648     if (!response) {\r
649         delete srt;\r
650         log.error("message was not a samlp:Response");\r
651         return;\r
652     }\r
653 \r
654     const vector<saml2::Assertion*>& assertions = const_cast<const saml2p::Response*>(response)->getAssertions();\r
655     if (assertions.size()>1)\r
656         log.warn("simple resolver only supports one assertion in the query response");\r
657 \r
658     auto_ptr<saml2p::Response> wrapper(response);\r
659     saml2::Assertion* newtoken = assertions.front();\r
660 \r
661     if (!newtoken->getSignature() && signedAssertions.first && signedAssertions.second) {\r
662         log.error("assertion unsigned, rejecting it based on signedAssertions policy");\r
663         return;\r
664     }\r
665 \r
666     try {\r
667         policy.evaluate(*newtoken);\r
668         if (!policy.isSecure())\r
669             throw SecurityPolicyException("Security of SAML 2.0 query result not established.");\r
670         saml2::AssertionValidator tokval(ctx.getApplication().getAudiences(), time(NULL));\r
671         tokval.validateAssertion(*newtoken);\r
672     }\r
673     catch (exception& ex) {\r
674         log.error("assertion failed policy/validation: %s", ex.what());\r
675     }\r
676     newtoken->detach();\r
677     wrapper.release();\r
678     ctx.getResolvedAssertions().push_back(newtoken);\r
679     resolve(ctx, newtoken, attributes);\r
680 }\r
681 \r
682 void SimpleResolver::resolveAttributes(ResolutionContext& ctx, const vector<const char*>* attributes) const\r
683 {\r
684 #ifdef _DEBUG\r
685     xmltooling::NDC ndc("resolveAttributes");\r
686 #endif\r
687     Category& log=Category::getInstance(SHIBSP_LOGCAT".AttributeResolver");\r
688     \r
689     log.debug("examining tokens to resolve");\r
690 \r
691     bool query = m_impl->m_allowQuery;\r
692     const saml1::Assertion* token1;\r
693     const saml2::Assertion* token2;\r
694     if (ctx.getTokens()) {\r
695         for (vector<const opensaml::Assertion*>::const_iterator t = ctx.getTokens()->begin(); t!=ctx.getTokens()->end(); ++t) {\r
696             token2 = dynamic_cast<const saml2::Assertion*>(*t);\r
697             if (token2 && !token2->getAttributeStatements().empty()) {\r
698                 log.debug("resolving SAML 2 token with an AttributeStatement");\r
699                 m_impl->resolve(ctx, token2, attributes);\r
700                 query = false;\r
701             }\r
702             else {\r
703                 token1 = dynamic_cast<const saml1::Assertion*>(*t);\r
704                 if (token1 && !token1->getAttributeStatements().empty()) {\r
705                     log.debug("resolving SAML 1 token with an AttributeStatement");\r
706                     m_impl->resolve(ctx, token1, attributes);\r
707                     query = false;\r
708                 }\r
709             }\r
710         }\r
711     }\r
712 \r
713     if (query) {\r
714         if (token1 && !token1->getAuthenticationStatements().empty()) {\r
715             const AuthenticationStatement* statement = token1->getAuthenticationStatements().front();\r
716             if (statement && statement->getSubject() && statement->getSubject()->getNameIdentifier()) {\r
717                 log.debug("attempting SAML 1.x attribute query");\r
718                 return m_impl->query(ctx, *(statement->getSubject()->getNameIdentifier()), attributes);\r
719             }\r
720         }\r
721         else if (token2 && ctx.getNameID()) {\r
722             log.debug("attempting SAML 2.0 attribute query");\r
723             return m_impl->query(ctx, *ctx.getNameID(), attributes);\r
724         }\r
725         log.warn("can't attempt attribute query, no identifier in assertion subject");\r
726     }\r
727 }\r
728 \r
729 pair<bool,DOMElement*> SimpleResolver::load()\r
730 {\r
731     // Load from source using base class.\r
732     pair<bool,DOMElement*> raw = ReloadableXMLFile::load();\r
733     \r
734     // If we own it, wrap it.\r
735     XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : NULL);\r
736 \r
737     SimpleResolverImpl* impl = new SimpleResolverImpl(raw.second);\r
738     \r
739     // If we held the document, transfer it to the impl. If we didn't, it's a no-op.\r
740     impl->setDocument(docjanitor.release());\r
741 \r
742     delete m_impl;\r
743     m_impl = impl;\r
744 \r
745     return make_pair(false,(DOMElement*)NULL);\r
746 }\r