Bypass spurious warning
[shibboleth/sp.git] / shib / AAP.cpp
1 /*
2  * The Shibboleth License, Version 1.
3  * Copyright (c) 2002
4  * University Corporation for Advanced Internet Development, Inc.
5  * All rights reserved
6  *
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  * Redistributions of source code must retain the above copyright notice, this
12  * list of conditions and the following disclaimer.
13  *
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.
22  *
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
28  *
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.
33  *
34  *
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.
48  */
49
50
51 /* AAP.cpp - XML AAP implementation
52
53    Scott Cantor
54    12/21/02
55
56    $History:$
57 */
58
59 #include "internal.h"
60
61 #include <log4cpp/Category.hh>
62
63 using namespace shibboleth;
64 using namespace saml;
65 using namespace log4cpp;
66 using namespace std;
67
68 #include <xercesc/framework/URLInputSource.hpp>
69 #include <xercesc/util/regx/RegularExpression.hpp>
70
71 AAP::AAP(const char* uri)
72 {
73     NDC ndc("AAP");
74     Category& log=Category::getInstance(SHIB_LOGCAT".AAP");
75
76     saml::XML::Parser p;
77     DOMDocument* doc=NULL;
78         try
79     {
80         static XMLCh base[]={chLatin_f, chLatin_i, chLatin_l, chLatin_e, chColon, chForwardSlash, chForwardSlash, chForwardSlash, chNull};
81         URLInputSource src(base,uri);
82         Wrapper4InputSource dsrc(&src,false);
83                 doc=p.parse(dsrc);
84
85         log.infoStream() << "Loaded and parsed AAP (" << uri << ")" << CategoryStream::ENDLINE;
86
87                 DOMElement* e = doc->getDocumentElement();
88         if (XMLString::compareString(XML::SHIB_NS,e->getNamespaceURI()) ||
89             XMLString::compareString(XML::Literals::AttributeAcceptancePolicy,e->getLocalName()))
90         {
91                         log.error("Construction requires a valid AAP file: (shib:AttributeAcceptancePolicy as root element)");
92                         throw MalformedException("Construction requires a valid site file: (shib:AttributeAcceptancePolicy as root element)");
93                 }
94
95                 // Loop over the AttributeRule elements.
96         DOMNodeList* nlist = e->getElementsByTagNameNS(XML::SHIB_NS,XML::Literals::AttributeRule);
97                 for (int i=0; nlist && i<nlist->getLength(); i++)
98         {
99             // Insert an empty rule, then get a reference to it.
100             m_attrMap[static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,XML::Literals::Name)]=AttributeRule();
101             AttributeRule& arule=m_attrMap[static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,XML::Literals::Name)];
102
103             // Check for an AnySite rule.
104                         DOMNode* anysite = nlist->item(i)->getFirstChild();
105                         while (anysite && anysite->getNodeType()!=DOMNode::ELEMENT_NODE)
106             {
107                                 anysite = anysite->getNextSibling();
108                                 continue;
109                         }
110
111             if (anysite && !XMLString::compareString(XML::SHIB_NS,static_cast<DOMElement*>(anysite)->getNamespaceURI()) &&
112                 !XMLString::compareString(XML::Literals::AnySite,static_cast<DOMElement*>(anysite)->getLocalName()))
113             {
114                 // Check for an AnyValue rule.
115                 DOMNode* anyval = anysite->getFirstChild();
116                         while (anyval && anyval->getNodeType()!=DOMNode::ELEMENT_NODE)
117                 {
118                                 anyval = anyval->getNextSibling();
119                                 continue;
120                             }
121
122                 if (anyval && !XMLString::compareString(XML::SHIB_NS,static_cast<DOMElement*>(anyval)->getNamespaceURI()) &&
123                     !XMLString::compareString(XML::Literals::AnyValue,static_cast<DOMElement*>(anyval)->getLocalName()))
124                 {
125                     arule.m_anySiteRule.anyValue=true;
126                 }
127                 else
128                 {
129                     // Process each Value element.
130                     DOMNodeList* vlist = static_cast<DOMElement*>(anysite)->getElementsByTagNameNS(XML::SHIB_NS,XML::Literals::Value);
131                     for (int j=0; vlist && j<vlist->getLength(); j++)
132                     {
133                         DOMElement* ve=static_cast<DOMElement*>(vlist->item(j));
134                         DOMNode* valnode=ve->getFirstChild();
135                         if (valnode && valnode->getNodeType()==DOMNode::TEXT_NODE)
136                         {
137                             if (!XMLString::compareString(XML::Literals::literal,ve->getAttributeNS(NULL,XML::Literals::Type)))
138                                 arule.m_anySiteRule.valueRules.push_back(
139                                     pair<AttributeRule::value_type,xstring>(AttributeRule::literal,valnode->getNodeValue())
140                                     );
141                             else if (!XMLString::compareString(XML::Literals::regexp,ve->getAttributeNS(NULL,XML::Literals::Type)))
142                                 arule.m_anySiteRule.valueRules.push_back(
143                                     pair<AttributeRule::value_type,xstring>(AttributeRule::regexp,valnode->getNodeValue())
144                                     );
145                             else if (!XMLString::compareString(XML::Literals::xpath,ve->getAttributeNS(NULL,XML::Literals::Type)))
146                                 arule.m_anySiteRule.valueRules.push_back(
147                                     pair<AttributeRule::value_type,xstring>(AttributeRule::xpath,valnode->getNodeValue())
148                                     );
149                         }
150                     }
151                 }
152             }
153
154             // Loop over the SiteRule elements.
155             DOMNodeList* slist = static_cast<DOMElement*>(nlist->item(i))->getElementsByTagNameNS(XML::SHIB_NS,XML::Literals::SiteRule);
156                     for (int k=0; slist && k<slist->getLength(); k++)
157             {
158                 arule.m_siteMap[static_cast<DOMElement*>(slist->item(k))->getAttributeNS(NULL,XML::Literals::Name)]=AttributeRule::SiteRule();
159                 AttributeRule::SiteRule& srule=arule.m_siteMap[static_cast<DOMElement*>(slist->item(k))->getAttributeNS(NULL,XML::Literals::Name)];
160
161                 // Check for an AnyValue rule.
162                 DOMNode* anyval = slist->item(k)->getFirstChild();
163                         while (anyval && anyval->getNodeType()!=DOMNode::ELEMENT_NODE)
164                 {
165                                 anyval = anyval->getNextSibling();
166                                 continue;
167                             }
168
169                 if (anyval && !XMLString::compareString(XML::SHIB_NS,static_cast<DOMElement*>(anyval)->getNamespaceURI()) &&
170                     !XMLString::compareString(XML::Literals::AnyValue,static_cast<DOMElement*>(anyval)->getLocalName()))
171                 {
172                     srule.anyValue=true;
173                 }
174                 else
175                 {
176                     // Process each Value element.
177                     DOMNodeList* vlist = static_cast<DOMElement*>(slist->item(k))->getElementsByTagNameNS(XML::SHIB_NS,XML::Literals::Value);
178                     for (int j=0; vlist && j<vlist->getLength(); j++)
179                     {
180                         DOMElement* ve=static_cast<DOMElement*>(vlist->item(j));
181                         DOMNode* valnode=ve->getFirstChild();
182                         if (valnode && valnode->getNodeType()==DOMNode::TEXT_NODE)
183                         {
184                             if (!XMLString::compareString(XML::Literals::literal,ve->getAttributeNS(NULL,XML::Literals::Type)))
185                                 srule.valueRules.push_back(
186                                     pair<AttributeRule::value_type,xstring>(AttributeRule::literal,valnode->getNodeValue())
187                                     );
188                             else if (!XMLString::compareString(XML::Literals::regexp,ve->getAttributeNS(NULL,XML::Literals::Type)))
189                                 srule.valueRules.push_back(
190                                     pair<AttributeRule::value_type,xstring>(AttributeRule::regexp,valnode->getNodeValue())
191                                     );
192                             else if (!XMLString::compareString(XML::Literals::xpath,ve->getAttributeNS(NULL,XML::Literals::Type)))
193                                 srule.valueRules.push_back(
194                                     pair<AttributeRule::value_type,xstring>(AttributeRule::xpath,valnode->getNodeValue())
195                                     );
196                         }
197                     }
198                 }
199             }
200                 }
201     }
202     catch (SAMLException& e)
203     {
204         log.errorStream() << "XML error while parsing AAP: " << e.what() << CategoryStream::ENDLINE;
205         if (doc)
206             doc->release();
207                 throw;
208         }
209     catch (...)
210     {
211                 log.error("Unexpected error while parsing AAP");
212         if (doc)
213             doc->release();
214                 throw;
215     }
216
217 }
218
219 bool AAP::accept(const XMLCh* name, const XMLCh* originSite, DOMElement* e)
220 {
221     NDC ndc("accept");
222     log4cpp::Category& log=log4cpp::Category::getInstance(SHIB_LOGCAT".AAP");
223     
224     if (log.isDebugEnabled())
225     {
226         auto_ptr<char> temp(XMLString::transcode(name));
227         auto_ptr<char> temp2(XMLString::transcode(originSite));
228         log.debug("evaluating value for attribute '%s' from site '%s'",temp.get(),temp2.get());
229     }
230
231     map<xstring,AttributeRule>::const_iterator arule=m_attrMap.find(name);
232     if (arule==m_attrMap.end())
233     {
234         log.warn("attribute not found in AAP, any value is rejected");
235         return false;
236     }
237
238     // Don't currently support non-simple content models...
239     DOMNode* n=e->getFirstChild();
240     if (!n || n->getNodeType()!=DOMNode::TEXT_NODE)
241     {
242         log.warn("implementation does not support complex attribute values");
243         return false;
244     }
245
246     if (arule->second.m_anySiteRule.anyValue)
247     {
248         log.debug("any site, any value, match");
249         return true;
250     }
251
252     for (vector<pair<AttributeRule::value_type,xstring> >::const_iterator i=arule->second.m_anySiteRule.valueRules.begin();
253             i!=arule->second.m_anySiteRule.valueRules.end(); i++)
254     {
255         if (i->first==AttributeRule::literal)
256         {
257             if (i->second==n->getNodeValue())
258             {
259                 log.debug("any site, literal match");
260                 return true;
261             }
262         }
263         else if (i->first==AttributeRule::regexp)
264         {
265             try
266             {
267                 RegularExpression re(i->second.c_str());
268                 if (re.matches(n->getNodeValue()))
269                 {
270                     log.debug("any site, regexp match");
271                     return true;
272                 }
273             }
274             catch (XMLException& ex)
275             {
276                 auto_ptr<char> tmp(XMLString::transcode(ex.getMessage()));
277                 log.errorStream() << "caught exception while parsing regular expression: " << tmp.get()
278                     << log4cpp::CategoryStream::ENDLINE;
279             }
280         }
281         else
282             log.warn("implementation does not support XPath value rules");
283     }
284
285     map<xstring,AttributeRule::SiteRule>::const_iterator srule=arule->second.m_siteMap.find(originSite);
286     if (srule==arule->second.m_siteMap.end())
287     {
288         log.warn("site not found in attribute ruleset, any value is rejected");
289         return false;
290     }
291
292     if (srule->second.anyValue)
293     {
294         log.debug("matching site, any value, match");
295         return true;
296     }
297
298     for (vector<pair<AttributeRule::value_type,xstring> >::const_iterator j=srule->second.valueRules.begin();
299             j!=srule->second.valueRules.end(); j++)
300     {
301         if (j->first==AttributeRule::literal)
302         {
303             if (j->second==n->getNodeValue())
304             {
305                 log.debug("matching site, literal match");
306                 return true;
307             }
308         }
309         else if (j->first==AttributeRule::regexp)
310         {
311             try
312             {
313                 RegularExpression re(j->second.c_str());
314                 if (re.matches(n->getNodeValue()))
315                 {
316                     log.debug("matching site, regexp match");
317                     return true;
318                 }
319             }
320             catch (XMLException& ex)
321             {
322                 auto_ptr<char> tmp(XMLString::transcode(ex.getMessage()));
323                 log.errorStream() << "caught exception while parsing regular expression: " << tmp.get()
324                     << log4cpp::CategoryStream::ENDLINE;
325             }
326         }
327         else
328             log.warn("implementation does not support XPath value rules");
329     }
330
331     log.warn("attribute value could not be validated by AAP, rejecting it");
332     return false;
333 }