[shibboleth-sp2] 19/82: SSPCPP-711 Bracket some regexp.match() statements with a try/catch
Etienne Dysli Metref
edm-guest at moszumanska.debian.org
Thu Nov 16 08:16:21 UTC 2017
This is an automated email from the git hooks/post-receive script.
edm-guest pushed a commit to branch master
in repository shibboleth-sp2.
commit 7f340c95d33bcd6c3baebc5b4d8eb976599e83fb
Author: Rod Widdowson <rdw at steadingsoftware.com>
Date: Wed Mar 1 16:19:30 2017 +0000
SSPCPP-711 Bracket some regexp.match() statements with a try/catch
https://issues.shibboleth.net/jira/browse/SSPCPP-711
---
shibsp/impl/XMLRequestMapper.cpp | 1348 +++++++++++++++++++-------------------
1 file changed, 682 insertions(+), 666 deletions(-)
diff --git a/shibsp/impl/XMLRequestMapper.cpp b/shibsp/impl/XMLRequestMapper.cpp
index d32eb97..6acad99 100644
--- a/shibsp/impl/XMLRequestMapper.cpp
+++ b/shibsp/impl/XMLRequestMapper.cpp
@@ -1,666 +1,682 @@
-/**
- * Licensed to the University Corporation for Advanced Internet
- * Development, Inc. (UCAID) under one or more contributor license
- * agreements. See the NOTICE file distributed with this work for
- * additional information regarding copyright ownership.
- *
- * UCAID licenses this file to you under the Apache License,
- * Version 2.0 (the "License"); you may not use this file except
- * in compliance with the License. You may obtain a copy of the
- * License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
- * either express or implied. See the License for the specific
- * language governing permissions and limitations under the License.
- */
-
-/** XMLRequestMapper.cpp
- *
- * XML-based RequestMapper implementation.
- */
-
-#include "internal.h"
-#include "exceptions.h"
-#include "AccessControl.h"
-#include "RequestMapper.h"
-#include "SPRequest.h"
-#include "util/CGIParser.h"
-#include "util/DOMPropertySet.h"
-#include "util/SPConstants.h"
-
-#include <algorithm>
-#include <boost/shared_ptr.hpp>
-#include <boost/lexical_cast.hpp>
-#include <boost/tokenizer.hpp>
-#include <boost/tuple/tuple.hpp>
-#include <boost/algorithm/string.hpp>
-#include <xmltooling/util/NDC.h>
-#include <xmltooling/util/ReloadableXMLFile.h>
-#include <xmltooling/util/Threads.h>
-#include <xmltooling/util/XMLHelper.h>
-#include <xercesc/util/XMLUniDefs.hpp>
-#include <xercesc/util/regx/RegularExpression.hpp>
-
-using shibspconstants::SHIB2SPCONFIG_NS;
-using namespace shibsp;
-using namespace xmltooling;
-using namespace boost;
-using namespace std;
-
-namespace shibsp {
-
- // Blocks access when an ACL plugin fails to load.
- class AccessControlDummy : public AccessControl
- {
- public:
- Lockable* lock() {
- return this;
- }
-
- void unlock() {}
-
- aclresult_t authorized(const SPRequest& request, const Session* session) const {
- return shib_acl_false;
- }
- };
-
- class Override : public DOMPropertySet, public DOMNodeFilter
- {
- public:
- Override(bool unicodeAware=false) : m_unicodeAware(unicodeAware) {}
- Override(bool unicodeAware, const DOMElement* e, Category& log, const Override* base=nullptr);
- ~Override() {}
-
- // Provides filter to exclude special config elements.
-#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE
- short
-#else
- FilterAction
-#endif
- acceptNode(const DOMNode* node) const {
- return FILTER_REJECT;
- }
-
- const Override* locate(const HTTPRequest& request) const;
- AccessControl* getAC() const { return (m_acl ? m_acl.get() : (getParent() ? dynamic_cast<const Override*>(getParent())->getAC() : nullptr)); }
-
- protected:
- void loadACL(const DOMElement* e, Category& log);
-
- bool m_unicodeAware;
- map< string,boost::shared_ptr<Override> > m_map;
- vector< pair< boost::shared_ptr<RegularExpression>,boost::shared_ptr<Override> > > m_regexps;
- vector< boost::tuple< string,boost::shared_ptr<RegularExpression>,boost::shared_ptr<Override> > > m_queries;
-
- private:
- scoped_ptr<AccessControl> m_acl;
- };
-
- class XMLRequestMapperImpl : public Override
- {
- public:
- XMLRequestMapperImpl(const DOMElement* e, Category& log);
-
- ~XMLRequestMapperImpl() {
- if (m_document)
- m_document->release();
- }
-
- void setDocument(DOMDocument* doc) {
- m_document = doc;
- }
-
- const Override* findOverride(const char* vhost, const HTTPRequest& request) const;
-
- private:
- DOMDocument* m_document;
- };
-
-#if defined (_MSC_VER)
- #pragma warning( push )
- #pragma warning( disable : 4250 )
-#endif
-
- class XMLRequestMapper : public RequestMapper, public ReloadableXMLFile
- {
- public:
- XMLRequestMapper(const DOMElement* e) : ReloadableXMLFile(e,Category::getInstance(SHIBSP_LOGCAT ".RequestMapper")) {
- background_load();
- }
-
- ~XMLRequestMapper() {
- shutdown();
- }
-
- Settings getSettings(const HTTPRequest& request) const;
-
- protected:
- pair<bool,DOMElement*> background_load();
-
- private:
- scoped_ptr<XMLRequestMapperImpl> m_impl;
- };
-
-#if defined (_MSC_VER)
- #pragma warning( pop )
-#endif
-
- RequestMapper* SHIBSP_DLLLOCAL XMLRequestMapperFactory(const DOMElement* const & e)
- {
- return new XMLRequestMapper(e);
- }
-
- static const XMLCh _AccessControl[] = UNICODE_LITERAL_13(A,c,c,e,s,s,C,o,n,t,r,o,l);
- static const XMLCh AccessControlProvider[] = UNICODE_LITERAL_21(A,c,c,e,s,s,C,o,n,t,r,o,l,P,r,o,v,i,d,e,r);
- static const XMLCh caseInsensitiveOption[] = UNICODE_LITERAL_1(i);
- static const XMLCh Host[] = UNICODE_LITERAL_4(H,o,s,t);
- static const XMLCh HostRegex[] = UNICODE_LITERAL_9(H,o,s,t,R,e,g,e,x);
- static const XMLCh htaccess[] = UNICODE_LITERAL_8(h,t,a,c,c,e,s,s);
- static const XMLCh ignoreCase[] = UNICODE_LITERAL_10(i,g,n,o,r,e,C,a,s,e);
- static const XMLCh Path[] = UNICODE_LITERAL_4(P,a,t,h);
- static const XMLCh PathRegex[] = UNICODE_LITERAL_9(P,a,t,h,R,e,g,e,x);
- static const XMLCh Query[] = UNICODE_LITERAL_5(Q,u,e,r,y);
- static const XMLCh name[] = UNICODE_LITERAL_4(n,a,m,e);
- static const XMLCh regex[] = UNICODE_LITERAL_5(r,e,g,e,x);
- static const XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e);
-}
-
-void SHIBSP_API shibsp::registerRequestMappers()
-{
- SPConfig& conf=SPConfig::getConfig();
- conf.RequestMapperManager.registerFactory(XML_REQUEST_MAPPER, XMLRequestMapperFactory);
- conf.RequestMapperManager.registerFactory(NATIVE_REQUEST_MAPPER, XMLRequestMapperFactory);
-}
-
-RequestMapper::RequestMapper()
-{
-}
-
-RequestMapper::~RequestMapper()
-{
-}
-
-void Override::loadACL(const DOMElement* e, Category& log)
-{
- try {
- const DOMElement* acl = XMLHelper::getFirstChildElement(e,htaccess);
- if (acl) {
- log.info("building Apache htaccess AccessControl provider...");
- m_acl.reset(SPConfig::getConfig().AccessControlManager.newPlugin(HT_ACCESS_CONTROL,acl));
- }
- else {
- acl = XMLHelper::getFirstChildElement(e,_AccessControl);
- if (acl) {
- log.info("building XML-based AccessControl provider...");
- m_acl.reset(SPConfig::getConfig().AccessControlManager.newPlugin(XML_ACCESS_CONTROL,acl));
- }
- else {
- acl = XMLHelper::getFirstChildElement(e,AccessControlProvider);
- if (acl) {
- string t(XMLHelper::getAttrString(acl, nullptr, _type));
- if (!t.empty()) {
- log.info("building AccessControl provider of type %s...", t.c_str());
- m_acl.reset(SPConfig::getConfig().AccessControlManager.newPlugin(t.c_str(), acl));
- }
- else {
- throw ConfigurationException("<AccessControlProvider> missing type attribute.");
- }
- }
- }
- }
- }
- catch (std::exception& ex) {
- log.crit("exception building AccessControl provider: %s", ex.what());
- m_acl.reset(new AccessControlDummy());
- }
-}
-
-Override::Override(bool unicodeAware, const DOMElement* e, Category& log, const Override* base)
- : m_unicodeAware(unicodeAware)
-{
- // Load the property set.
- load(e, nullptr, this);
- setParent(base);
-
- // Load any AccessControl provider.
- loadACL(e, log);
-
- // Handle nested Paths.
- DOMElement* path = XMLHelper::getFirstChildElement(e, Path);
- for (int i = 1; path; ++i, path = XMLHelper::getNextSiblingElement(path, Path)) {
- const XMLCh* n = path->getAttributeNS(nullptr,name);
-
- // Skip any leading slashes.
- while (n && *n == chForwardSlash)
- n++;
-
- // Check for empty name.
- if (!n || !*n) {
- log.warn("skipping Path element (%d) with empty name attribute", i);
- continue;
- }
-
- // Check for an embedded slash.
- int slash = XMLString::indexOf(n, chForwardSlash);
- if (slash > 0) {
- // Copy the first path segment.
- xstring namebuf;
- for (int pos = 0; pos < slash; ++pos)
- namebuf += n[pos];
-
- // Move past the slash in the original pathname.
- n = n + slash + 1;
-
- // Skip any leading slashes again.
- while (*n == chForwardSlash)
- ++n;
-
- if (*n) {
- // Create a placeholder Path element for the first path segment and replant under it.
- DOMElement* newpath = path->getOwnerDocument()->createElementNS(shibspconstants::SHIB2SPCONFIG_NS, Path);
- newpath->setAttributeNS(nullptr, name, namebuf.c_str());
- path->setAttributeNS(nullptr, name, n);
- path->getParentNode()->replaceChild(newpath, path);
- newpath->appendChild(path);
-
- // Repoint our locals at the new parent.
- path = newpath;
- n = path->getAttributeNS(nullptr, name);
- }
- else {
- // All we had was a pathname with trailing slash(es), so just reset it without them.
- path->setAttributeNS(nullptr, name, namebuf.c_str());
- n = path->getAttributeNS(nullptr, name);
- }
- }
-
- char* dup = nullptr;
- try {
- boost::shared_ptr<Override> o(new Override(m_unicodeAware, path, log, this));
- if (m_unicodeAware) {
- dup = toUTF8(o->getXMLString("name").second, true /* use malloc */);
- }
- else {
- dup = strdup(o->getString("name").second);
- for (char* pch = dup; *pch; ++pch)
- *pch = tolower(*pch);
- }
- if (m_map.count(dup)) {
- log.warn("skipping duplicate Path element (%s)", dup);
- }
- else {
- m_map[dup] = o;
- log.debug("added Path mapping (%s)", dup);
- }
- free(dup);
- }
- catch (std::exception&) {
- free(dup);
- throw;
- }
- }
-
- if (!XMLString::equals(e->getLocalName(), PathRegex)) {
- // Handle nested PathRegexs.
- path = XMLHelper::getFirstChildElement(e, PathRegex);
- for (int i = 1; path; ++i, path = XMLHelper::getNextSiblingElement(path, PathRegex)) {
- const XMLCh* n = path->getAttributeNS(nullptr, regex);
- if (!n || !*n) {
- log.warn("skipping PathRegex element (%d) with empty regex attribute",i);
- continue;
- }
-
- boost::shared_ptr<Override> o(new Override(m_unicodeAware, path, log, this));
-
- bool caseSensitive;
- if (path && path->hasAttributeNS(nullptr, ignoreCase)) {
- // In this one case, we've left ignoreCase reversed (true means case sensitive, false means insensitive).
- // This was to protect people who followed the security advisory for SSPCPP-691 and reversed their setting.
- log.error("Deprecated ignoreCase attribute in PathRegex element will be interpreted backwards. Replace with caseSensitive");
- caseSensitive = XMLHelper::getAttrBool(path, true, ignoreCase);
- } else {
- // If the old ignoreCase setting isn't set, then we just process normally.
- caseSensitive = XMLHelper::getCaseSensitive(path, false);
- }
- try {
- boost::shared_ptr<RegularExpression> re(new RegularExpression(n, caseSensitive ? &chNull : caseInsensitiveOption));
- m_regexps.push_back(make_pair(re, o));
- }
- catch (XMLException& ex) {
- auto_ptr_char tmp(ex.getMessage());
- log.error("caught exception while parsing PathRegex regular expression (%d): %s", i, tmp.get());
- throw ConfigurationException("Invalid regular expression in PathRegex element.");
- }
-
- if (log.isDebugEnabled())
- log.debug("added <PathRegex> mapping (%s)", o->getString("regex").second);
- }
- }
-
- // Handle nested Querys.
- path = XMLHelper::getFirstChildElement(e, Query);
- for (int i = 1; path; ++i, path = XMLHelper::getNextSiblingElement(path, Query)) {
- const XMLCh* n = path->getAttributeNS(nullptr, name);
- if (!n || !*n) {
- log.warn("skipping Query element (%d) with empty name attribute",i);
- continue;
- }
- auto_ptr_char ntemp(n);
- const XMLCh* v = path->getAttributeNS(nullptr, regex);
-
- try {
- boost::shared_ptr<Override> o(new Override(m_unicodeAware, path, log, this));
- boost::shared_ptr<RegularExpression> re((v && *v) ? new RegularExpression(v) : nullptr);
- m_queries.push_back(boost::make_tuple(string(ntemp.get()), re, o));
- }
- catch (XMLException& ex) {
- auto_ptr_char tmp(ex.getMessage());
- log.error("caught exception while parsing Query regular expression (%d): %s", i, tmp.get());
- throw ConfigurationException("Invalid regular expression in Query element.");
- }
-
- log.debug("added <Query> mapping (%s)", ntemp.get());
- }
-}
-
-const Override* Override::locate(const HTTPRequest& request) const
-{
- // This function is confusing because it's *not* recursive.
- // The whole path is tokenized and mapped in a loop, so the
- // path parameter starts with the entire request path and
- // we can skip the leading slash as irrelevant.
- const char* path = request.getRequestURI();
- if (*path == '/')
- path++;
-
- // Now we copy the path, chop the query string, and possibly lower case it.
- string dup(path);
- string::size_type sep = dup.find('?');
- if (sep != string::npos)
- dup = dup.substr(0, sep);
- if (!m_unicodeAware) {
- to_lower(dup);
- }
-
- // Default is for the current object to provide settings.
- const Override* o = this;
-
- // Tokenize the path by segment and try and map each segment.
- tokenizer< char_separator<char> > tokens(dup, char_separator<char>("/"));
- for (tokenizer< char_separator<char> >::iterator token = tokens.begin(); token != tokens.end(); ++token) {
- map< string,boost::shared_ptr<Override> >::const_iterator i = o->m_map.find(*token);
- if (i == o->m_map.end())
- break; // Once there's no match, we've consumed as much of the path as possible here.
- // We found a match, so reset the settings pointer.
- o = i->second.get();
-
- // We descended a step down the path, so we need to advance the original
- // parameter for the regex step later.
- path += token->length();
- if (*path == '/')
- path++;
- }
-
- // If there's anything left, we try for a regex match on the rest of the path minus the query string.
- if (*path) {
- string path2(path);
- sep = path2.find('?');
- if (sep != string::npos)
- path2 = path2.substr(0, sep);
-
- for (vector< pair< boost::shared_ptr<RegularExpression>,boost::shared_ptr<Override> > >::const_iterator re = o->m_regexps.begin(); re != o->m_regexps.end(); ++re) {
- if (re->first->matches(path2.c_str())) {
- o = re->second.get();
- break;
- }
- }
- }
-
- // Finally, check for query string matches. This is another "unrolled" recursive descent in a loop.
- // To avoid consuming any POST data, we use a dedicated CGIParser.
- if (!o->m_queries.empty()) {
- bool descended;
- CGIParser cgi(request, true);
- do {
- descended = false;
- for (vector< boost::tuple< string,boost::shared_ptr<RegularExpression>,boost::shared_ptr<Override> > >::const_iterator q = o->m_queries.begin(); !descended && q != o->m_queries.end(); ++q) {
- pair<CGIParser::walker,CGIParser::walker> vals = cgi.getParameters(q->get<0>().c_str());
- if (vals.first != vals.second) {
- if (q->get<1>()) {
- // We have to match one of the values.
- while (vals.first != vals.second) {
- if (q->get<1>()->matches(vals.first->second)) {
- o = q->get<2>().get();
- descended = true;
- break;
- }
- ++vals.first;
- }
- }
- else {
- // The simple presence of the parameter is sufficient to match.
- o = q->get<2>().get();
- descended = true;
- }
- }
- }
- } while (descended);
- }
-
- return o;
-}
-
-XMLRequestMapperImpl::XMLRequestMapperImpl(const DOMElement* e, Category& log) : m_document(nullptr)
-{
-#ifdef _DEBUG
- xmltooling::NDC ndc("XMLRequestMapperImpl");
-#endif
- static const XMLCh _RequestMap[] = UNICODE_LITERAL_10(R,e,q,u,e,s,t,M,a,p);
-
- if (e && !XMLHelper::isNodeNamed(e, SHIB2SPCONFIG_NS, _RequestMap))
- throw ConfigurationException("XML RequestMapper requires conf:RequestMap at root of configuration.");
-
- // Load the property set.
- load(e, nullptr, this);
-
- // Inject "default" app ID if not explicit.
- if (!getString("applicationId").first)
- setProperty("applicationId", "default");
-
- // Load any AccessControl provider.
- loadACL(e, log);
-
- pair<bool,bool> unicodeAware = getBool("unicodeAware");
- m_unicodeAware = (unicodeAware.first && unicodeAware.second);
-
- // Loop over the HostRegex elements.
- const DOMElement* host = XMLHelper::getFirstChildElement(e, HostRegex);
- for (int i = 1; host; ++i, host = XMLHelper::getNextSiblingElement(host, HostRegex)) {
- const XMLCh* n = host->getAttributeNS(nullptr,regex);
- if (!n || !*n) {
- log.warn("Skipping HostRegex element (%d) with empty regex attribute", i);
- continue;
- }
-
- boost::shared_ptr<Override> o(new Override(m_unicodeAware, host, log, this));
-
- const bool caseSensitive = XMLHelper::getCaseSensitive(host, false);
- try {
- boost::shared_ptr<RegularExpression> re(
- new RegularExpression(n, caseSensitive ? &chNull : caseInsensitiveOption)
- );
- m_regexps.push_back(make_pair(re, o));
- }
- catch (XMLException& ex) {
- auto_ptr_char tmp(ex.getMessage());
- log.error("caught exception while parsing HostRegex regular expression (%d): %s", i, tmp.get());
- }
-
- log.debug("Added <HostRegex> mapping for %s", m_regexps.back().second->getString("regex").second);
- }
-
- // Loop over the Host elements.
- host = XMLHelper::getFirstChildElement(e, Host);
- for (int i = 1; host; ++i, host = XMLHelper::getNextSiblingElement(host, Host)) {
- const XMLCh* n=host->getAttributeNS(nullptr,name);
- if (!n || !*n) {
- log.warn("Skipping Host element (%d) with empty name attribute", i);
- continue;
- }
-
- boost::shared_ptr<Override> o(new Override(m_unicodeAware, host, log, this));
- pair<bool,const char*> name=o->getString("name");
- pair<bool,const char*> scheme=o->getString("scheme");
- pair<bool,const char*> port=o->getString("port");
-
- string dup(name.first ? name.second : "");
- to_lower(dup);
-
- if (!scheme.first && port.first) {
- // No scheme, but a port, so assume http.
- scheme = pair<bool,const char*>(true,"http");
- }
- else if (scheme.first && !port.first) {
- // Scheme, no port, so default it.
- // XXX Use getservbyname instead?
- port.first = true;
- if (!strcmp(scheme.second,"http"))
- port.second = "80";
- else if (!strcmp(scheme.second,"https"))
- port.second = "443";
- else if (!strcmp(scheme.second,"ftp"))
- port.second = "21";
- else if (!strcmp(scheme.second,"ldap"))
- port.second = "389";
- else if (!strcmp(scheme.second,"ldaps"))
- port.second = "636";
- }
-
- if (scheme.first) {
- string url(scheme.second);
- url=url + "://" + dup;
-
- // Is this the default port?
- if ((!strcmp(scheme.second,"http") && !strcmp(port.second,"80")) ||
- (!strcmp(scheme.second,"https") && !strcmp(port.second,"443")) ||
- (!strcmp(scheme.second,"ftp") && !strcmp(port.second,"21")) ||
- (!strcmp(scheme.second,"ldap") && !strcmp(port.second,"389")) ||
- (!strcmp(scheme.second,"ldaps") && !strcmp(port.second,"636"))) {
- // First store a port-less version.
- if (m_map.count(url)) {
- log.warn("Skipping duplicate Host element (%s)",url.c_str());
- continue;
- }
- m_map[url] = o;
- log.debug("Added <Host> mapping for %s",url.c_str());
-
- // Now append the port. The shared_ptr should refcount the Override to avoid double deletes.
- url=url + ':' + port.second;
- m_map[url] = o;
- log.debug("Added <Host> mapping for %s",url.c_str());
- }
- else {
- url=url + ':' + port.second;
- if (m_map.count(url)) {
- log.warn("Skipping duplicate Host element (%s)",url.c_str());
- continue;
- }
- m_map[url] = o;
- log.debug("Added <Host> mapping for %s",url.c_str());
- }
- }
- else {
- // No scheme or port, so we enter dual hosts on http:80 and https:443
- string url("http://");
- url += dup;
- if (m_map.count(url)) {
- log.warn("Skipping duplicate Host element (%s)",url.c_str());
- continue;
- }
- m_map[url] = o;
- log.debug("Added <Host> mapping for %s",url.c_str());
-
- url += ":80";
- if (m_map.count(url)) {
- log.warn("Skipping duplicate Host element (%s)",url.c_str());
- continue;
- }
- m_map[url] = o;
- log.debug("Added <Host> mapping for %s",url.c_str());
-
- url = "https://" + dup;
- if (m_map.count(url)) {
- log.warn("Skipping duplicate Host element (%s)",url.c_str());
- continue;
- }
- m_map[url] = o;
- log.debug("Added <Host> mapping for %s",url.c_str());
-
- url += ":443";
- if (m_map.count(url)) {
- log.warn("Skipping duplicate Host element (%s)",url.c_str());
- continue;
- }
- m_map[url] = o;
- log.debug("Added <Host> mapping for %s",url.c_str());
- }
- }
-}
-
-const Override* XMLRequestMapperImpl::findOverride(const char* vhost, const HTTPRequest& request) const
-{
- const Override* o = nullptr;
- map< string,boost::shared_ptr<Override> >::const_iterator i = m_map.find(vhost);
- if (i != m_map.end())
- o = i->second.get();
- else {
- for (vector< pair< boost::shared_ptr<RegularExpression>,boost::shared_ptr<Override> > >::const_iterator re = m_regexps.begin(); !o && re != m_regexps.end(); ++re) {
- if (re->first->matches(vhost))
- o=re->second.get();
- }
- }
-
- return o ? o->locate(request) : this;
-}
-
-pair<bool,DOMElement*> XMLRequestMapper::background_load()
-{
- // Load from source using base class.
- pair<bool,DOMElement*> raw = ReloadableXMLFile::load();
-
- // If we own it, wrap it.
- XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : nullptr);
-
- scoped_ptr<XMLRequestMapperImpl> impl(new XMLRequestMapperImpl(raw.second, m_log));
-
- // If we held the document, transfer it to the impl. If we didn't, it's a no-op.
- impl->setDocument(docjanitor.release());
-
- // Perform the swap inside a lock.
- if (m_lock)
- m_lock->wrlock();
- SharedLock locker(m_lock, false);
- m_impl.swap(impl);
-
- return make_pair(false,(DOMElement*)nullptr);
-}
-
-RequestMapper::Settings XMLRequestMapper::getSettings(const HTTPRequest& request) const
-{
- try {
- string normalizedhost(request.getHostname());
- to_lower(normalizedhost);
- string vhost = string(request.getScheme()) + "://" + normalizedhost + ':' + lexical_cast<string>(request.getPort());
- const Override* o = m_impl->findOverride(vhost.c_str(), request);
- return Settings(o, o->getAC());
- }
- catch (XMLException& ex) {
- auto_ptr_char tmp(ex.getMessage());
- m_log.error("caught exception while locating content settings: %s", tmp.get());
- throw ConfigurationException("XML-based RequestMapper failed to retrieve content settings.");
- }
-}
+/**
+ * Licensed to the University Corporation for Advanced Internet
+ * Development, Inc. (UCAID) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * UCAID licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the
+ * License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
+ */
+
+/** XMLRequestMapper.cpp
+ *
+ * XML-based RequestMapper implementation.
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "AccessControl.h"
+#include "RequestMapper.h"
+#include "SPRequest.h"
+#include "util/CGIParser.h"
+#include "util/DOMPropertySet.h"
+#include "util/SPConstants.h"
+
+#include <algorithm>
+#include <boost/shared_ptr.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/tokenizer.hpp>
+#include <boost/tuple/tuple.hpp>
+#include <boost/algorithm/string.hpp>
+#include <xmltooling/util/NDC.h>
+#include <xmltooling/util/ReloadableXMLFile.h>
+#include <xmltooling/util/Threads.h>
+#include <xmltooling/util/XMLHelper.h>
+#include <xercesc/util/XMLUniDefs.hpp>
+#include <xercesc/util/regx/RegularExpression.hpp>
+
+using shibspconstants::SHIB2SPCONFIG_NS;
+using namespace shibsp;
+using namespace xmltooling;
+using namespace boost;
+using namespace std;
+
+namespace shibsp {
+
+ // Blocks access when an ACL plugin fails to load.
+ class AccessControlDummy : public AccessControl
+ {
+ public:
+ Lockable* lock() {
+ return this;
+ }
+
+ void unlock() {}
+
+ aclresult_t authorized(const SPRequest& request, const Session* session) const {
+ return shib_acl_false;
+ }
+ };
+
+ class Override : public DOMPropertySet, public DOMNodeFilter
+ {
+ public:
+ Override(bool unicodeAware=false) : m_unicodeAware(unicodeAware) {}
+ Override(bool unicodeAware, const DOMElement* e, Category& log, const Override* base=nullptr);
+ ~Override() {}
+
+ // Provides filter to exclude special config elements.
+#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE
+ short
+#else
+ FilterAction
+#endif
+ acceptNode(const DOMNode* node) const {
+ return FILTER_REJECT;
+ }
+
+ const Override* locate(const HTTPRequest& request) const;
+ AccessControl* getAC() const { return (m_acl ? m_acl.get() : (getParent() ? dynamic_cast<const Override*>(getParent())->getAC() : nullptr)); }
+
+ protected:
+ void loadACL(const DOMElement* e, Category& log);
+
+ bool m_unicodeAware;
+ map< string,boost::shared_ptr<Override> > m_map;
+ vector< pair< boost::shared_ptr<RegularExpression>,boost::shared_ptr<Override> > > m_regexps;
+ vector< boost::tuple< string,boost::shared_ptr<RegularExpression>,boost::shared_ptr<Override> > > m_queries;
+
+ private:
+ scoped_ptr<AccessControl> m_acl;
+ };
+
+ class XMLRequestMapperImpl : public Override
+ {
+ public:
+ XMLRequestMapperImpl(const DOMElement* e, Category& log);
+
+ ~XMLRequestMapperImpl() {
+ if (m_document)
+ m_document->release();
+ }
+
+ void setDocument(DOMDocument* doc) {
+ m_document = doc;
+ }
+
+ const Override* findOverride(const char* vhost, const HTTPRequest& request) const;
+
+ private:
+ DOMDocument* m_document;
+ };
+
+#if defined (_MSC_VER)
+ #pragma warning( push )
+ #pragma warning( disable : 4250 )
+#endif
+
+ class XMLRequestMapper : public RequestMapper, public ReloadableXMLFile
+ {
+ public:
+ XMLRequestMapper(const DOMElement* e) : ReloadableXMLFile(e,Category::getInstance(SHIBSP_LOGCAT ".RequestMapper")) {
+ background_load();
+ }
+
+ ~XMLRequestMapper() {
+ shutdown();
+ }
+
+ Settings getSettings(const HTTPRequest& request) const;
+
+ protected:
+ pair<bool,DOMElement*> background_load();
+
+ private:
+ scoped_ptr<XMLRequestMapperImpl> m_impl;
+ };
+
+#if defined (_MSC_VER)
+ #pragma warning( pop )
+#endif
+
+ RequestMapper* SHIBSP_DLLLOCAL XMLRequestMapperFactory(const DOMElement* const & e)
+ {
+ return new XMLRequestMapper(e);
+ }
+
+ static const XMLCh _AccessControl[] = UNICODE_LITERAL_13(A,c,c,e,s,s,C,o,n,t,r,o,l);
+ static const XMLCh AccessControlProvider[] = UNICODE_LITERAL_21(A,c,c,e,s,s,C,o,n,t,r,o,l,P,r,o,v,i,d,e,r);
+ static const XMLCh caseInsensitiveOption[] = UNICODE_LITERAL_1(i);
+ static const XMLCh Host[] = UNICODE_LITERAL_4(H,o,s,t);
+ static const XMLCh HostRegex[] = UNICODE_LITERAL_9(H,o,s,t,R,e,g,e,x);
+ static const XMLCh htaccess[] = UNICODE_LITERAL_8(h,t,a,c,c,e,s,s);
+ static const XMLCh ignoreCase[] = UNICODE_LITERAL_10(i,g,n,o,r,e,C,a,s,e);
+ static const XMLCh Path[] = UNICODE_LITERAL_4(P,a,t,h);
+ static const XMLCh PathRegex[] = UNICODE_LITERAL_9(P,a,t,h,R,e,g,e,x);
+ static const XMLCh Query[] = UNICODE_LITERAL_5(Q,u,e,r,y);
+ static const XMLCh name[] = UNICODE_LITERAL_4(n,a,m,e);
+ static const XMLCh regex[] = UNICODE_LITERAL_5(r,e,g,e,x);
+ static const XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e);
+}
+
+void SHIBSP_API shibsp::registerRequestMappers()
+{
+ SPConfig& conf=SPConfig::getConfig();
+ conf.RequestMapperManager.registerFactory(XML_REQUEST_MAPPER, XMLRequestMapperFactory);
+ conf.RequestMapperManager.registerFactory(NATIVE_REQUEST_MAPPER, XMLRequestMapperFactory);
+}
+
+RequestMapper::RequestMapper()
+{
+}
+
+RequestMapper::~RequestMapper()
+{
+}
+
+void Override::loadACL(const DOMElement* e, Category& log)
+{
+ try {
+ const DOMElement* acl = XMLHelper::getFirstChildElement(e,htaccess);
+ if (acl) {
+ log.info("building Apache htaccess AccessControl provider...");
+ m_acl.reset(SPConfig::getConfig().AccessControlManager.newPlugin(HT_ACCESS_CONTROL,acl));
+ }
+ else {
+ acl = XMLHelper::getFirstChildElement(e,_AccessControl);
+ if (acl) {
+ log.info("building XML-based AccessControl provider...");
+ m_acl.reset(SPConfig::getConfig().AccessControlManager.newPlugin(XML_ACCESS_CONTROL,acl));
+ }
+ else {
+ acl = XMLHelper::getFirstChildElement(e,AccessControlProvider);
+ if (acl) {
+ string t(XMLHelper::getAttrString(acl, nullptr, _type));
+ if (!t.empty()) {
+ log.info("building AccessControl provider of type %s...", t.c_str());
+ m_acl.reset(SPConfig::getConfig().AccessControlManager.newPlugin(t.c_str(), acl));
+ }
+ else {
+ throw ConfigurationException("<AccessControlProvider> missing type attribute.");
+ }
+ }
+ }
+ }
+ }
+ catch (std::exception& ex) {
+ log.crit("exception building AccessControl provider: %s", ex.what());
+ m_acl.reset(new AccessControlDummy());
+ }
+}
+
+Override::Override(bool unicodeAware, const DOMElement* e, Category& log, const Override* base)
+ : m_unicodeAware(unicodeAware)
+{
+ // Load the property set.
+ load(e, nullptr, this);
+ setParent(base);
+
+ // Load any AccessControl provider.
+ loadACL(e, log);
+
+ // Handle nested Paths.
+ DOMElement* path = XMLHelper::getFirstChildElement(e, Path);
+ for (int i = 1; path; ++i, path = XMLHelper::getNextSiblingElement(path, Path)) {
+ const XMLCh* n = path->getAttributeNS(nullptr,name);
+
+ // Skip any leading slashes.
+ while (n && *n == chForwardSlash)
+ n++;
+
+ // Check for empty name.
+ if (!n || !*n) {
+ log.warn("skipping Path element (%d) with empty name attribute", i);
+ continue;
+ }
+
+ // Check for an embedded slash.
+ int slash = XMLString::indexOf(n, chForwardSlash);
+ if (slash > 0) {
+ // Copy the first path segment.
+ xstring namebuf;
+ for (int pos = 0; pos < slash; ++pos)
+ namebuf += n[pos];
+
+ // Move past the slash in the original pathname.
+ n = n + slash + 1;
+
+ // Skip any leading slashes again.
+ while (*n == chForwardSlash)
+ ++n;
+
+ if (*n) {
+ // Create a placeholder Path element for the first path segment and replant under it.
+ DOMElement* newpath = path->getOwnerDocument()->createElementNS(shibspconstants::SHIB2SPCONFIG_NS, Path);
+ newpath->setAttributeNS(nullptr, name, namebuf.c_str());
+ path->setAttributeNS(nullptr, name, n);
+ path->getParentNode()->replaceChild(newpath, path);
+ newpath->appendChild(path);
+
+ // Repoint our locals at the new parent.
+ path = newpath;
+ n = path->getAttributeNS(nullptr, name);
+ }
+ else {
+ // All we had was a pathname with trailing slash(es), so just reset it without them.
+ path->setAttributeNS(nullptr, name, namebuf.c_str());
+ n = path->getAttributeNS(nullptr, name);
+ }
+ }
+
+ char* dup = nullptr;
+ try {
+ boost::shared_ptr<Override> o(new Override(m_unicodeAware, path, log, this));
+ if (m_unicodeAware) {
+ dup = toUTF8(o->getXMLString("name").second, true /* use malloc */);
+ }
+ else {
+ dup = strdup(o->getString("name").second);
+ for (char* pch = dup; *pch; ++pch)
+ *pch = tolower(*pch);
+ }
+ if (m_map.count(dup)) {
+ log.warn("skipping duplicate Path element (%s)", dup);
+ }
+ else {
+ m_map[dup] = o;
+ log.debug("added Path mapping (%s)", dup);
+ }
+ free(dup);
+ }
+ catch (std::exception&) {
+ free(dup);
+ throw;
+ }
+ }
+
+ if (!XMLString::equals(e->getLocalName(), PathRegex)) {
+ // Handle nested PathRegexs.
+ path = XMLHelper::getFirstChildElement(e, PathRegex);
+ for (int i = 1; path; ++i, path = XMLHelper::getNextSiblingElement(path, PathRegex)) {
+ const XMLCh* n = path->getAttributeNS(nullptr, regex);
+ if (!n || !*n) {
+ log.warn("skipping PathRegex element (%d) with empty regex attribute",i);
+ continue;
+ }
+
+ boost::shared_ptr<Override> o(new Override(m_unicodeAware, path, log, this));
+
+ bool caseSensitive;
+ if (path && path->hasAttributeNS(nullptr, ignoreCase)) {
+ // In this one case, we've left ignoreCase reversed (true means case sensitive, false means insensitive).
+ // This was to protect people who followed the security advisory for SSPCPP-691 and reversed their setting.
+ log.error("Deprecated ignoreCase attribute in PathRegex element will be interpreted backwards. Replace with caseSensitive");
+ caseSensitive = XMLHelper::getAttrBool(path, true, ignoreCase);
+ } else {
+ // If the old ignoreCase setting isn't set, then we just process normally.
+ caseSensitive = XMLHelper::getCaseSensitive(path, false);
+ }
+ try {
+ boost::shared_ptr<RegularExpression> re(new RegularExpression(n, caseSensitive ? &chNull : caseInsensitiveOption));
+ m_regexps.push_back(make_pair(re, o));
+ }
+ catch (XMLException& ex) {
+ auto_ptr_char tmp(ex.getMessage());
+ log.error("caught exception while parsing PathRegex regular expression (%d): %s", i, tmp.get());
+ throw ConfigurationException("Invalid regular expression in PathRegex element.");
+ }
+
+ if (log.isDebugEnabled())
+ log.debug("added <PathRegex> mapping (%s)", o->getString("regex").second);
+ }
+ }
+
+ // Handle nested Querys.
+ path = XMLHelper::getFirstChildElement(e, Query);
+ for (int i = 1; path; ++i, path = XMLHelper::getNextSiblingElement(path, Query)) {
+ const XMLCh* n = path->getAttributeNS(nullptr, name);
+ if (!n || !*n) {
+ log.warn("skipping Query element (%d) with empty name attribute",i);
+ continue;
+ }
+ auto_ptr_char ntemp(n);
+ const XMLCh* v = path->getAttributeNS(nullptr, regex);
+
+ try {
+ boost::shared_ptr<Override> o(new Override(m_unicodeAware, path, log, this));
+ boost::shared_ptr<RegularExpression> re((v && *v) ? new RegularExpression(v) : nullptr);
+ m_queries.push_back(boost::make_tuple(string(ntemp.get()), re, o));
+ }
+ catch (XMLException& ex) {
+ auto_ptr_char tmp(ex.getMessage());
+ log.error("caught exception while parsing Query regular expression (%d): %s", i, tmp.get());
+ throw ConfigurationException("Invalid regular expression in Query element.");
+ }
+
+ log.debug("added <Query> mapping (%s)", ntemp.get());
+ }
+}
+
+const Override* Override::locate(const HTTPRequest& request) const
+{
+ // This function is confusing because it's *not* recursive.
+ // The whole path is tokenized and mapped in a loop, so the
+ // path parameter starts with the entire request path and
+ // we can skip the leading slash as irrelevant.
+ const char* path = request.getRequestURI();
+ if (*path == '/')
+ path++;
+
+ // Now we copy the path, chop the query string, and possibly lower case it.
+ string dup(path);
+ string::size_type sep = dup.find('?');
+ if (sep != string::npos)
+ dup = dup.substr(0, sep);
+ if (!m_unicodeAware) {
+ to_lower(dup);
+ }
+
+ // Default is for the current object to provide settings.
+ const Override* o = this;
+
+ // Tokenize the path by segment and try and map each segment.
+ tokenizer< char_separator<char> > tokens(dup, char_separator<char>("/"));
+ for (tokenizer< char_separator<char> >::iterator token = tokens.begin(); token != tokens.end(); ++token) {
+ map< string,boost::shared_ptr<Override> >::const_iterator i = o->m_map.find(*token);
+ if (i == o->m_map.end())
+ break; // Once there's no match, we've consumed as much of the path as possible here.
+ // We found a match, so reset the settings pointer.
+ o = i->second.get();
+
+ // We descended a step down the path, so we need to advance the original
+ // parameter for the regex step later.
+ path += token->length();
+ if (*path == '/')
+ path++;
+ }
+
+ // If there's anything left, we try for a regex match on the rest of the path minus the query string.
+ if (*path) {
+ string path2(path);
+ sep = path2.find('?');
+ if (sep != string::npos)
+ path2 = path2.substr(0, sep);
+
+ for (vector< pair< boost::shared_ptr<RegularExpression>,boost::shared_ptr<Override> > >::const_iterator re = o->m_regexps.begin(); re != o->m_regexps.end(); ++re) {
+ try {
+ if (re->first->matches(path2.c_str())) {
+ o = re->second.get();
+ break;
+ }
+ } catch (XMLException& ex) {
+ auto_ptr_char tmp(ex.getMessage());
+ throw ConfigurationException("Caught exception while matching PathRegex : $1", params(1, tmp.get()));
+ }
+ }
+ }
+
+ // Finally, check for query string matches. This is another "unrolled" recursive descent in a loop.
+ // To avoid consuming any POST data, we use a dedicated CGIParser.
+ if (!o->m_queries.empty()) {
+ bool descended;
+ CGIParser cgi(request, true);
+ do {
+ descended = false;
+ for (vector< boost::tuple< string,boost::shared_ptr<RegularExpression>,boost::shared_ptr<Override> > >::const_iterator q = o->m_queries.begin(); !descended && q != o->m_queries.end(); ++q) {
+ pair<CGIParser::walker,CGIParser::walker> vals = cgi.getParameters(q->get<0>().c_str());
+ if (vals.first != vals.second) {
+ if (q->get<1>()) {
+ // We have to match one of the values.
+ while (vals.first != vals.second) {
+ try{
+ if (q->get<1>()->matches(vals.first->second)) {
+ o = q->get<2>().get();
+ descended = true;
+ break;
+ }
+ } catch (XMLException& ex) {
+ auto_ptr_char tmp(ex.getMessage());
+ throw ConfigurationException("Caught exception while matching Query regular expression : $1", params(1, tmp.get()));
+ }
+ ++vals.first;
+ }
+ }
+ else {
+ // The simple presence of the parameter is sufficient to match.
+ o = q->get<2>().get();
+ descended = true;
+ }
+ }
+ }
+ } while (descended);
+ }
+
+ return o;
+}
+
+XMLRequestMapperImpl::XMLRequestMapperImpl(const DOMElement* e, Category& log) : m_document(nullptr)
+{
+#ifdef _DEBUG
+ xmltooling::NDC ndc("XMLRequestMapperImpl");
+#endif
+ static const XMLCh _RequestMap[] = UNICODE_LITERAL_10(R,e,q,u,e,s,t,M,a,p);
+
+ if (e && !XMLHelper::isNodeNamed(e, SHIB2SPCONFIG_NS, _RequestMap))
+ throw ConfigurationException("XML RequestMapper requires conf:RequestMap at root of configuration.");
+
+ // Load the property set.
+ load(e, nullptr, this);
+
+ // Inject "default" app ID if not explicit.
+ if (!getString("applicationId").first)
+ setProperty("applicationId", "default");
+
+ // Load any AccessControl provider.
+ loadACL(e, log);
+
+ pair<bool,bool> unicodeAware = getBool("unicodeAware");
+ m_unicodeAware = (unicodeAware.first && unicodeAware.second);
+
+ // Loop over the HostRegex elements.
+ const DOMElement* host = XMLHelper::getFirstChildElement(e, HostRegex);
+ for (int i = 1; host; ++i, host = XMLHelper::getNextSiblingElement(host, HostRegex)) {
+ const XMLCh* n = host->getAttributeNS(nullptr,regex);
+ if (!n || !*n) {
+ log.warn("Skipping HostRegex element (%d) with empty regex attribute", i);
+ continue;
+ }
+
+ boost::shared_ptr<Override> o(new Override(m_unicodeAware, host, log, this));
+
+ const bool caseSensitive = XMLHelper::getCaseSensitive(host, false);
+ try {
+ boost::shared_ptr<RegularExpression> re(
+ new RegularExpression(n, caseSensitive ? &chNull : caseInsensitiveOption)
+ );
+ m_regexps.push_back(make_pair(re, o));
+ }
+ catch (XMLException& ex) {
+ auto_ptr_char tmp(ex.getMessage());
+ log.error("caught exception while parsing HostRegex regular expression (%d): %s", i, tmp.get());
+ }
+
+ log.debug("Added <HostRegex> mapping for %s", m_regexps.back().second->getString("regex").second);
+ }
+
+ // Loop over the Host elements.
+ host = XMLHelper::getFirstChildElement(e, Host);
+ for (int i = 1; host; ++i, host = XMLHelper::getNextSiblingElement(host, Host)) {
+ const XMLCh* n=host->getAttributeNS(nullptr,name);
+ if (!n || !*n) {
+ log.warn("Skipping Host element (%d) with empty name attribute", i);
+ continue;
+ }
+
+ boost::shared_ptr<Override> o(new Override(m_unicodeAware, host, log, this));
+ pair<bool,const char*> name=o->getString("name");
+ pair<bool,const char*> scheme=o->getString("scheme");
+ pair<bool,const char*> port=o->getString("port");
+
+ string dup(name.first ? name.second : "");
+ to_lower(dup);
+
+ if (!scheme.first && port.first) {
+ // No scheme, but a port, so assume http.
+ scheme = pair<bool,const char*>(true,"http");
+ }
+ else if (scheme.first && !port.first) {
+ // Scheme, no port, so default it.
+ // XXX Use getservbyname instead?
+ port.first = true;
+ if (!strcmp(scheme.second,"http"))
+ port.second = "80";
+ else if (!strcmp(scheme.second,"https"))
+ port.second = "443";
+ else if (!strcmp(scheme.second,"ftp"))
+ port.second = "21";
+ else if (!strcmp(scheme.second,"ldap"))
+ port.second = "389";
+ else if (!strcmp(scheme.second,"ldaps"))
+ port.second = "636";
+ }
+
+ if (scheme.first) {
+ string url(scheme.second);
+ url=url + "://" + dup;
+
+ // Is this the default port?
+ if ((!strcmp(scheme.second,"http") && !strcmp(port.second,"80")) ||
+ (!strcmp(scheme.second,"https") && !strcmp(port.second,"443")) ||
+ (!strcmp(scheme.second,"ftp") && !strcmp(port.second,"21")) ||
+ (!strcmp(scheme.second,"ldap") && !strcmp(port.second,"389")) ||
+ (!strcmp(scheme.second,"ldaps") && !strcmp(port.second,"636"))) {
+ // First store a port-less version.
+ if (m_map.count(url)) {
+ log.warn("Skipping duplicate Host element (%s)",url.c_str());
+ continue;
+ }
+ m_map[url] = o;
+ log.debug("Added <Host> mapping for %s",url.c_str());
+
+ // Now append the port. The shared_ptr should refcount the Override to avoid double deletes.
+ url=url + ':' + port.second;
+ m_map[url] = o;
+ log.debug("Added <Host> mapping for %s",url.c_str());
+ }
+ else {
+ url=url + ':' + port.second;
+ if (m_map.count(url)) {
+ log.warn("Skipping duplicate Host element (%s)",url.c_str());
+ continue;
+ }
+ m_map[url] = o;
+ log.debug("Added <Host> mapping for %s",url.c_str());
+ }
+ }
+ else {
+ // No scheme or port, so we enter dual hosts on http:80 and https:443
+ string url("http://");
+ url += dup;
+ if (m_map.count(url)) {
+ log.warn("Skipping duplicate Host element (%s)",url.c_str());
+ continue;
+ }
+ m_map[url] = o;
+ log.debug("Added <Host> mapping for %s",url.c_str());
+
+ url += ":80";
+ if (m_map.count(url)) {
+ log.warn("Skipping duplicate Host element (%s)",url.c_str());
+ continue;
+ }
+ m_map[url] = o;
+ log.debug("Added <Host> mapping for %s",url.c_str());
+
+ url = "https://" + dup;
+ if (m_map.count(url)) {
+ log.warn("Skipping duplicate Host element (%s)",url.c_str());
+ continue;
+ }
+ m_map[url] = o;
+ log.debug("Added <Host> mapping for %s",url.c_str());
+
+ url += ":443";
+ if (m_map.count(url)) {
+ log.warn("Skipping duplicate Host element (%s)",url.c_str());
+ continue;
+ }
+ m_map[url] = o;
+ log.debug("Added <Host> mapping for %s",url.c_str());
+ }
+ }
+}
+
+const Override* XMLRequestMapperImpl::findOverride(const char* vhost, const HTTPRequest& request) const
+{
+ const Override* o = nullptr;
+ map< string,boost::shared_ptr<Override> >::const_iterator i = m_map.find(vhost);
+ if (i != m_map.end())
+ o = i->second.get();
+ else {
+ for (vector< pair< boost::shared_ptr<RegularExpression>,boost::shared_ptr<Override> > >::const_iterator re = m_regexps.begin(); !o && re != m_regexps.end(); ++re) {
+ try{
+ if (re->first->matches(vhost))
+ o=re->second.get();
+ } catch (XMLException& ex) {
+ auto_ptr_char tmp(ex.getMessage());
+ throw ConfigurationException("Caught exception while matching HostRegex : $1", params(1, tmp.get()));
+ }
+
+ }
+ }
+
+ return o ? o->locate(request) : this;
+}
+
+pair<bool,DOMElement*> XMLRequestMapper::background_load()
+{
+ // Load from source using base class.
+ pair<bool,DOMElement*> raw = ReloadableXMLFile::load();
+
+ // If we own it, wrap it.
+ XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : nullptr);
+
+ scoped_ptr<XMLRequestMapperImpl> impl(new XMLRequestMapperImpl(raw.second, m_log));
+
+ // If we held the document, transfer it to the impl. If we didn't, it's a no-op.
+ impl->setDocument(docjanitor.release());
+
+ // Perform the swap inside a lock.
+ if (m_lock)
+ m_lock->wrlock();
+ SharedLock locker(m_lock, false);
+ m_impl.swap(impl);
+
+ return make_pair(false,(DOMElement*)nullptr);
+}
+
+RequestMapper::Settings XMLRequestMapper::getSettings(const HTTPRequest& request) const
+{
+ try {
+ string normalizedhost(request.getHostname());
+ to_lower(normalizedhost);
+ string vhost = string(request.getScheme()) + "://" + normalizedhost + ':' + lexical_cast<string>(request.getPort());
+ const Override* o = m_impl->findOverride(vhost.c_str(), request);
+ return Settings(o, o->getAC());
+ }
+ catch (XMLException& ex) {
+ auto_ptr_char tmp(ex.getMessage());
+ m_log.error("caught exception while locating content settings: %s", tmp.get());
+ throw ConfigurationException("XML-based RequestMapper failed to retrieve content settings.");
+ }
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-shibboleth/shibboleth-sp2.git
More information about the Pkg-shibboleth-devel
mailing list