From 65addbf79063b8677c71c0cab3eeab651ef362b2 Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Thu, 18 Jan 2007 03:47:52 +0000 Subject: [PATCH] Migrated ShibTarget logic into ServiceProvider base. --- apache/mod_apache.cpp | 18 +- isapi_shib/isapi_shib.cpp | 20 +- nsapi_shib/nsapi_shib.cpp | 16 +- shib-target/Makefile.am | 3 +- shib-target/shib-handlers.cpp | 1 + shib-target/shib-target.cpp | 641 ------------------------------------- shib-target/shib-target.h | 40 --- shib-target/shibtarget.vcproj | 4 - shibsp/Makefile.am | 6 +- shibsp/ServiceProvider.cpp | 581 ++++++++++++++++++++++++++++++++- shibsp/ServiceProvider.h | 1 + shibsp/shibsp.vcproj | 8 + shibsp/util/TemplateParameters.cpp | 53 +++ shibsp/util/TemplateParameters.h | 62 ++++ 14 files changed, 731 insertions(+), 723 deletions(-) delete mode 100644 shib-target/shib-target.cpp create mode 100644 shibsp/util/TemplateParameters.cpp create mode 100644 shibsp/util/TemplateParameters.h diff --git a/apache/mod_apache.cpp b/apache/mod_apache.cpp index 3161beb..de20b46 100644 --- a/apache/mod_apache.cpp +++ b/apache/mod_apache.cpp @@ -31,6 +31,7 @@ # define _CRT_SECURE_NO_DEPRECATE 1 #endif +#include #include #include #include @@ -73,7 +74,6 @@ #endif using namespace shibsp; -using namespace shibtarget; using namespace xmltooling; using namespace std; @@ -82,7 +82,7 @@ extern "C" module MODULE_VAR_EXPORT mod_shib; namespace { char* g_szSHIBConfig = NULL; char* g_szSchemaDir = NULL; - ShibTargetConfig* g_Config = NULL; + shibtarget::ShibTargetConfig* g_Config = NULL; string g_unsetHeaderValue; static const char* g_UserDataKey = "_shib_check_user_"; } @@ -259,7 +259,7 @@ extern "C" const char* shib_ap_set_file_slot(cmd_parms* parms, /********************************************************************************/ // Apache ShibTarget subclass(es) here. -class ShibTargetApache : public ShibTarget +class ShibTargetApache : public AbstractSPRequest { mutable string m_body; mutable bool m_gotBody; @@ -438,12 +438,12 @@ extern "C" int shib_check_user(request_rec* r) ShibTargetApache sta(r); // Check user authentication and export information, then set the handler bypass - pair res = sta.doCheckAuthN(true); + pair res = sta.getServiceProvider().doAuthentication(sta,true); apr_pool_userdata_setn((const void*)42,g_UserDataKey,NULL,r->pool); if (res.first) return res.second; // user auth was okay -- export the assertions now - res = sta.doExportAssertions(); + res = sta.getServiceProvider().doExport(sta); if (res.first) return res.second; // export happened successfully.. this user is ok. @@ -488,7 +488,7 @@ extern "C" int shib_handler(request_rec* r) try { ShibTargetApache sta(r); - pair res = sta.doHandler(); + pair res = sta.getServiceProvider().doHandler(sta); if (res.first) return res.second; ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r), "doHandler() did not do anything."); @@ -525,7 +525,7 @@ extern "C" int shib_auth_checker(request_rec* r) try { ShibTargetApache sta(r); - pair res = sta.doCheckAuthZ(); + pair res = sta.getServiceProvider().doAuthorization(sta); if (res.first) return res.second; // We're all okay. @@ -837,7 +837,7 @@ bool htAccessControl::authorized(const SPRequest& request, const Session* sessio } } else { - saml::Iterator provs=dynamic_cast(request.getApplication()).getAAPProviders(); + saml::Iterator provs=dynamic_cast(request.getApplication()).getAAPProviders(); shibboleth::AAP wrapper(provs,w); if (wrapper.fail()) { request.log(SPRequest::SPWarn, string("htAccessControl plugin didn't recognize require rule: ") + w); @@ -1044,7 +1044,7 @@ extern "C" void shib_child_init(apr_pool_t* p, server_rec* s) } try { - g_Config=&ShibTargetConfig::getConfig(); + g_Config=&shibtarget::ShibTargetConfig::getConfig(); SPConfig::getConfig().setFeatures( SPConfig::Caching | SPConfig::Listener | diff --git a/isapi_shib/isapi_shib.cpp b/isapi_shib/isapi_shib.cpp index b9a5483..b1c1fad 100644 --- a/isapi_shib/isapi_shib.cpp +++ b/isapi_shib/isapi_shib.cpp @@ -25,6 +25,7 @@ #define _CRT_NONSTDC_NO_DEPRECATE 1 #define _CRT_SECURE_NO_DEPRECATE 1 +#include #include #include @@ -41,7 +42,6 @@ #include using namespace shibsp; -using namespace shibtarget; using namespace xmltooling; using namespace std; @@ -83,7 +83,7 @@ namespace { }; HINSTANCE g_hinstDLL; - ShibTargetConfig* g_Config = NULL; + shibtarget::ShibTargetConfig* g_Config = NULL; map g_Sites; bool g_bNormalizeRequest = true; } @@ -150,7 +150,7 @@ extern "C" BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer) LPCSTR config=getenv("SHIBCONFIG"); if (!config) config=SHIB_CONFIG; - g_Config=&ShibTargetConfig::getConfig(); + g_Config=&shibtarget::ShibTargetConfig::getConfig(); SPConfig::getConfig().setFeatures( SPConfig::Listener | SPConfig::Caching | @@ -328,7 +328,7 @@ void GetHeader(PHTTP_FILTER_PREPROC_HEADERS pn, PHTTP_FILTER_CONTEXT pfc, /****************************************************************************/ // ISAPI Filter -class ShibTargetIsapiF : public ShibTarget +class ShibTargetIsapiF : public AbstractSPRequest { PHTTP_FILTER_CONTEXT m_pfc; PHTTP_FILTER_PREPROC_HEADERS m_pn; @@ -550,14 +550,14 @@ extern "C" DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificat ShibTargetIsapiF stf(pfc, pn, map_i->second); // "false" because we don't override the Shib settings - pair res = stf.doCheckAuthN(); + pair res = stf.getServiceProvider().doAuthentication(stf); if (res.first) return res.second; // "false" because we don't override the Shib settings - res = stf.doExportAssertions(); + res = stf.getServiceProvider().doExport(stf); if (res.first) return res.second; - res = stf.doCheckAuthZ(); + res = stf.getServiceProvider().doAuthorization(stf); if (res.first) return res.second; return SF_STATUS_REQ_NEXT_NOTIFICATION; @@ -605,7 +605,7 @@ DWORD WriteClientError(LPEXTENSION_CONTROL_BLOCK lpECB, const char* msg) } -class ShibTargetIsapiE : public ShibTarget +class ShibTargetIsapiE : public AbstractSPRequest { LPEXTENSION_CONTROL_BLOCK m_lpECB; map m_headers; @@ -831,8 +831,6 @@ public: extern "C" DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB) { - string targeturl; - const IApplication* application=NULL; try { ostringstream threadid; threadid << "[" << getpid() << "] isapi_shib_extension" << '\0'; @@ -848,7 +846,7 @@ extern "C" DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB) return WriteClientError(lpECB, "Shibboleth Extension not configured for this web site."); ShibTargetIsapiE ste(lpECB, map_i->second); - pair res = ste.doHandler(); + pair res = ste.getServiceProvider().doHandler(ste); if (res.first) return res.second; return WriteClientError(lpECB, "Shibboleth Extension failed to process request"); diff --git a/nsapi_shib/nsapi_shib.cpp b/nsapi_shib/nsapi_shib.cpp index 3af946c..a896a9c 100644 --- a/nsapi_shib/nsapi_shib.cpp +++ b/nsapi_shib/nsapi_shib.cpp @@ -31,6 +31,7 @@ # define _CRT_SECURE_NO_DEPRECATE 1 #endif +#include #include #include #include @@ -60,7 +61,6 @@ extern "C" } using namespace shibsp; -using namespace shibtarget; using namespace xmltooling; using namespace std; @@ -69,7 +69,7 @@ using namespace std; if (IO_ERROR==net_write(sn->csd,str,strlen(str))) return REQ_EXIT namespace { - ShibTargetConfig* g_Config=NULL; + shibtarget::ShibTargetConfig* g_Config=NULL; string g_ServerName; string g_ServerScheme; string g_unsetHeaderValue; @@ -125,7 +125,7 @@ extern "C" NSAPI_PUBLIC int nsapi_shib_init(pblock* pb, ::Session* sn, Request* config=getenv("SHIBCONFIG"); if (!config) config=SHIB_CONFIG; - g_Config=&ShibTargetConfig::getConfig(); + g_Config=&shibtarget::ShibTargetConfig::getConfig(); SPConfig::getConfig().setFeatures( SPConfig::Listener | SPConfig::Caching | @@ -173,7 +173,7 @@ extern "C" NSAPI_PUBLIC int nsapi_shib_init(pblock* pb, ::Session* sn, Request* /********************************************************************************/ // NSAPI Shib Target Subclass -class ShibTargetNSAPI : public ShibTarget +class ShibTargetNSAPI : public AbstractSPRequest { string m_uri; mutable string m_body; @@ -370,7 +370,7 @@ extern "C" NSAPI_PUBLIC int nsapi_shib(pblock* pb, ::Session* sn, Request* rq) ShibTargetNSAPI stn(pb, sn, rq); // Check user authentication - pair res = stn.doCheckAuthN(); + pair res = stn.getServiceProvider().doAuthentication(stn); if (res.first) return (int)res.second; // user authN was okay -- export the assertions now @@ -378,11 +378,11 @@ extern "C" NSAPI_PUBLIC int nsapi_shib(pblock* pb, ::Session* sn, Request* rq) // This seems to be required in order to eventually set // the auth-user var. pblock_nvinsert("auth-type","shibboleth",rq->vars); - res = stn.doExportAssertions(); + res = stn.getServiceProvider().doExport(stn); if (res.first) return (int)res.second; // Check the Authorization - res = stn.doCheckAuthZ(); + res = stn.getServiceProvider().doAuthorization(stn); if (res.first) return (int)res.second; // this user is ok. @@ -411,7 +411,7 @@ extern "C" NSAPI_PUBLIC int shib_handler(pblock* pb, ::Session* sn, Request* rq) try { ShibTargetNSAPI stn(pb, sn, rq); - pair res = stn.doHandler(); + pair res = stn.getServiceProvider().doHandler(stn); if (res.first) return (int)res.second; return WriteClientError(sn, rq, FUNC, "Shibboleth handler did not do anything."); diff --git a/shib-target/Makefile.am b/shib-target/Makefile.am index 0206667..2ffa8d7 100644 --- a/shib-target/Makefile.am +++ b/shib-target/Makefile.am @@ -17,8 +17,7 @@ libshib_target_la_SOURCES = \ shib-ccache.cpp \ shib-config.cpp \ shib-handlers.cpp \ - shib-ini.cpp \ - shib-target.cpp + shib-ini.cpp # this is different from the project version # http://sources.redhat.com/autobook/autobook/autobook_91.html diff --git a/shib-target/shib-handlers.cpp b/shib-target/shib-handlers.cpp index 822b703..b5f2105 100644 --- a/shib-target/shib-handlers.cpp +++ b/shib-target/shib-handlers.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #ifdef HAVE_UNISTD_H # include diff --git a/shib-target/shib-target.cpp b/shib-target/shib-target.cpp deleted file mode 100644 index 10e86b4..0000000 --- a/shib-target/shib-target.cpp +++ /dev/null @@ -1,641 +0,0 @@ -/* - * Copyright 2001-2005 Internet2 - * - * Licensed 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. - */ - -/* - * shib-target.cpp -- The ShibTarget class, a superclass for general - * target code - * - * Created by: Derek Atkins - * - * $Id$ - */ - -#include "internal.h" - -#ifdef HAVE_UNISTD_H -# include -#endif - -#include - -#include -#include -#include -#include -#include -#include -#include - -#ifndef HAVE_STRCASECMP -# define strcasecmp stricmp -#endif - -using namespace shibsp; -using namespace shibtarget; -using namespace shibboleth; -using namespace saml; -using namespace opensaml::saml2md; -using namespace log4cpp; -using namespace std; - -using xmltooling::TemplateEngine; -using xmltooling::XMLToolingException; -using xmltooling::XMLToolingConfig; -using xmltooling::XMLHelper; - -namespace shibtarget { - - class ExtTemplateParameters : public TemplateEngine::TemplateParameters - { - const PropertySet* m_props; - public: - ExtTemplateParameters() : m_props(NULL) {} - ~ExtTemplateParameters() {} - - void setPropertySet(const PropertySet* props) { - m_props = props; - - // Create a timestamp. - time_t now = time(NULL); -#ifdef HAVE_CTIME_R - char timebuf[32]; - m_map["now"] = ctime_r(&now,timebuf); -#else - m_map["now"] = ctime(&now); -#endif - } - - const char* getParameter(const char* name) const { - const char* pch = TemplateParameters::getParameter(name); - if (pch || !m_props) - return pch; - pair p = m_props->getString(name); - return p.first ? p.second : NULL; - } - }; - - long _sendError(SPRequest* st, const char* page, ExtTemplateParameters& tp, const XMLToolingException* ex=NULL); - - static const XMLCh SessionInitiator[] = UNICODE_LITERAL_16(S,e,s,s,i,o,n,I,n,i,t,i,a,t,o,r); -} - - -/************************************************************************* - * Shib Target implementation - */ - - - -// These functions implement the server-agnostic shibboleth engine -// The web server modules implement a subclass and then call into -// these methods once they instantiate their request object. - -pair ShibTarget::doCheckAuthN(bool handler) -{ -#ifdef _DEBUG - xmltooling::NDC ndc("doCheckAuthN"); -#endif - - const char* procState = "Request Processing Error"; - string targetURL = getRequestURL(); - ExtTemplateParameters tp; - - try { - RequestMapper::Settings settings = getRequestSettings(); - const IApplication& app = dynamic_cast(getApplication()); - - // If not SSL, check to see if we should block or redirect it. - if (!strcmp("http",getScheme())) { - pair redirectToSSL = settings.first->getString("redirectToSSL"); - if (redirectToSSL.first) { - if (!strcasecmp("GET",getMethod()) || !strcasecmp("HEAD",getMethod())) { - // Compute the new target URL - string redirectURL = string("https://") + getHostname(); - if (strcmp(redirectToSSL.second,"443")) { - redirectURL = redirectURL + ':' + redirectToSSL.second; - } - redirectURL += getRequestURI(); - return make_pair(true, sendRedirect(redirectURL.c_str())); - } - else { - tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); - return make_pair(true,_sendError(this, "ssl", tp)); - } - } - } - - const char* handlerURL=getHandlerURL(targetURL.c_str()); - if (!handlerURL) - throw ConfigurationException("Cannot determine handler from resource URL, check configuration."); - - // If the request URL contains the handler base URL for this application, either dispatch - // directly (mainly Apache 2.0) or just pass back control. - if (strstr(targetURL.c_str(),handlerURL)) { - if (handler) - return doHandler(); - else - return make_pair(true, returnOK()); - } - - // Three settings dictate how to proceed. - pair authType = settings.first->getString("authType"); - pair requireSession = settings.first->getBool("requireSession"); - pair requireSessionWith = settings.first->getString("requireSessionWith"); - - // If no session is required AND the AuthType (an Apache-derived concept) isn't shibboleth, - // then we ignore this request and consider it unprotected. Apache might lie to us if - // ShibBasicHijack is on, but that's up to it. - if ((!requireSession.first || !requireSession.second) && !requireSessionWith.first && -#ifdef HAVE_STRCASECMP - (!authType.first || strcasecmp(authType.second,"shibboleth"))) -#else - (!authType.first || _stricmp(authType.second,"shibboleth"))) -#endif - return make_pair(true,returnDecline()); - - // Fix for secadv 20050901 - clearHeaders(); - - pair shib_cookie = app.getCookieNameProps("_shibsession_"); - const char* session_id = getCookie(shib_cookie.first.c_str()); - if (!session_id || !*session_id) { - // No session. Maybe that's acceptable? - if ((!requireSession.first || !requireSession.second) && !requireSessionWith.first) - return make_pair(true,returnOK()); - - // No cookie, but we require a session. Initiate a new session using the indicated method. - procState = "Session Initiator Error"; - const Handler* initiator=NULL; - if (requireSessionWith.first) { - initiator=app.getSessionInitiatorById(requireSessionWith.second); - if (!initiator) - throw ConfigurationException( - "No session initiator found with id ($1), check requireSessionWith command.", - xmltooling::params(1,requireSessionWith.second) - ); - } - else { - initiator=app.getDefaultSessionInitiator(); - if (!initiator) - throw ConfigurationException("No default session initiator found, check configuration."); - } - - return initiator->run(*this,false); - } - - procState = "Session Processing Error"; - const Session* session=NULL; - try { - session=getSession(); - // Make a localized exception throw if the session isn't valid. - if (!session) - throw RetryableProfileException("Session no longer valid."); - } - catch (exception& e) { - log(SPWarn, string("session processing failed: ") + e.what()); - - // If no session is required, bail now. - if ((!requireSession.first || !requireSession.second) && !requireSessionWith.first) - // Has to be OK because DECLINED will just cause Apache - // to fail when it can't locate anything to process the - // AuthType. No session plus requireSession false means - // do not authenticate the user at this time. - return make_pair(true, returnOK()); - - // Try and cast down. - exception* base = &e; - RetryableProfileException* trycast=dynamic_cast(base); - if (trycast) { - // Session is invalid but we can retry -- initiate a new session. - procState = "Session Initiator Error"; - const Handler* initiator=NULL; - if (requireSessionWith.first) { - initiator=app.getSessionInitiatorById(requireSessionWith.second); - if (!initiator) - throw ConfigurationException( - "No session initiator found with id ($1), check requireSessionWith command.", - xmltooling::params(1,requireSessionWith.second) - ); - } - else { - initiator=app.getDefaultSessionInitiator(); - if (!initiator) - throw ConfigurationException("No default session initiator found, check configuration."); - } - return initiator->run(*this,false); - } - throw; // send it to the outer handler - } - - // We're done. Everything is okay. Nothing to report. Nothing to do.. - // Let the caller decide how to proceed. - log(SPDebug, "doCheckAuthN succeeded"); - return make_pair(false,0); - } - catch (XMLToolingException& e) { - tp.m_map["errorType"] = procState; - tp.m_map["errorText"] = e.what(); - tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); - return make_pair(true,_sendError(this, "session", tp, &e)); - } -#ifndef _DEBUG - catch (...) { - tp.m_map["errorType"] = procState; - tp.m_map["errorText"] = "Caught an unknown exception."; - tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); - return make_pair(true,_sendError(this, "session", tp)); - } -#endif -} - -pair ShibTarget::doHandler(void) -{ -#ifdef _DEBUG - xmltooling::NDC ndc("doHandler"); -#endif - - const IApplication* app=NULL; - ExtTemplateParameters tp; - const char* procState = "Shibboleth Handler Error"; - string targetURL = getRequestURL(); - - try { - RequestMapper::Settings settings = getRequestSettings(); - app = dynamic_cast(&getApplication()); - - const char* handlerURL=getHandlerURL(targetURL.c_str()); - if (!handlerURL) - throw ConfigurationException("Cannot determine handler from resource URL, check configuration."); - - // Make sure we only process handler requests. - if (!strstr(targetURL.c_str(),handlerURL)) - return make_pair(true, returnDecline()); - - const PropertySet* sessionProps=app->getPropertySet("Sessions"); - if (!sessionProps) - throw ConfigurationException("Unable to map request to application session settings, check configuration."); - - // Process incoming request. - pair handlerSSL=sessionProps->getBool("handlerSSL"); - - // Make sure this is SSL, if it should be - if ((!handlerSSL.first || handlerSSL.second) && strcmp(getScheme(),"https")) - throw FatalProfileException("Blocked non-SSL access to Shibboleth handler."); - - // We dispatch based on our path info. We know the request URL begins with or equals the handler URL, - // so the path info is the next character (or null). - const Handler* handler=app->getHandler(targetURL.c_str() + strlen(handlerURL)); - if (!handler) - throw opensaml::BindingException("Shibboleth handler invoked at an unconfigured location."); - - if (XMLHelper::isNodeNamed(handler->getElement(),samlconstants::SAML20MD_NS,AssertionConsumerService::LOCAL_NAME)) - procState = "Session Creation Error"; - else if (XMLString::equals(handler->getElement()->getLocalName(),SessionInitiator)) - procState = "Session Initiator Error"; - else if (XMLHelper::isNodeNamed(handler->getElement(),samlconstants::SAML20MD_NS,SingleLogoutService::LOCAL_NAME)) - procState = "Session Termination Error"; - else - procState = "Protocol Handler Error"; - pair hret=handler->run(*this); - - // Did the handler run successfully? - if (hret.first) - return hret; - - throw opensaml::BindingException("Configured Shibboleth handler failed to process the request."); - } - catch (MetadataException& e) { - tp.m_map["errorText"] = e.what(); - // See if a metadata error page is installed. - const PropertySet* props=app->getPropertySet("Errors"); - if (props) { - pair p=props->getString("metadata"); - if (p.first) { - tp.m_map["errorType"] = procState; - tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); - return make_pair(true,_sendError(this, "metadata", tp)); - } - } - throw; - } - catch (XMLToolingException& e) { - tp.m_map["errorType"] = procState; - tp.m_map["errorText"] = e.what(); - tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); - return make_pair(true,_sendError(this, "session", tp, &e)); - } -#ifndef _DEBUG - catch (...) { - tp.m_map["errorType"] = procState; - tp.m_map["errorText"] = "Caught an unknown exception."; - tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); - return make_pair(true,_sendError(this, "session", tp)); - } -#endif -} - -pair ShibTarget::doCheckAuthZ(void) -{ -#ifdef _DEBUG - xmltooling::NDC ndc("doCheckAuthZ"); -#endif - - const IApplication* app=NULL; - ExtTemplateParameters tp; - const char* procState = "Authorization Processing Error"; - string targetURL = getRequestURL(); - - try { - RequestMapper::Settings settings = getRequestSettings(); - app = dynamic_cast(&getApplication()); - - // Three settings dictate how to proceed. - pair authType = settings.first->getString("authType"); - pair requireSession = settings.first->getBool("requireSession"); - pair requireSessionWith = settings.first->getString("requireSessionWith"); - - // If no session is required AND the AuthType (an Apache-derived concept) isn't shibboleth, - // then we ignore this request and consider it unprotected. Apache might lie to us if - // ShibBasicHijack is on, but that's up to it. - if ((!requireSession.first || !requireSession.second) && !requireSessionWith.first && -#ifdef HAVE_STRCASECMP - (!authType.first || strcasecmp(authType.second,"shibboleth"))) -#else - (!authType.first || _stricmp(authType.second,"shibboleth"))) -#endif - return make_pair(true,returnDecline()); - - // Do we have an access control plugin? - if (settings.second) { - const Session* session =NULL; - pair shib_cookie=app->getCookieNameProps("_shibsession_"); - const char *session_id = getCookie(shib_cookie.first.c_str()); - try { - if (session_id && *session_id) { - session = getSession(); - } - } - catch (exception&) { - log(SPWarn, "doCheckAuthZ: unable to obtain session information to pass to access control provider"); - } - - xmltooling::Locker acllock(settings.second); - if (settings.second->authorized(*this,session)) { - // Let the caller decide how to proceed. - log(SPDebug, "doCheckAuthZ: access control provider granted access"); - return make_pair(false,0); - } - else { - log(SPWarn, "doCheckAuthZ: access control provider denied access"); - tp.m_map["requestURL"] = targetURL; - return make_pair(true,_sendError(this, "access", tp)); - } - return make_pair(false,0); - } - else - return make_pair(true,returnDecline()); - } - catch (exception& e) { - tp.m_map["errorType"] = procState; - tp.m_map["errorText"] = e.what(); - tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); - return make_pair(true,_sendError(this, "access", tp)); - } -#ifndef _DEBUG - catch (...) { - tp.m_map["errorType"] = procState; - tp.m_map["errorText"] = "Caught an unknown exception."; - tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); - return make_pair(true,_sendError(this, "access", tp)); - } -#endif -} - -pair ShibTarget::doExportAssertions(bool requireSession) -{ -#ifdef _DEBUG - xmltooling::NDC ndc("doExportAssertions"); -#endif - - const IApplication* app=NULL; - ExtTemplateParameters tp; - const char* procState = "Attribute Processing Error"; - string targetURL = getRequestURL(); - - try { - RequestMapper::Settings settings = getRequestSettings(); - app = dynamic_cast(&getApplication()); - - const Session* session=NULL; - pair shib_cookie=app->getCookieNameProps("_shibsession_"); - const char *session_id = getCookie(shib_cookie.first.c_str()); - try { - if (session_id && *session_id) { - session = getSession(); - } - } - catch (exception&) { - log(SPWarn, "unable to obtain session information to export into request headers"); - // If we have to have a session, then this is a fatal error. - if (requireSession) - throw; - } - - // Still no data? - if (!session) { - if (requireSession) - throw RetryableProfileException("Unable to obtain session information for request."); - else - return make_pair(false,0); // just bail silently - } - - /* - TODO: port to new cache API - // Extract data from session. - pair sub=m_cacheEntry->getSubject(false,true); - pair unfiltered=m_cacheEntry->getTokens(true,false); - pair filtered=m_cacheEntry->getTokens(false,true); - - // Maybe export the tokens. - pair exp=m_settings.first->getBool("exportAssertion"); - if (exp.first && exp.second && unfiltered.first && *unfiltered.first) { - unsigned int outlen; - XMLByte* serialized = - Base64::encode(reinterpret_cast((char*)unfiltered.first), XMLString::stringLen(unfiltered.first), &outlen); - XMLByte *pos, *pos2; - for (pos=serialized, pos2=serialized; *pos2; pos2++) - if (isgraph(*pos2)) - *pos++=*pos2; - *pos=0; - setHeader("Shib-Attributes", reinterpret_cast(serialized)); - XMLString::release(&serialized); - } - - // Export the SAML AuthnMethod and the origin site name, and possibly the NameIdentifier. - setHeader("Shib-Origin-Site", m_cacheEntry->getProviderId()); - setHeader("Shib-Identity-Provider", m_cacheEntry->getProviderId()); - setHeader("Shib-Authentication-Method", m_cacheEntry->getAuthnContext()); - - // Get the AAP providers, which contain the attribute policy info. - Iterator provs=m_app->getAAPProviders(); - - // Export NameID? - while (provs.hasNext()) { - IAAP* aap=provs.next(); - xmltooling::Locker locker(aap); - const XMLCh* format = sub.second->getNameIdentifier()->getFormat(); - const IAttributeRule* rule=aap->lookup(format ? format : SAMLNameIdentifier::UNSPECIFIED); - if (rule && rule->getHeader()) { - auto_ptr_char form(format ? format : SAMLNameIdentifier::UNSPECIFIED); - auto_ptr_char nameid(sub.second->getNameIdentifier()->getName()); - setHeader("Shib-NameIdentifier-Format", form.get()); - if (!strcmp(rule->getHeader(),"REMOTE_USER")) - setRemoteUser(nameid.get()); - else - setHeader(rule->getHeader(), nameid.get()); - } - } - - setHeader("Shib-Application-ID", m_app->getId()); - - // Export the attributes. - Iterator a_iter(filtered.second ? filtered.second->getAssertions() : EMPTY(SAMLAssertion*)); - while (a_iter.hasNext()) { - SAMLAssertion* assert=a_iter.next(); - Iterator statements=assert->getStatements(); - while (statements.hasNext()) { - SAMLAttributeStatement* astate=dynamic_cast(statements.next()); - if (!astate) - continue; - Iterator attrs=astate->getAttributes(); - while (attrs.hasNext()) { - SAMLAttribute* attr=attrs.next(); - - // Are we supposed to export it? - provs.reset(); - while (provs.hasNext()) { - IAAP* aap=provs.next(); - xmltooling::Locker locker(aap); - const IAttributeRule* rule=aap->lookup(attr->getName(),attr->getNamespace()); - if (!rule || !rule->getHeader()) - continue; - - Iterator vals=attr->getSingleByteValues(); - if (!strcmp(rule->getHeader(),"REMOTE_USER") && vals.hasNext()) - setRemoteUser(vals.next().c_str()); - else { - int it=0; - string header = getSecureHeader(rule->getHeader()); - if (!header.empty()) - it++; - for (; vals.hasNext(); it++) { - string value = vals.next(); - for (string::size_type pos = value.find_first_of(";", string::size_type(0)); - pos != string::npos; - pos = value.find_first_of(";", pos)) { - value.insert(pos, "\\"); - pos += 2; - } - if (it) - header += ";"; - header += value; - } - setHeader(rule->getHeader(), header.c_str()); - } - } - } - } - } - */ - - return make_pair(false,0); - } - catch (XMLToolingException& e) { - tp.m_map["errorType"] = procState; - tp.m_map["errorText"] = e.what(); - tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); - return make_pair(true,_sendError(this, "rm", tp, &e)); - } -#ifndef _DEBUG - catch (...) { - tp.m_map["errorType"] = procState; - tp.m_map["errorText"] = "Caught an unknown exception."; - tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); - return make_pair(true,_sendError(this, "rm", tp)); - } -#endif -} - -/************************************************************************* - * Shib Target Private implementation - */ - -long shibtarget::_sendError( - SPRequest* st, const char* page, ExtTemplateParameters& tp, const XMLToolingException* ex - ) -{ - st->setContentType("text/html"); - st->setResponseHeader("Expires","01-Jan-1997 12:00:00 GMT"); - st->setResponseHeader("Cache-Control","private,no-store,no-cache"); - - TemplateEngine* engine = XMLToolingConfig::getConfig().getTemplateEngine(); - const PropertySet* props=st->getApplication().getPropertySet("Errors"); - if (props) { - pair p=props->getString(page); - if (p.first) { - ifstream infile(p.second); - if (infile) { - tp.setPropertySet(props); - stringstream str; - engine->run(infile, str, tp, ex); - return st->sendResponse(str); - } - } - else if (!strcmp(page,"access")) { - istringstream msg("Access Denied"); - return static_cast(st)->sendResponse(msg, opensaml::HTTPResponse::SAML_HTTP_STATUS_FORBIDDEN); - } - } - - string errstr = string("sendError could not process error template (") + page + ")"; - st->log(SPRequest::SPError, errstr); - istringstream msg("Internal Server Error. Please contact the site administrator."); - return st->sendError(msg); -} - -void ShibTarget::clearHeaders() -{ - // Clear invariant stuff. - clearHeader("Shib-Origin-Site"); - clearHeader("Shib-Identity-Provider"); - clearHeader("Shib-Authentication-Method"); - clearHeader("Shib-NameIdentifier-Format"); - clearHeader("Shib-Attributes"); - clearHeader("Shib-Application-ID"); - - // Clear out the list of mapped attributes - Iterator provs=dynamic_cast(getApplication()).getAAPProviders(); - while (provs.hasNext()) { - IAAP* aap=provs.next(); - xmltooling::Locker locker(aap); - Iterator rules=aap->getAttributeRules(); - while (rules.hasNext()) { - const char* header=rules.next()->getHeader(); - if (header) - clearHeader(header); - } - } -} diff --git a/shib-target/shib-target.h b/shib-target/shib-target.h index 782c31e..15e6913 100644 --- a/shib-target/shib-target.h +++ b/shib-target/shib-target.h @@ -26,7 +26,6 @@ #define SHIB_TARGET_H // New headers -#include #include #include #include @@ -234,45 +233,6 @@ namespace shibtarget { static ShibTargetConfig& getConfig(); }; - class ShibTargetPriv; - class SHIBTARGET_EXPORTS ShibTarget : public shibsp::AbstractSPRequest { - public: - virtual ~ShibTarget() {} - - // - // Note: Subclasses need not implement anything below this line - // - - // These functions implement the server-agnostic shibboleth engine - // The web server modules implement a subclass and then call into - // these methods once they instantiate their request object. - // - // Return value: - // these APIs will always return the result of sendPage(), sendRedirect(), - // returnDecline(), or returnOK() in the void* portion of the return code. - // Exactly what those values are is module- (subclass-) implementation - // specific. The 'bool' part of the return value declares whether the - // void* is valid or not. If the bool is true then the void* is valid. - // If the bool is false then the API did not call any callback, the void* - // is not valid, and the caller should continue processing (the API Call - // finished successfully). - // - // The handleProfile argument declares whether doCheckAuthN() should - // automatically call doHandlePOST() when it encounters a request for - // the ShireURL; if false it will call returnOK() instead. - // - std::pair doCheckAuthN(bool handler = false); - std::pair doHandler(); - std::pair doCheckAuthZ(); - std::pair doExportAssertions(bool requireSession = true); - - protected: - ShibTarget() {} - - private: - void clearHeaders(); - }; - } #endif /* SHIB_TARGET_H */ diff --git a/shib-target/shibtarget.vcproj b/shib-target/shibtarget.vcproj index d5cd8d6..49d8d83 100644 --- a/shib-target/shibtarget.vcproj +++ b/shib-target/shibtarget.vcproj @@ -246,10 +246,6 @@ > - - diff --git a/shibsp/Makefile.am b/shibsp/Makefile.am index 8924527..b30255b 100644 --- a/shibsp/Makefile.am +++ b/shibsp/Makefile.am @@ -44,7 +44,8 @@ secinclude_HEADERS = \ utilinclude_HEADERS = \ util/DOMPropertySet.h \ util/PropertySet.h \ - util/SPConstants.h + util/SPConstants.h \ + util/TemplateParameters.h noinst_HEADERS = \ internal.h \ @@ -68,7 +69,8 @@ libshibsp_la_SOURCES = \ remoting/impl/UnixListener.cpp \ security/PKIXTrustEngine.cpp \ util/DOMPropertySet.cpp \ - util/SPConstants.cpp + util/SPConstants.cpp \ + util/TemplateParameters.cpp # this is different from the project version # http://sources.redhat.com/autobook/autobook/autobook_91.html diff --git a/shibsp/ServiceProvider.cpp b/shibsp/ServiceProvider.cpp index 0744e35..7e41392 100644 --- a/shibsp/ServiceProvider.cpp +++ b/shibsp/ServiceProvider.cpp @@ -21,17 +21,90 @@ */ #include "internal.h" +#include "exceptions.h" +#include "AccessControl.h" +#include "Application.h" +#include "Handler.h" #include "ServiceProvider.h" +#include "SessionCache.h" +#include "SPRequest.h" +#include "util/TemplateParameters.h" -#include +#include +#include +#include +#include +#include +#include +#include using namespace shibsp; +using namespace opensaml::saml2md; +using namespace opensaml; using namespace xmltooling; -using namespace xercesc; using namespace std; namespace shibsp { //SHIBSP_DLLLOCAL PluginManager::Factory XMLServiceProviderFactory; + + long SHIBSP_DLLLOCAL sendError( + SPRequest& request, const Application* app, const char* page, TemplateParameters& tp, const XMLToolingException* ex=NULL + ) + { + request.setContentType("text/html"); + request.setResponseHeader("Expires","01-Jan-1997 12:00:00 GMT"); + request.setResponseHeader("Cache-Control","private,no-store,no-cache"); + + const PropertySet* props=app ? app->getPropertySet("Errors") : NULL; + if (props) { + pair p=props->getString(page); + if (p.first) { + ifstream infile(p.second); + if (infile) { + tp.setPropertySet(props); + stringstream str; + XMLToolingConfig::getConfig().getTemplateEngine()->run(infile, str, tp, ex); + return request.sendResponse(str); + } + } + else if (!strcmp(page,"access")) { + istringstream msg("Access Denied"); + return static_cast(request).sendResponse(msg, HTTPResponse::SAML_HTTP_STATUS_FORBIDDEN); + } + } + + string errstr = string("sendError could not process error template (") + page + ")"; + request.log(SPRequest::SPError, errstr); + istringstream msg("Internal Server Error. Please contact the site administrator."); + return request.sendError(msg); + } + + void SHIBSP_DLLLOCAL clearHeaders(SPRequest& request) { + // Clear invariant stuff. + request.clearHeader("Shib-Origin-Site"); + request.clearHeader("Shib-Identity-Provider"); + request.clearHeader("Shib-Authentication-Method"); + request.clearHeader("Shib-NameIdentifier-Format"); + request.clearHeader("Shib-Attributes"); + request.clearHeader("Shib-Application-ID"); + + // Clear out the list of mapped attributes + /* TODO: port + Iterator provs=dynamic_cast(getApplication()).getAAPProviders(); + while (provs.hasNext()) { + IAAP* aap=provs.next(); + xmltooling::Locker locker(aap); + Iterator rules=aap->getAttributeRules(); + while (rules.hasNext()) { + const char* header=rules.next()->getHeader(); + if (header) + request.clearHeader(header); + } + } + */ + } + + static const XMLCh SessionInitiator[] = UNICODE_LITERAL_16(S,e,s,s,i,o,n,I,n,i,t,i,a,t,o,r); }; void SHIBSP_API shibsp::registerServiceProviders() @@ -41,20 +114,516 @@ void SHIBSP_API shibsp::registerServiceProviders() pair ServiceProvider::doAuthentication(SPRequest& request, bool handler) const { - return make_pair(true,0); +#ifdef _DEBUG + xmltooling::NDC ndc("doAuthentication"); +#endif + + const Application* app=NULL; + const char* procState = "Request Processing Error"; + string targetURL = request.getRequestURL(); + + try { + RequestMapper::Settings settings = request.getRequestSettings(); + app = &(request.getApplication()); + + // If not SSL, check to see if we should block or redirect it. + if (!request.isSecure()) { + pair redirectToSSL = settings.first->getString("redirectToSSL"); + if (redirectToSSL.first) { +#ifdef HAVE_STRCASECMP + if (!strcasecmp("GET",request.getMethod()) || !strcasecmp("HEAD",request.getMethod())) { +#else + if (!stricmp("GET",request.getMethod()) || !stricmp("HEAD",request.getMethod())) { +#endif + // Compute the new target URL + string redirectURL = string("https://") + request.getHostname(); + if (strcmp(redirectToSSL.second,"443")) { + redirectURL = redirectURL + ':' + redirectToSSL.second; + } + redirectURL += request.getRequestURI(); + return make_pair(true, request.sendRedirect(redirectURL.c_str())); + } + else { + TemplateParameters tp; + tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); + return make_pair(true,sendError(request, app, "ssl", tp)); + } + } + } + + const char* handlerURL=request.getHandlerURL(targetURL.c_str()); + if (!handlerURL) + throw ConfigurationException("Cannot determine handler from resource URL, check configuration."); + + // If the request URL contains the handler base URL for this application, either dispatch + // directly (mainly Apache 2.0) or just pass back control. + if (strstr(targetURL.c_str(),handlerURL)) { + if (handler) + return doHandler(request); + else + return make_pair(true, request.returnOK()); + } + + // Three settings dictate how to proceed. + pair authType = settings.first->getString("authType"); + pair requireSession = settings.first->getBool("requireSession"); + pair requireSessionWith = settings.first->getString("requireSessionWith"); + + // If no session is required AND the AuthType (an Apache-derived concept) isn't shibboleth, + // then we ignore this request and consider it unprotected. Apache might lie to us if + // ShibBasicHijack is on, but that's up to it. + if ((!requireSession.first || !requireSession.second) && !requireSessionWith.first && +#ifdef HAVE_STRCASECMP + (!authType.first || strcasecmp(authType.second,"shibboleth"))) +#else + (!authType.first || _stricmp(authType.second,"shibboleth"))) +#endif + return make_pair(true,request.returnDecline()); + + // Fix for secadv 20050901 + clearHeaders(request); + + pair shib_cookie = app->getCookieNameProps("_shibsession_"); + const char* session_id = request.getCookie(shib_cookie.first.c_str()); + if (!session_id || !*session_id) { + // No session. Maybe that's acceptable? + if ((!requireSession.first || !requireSession.second) && !requireSessionWith.first) + return make_pair(true,request.returnOK()); + + // No cookie, but we require a session. Initiate a new session using the indicated method. + procState = "Session Initiator Error"; + const Handler* initiator=NULL; + if (requireSessionWith.first) { + initiator=app->getSessionInitiatorById(requireSessionWith.second); + if (!initiator) + throw ConfigurationException( + "No session initiator found with id ($1), check requireSessionWith command.", + params(1,requireSessionWith.second) + ); + } + else { + initiator=app->getDefaultSessionInitiator(); + if (!initiator) + throw ConfigurationException("No default session initiator found, check configuration."); + } + + return initiator->run(request,false); + } + + procState = "Session Processing Error"; + const Session* session=NULL; + try { + session=request.getSession(); + // Make a localized exception throw if the session isn't valid. + if (!session) + throw RetryableProfileException("Session no longer valid."); + } + catch (exception& e) { + request.log(SPRequest::SPWarn, string("session processing failed: ") + e.what()); + + // If no session is required, bail now. + if ((!requireSession.first || !requireSession.second) && !requireSessionWith.first) + // Has to be OK because DECLINED will just cause Apache + // to fail when it can't locate anything to process the + // AuthType. No session plus requireSession false means + // do not authenticate the user at this time. + return make_pair(true, request.returnOK()); + + // Try and cast down. + exception* base = &e; + RetryableProfileException* trycast=dynamic_cast(base); + if (trycast) { + // Session is invalid but we can retry -- initiate a new session. + procState = "Session Initiator Error"; + const Handler* initiator=NULL; + if (requireSessionWith.first) { + initiator=app->getSessionInitiatorById(requireSessionWith.second); + if (!initiator) + throw ConfigurationException( + "No session initiator found with id ($1), check requireSessionWith command.", + params(1,requireSessionWith.second) + ); + } + else { + initiator=app->getDefaultSessionInitiator(); + if (!initiator) + throw ConfigurationException("No default session initiator found, check configuration."); + } + return initiator->run(request,false); + } + throw; // send it to the outer handler + } + + // We're done. Everything is okay. Nothing to report. Nothing to do.. + // Let the caller decide how to proceed. + request.log(SPRequest::SPDebug, "doAuthentication succeeded"); + return make_pair(false,0); + } + catch (XMLToolingException& e) { + TemplateParameters tp; + tp.m_map["errorType"] = procState; + tp.m_map["errorText"] = e.what(); + tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); + return make_pair(true,sendError(request, app, "session", tp, &e)); + } + catch (exception& e) { + TemplateParameters tp; + tp.m_map["errorType"] = procState; + tp.m_map["errorText"] = e.what(); + tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); + return make_pair(true,sendError(request, app, "session", tp)); + } +#ifndef _DEBUG + catch (...) { + TemplateParameters tp; + tp.m_map["errorType"] = procState; + tp.m_map["errorText"] = "Caught an unknown exception."; + tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); + return make_pair(true,sendError(request, app, "session", tp)); + } +#endif } pair ServiceProvider::doAuthorization(SPRequest& request) const { - return make_pair(true,0); +#ifdef _DEBUG + xmltooling::NDC ndc("doAuthorization"); +#endif + + const Application* app=NULL; + const char* procState = "Authorization Processing Error"; + string targetURL = request.getRequestURL(); + + try { + RequestMapper::Settings settings = request.getRequestSettings(); + app = &(request.getApplication()); + + // Three settings dictate how to proceed. + pair authType = settings.first->getString("authType"); + pair requireSession = settings.first->getBool("requireSession"); + pair requireSessionWith = settings.first->getString("requireSessionWith"); + + // If no session is required AND the AuthType (an Apache-derived concept) isn't shibboleth, + // then we ignore this request and consider it unprotected. Apache might lie to us if + // ShibBasicHijack is on, but that's up to it. + if ((!requireSession.first || !requireSession.second) && !requireSessionWith.first && +#ifdef HAVE_STRCASECMP + (!authType.first || strcasecmp(authType.second,"shibboleth"))) +#else + (!authType.first || _stricmp(authType.second,"shibboleth"))) +#endif + return make_pair(true,request.returnDecline()); + + // Do we have an access control plugin? + if (settings.second) { + const Session* session =NULL; + pair shib_cookie=app->getCookieNameProps("_shibsession_"); + const char *session_id = request.getCookie(shib_cookie.first.c_str()); + try { + if (session_id && *session_id) { + session = request.getSession(); + } + } + catch (exception&) { + request.log(SPRequest::SPWarn, "unable to obtain session information to pass to access control provider"); + } + + Locker acllock(settings.second); + if (settings.second->authorized(request,session)) { + // Let the caller decide how to proceed. + request.log(SPRequest::SPDebug, "access control provider granted access"); + return make_pair(false,0); + } + else { + request.log(SPRequest::SPWarn, "access control provider denied access"); + TemplateParameters tp; + tp.m_map["requestURL"] = targetURL; + return make_pair(true,sendError(request, app, "access", tp)); + } + return make_pair(false,0); + } + else + return make_pair(true,request.returnDecline()); + } + catch (XMLToolingException& e) { + TemplateParameters tp; + tp.m_map["errorType"] = procState; + tp.m_map["errorText"] = e.what(); + tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); + return make_pair(true,sendError(request, app, "session", tp, &e)); + } + catch (exception& e) { + TemplateParameters tp; + tp.m_map["errorType"] = procState; + tp.m_map["errorText"] = e.what(); + tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); + return make_pair(true,sendError(request, app, "access", tp)); + } +#ifndef _DEBUG + catch (...) { + TemplateParameters tp; + tp.m_map["errorType"] = procState; + tp.m_map["errorText"] = "Caught an unknown exception."; + tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); + return make_pair(true,sendError(request, app, "access", tp)); + } +#endif } pair ServiceProvider::doExport(SPRequest& request, bool requireSession) const { - return make_pair(true,0); +#ifdef _DEBUG + xmltooling::NDC ndc("doExport"); +#endif + + const Application* app=NULL; + const char* procState = "Attribute Processing Error"; + string targetURL = request.getRequestURL(); + + try { + RequestMapper::Settings settings = request.getRequestSettings(); + app = &(request.getApplication()); + + const Session* session=NULL; + pair shib_cookie=app->getCookieNameProps("_shibsession_"); + const char *session_id = request.getCookie(shib_cookie.first.c_str()); + try { + if (session_id && *session_id) { + session = request.getSession(); + } + } + catch (exception&) { + request.log(SPRequest::SPWarn, "unable to obtain session information to export into request headers"); + // If we have to have a session, then this is a fatal error. + if (requireSession) + throw; + } + + // Still no data? + if (!session) { + if (requireSession) + throw RetryableProfileException("Unable to obtain session information for request."); + else + return make_pair(false,0); // just bail silently + } + + /* + TODO: port to new cache API + // Extract data from session. + pair sub=m_cacheEntry->getSubject(false,true); + pair unfiltered=m_cacheEntry->getTokens(true,false); + pair filtered=m_cacheEntry->getTokens(false,true); + + // Maybe export the tokens. + pair exp=m_settings.first->getBool("exportAssertion"); + if (exp.first && exp.second && unfiltered.first && *unfiltered.first) { + unsigned int outlen; + XMLByte* serialized = + Base64::encode(reinterpret_cast((char*)unfiltered.first), XMLString::stringLen(unfiltered.first), &outlen); + XMLByte *pos, *pos2; + for (pos=serialized, pos2=serialized; *pos2; pos2++) + if (isgraph(*pos2)) + *pos++=*pos2; + *pos=0; + setHeader("Shib-Attributes", reinterpret_cast(serialized)); + XMLString::release(&serialized); + } + + // Export the SAML AuthnMethod and the origin site name, and possibly the NameIdentifier. + setHeader("Shib-Origin-Site", m_cacheEntry->getProviderId()); + setHeader("Shib-Identity-Provider", m_cacheEntry->getProviderId()); + setHeader("Shib-Authentication-Method", m_cacheEntry->getAuthnContext()); + + // Get the AAP providers, which contain the attribute policy info. + Iterator provs=m_app->getAAPProviders(); + + // Export NameID? + while (provs.hasNext()) { + IAAP* aap=provs.next(); + xmltooling::Locker locker(aap); + const XMLCh* format = sub.second->getNameIdentifier()->getFormat(); + const IAttributeRule* rule=aap->lookup(format ? format : SAMLNameIdentifier::UNSPECIFIED); + if (rule && rule->getHeader()) { + auto_ptr_char form(format ? format : SAMLNameIdentifier::UNSPECIFIED); + auto_ptr_char nameid(sub.second->getNameIdentifier()->getName()); + setHeader("Shib-NameIdentifier-Format", form.get()); + if (!strcmp(rule->getHeader(),"REMOTE_USER")) + setRemoteUser(nameid.get()); + else + setHeader(rule->getHeader(), nameid.get()); + } + } + + setHeader("Shib-Application-ID", m_app->getId()); + + // Export the attributes. + Iterator a_iter(filtered.second ? filtered.second->getAssertions() : EMPTY(SAMLAssertion*)); + while (a_iter.hasNext()) { + SAMLAssertion* assert=a_iter.next(); + Iterator statements=assert->getStatements(); + while (statements.hasNext()) { + SAMLAttributeStatement* astate=dynamic_cast(statements.next()); + if (!astate) + continue; + Iterator attrs=astate->getAttributes(); + while (attrs.hasNext()) { + SAMLAttribute* attr=attrs.next(); + + // Are we supposed to export it? + provs.reset(); + while (provs.hasNext()) { + IAAP* aap=provs.next(); + xmltooling::Locker locker(aap); + const IAttributeRule* rule=aap->lookup(attr->getName(),attr->getNamespace()); + if (!rule || !rule->getHeader()) + continue; + + Iterator vals=attr->getSingleByteValues(); + if (!strcmp(rule->getHeader(),"REMOTE_USER") && vals.hasNext()) + setRemoteUser(vals.next().c_str()); + else { + int it=0; + string header = getSecureHeader(rule->getHeader()); + if (!header.empty()) + it++; + for (; vals.hasNext(); it++) { + string value = vals.next(); + for (string::size_type pos = value.find_first_of(";", string::size_type(0)); + pos != string::npos; + pos = value.find_first_of(";", pos)) { + value.insert(pos, "\\"); + pos += 2; + } + if (it) + header += ";"; + header += value; + } + setHeader(rule->getHeader(), header.c_str()); + } + } + } + } + } + */ + + return make_pair(false,0); + } + catch (XMLToolingException& e) { + TemplateParameters tp; + tp.m_map["errorType"] = procState; + tp.m_map["errorText"] = e.what(); + tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); + return make_pair(true,sendError(request, app, "rm", tp, &e)); + } + catch (exception& e) { + TemplateParameters tp; + tp.m_map["errorType"] = procState; + tp.m_map["errorText"] = e.what(); + tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); + return make_pair(true,sendError(request, app, "rm", tp)); + } +#ifndef _DEBUG + catch (...) { + TemplateParameters tp; + tp.m_map["errorType"] = procState; + tp.m_map["errorText"] = "Caught an unknown exception."; + tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); + return make_pair(true,sendError(request, app, "rm", tp)); + } +#endif } pair ServiceProvider::doHandler(SPRequest& request) const { - return make_pair(true,0); +#ifdef _DEBUG + xmltooling::NDC ndc("doHandler"); +#endif + + const Application* app=NULL; + const char* procState = "Shibboleth Handler Error"; + string targetURL = request.getRequestURL(); + + try { + RequestMapper::Settings settings = request.getRequestSettings(); + app = &(request.getApplication()); + + const char* handlerURL=request.getHandlerURL(targetURL.c_str()); + if (!handlerURL) + throw ConfigurationException("Cannot determine handler from resource URL, check configuration."); + + // Make sure we only process handler requests. + if (!strstr(targetURL.c_str(),handlerURL)) + return make_pair(true, request.returnDecline()); + + const PropertySet* sessionProps=app->getPropertySet("Sessions"); + if (!sessionProps) + throw ConfigurationException("Unable to map request to application session settings, check configuration."); + + // Process incoming request. + pair handlerSSL=sessionProps->getBool("handlerSSL"); + + // Make sure this is SSL, if it should be + if ((!handlerSSL.first || handlerSSL.second) && strcmp(request.getScheme(),"https")) + throw FatalProfileException("Blocked non-SSL access to Shibboleth handler."); + + // We dispatch based on our path info. We know the request URL begins with or equals the handler URL, + // so the path info is the next character (or null). + const Handler* handler=app->getHandler(targetURL.c_str() + strlen(handlerURL)); + if (!handler) + throw BindingException("Shibboleth handler invoked at an unconfigured location."); + + if (XMLHelper::isNodeNamed(handler->getElement(),samlconstants::SAML20MD_NS,AssertionConsumerService::LOCAL_NAME)) + procState = "Session Creation Error"; + else if (XMLString::equals(handler->getElement()->getLocalName(),SessionInitiator)) + procState = "Session Initiator Error"; + else if (XMLHelper::isNodeNamed(handler->getElement(),samlconstants::SAML20MD_NS,SingleLogoutService::LOCAL_NAME)) + procState = "Session Termination Error"; + else + procState = "Protocol Handler Error"; + pair hret=handler->run(request); + + // Did the handler run successfully? + if (hret.first) + return hret; + + throw BindingException("Configured Shibboleth handler failed to process the request."); + } + catch (MetadataException& e) { + TemplateParameters tp; + tp.m_map["errorText"] = e.what(); + // See if a metadata error page is installed. + const PropertySet* props=app->getPropertySet("Errors"); + if (props) { + pair p=props->getString("metadata"); + if (p.first) { + tp.m_map["errorType"] = procState; + tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); + return make_pair(true,sendError(request, app, "metadata", tp, &e)); + } + } + throw; + } + catch (XMLToolingException& e) { + TemplateParameters tp; + tp.m_map["errorType"] = procState; + tp.m_map["errorText"] = e.what(); + tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); + return make_pair(true,sendError(request, app, "session", tp, &e)); + } + catch (exception& e) { + TemplateParameters tp; + tp.m_map["errorType"] = procState; + tp.m_map["errorText"] = e.what(); + tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); + return make_pair(true,sendError(request, app, "session", tp)); + } +#ifndef _DEBUG + catch (...) { + TemplateParameters tp; + tp.m_map["errorType"] = procState; + tp.m_map["errorText"] = "Caught an unknown exception."; + tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); + return make_pair(true,sendError(request, app, "session", tp)); + } +#endif } diff --git a/shibsp/ServiceProvider.h b/shibsp/ServiceProvider.h index b6c55e1..bebe351 100644 --- a/shibsp/ServiceProvider.h +++ b/shibsp/ServiceProvider.h @@ -33,6 +33,7 @@ namespace shibsp { class SHIBSP_API RequestMapper; class SHIBSP_API SessionCache; class SHIBSP_API SPRequest; + class SHIBSP_API TemplateParameters; /** * Interface to a Shibboleth ServiceProvider instance. diff --git a/shibsp/shibsp.vcproj b/shibsp/shibsp.vcproj index 3ccdea9..64bbd8f 100644 --- a/shibsp/shibsp.vcproj +++ b/shibsp/shibsp.vcproj @@ -219,6 +219,10 @@ RelativePath=".\util\SPConstants.cpp" > + + + + + +using namespace shibsp; +using namespace xmltooling; +using namespace std; + +void TemplateParameters::setPropertySet(const PropertySet* props) +{ + m_props = props; + + // Create a timestamp. + time_t now = time(NULL); +#ifdef HAVE_CTIME_R + char timebuf[32]; + m_map["now"] = ctime_r(&now,timebuf); +#else + m_map["now"] = ctime(&now); +#endif +} + +const char* TemplateParameters::getParameter(const char* name) const +{ + const char* pch = TemplateEngine::TemplateParameters::getParameter(name); + if (pch || !m_props) + return pch; + pair p = m_props->getString(name); + return p.first ? p.second : NULL; +} diff --git a/shibsp/util/TemplateParameters.h b/shibsp/util/TemplateParameters.h new file mode 100644 index 0000000..e276734 --- /dev/null +++ b/shibsp/util/TemplateParameters.h @@ -0,0 +1,62 @@ +/* + * Copyright 2001-2006 Internet2 + * + * Licensed 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. + */ + +/** + * @file shibsp/util/TemplateParameters.h + * + * Supplies xmltooling TemplateEngine with additional parameters from a PropertySet. + */ + +#ifndef __shibsp_tempparams_h__ +#define __shibsp_tempparams_h__ + +#include +#include + +namespace shibsp { + + /** + * Supplies xmltooling TemplateEngine with additional parameters from a PropertySet. + */ + class SHIBSP_API TemplateParameters : public xmltooling::TemplateEngine::TemplateParameters + { + public: + /** + * Constructor + * + * @param props a PropertySet to supply additional parameters + */ + TemplateParameters(const PropertySet* props=NULL) { + setPropertySet(props); + } + + virtual ~TemplateParameters() {} + + /** + * Sets a PropertySet to supply additional parameters. + * + * @param props a PropertySet to supply additional parameters + */ + void setPropertySet(const PropertySet* props); + + const char* getParameter(const char* name) const; + + private: + const PropertySet* m_props; + }; +}; + +#endif /* __shibsp_tempparams_h__ */ -- 2.1.4