From 31bb7f75c45c9fed7c7adad32e82f6bfdfb03bd3 Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Wed, 8 Feb 2012 16:49:49 +0000 Subject: [PATCH] https://issues.shibboleth.net/jira/browse/SSPCPP-245 --- Shibboleth.sln | 2 +- configs/Makefile.am | 1 + configs/attrChecker.html | 47 ++++++ configs/example-shibboleth2.xml | 12 +- configs/shibboleth2.xml | 1 + configs/win-shibboleth2.xml | 1 + doc/main.css | 20 ++- schemas/shibboleth-2.0-native-sp-config.xsd | 1 + shibsp/Makefile.am | 1 + shibsp/handler/Handler.h | 3 + shibsp/handler/impl/AbstractHandler.cpp | 2 + shibsp/handler/impl/AttributeCheckerHandler.cpp | 208 ++++++++++++++++++++++++ shibsp/impl/XMLAccessControl.cpp | 28 +++- shibsp/shibsp-lite.vcxproj | 3 +- shibsp/shibsp-lite.vcxproj.filters | 3 + shibsp/shibsp.vcxproj | 1 + shibsp/shibsp.vcxproj.filters | 3 + 17 files changed, 325 insertions(+), 12 deletions(-) create mode 100644 configs/attrChecker.html create mode 100644 shibsp/handler/impl/AttributeCheckerHandler.cpp diff --git a/Shibboleth.sln b/Shibboleth.sln index 9468764..7b29ba4 100644 --- a/Shibboleth.sln +++ b/Shibboleth.sln @@ -36,6 +36,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Config", "Config", "{2543BC configs\apache.config.in = configs\apache.config.in configs\apache2.config.in = configs\apache2.config.in configs\apache22.config.in = configs\apache22.config.in + configs\attrChecker.html = configs\attrChecker.html configs\attribute-map.xml = configs\attribute-map.xml configs\attribute-policy.xml = configs\attribute-policy.xml configs\bindingTemplate.html = configs\bindingTemplate.html @@ -55,7 +56,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Config", "Config", "{2543BC configs\postTemplate.html = configs\postTemplate.html configs\protocols.xml = configs\protocols.xml configs\security-policy.xml = configs\security-policy.xml - configs\sessionError.html = configs\sessionError.html configs\shibboleth2.xml = configs\shibboleth2.xml configs\shibd-debian.in = configs\shibd-debian.in configs\shibd-osx.plist.in = configs\shibd-osx.plist.in diff --git a/configs/Makefile.am b/configs/Makefile.am index 5224c4e..8028341 100644 --- a/configs/Makefile.am +++ b/configs/Makefile.am @@ -42,6 +42,7 @@ CONFIGFILES = \ console.logger \ syslog.logger \ accessError.html \ + attrChecker.html \ sessionError.html \ metadataError.html \ bindingTemplate.html \ diff --git a/configs/attrChecker.html b/configs/attrChecker.html new file mode 100644 index 0000000..c382db6 --- /dev/null +++ b/configs/attrChecker.html @@ -0,0 +1,47 @@ + + + + + + + + Insufficient Information + + + + +Logo +

We're sorry, but you cannot access this service at this time.

+ +

This service requires information about you that your identity provider +() +did not release. To gain access to this service, your identity provider +must release the required information.

+ + +

+

+Please visit + +the support page +this support page + +for further instructions. +
+

+
+ +

+You were trying to access the following URL: +

+

+ + +

For more information about this service, including what user information is required for access, +please visit our information page.

