SSPCPP-616 - fix tuple namespaces and string literal errors
[shibboleth/cpp-sp.git] / shibsp / impl / XMLServiceProvider.cpp
index 96f90f8..59c86eb 100644 (file)
@@ -50,7 +50,7 @@
 #endif
 #include <algorithm>
 #include <boost/bind.hpp>
-#include <boost/scoped_ptr.hpp>
+#include <boost/lexical_cast.hpp>
 #include <boost/shared_ptr.hpp>
 #include <boost/algorithm/string.hpp>
 #include <boost/tuple/tuple.hpp>
 # include "attribute/resolver/AttributeResolver.h"
 # include "security/PKIXTrustEngine.h"
 # include "security/SecurityPolicyProvider.h"
-# include <saml/SAMLConfig.h>
+# include <saml/exceptions.h>
 # include <saml/version.h>
+# include <saml/SAMLConfig.h>
 # include <saml/binding/ArtifactMap.h>
 # include <saml/binding/SAMLArtifact.h>
 # include <saml/saml1/core/Assertions.h>
 # include <saml/saml2/core/Assertions.h>
 # include <saml/saml2/binding/SAML2ArtifactType0004.h>
+# include <saml/saml2/metadata/EntityMatcher.h>
 # include <saml/saml2/metadata/Metadata.h>
 # include <saml/saml2/metadata/MetadataProvider.h>
 # include <saml/util/SAMLConstants.h>
@@ -162,6 +164,7 @@ namespace {
         }
         const PropertySet* getRelyingParty(const EntityDescriptor* provider) const;
         const PropertySet* getRelyingParty(const XMLCh* entityID) const;
+
         const vector<const XMLCh*>* getAudiences() const {
             return (m_audiences.empty() && m_base) ? m_base->getAudiences() : &m_audiences;
         }
