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
+
+
+
+
+
+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