+
+ + + diff --git a/configs/example-shibboleth2.xml b/configs/example-shibboleth2.xml index cf952dc..dee3782 100644 --- a/configs/example-shibboleth2.xml +++ b/configs/example-shibboleth2.xml @@ -93,6 +93,8 @@ --> + + + @@ -237,7 +244,10 @@ - + + + + diff --git a/configs/shibboleth2.xml b/configs/shibboleth2.xml index 134cb66..ccea6dc 100644 --- a/configs/shibboleth2.xml +++ b/configs/shibboleth2.xml @@ -66,6 +66,7 @@ also add attributes with values that can be plugged into the templates. --> diff --git a/configs/win-shibboleth2.xml b/configs/win-shibboleth2.xml index c32f66c..448f3a7 100644 --- a/configs/win-shibboleth2.xml +++ b/configs/win-shibboleth2.xml @@ -109,6 +109,7 @@ also add attributes with values that can be plugged into the templates. --> diff --git a/doc/main.css b/doc/main.css index a11cebe..ee6493d 100644 --- a/doc/main.css +++ b/doc/main.css @@ -29,11 +29,25 @@ h2 { font-weight: bold; } +img { + margin-bottom: 15px; +} + .error { font-size: 10pt; font-weight: bold; } -img { - margin-bottom: 15px; -} \ No newline at end of file +.boxed-content { + margin-top: 2ex; + margin-right: 3em; + margin-bottom: 2ex; + margin-left: 3em; + padding-top: 0.5ex; + padding-right: 1em; + padding-bottom: 0.5ex; + padding-left: 1em; + border-width: 1px; + border-style: solid; + border-color: #999999; +} diff --git a/schemas/shibboleth-2.0-native-sp-config.xsd b/schemas/shibboleth-2.0-native-sp-config.xsd index db8a688..045bf39 100644 --- a/schemas/shibboleth-2.0-native-sp-config.xsd +++ b/schemas/shibboleth-2.0-native-sp-config.xsd @@ -649,6 +649,7 @@ + diff --git a/shibsp/Makefile.am b/shibsp/Makefile.am index d2b2358..f525df9 100644 --- a/shibsp/Makefile.am +++ b/shibsp/Makefile.am @@ -124,6 +124,7 @@ common_sources = \ handler/impl/AbstractHandler.cpp \ handler/impl/AssertionConsumerService.cpp \ handler/impl/AssertionLookup.cpp \ + handler/impl/AttributeCheckerHandler.cpp \ handler/impl/ChainingLogoutInitiator.cpp \ handler/impl/ChainingSessionInitiator.cpp \ handler/impl/CookieSessionInitiator.cpp \ diff --git a/shibsp/handler/Handler.h b/shibsp/handler/Handler.h index 5c93e1e..f64cfed 100644 --- a/shibsp/handler/Handler.h +++ b/shibsp/handler/Handler.h @@ -158,6 +158,9 @@ namespace shibsp { /** Handler for SAML 2.0 Artifact Resolution. */ #define SAML20_ARTIFACT_RESOLUTION_SERVICE "SAML2" + /** Handler for hooking new sessions with attribute checking. */ + #define ATTR_CHECKER_HANDLER "AttributeChecker" + /** Handler for metadata generation. */ #define DISCOVERY_FEED_HANDLER "DiscoveryFeed" diff --git a/shibsp/handler/impl/AbstractHandler.cpp b/shibsp/handler/impl/AbstractHandler.cpp index 9136242..c1b0f25 100644 --- a/shibsp/handler/impl/AbstractHandler.cpp +++ b/shibsp/handler/impl/AbstractHandler.cpp @@ -81,6 +81,7 @@ namespace shibsp { SHIBSP_DLLLOCAL PluginManager< Handler,string,pair >::Factory SAML2LogoutFactory; SHIBSP_DLLLOCAL PluginManager< Handler,string,pair >::Factory SAML2NameIDMgmtFactory; SHIBSP_DLLLOCAL PluginManager< Handler,string,pair >::Factory AssertionLookupFactory; + SHIBSP_DLLLOCAL PluginManager< Handler,string,pair >::Factory AttributeCheckerFactory; SHIBSP_DLLLOCAL PluginManager< Handler,string,pair >::Factory DiscoveryFeedFactory; SHIBSP_DLLLOCAL PluginManager< Handler,string,pair >::Factory MetadataGeneratorFactory; SHIBSP_DLLLOCAL PluginManager< Handler,string,pair >::Factory StatusHandlerFactory; @@ -121,6 +122,7 @@ void SHIBSP_API shibsp::registerHandlers() conf.ArtifactResolutionServiceManager.registerFactory(SAML20_BINDING_SOAP, SAML2ArtifactResolutionFactory); conf.HandlerManager.registerFactory(SAML20_BINDING_URI, AssertionLookupFactory); + conf.HandlerManager.registerFactory(ATTR_CHECKER_HANDLER, AttributeCheckerFactory); conf.HandlerManager.registerFactory(DISCOVERY_FEED_HANDLER, DiscoveryFeedFactory); conf.HandlerManager.registerFactory(METADATA_GENERATOR_HANDLER, MetadataGeneratorFactory); conf.HandlerManager.registerFactory(STATUS_HANDLER, StatusHandlerFactory); diff --git a/shibsp/handler/impl/AttributeCheckerHandler.cpp b/shibsp/handler/impl/AttributeCheckerHandler.cpp new file mode 100644 index 0000000..da37277 --- /dev/null +++ b/shibsp/handler/impl/AttributeCheckerHandler.cpp @@ -0,0 +1,208 @@ +/** + * 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. + */ + +/** + * AttributeCheckerHandler.cpp + * + * Handler for checking a session for required attributes. + */ + +#include "internal.h" +#include "AccessControl.h" +#include "Application.h" +#include "exceptions.h" +#include "ServiceProvider.h" +#include "SessionCache.h" +#include "SPRequest.h" +#include "attribute/Attribute.h" +#include "handler/AbstractHandler.h" +#include "util/TemplateParameters.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace shibsp; +using namespace xmltooling; +using namespace boost; +using namespace std; + +namespace shibsp { + +#if defined (_MSC_VER) + #pragma warning( push ) + #pragma warning( disable : 4250 ) +#endif + + class SHIBSP_DLLLOCAL Blocker : public DOMNodeFilter + { + public: +#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE + short +#else + FilterAction +#endif + acceptNode(const DOMNode* node) const { + return FILTER_REJECT; + } + }; + + static SHIBSP_DLLLOCAL Blocker g_Blocker; + + class SHIBSP_API AttributeCheckerHandler : public AbstractHandler + { + public: + AttributeCheckerHandler(const DOMElement* e, const char* appId); + virtual ~AttributeCheckerHandler() {} + + pair run(SPRequest& request, bool isHandler=true) const; + + private: + void flushSession(SPRequest& request) const { + try { + request.getApplication().getServiceProvider().getSessionCache()->remove(request.getApplication(), request, &request); + } + catch (std::exception&) { + } + } + + string m_template; + bool m_flushSession; + vector m_attributes; + scoped_ptr m_acl; + }; + +#if defined (_MSC_VER) + #pragma warning( pop ) +#endif + + Handler* SHIBSP_DLLLOCAL AttributeCheckerFactory(const pair& p) + { + return new AttributeCheckerHandler(p.first, p.second); + } + + static const XMLCh attributes[] = UNICODE_LITERAL_10(a,t,t,r,i,b,u,t,e,s); + static const XMLCh _flushSession[] = UNICODE_LITERAL_12(f,l,u,s,h,S,e,s,s,i,o,n); + static const XMLCh _template[] = UNICODE_LITERAL_8(t,e,m,p,l,a,t,e); +}; + +AttributeCheckerHandler::AttributeCheckerHandler(const DOMElement* e, const char* appId) + : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".AttributeCheckerHandler"), &g_Blocker) +{ + if (!SPConfig::getConfig().isEnabled(SPConfig::InProcess)) + return; + m_template = XMLHelper::getAttrString(e, nullptr, _template); + if (m_template.empty()) + throw ConfigurationException("AttributeChecker missing required template setting."); + XMLToolingConfig::getConfig().getPathResolver()->resolve(m_template, PathResolver::XMLTOOLING_CFG_FILE); + + m_flushSession = XMLHelper::getAttrBool(e, false, _flushSession); + + string attrs(XMLHelper::getAttrString(e, nullptr, attributes)); + if (!attrs.empty()) { + split(m_attributes, attrs, is_space(), algorithm::token_compress_on); + if (m_attributes.empty()) + throw ConfigurationException("AttributeChecker unable to parse attributes setting."); + } + else { + m_acl.reset(SPConfig::getConfig().AccessControlManager.newPlugin(XML_ACCESS_CONTROL, e)); + } +} + +pair AttributeCheckerHandler::run(SPRequest& request, bool isHandler) const +{ + // If the checking passes, we route to the return URL, target URL, or homeURL in that order. + const char* returnURL = request.getParameter("return"); + const char* target = request.getParameter("target"); + if (!returnURL) + returnURL = target; + if (returnURL) + request.getApplication().limitRedirect(request, returnURL); + else + returnURL = request.getApplication().getString("homeURL").second; + if (!returnURL) + returnURL = "/"; + + Session* session = nullptr; + try { + session = request.getSession(true, false, false); + if (!session) + request.log(SPRequest::SPWarn, "AttributeChecker found session unavailable immediately after creation"); + } + catch (std::exception& ex) { + request.log(SPRequest::SPWarn, string("AttributeChecker caught exception accessing session immediately after creation: ") + ex.what()); + } + + Locker sessionLocker(session, false); + + bool checked = false; + if (session) { + if (!m_attributes.empty()) { + typedef multimap indexed_t; + static indexed_t::const_iterator (indexed_t::* fn)(const string&) const = &indexed_t::find; + const indexed_t& indexed = session->getIndexedAttributes(); + // Look for an attribute in the list that is not in the session multimap. + // If that fails, the check succeeds. + checked = ( + find_if(m_attributes.begin(), m_attributes.end(), + boost::bind(fn, boost::cref(indexed), _1) == indexed.end()) == m_attributes.end() + ); + } + else { + checked = (m_acl && m_acl->authorized(request, session) == AccessControl::shib_acl_true); + } + } + + if (checked) { + string loc(returnURL); + request.absolutize(loc); + return make_pair(true, request.sendRedirect(loc.c_str())); + } + + request.setContentType("text/html; charset=UTF-8"); + request.setResponseHeader("Expires","Wed, 01 Jan 1997 12:00:00 GMT"); + request.setResponseHeader("Cache-Control","private,no-store,no-cache,max-age=0"); + + ifstream infile(m_template); + if (infile) { + TemplateParameters tp(nullptr, request.getApplication().getPropertySet("Errors"), session); + tp.m_request = &request; + stringstream str; + XMLToolingConfig::getConfig().getTemplateEngine()->run(infile, str, tp); + if (m_flushSession) { + sessionLocker.assign(); // unlock the session + flushSession(request); + } + return make_pair(true, request.sendError(str)); + } + + if (m_flushSession) { + sessionLocker.assign(); // unlock the session + flushSession(request); + } + m_log.error("could not process error template (%s)", m_template.c_str()); + istringstream msg("Internal Server Error. Please contact the site administrator."); + return make_pair(true, request.sendResponse(msg)); +} diff --git a/shibsp/impl/XMLAccessControl.cpp b/shibsp/impl/XMLAccessControl.cpp index 059ab09..e5d49f1 100644 --- a/shibsp/impl/XMLAccessControl.cpp +++ b/shibsp/impl/XMLAccessControl.cpp @@ -139,6 +139,7 @@ namespace shibsp { } static const XMLCh _AccessControl[] = UNICODE_LITERAL_13(A,c,c,e,s,s,C,o,n,t,r,o,l); + static const XMLCh _Handler[] = UNICODE_LITERAL_7(H,a,n,d,l,e,r); static const XMLCh ignoreCase[] = UNICODE_LITERAL_10(i,g,n,o,r,e,C,a,s,e); static const XMLCh ignoreOption[] = UNICODE_LITERAL_1(i); static const XMLCh _list[] = UNICODE_LITERAL_4(l,i,s,t); @@ -154,20 +155,23 @@ Rule::Rule(const DOMElement* e) : m_alias(XMLHelper::getAttrString(e, nullptr, r { if (m_alias.empty()) throw ConfigurationException("Access control rule missing require attribute"); + if (!e->hasChildNodes()) + return; // empty rule - auto_arrayptr vals(toUTF8(e->hasChildNodes() ? e->getFirstChild()->getNodeValue() : nullptr)); - if (!vals.get()) - return; + auto_arrayptr vals(toUTF8(e->getTextContent())); + if (!vals.get() || !*vals.get()) + throw ConfigurationException("Unable to convert Rule content into UTF-8."); bool listflag = XMLHelper::getAttrBool(e, true, _list); if (!listflag) { - if (*vals.get()) - m_vals.insert(vals.get()); + m_vals.insert(vals.get()); return; } string temp(vals.get()); split(m_vals, temp, boost::is_space(), algorithm::token_compress_on); + if (m_vals.empty()) + throw ConfigurationException("Rule did not contain any usable values."); } AccessControl::aclresult_t Rule::authorized(const SPRequest& request, const Session* session) const @@ -219,6 +223,10 @@ AccessControl::aclresult_t Rule::authorized(const SPRequest& request, const Sess request.log(SPRequest::SPWarn, string("rule requires attribute (") + m_alias + "), not found in session"); return shib_acl_false; } + else if (m_vals.empty()) { + request.log(SPRequest::SPDebug, string("AccessControl plugin requires presence of attribute (") + m_alias + "), authz granted"); + return shib_acl_true; + } for (; attrs.first != attrs.second; ++attrs.first) { bool caseSensitive = attrs.first->second->isCaseSensitive(); @@ -407,8 +415,16 @@ pair XMLAccessControl::background_load() XercesJanitor docjanitor(raw.first ? raw.second->getOwnerDocument() : nullptr); // Check for AccessControl wrapper and drop a level. - if (XMLString::equals(raw.second->getLocalName(),_AccessControl)) + if (XMLString::equals(raw.second->getLocalName(),_AccessControl)) { raw.second = XMLHelper::getFirstChildElement(raw.second); + if (!raw.second) + throw ConfigurationException("No child element found in AccessControl parent element."); + } + else if (XMLString::equals(raw.second->getLocalName(),_Handler)) { + raw.second = XMLHelper::getFirstChildElement(raw.second); + if (!raw.second) + throw ConfigurationException("No child element found in Handler parent element."); + } scoped_ptr authz; if (XMLString::equals(raw.second->getLocalName(),_Rule)) diff --git a/shibsp/shibsp-lite.vcxproj b/shibsp/shibsp-lite.vcxproj index a7449b7..6f4f036 100644 --- a/shibsp/shibsp-lite.vcxproj +++ b/shibsp/shibsp-lite.vcxproj @@ -185,6 +185,7 @@ + @@ -294,4 +295,4 @@ - + \ No newline at end of file diff --git a/shibsp/shibsp-lite.vcxproj.filters b/shibsp/shibsp-lite.vcxproj.filters index 8d81e3d..a3c5028 100644 --- a/shibsp/shibsp-lite.vcxproj.filters +++ b/shibsp/shibsp-lite.vcxproj.filters @@ -231,6 +231,9 @@ Source Files\attribute + + Source Files\handler\impl + diff --git a/shibsp/shibsp.vcxproj b/shibsp/shibsp.vcxproj index 23b79eb..f878453 100644 --- a/shibsp/shibsp.vcxproj +++ b/shibsp/shibsp.vcxproj @@ -182,6 +182,7 @@ + diff --git a/shibsp/shibsp.vcxproj.filters b/shibsp/shibsp.vcxproj.filters index d0bd18c..5419a76 100644 --- a/shibsp/shibsp.vcxproj.filters +++ b/shibsp/shibsp.vcxproj.filters @@ -405,6 +405,9 @@ Source Files\attribute\resolver\impl + + Source Files\handler\impl + -- 2.1.4