@@ -184,6 +187,7 @@ namespace {
         const vector<const Handler*>& getAssertionConsumerServicesByBinding(const XMLCh* binding) const;
         const Handler* getHandler(const char* path) const;
         void getHandlers(vector<const Handler*>& handlers) const;
+        void limitRedirect(const GenericRequest& request, const char* url) const;
 
         void receive(DDF& in, ostream& out) {
             // Only current function is to return the headers to clear.
@@ -235,7 +239,8 @@ namespace {
         vector<const XMLCh*> m_audiences;
 
         // RelyingParty properties
-        map< xstring,boost::shared_ptr<PropertySet> > m_partyMap;
+        map< xstring,boost::shared_ptr<PropertySet> > m_partyMap;   // name-based matching
+        vector< pair< boost::shared_ptr<EntityMatcher>,boost::shared_ptr<PropertySet> > > m_partyVec;  // plugin-based matching
 #endif
         vector<string> m_remoteUsers,m_frontLogout,m_backLogout;
 
@@ -272,6 +277,17 @@ namespace {
             if (m_artifactResolutionDefault) return m_artifactResolutionDefault->getInt("index");
             return m_base ? m_base->getArtifactEndpointIndex() : make_pair(false,0);
         }
+
+        enum {
+            REDIRECT_LIMIT_INHERIT,
+            REDIRECT_LIMIT_NONE,
+            REDIRECT_LIMIT_EXACT,
+            REDIRECT_LIMIT_HOST,
+            REDIRECT_LIMIT_WHITELIST,
+            REDIRECT_LIMIT_EXACT_WHITELIST,
+            REDIRECT_LIMIT_HOST_WHITELIST
+        } m_redirectLimit;
+        vector<string> m_redirectWhitelist;
     };
 
     // Top-level configuration implementation
@@ -288,7 +304,7 @@ namespace {
 #ifndef SHIBSP_LITE
         scoped_ptr<TransactionLog> m_tranLog;
         scoped_ptr<SecurityPolicyProvider> m_policy;
-        vector< tuple<string,string,string> > m_transportOptions;
+        vector< boost::tuple<string,string,string> > m_transportOptions;
 #endif
         scoped_ptr<RequestMapper> m_requestMapper;
         map< string,boost::shared_ptr<Application> > m_appmap;
@@ -319,7 +335,7 @@ namespace {
 #endif
     {
     public:
-        XMLConfig(const DOMElement* e) : ReloadableXMLFile(e, Category::getInstance(SHIBSP_LOGCAT".Config")) {}
+        XMLConfig(const DOMElement* e) : ReloadableXMLFile(e, Category::getInstance(SHIBSP_LOGCAT ".Config")) {}
 
         void init() {
             background_load();
@@ -423,7 +439,7 @@ namespace {
 
         bool setTransportOptions(SOAPTransport& transport) const {
             bool ret = true;
-            for (vector< tuple<string,string,string> >::const_iterator opt = m_impl->m_transportOptions.begin();
+            for (vector< boost::tuple<string,string,string> >::const_iterator opt = m_impl->m_transportOptions.begin();
                     opt != m_impl->m_transportOptions.end(); ++opt) {
                 if (!transport.setProviderOption(opt->get<0>().c_str(), opt->get<1>().c_str(), opt->get<2>().c_str())) {
                     m_log.error("failed to set SOAPTransport option (%s)", opt->get<1>().c_str());
@@ -487,6 +503,7 @@ namespace {
     static const XMLCh _option[] =              UNICODE_LITERAL_6(o,p,t,i,o,n);
     static const XMLCh OutOfProcess[] =         UNICODE_LITERAL_12(O,u,t,O,f,P,r,o,c,e,s,s);
     static const XMLCh _path[] =                UNICODE_LITERAL_4(p,a,t,h);
+    static const XMLCh _policyId[] =            UNICODE_LITERAL_8(p,o,l,i,c,y,I,d);
     static const XMLCh _ProtocolProvider[] =    UNICODE_LITERAL_16(P,r,o,t,o,c,o,l,P,r,o,v,i,d,e,r);
     static const XMLCh _provider[] =            UNICODE_LITERAL_8(p,r,o,v,i,d,e,r);
     static const XMLCh RelyingParty[] =         UNICODE_LITERAL_12(R,e,l,y,i,n,g,P,a,r,t,y);
@@ -527,10 +544,71 @@ XMLApplication::XMLApplication(
 #ifdef _DEBUG
     xmltooling::NDC ndc("XMLApplication");
 #endif
-    Category& log=Category::getInstance(SHIBSP_LOGCAT".Application");
+    Category& log = Category::getInstance(SHIBSP_LOGCAT ".Application");
 
     // First load any property sets.
-    load(e, nullptr, this);
+    map<string,string> remapper;
+    remapper["relayStateLimit"] = "redirectLimit";
+    remapper["relayStateWhitelist"] = "redirectWhitelist";
+    load(e, nullptr, this, &remapper);
+
+    // Process redirect limit policy. Do this before assigning the parent pointer
+    // to ensure we get only our Sessions element.
+    const PropertySet* sessionProps = getPropertySet("Sessions");
+    if (sessionProps) {
+        pair<bool,const char*> prop = sessionProps->getString("redirectLimit");
+        if (prop.first) {
+            if (!strcmp(prop.second, "none"))
+                m_redirectLimit = REDIRECT_LIMIT_NONE;
+            else if (!strcmp(prop.second, "exact"))
+                m_redirectLimit = REDIRECT_LIMIT_EXACT;
+            else if (!strcmp(prop.second, "host"))
+                m_redirectLimit = REDIRECT_LIMIT_HOST;
+            else {
+                if (!strcmp(prop.second, "exact+whitelist"))
+                    m_redirectLimit = REDIRECT_LIMIT_EXACT_WHITELIST;
+                else if (!strcmp(prop.second, "host+whitelist"))
+                    m_redirectLimit = REDIRECT_LIMIT_HOST_WHITELIST;
+                else if (!strcmp(prop.second, "whitelist"))
+                    m_redirectLimit = REDIRECT_LIMIT_WHITELIST;
+                else
+                    throw ConfigurationException("Unrecognized redirectLimit setting ($1)", params(1, prop.second));
+                prop = sessionProps->getString("redirectWhitelist");
+                if (prop.first) {
+                    string dup(prop.second);
+                    trim(dup);
+                    split(m_redirectWhitelist, dup, is_space(), algorithm::token_compress_on);
+                }
+            }
+        }
+        else {
+            m_redirectLimit = base ? REDIRECT_LIMIT_INHERIT : REDIRECT_LIMIT_NONE;
+        }
+
+        // Audit some additional settings for logging purposes.
+        prop = sessionProps->getString("cookieProps");
+        if (!prop.first) {
+            log.warn("empty/missing cookieProps setting, set to \"https\" for SSL/TLS-only usage");
+        }
+        else if (!strcmp(prop.second, "http")) {
+            log.warn("insecure cookieProps setting, set to \"https\" for SSL/TLS-only usage");
+        }
+        else if (strcmp(prop.second, "https")) {
+            if (!strstr(prop.second, ";secure") && !strstr(prop.second, "; secure"))
+                log.warn("custom cookieProps setting should include \"; secure\" for SSL/TLS-only usage");
+            else if (!strstr(prop.second, ";HttpOnly") && !strstr(prop.second, "; HttpOnly"))
+                log.warn("custom cookieProps setting should include \"; HttpOnly\", site is vulnerable to client-side cookie theft");
+        }
+
+        pair<bool,bool> handlerSSL = sessionProps->getBool("handlerSSL");
+        if (handlerSSL.first && !handlerSSL.second)
+            log.warn("handlerSSL should be enabled for SSL/TLS-enabled web sites");
+    }
+    else {
+        m_redirectLimit = base ? REDIRECT_LIMIT_INHERIT : REDIRECT_LIMIT_NONE;
+    }
+
+    // Assign parent.
     if (base)
         setParent(base);
 
@@ -587,7 +665,7 @@ XMLApplication::XMLApplication(
             if (m_metadata)
                 m_metadata->init();
             else if (!m_base)
-                log.crit("no MetadataProvider available, configuration is probably unusable");
+                log.warn("no MetadataProvider available, configure at least one for standard SSO usage");
         }
         catch (std::exception& ex) {
             log.crit("error initializing MetadataProvider: %s", ex.what());
@@ -629,9 +707,17 @@ XMLApplication::XMLApplication(
             rp->setParent(this);
             m_partyMap[child->getAttributeNS(nullptr, saml2::Attribute::NAME_ATTRIB_NAME)] = rp;
         }
+        else if (child->hasAttributeNS(nullptr, _type)) {
+            string emtype(XMLHelper::getAttrString(child, nullptr, _type));
+            boost::shared_ptr<EntityMatcher> em(SAMLConfig::getConfig().EntityMatcherManager.newPlugin(emtype, child));
+            boost::shared_ptr<DOMPropertySet> rp(new DOMPropertySet());
+            rp->load(child, nullptr, this);
+            rp->setParent(this);
+            m_partyVec.push_back(make_pair(em, rp));
+        }
         child = XMLHelper::getNextSiblingElement(child, RelyingParty);
     }
-    if (base && m_partyMap.empty() && !base->m_partyMap.empty()) {
+    if (base && m_partyMap.empty() && m_partyVec.empty() && (!base->m_partyMap.empty() || !base->m_partyVec.empty())) {
         // For inheritance of RPs to work, we have to pull them in to the override by cloning the DOM.
         child = XMLHelper::getFirstChildElement(base->getElement(), RelyingParty);
         while (child) {
@@ -642,6 +728,15 @@ XMLApplication::XMLApplication(
                 rp->setParent(this);
                 m_partyMap[rpclone->getAttributeNS(nullptr, saml2::Attribute::NAME_ATTRIB_NAME)] = rp;
             }
+            else if (child->hasAttributeNS(nullptr, _type)) {
+                DOMElement* rpclone = static_cast<DOMElement*>(child->cloneNode(true));
+                string emtype(XMLHelper::getAttrString(rpclone, nullptr, _type));
+                boost::shared_ptr<EntityMatcher> em(SAMLConfig::getConfig().EntityMatcherManager.newPlugin(emtype, rpclone));
+                boost::shared_ptr<DOMPropertySet> rp(new DOMPropertySet());
+                rp->load(rpclone, nullptr, this);
+                rp->setParent(this);
+                m_partyVec.push_back(make_pair(em, rp));
+            }
             child = XMLHelper::getNextSiblingElement(child, RelyingParty);
         }
     }
@@ -738,6 +833,7 @@ void XMLApplication::doAttributeInfo()
     pair<bool,const char*> attributes = getString("REMOTE_USER");
     if (attributes.first) {
         string dup(attributes.second);
+        trim(dup);
         split(m_remoteUsers, dup, is_space(), algorithm::token_compress_on);
     }
 
@@ -757,6 +853,7 @@ void XMLApplication::doAttributeInfo()
             }
 
             string dup(attributes.second);
+            trim(dup);
             vector<string> headerNames;
             split(headerNames, dup, is_space(), algorithm::token_compress_on);
             for (vector<string>::const_iterator h = headerNames.begin(); h != headerNames.end(); ++h) {
@@ -986,9 +1083,13 @@ void XMLApplication::doSSO(const ProtocolProvider& pp, set<string>& protocols, D
 {
     if (!e->hasChildNodes())
         return;
+    DOMNamedNodeMap* ssoprops = e->getAttributes();
+    XMLSize_t ssopropslen = ssoprops ? ssoprops->getLength() : 0;
 
     SPConfig& conf = SPConfig::getConfig();
 
+    int index = 0; // track ACS indexes globally across all protocols
+
     // Tokenize the protocol list inside the element.
     XMLStringTokenizer prottokens(e->getTextContent());
     while (prottokens.hasMoreTokens()) {
@@ -1018,16 +1119,29 @@ void XMLApplication::doSSO(const ProtocolProvider& pp, set<string>& protocols, D
         const vector<const PropertySet*>& bindings = pp.getBindings(prot.get(), "SSO");
         if (!bindings.empty()) {
             log.info("auto-configuring SSO endpoints for protocol (%s)", prot.get());
-            int index = 0;
             pair<bool,const XMLCh*> idprop,pathprop;
             for (vector<const PropertySet*>::const_iterator b = bindings.begin(); b != bindings.end(); ++b, ++index) {
                 idprop = (*b)->getXMLString("id");
                 pathprop = (*b)->getXMLString("path");
                 if (idprop.first && pathprop.first) {
                     DOMElement* acsdom = e->getOwnerDocument()->createElementNS(samlconstants::SAML20MD_NS, _AssertionConsumerService);
+
+                    // Copy in any attributes from the <SSO> element so they can be accessed as properties in the ACS handler.
+                    for (XMLSize_t p = 0; p < ssopropslen; ++p) {
+                        DOMNode* ssoprop = ssoprops->item(p);
+                        if (ssoprop->getNodeType() == DOMNode::ATTRIBUTE_NODE) {
+                            acsdom->setAttributeNS(
+                                ((DOMAttr*)ssoprop)->getNamespaceURI(),
+                                ((DOMAttr*)ssoprop)->getLocalName(),
+                                ((DOMAttr*)ssoprop)->getValue()
+                                );
+                        }
+                    }
+
+                    // Set necessary properties based on context.
                     acsdom->setAttributeNS(nullptr, Binding, idprop.second);
                     acsdom->setAttributeNS(nullptr, Location, pathprop.second);
-                    xstring indexbuf(chDigit_1 + (index % 10), 1);
+                    xstring indexbuf(1, chDigit_1 + (index % 10));
                     if (index / 10)
                         indexbuf = (XMLCh)(chDigit_1 + (index / 10)) + indexbuf;
                     acsdom->setAttributeNS(nullptr, _index, indexbuf.c_str());
@@ -1107,6 +1221,8 @@ void XMLApplication::doLogout(const ProtocolProvider& pp, set<string>& protocols
 {
     if (!e->hasChildNodes())
         return;
+    DOMNamedNodeMap* sloprops = e->getAttributes();
+    XMLSize_t slopropslen = sloprops ? sloprops->getLength() : 0;
 
     SPConfig& conf = SPConfig::getConfig();
 
@@ -1147,8 +1263,24 @@ void XMLApplication::doLogout(const ProtocolProvider& pp, set<string>& protocols
                 pathprop = (*b)->getXMLString("path");
                 if (idprop.first && pathprop.first) {
                     DOMElement* slodom = e->getOwnerDocument()->createElementNS(samlconstants::SAML20MD_NS, _SingleLogoutService);
+
+                    // Copy in any attributes from the <Logout> element so they can be accessed as properties in the SLO handler.
+                    for (XMLSize_t p = 0; p < slopropslen; ++p) {
+                        DOMNode* sloprop = sloprops->item(p);
+                        if (sloprop->getNodeType() == DOMNode::ATTRIBUTE_NODE) {
+                            slodom->setAttributeNS(
+                                ((DOMAttr*)sloprop)->getNamespaceURI(),
+                                ((DOMAttr*)sloprop)->getLocalName(),
+                                ((DOMAttr*)sloprop)->getValue()
+                                );
+                        }
+                    }
+
+                    // Set necessary properties based on context.
                     slodom->setAttributeNS(nullptr, Binding, idprop.second);
                     slodom->setAttributeNS(nullptr, Location, pathprop.second);
+                    if (e->hasAttributeNS(nullptr, _policyId))
+                        slodom->setAttributeNS(shibspconstants::SHIB2SPCONFIG_NS, _policyId, e->getAttributeNS(nullptr, _policyId));
 
                     log.info("adding SingleLogoutService for Binding (%s) at (%s)", (*b)->getString("id").second, (*b)->getString("path").second);
                     boost::shared_ptr<Handler> handler(
@@ -1195,6 +1327,8 @@ void XMLApplication::doNameIDMgmt(const ProtocolProvider& pp, set<string>& proto
 {
     if (!e->hasChildNodes())
         return;
+    DOMNamedNodeMap* nimprops = e->getAttributes();
+    XMLSize_t nimpropslen = nimprops ? nimprops->getLength() : 0;
 
     SPConfig& conf = SPConfig::getConfig();
 
@@ -1213,8 +1347,24 @@ void XMLApplication::doNameIDMgmt(const ProtocolProvider& pp, set<string>& proto
                 pathprop = (*b)->getXMLString("path");
                 if (idprop.first && pathprop.first) {
                     DOMElement* nimdom = e->getOwnerDocument()->createElementNS(samlconstants::SAML20MD_NS, _ManageNameIDService);
+
+                    // Copy in any attributes from the <NameIDMgmt> element so they can be accessed as properties in the NIM handler.
+                    for (XMLSize_t p = 0; p < nimpropslen; ++p) {
+                        DOMNode* nimprop = nimprops->item(p);
+                        if (nimprop->getNodeType() == DOMNode::ATTRIBUTE_NODE) {
+                            nimdom->setAttributeNS(
+                                ((DOMAttr*)nimprop)->getNamespaceURI(),
+                                ((DOMAttr*)nimprop)->getLocalName(),
+                                ((DOMAttr*)nimprop)->getValue()
+                                );
+                        }
+                    }
+
+                    // Set necessary properties based on context.
                     nimdom->setAttributeNS(nullptr, Binding, idprop.second);
                     nimdom->setAttributeNS(nullptr, Location, pathprop.second);
+                    if (e->hasAttributeNS(nullptr, _policyId))
+                        nimdom->setAttributeNS(shibspconstants::SHIB2SPCONFIG_NS, _policyId, e->getAttributeNS(nullptr, _policyId));
 
                     log.info("adding ManageNameIDService for Binding (%s) at (%s)", (*b)->getString("id").second, (*b)->getString("path").second);
                     boost::shared_ptr<Handler> handler(
@@ -1249,20 +1399,21 @@ void XMLApplication::doArtifactResolution(const ProtocolProvider& pp, const char
 {
     SPConfig& conf = SPConfig::getConfig();
 
+    int index = 0; // track indexes globally across all protocols
+
     // Look for incoming bindings.
     const vector<const PropertySet*>& bindings = pp.getBindings(protocol, "ArtifactResolution");
     if (!bindings.empty()) {
         log.info("auto-configuring ArtifactResolution endpoints for protocol (%s)", protocol);
-        int index = 0;
         pair<bool,const XMLCh*> idprop,pathprop;
-        for (vector<const PropertySet*>::const_iterator b = bindings.begin(); b != bindings.end(); ++b) {
+        for (vector<const PropertySet*>::const_iterator b = bindings.begin(); b != bindings.end(); ++b, ++index) {
             idprop = (*b)->getXMLString("id");
             pathprop = (*b)->getXMLString("path");
             if (idprop.first && pathprop.first) {
                 DOMElement* artdom = e->getOwnerDocument()->createElementNS(samlconstants::SAML20MD_NS, _ArtifactResolutionService);
                 artdom->setAttributeNS(nullptr, Binding, idprop.second);
                 artdom->setAttributeNS(nullptr, Location, pathprop.second);
-                xstring indexbuf(chDigit_1 + (index % 10), 1);
+                xstring indexbuf(1, chDigit_1 + (index % 10));
                 if (index / 10)
                     indexbuf = (XMLCh)(chDigit_1 + (index / 10)) + indexbuf;
                 artdom->setAttributeNS(nullptr, _index, indexbuf.c_str());
@@ -1393,9 +1544,19 @@ const PropertySet* XMLApplication::getRelyingParty(const EntityDescriptor* provi
     if (!provider)
         return this;
 
+    // Check for exact match on name.
     map< xstring,boost::shared_ptr<PropertySet> >::const_iterator i = m_partyMap.find(provider->getEntityID());
     if (i != m_partyMap.end())
         return i->second.get();
+
+    // Check for extensible matching.
+    vector < pair< boost::shared_ptr<EntityMatcher>,boost::shared_ptr<PropertySet> > >::const_iterator j;
+    for (j = m_partyVec.begin(); j != m_partyVec.end(); ++j) {
+        if (j->first->matches(*provider))
+            return j->second.get();
+    }
+
+    // Check for group match.
     const EntitiesDescriptor* group = dynamic_cast<const EntitiesDescriptor*>(provider->getParent());
     while (group) {
         if (group->getName()) {
@@ -1603,6 +1764,43 @@ void XMLApplication::getHandlers(vector<const Handler*>& handlers) const
     }
 }
 
+void XMLApplication::limitRedirect(const GenericRequest& request, const char* url) const
+{
+    if (!url || *url == '/')
+        return;
+    if (m_redirectLimit == REDIRECT_LIMIT_INHERIT)
+        return m_base->limitRedirect(request, url);
+    if (m_redirectLimit != REDIRECT_LIMIT_NONE) {
+        vector<string> whitelist;
+        if (m_redirectLimit == REDIRECT_LIMIT_EXACT || m_redirectLimit == REDIRECT_LIMIT_EXACT_WHITELIST) {
+            // Scheme and hostname have to match.
+            if (request.isDefaultPort()) {
+                whitelist.push_back(string(request.getScheme()) + "://" + request.getHostname() + '/');
+            }
+            whitelist.push_back(string(request.getScheme()) + "://" + request.getHostname() + ':' + lexical_cast<string>(request.getPort()) + '/');
+        }
+        else if (m_redirectLimit == REDIRECT_LIMIT_HOST || m_redirectLimit == REDIRECT_LIMIT_HOST_WHITELIST) {
+            // Allow any scheme or port.
+            whitelist.push_back(string("https://") + request.getHostname() + '/');
+            whitelist.push_back(string("http://") + request.getHostname() + '/');
+            whitelist.push_back(string("https://") + request.getHostname() + ':');
+            whitelist.push_back(string("http://") + request.getHostname() + ':');
+        }
+
+        static bool (*startsWithI)(const char*,const char*) = XMLString::startsWithI;
+        if (!whitelist.empty() && find_if(whitelist.begin(), whitelist.end(),
+                boost::bind(startsWithI, url, boost::bind(&string::c_str, _1))) != whitelist.end()) {
+            return;
+        }
+        else if (!m_redirectWhitelist.empty() && find_if(m_redirectWhitelist.begin(), m_redirectWhitelist.end(),
+                boost::bind(startsWithI, url, boost::bind(&string::c_str, _1))) != m_redirectWhitelist.end()) {
+            return;
+        }
+        Category::getInstance(SHIBSP_LOGCAT ".Application").warn("redirectLimit policy enforced, blocked redirect to (%s)", url);
+        throw opensaml::SecurityPolicyException("Blocked unacceptable redirect location.");
+    }
+}
+
 #ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE
 short
 #else
@@ -1799,6 +1997,8 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, XMLConfig* outer,
     const DOMElement* SHIRE=XMLHelper::getFirstChildElement(e, InProcess);
 
     // Initialize logging manually in order to redirect log messages as soon as possible.
+    // If no explicit config is supplied, we now assume the caller has done this, so that
+    // setuid processes can potentially do this as root.
     if (conf.isEnabled(SPConfig::Logging)) {
         string logconf;
         if (conf.isEnabled(SPConfig::OutOfProcess))
@@ -1807,15 +2007,6 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, XMLConfig* outer,
             logconf = XMLHelper::getAttrString(SHIRE, nullptr, logger);
         if (logconf.empty())
             logconf = XMLHelper::getAttrString(e, nullptr, logger);
-        if (logconf.empty() && !getenv("SHIBSP_LOGGING")) {
-            // No properties found, so default them.
-            if (conf.isEnabled(SPConfig::OutOfProcess) && !conf.isEnabled(SPConfig::InProcess))
-                logconf = "shibd.logger";
-            else if (!conf.isEnabled(SPConfig::OutOfProcess) && conf.isEnabled(SPConfig::InProcess))
-                logconf = "native.logger";
-            else
-                logconf = "shibboleth.logger";
-        }
         if (!logconf.empty()) {
             log.debug("loading new logging configuration from (%s), check log destination for status of configuration", logconf.c_str());
             if (!XMLToolingConfig::getConfig().log_config(logconf.c_str()))
@@ -1880,6 +2071,7 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, XMLConfig* outer,
         if (unsafe.first) {
             HTTPResponse::getAllowedSchemes().clear();
             string schemes(unsafe.second);
+            trim(schemes);
             split(HTTPResponse::getAllowedSchemes(), schemes, is_space(), algorithm::token_compress_on);
         }
 
@@ -1953,7 +2145,7 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, XMLConfig* outer,
         // For backward compatibility, wrap in a plugin element.
         DOMElement* polwrapper = e->getOwnerDocument()->createElementNS(nullptr, _SecurityPolicyProvider);
         polwrapper->appendChild(child);
-        log.info("building SecurityPolicyProvider of type %s...", XML_SECURITYPOLICY_PROVIDER);
+        log.warn("deprecated/legacy SecurityPolicy configuration, consider externalizing with <SecurityPolicyProvider>");
         m_policy.reset(conf.SecurityPolicyProviderManager.newPlugin(XML_SECURITYPOLICY_PROVIDER, polwrapper));
     }
     else {
@@ -1962,22 +2154,32 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, XMLConfig* outer,
     }
 
     if (first) {
-        if (!m_policy->getAlgorithmBlacklist().empty()) {
+        if (!m_policy->getAlgorithmWhitelist().empty()) {
 #ifdef SHIBSP_XMLSEC_WHITELISTING
-            for_each(
-                m_policy->getAlgorithmBlacklist().begin(), m_policy->getAlgorithmBlacklist().end(),
-                boost::bind(&XSECPlatformUtils::blacklistAlgorithm, boost::bind(&xstring::c_str, _1))
-                );
+            for (vector<xstring>::const_iterator white = m_policy->getAlgorithmWhitelist().begin();
+                    white != m_policy->getAlgorithmWhitelist().end(); ++white) {
+                XSECPlatformUtils::whitelistAlgorithm(white->c_str());
+                auto_ptr_char whitelog(white->c_str());
+                log.info("explicitly whitelisting security algorithm (%s)", whitelog.get());
+            }
 #else
             log.crit("XML-Security-C library prior to 1.6.0 does not support algorithm white/blacklists");
 #endif
         }
-        else if (!m_policy->getAlgorithmWhitelist().empty()) {
+        else if (!m_policy->getDefaultAlgorithmBlacklist().empty() || !m_policy->getAlgorithmBlacklist().empty()) {
 #ifdef SHIBSP_XMLSEC_WHITELISTING
-            for_each(
-                m_policy->getAlgorithmWhitelist().begin(), m_policy->getAlgorithmWhitelist().end(),
-                boost::bind(&XSECPlatformUtils::whitelistAlgorithm, boost::bind(&xstring::c_str, _1))
-                );
+            for (vector<xstring>::const_iterator black = m_policy->getDefaultAlgorithmBlacklist().begin();
+                    black != m_policy->getDefaultAlgorithmBlacklist().end(); ++black) {
+                XSECPlatformUtils::blacklistAlgorithm(black->c_str());
+                auto_ptr_char blacklog(black->c_str());
+                log.info("automatically blacklisting security algorithm (%s)", blacklog.get());
+            }
+            for (vector<xstring>::const_iterator black = m_policy->getAlgorithmBlacklist().begin();
+                    black != m_policy->getAlgorithmBlacklist().end(); ++black) {
+                XSECPlatformUtils::blacklistAlgorithm(black->c_str());
+                auto_ptr_char blacklog(black->c_str());
+                log.info("explicitly blacklisting security algorithm (%s)", blacklog.get());
+            }
 #else
             log.crit("XML-Security-C library prior to 1.6.0 does not support algorithm white/blacklists");
 #endif
@@ -1992,7 +2194,7 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, XMLConfig* outer,
             string option(XMLHelper::getAttrString(child, nullptr, _option));
             auto_ptr_char value(child->getFirstChild()->getNodeValue());
             if (!provider.empty() && !option.empty() && value.get() && *value.get()) {
-                m_transportOptions.push_back(make_tuple(provider, option, string(value.get())));
+                m_transportOptions.push_back(boost::make_tuple(provider, option, string(value.get())));
             }
         }
         child = XMLHelper::getPreviousSiblingElement(child, TransportOption);
@@ -2039,7 +2241,9 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, XMLConfig* outer,
             pair<bool,const char*> extraAuthTypes = inprocs->getString("extraAuthTypes");
             if (extraAuthTypes.first) {
                 string types(extraAuthTypes.second);
+                trim(types);
                 split(outer->m_authTypes, types, is_space(), algorithm::token_compress_on);
+                outer->m_authTypes.insert("shibboleth");
             }
         }
     }
@@ -2067,7 +2271,7 @@ void XMLConfig::receive(DDF& in, ostream& out)
             }
         }
         else {
-            Category::getInstance(SHIBSP_LOGCAT".ServiceProvider").error(
+            Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider").error(
                 "Storage-backed RelayState with invalid StorageService ID (%s)", id
                 );
         }
@@ -2094,7 +2298,7 @@ void XMLConfig::receive(DDF& in, ostream& out)
                 storage->createText("RelayState", rsKey.c_str(), value, time(nullptr) + 600);
         }
         else {
-            Category::getInstance(SHIBSP_LOGCAT".ServiceProvider").error(
+            Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider").error(
                 "Storage-backed RelayState with invalid StorageService ID (%s)", id
                 );
         }
@@ -2118,7 +2322,7 @@ void XMLConfig::receive(DDF& in, ostream& out)
             }
         }
         else {
-            Category::getInstance(SHIBSP_LOGCAT".ServiceProvider").error(
+            Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider").error(
                 "Storage-backed PostData with invalid StorageService ID (%s)", id
                 );
         }
@@ -2148,7 +2352,7 @@ void XMLConfig::receive(DDF& in, ostream& out)
             storage->createText("PostData", rsKey.c_str(), params.str().c_str(), time(nullptr) + 600);
         }
         else {
-            Category::getInstance(SHIBSP_LOGCAT".ServiceProvider").error(
+            Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider").error(
                 "Storage-backed PostData with invalid StorageService ID (%s)", id
                 );
         }