#include "exceptions.h"
#include "AccessControl.h"
#include "Application.h"
-#include "Handler.h"
#include "ServiceProvider.h"
#include "SessionCache.h"
#include "SPRequest.h"
#include "attribute/Attribute.h"
+#include "handler/SessionInitiator.h"
#include "util/TemplateParameters.h"
#include <fstream>
#include <sstream>
-#include <saml/saml2/metadata/Metadata.h>
-#include <saml/util/SAMLConstants.h>
#include <xmltooling/XMLToolingConfig.h>
#include <xmltooling/util/NDC.h>
+#include <xmltooling/util/URLEncoder.h>
#include <xmltooling/util/XMLHelper.h>
using namespace shibsp;
-using namespace opensaml::saml2md;
-using namespace opensaml;
using namespace xmltooling;
using namespace std;
namespace shibsp {
- //SHIBSP_DLLLOCAL PluginManager<ServiceProvider,const DOMElement*>::Factory XMLServiceProviderFactory;
+ SHIBSP_DLLLOCAL PluginManager<ServiceProvider,string,const DOMElement*>::Factory XMLServiceProviderFactory;
- long SHIBSP_DLLLOCAL sendError(
- SPRequest& request, const Application* app, const char* page, TemplateParameters& tp, const XMLToolingException* ex=NULL
- )
+ long SHIBSP_DLLLOCAL sendError(SPRequest& request, const Application* app, const char* page, TemplateParameters& tp, bool mayRedirect=false)
{
+ if (mayRedirect) {
+ // Check for redirection on errors instead of template.
+ pair<bool,const char*> redirectErrors = app ? app->getString("redirectErrors") : pair<bool,const char*>(false,NULL);
+ if (redirectErrors.first) {
+ string loc(redirectErrors.second);
+ loc = loc + '?' + tp.toQueryString();
+ return request.sendRedirect(loc.c_str());
+ }
+ }
+
request.setContentType("text/html");
request.setResponseHeader("Expires","01-Jan-1997 12:00:00 GMT");
request.setResponseHeader("Cache-Control","private,no-store,no-cache");
+ // Error templates come from the request's settings or from the Errors property set.
+ pair<bool,const char*> pathname = pair<bool,const char*>(false,NULL);
+ try {
+ RequestMapper::Settings settings = request.getRequestSettings();
+ string pagename(page);
+ pagename += "Error";
+ pathname = settings.first->getString(pagename.c_str());
+ }
+ catch (exception& ex) {
+ request.log(SPRequest::SPError, ex.what());
+ }
+
+ // Nothing for request, so check app properties.
const PropertySet* props=app ? app->getPropertySet("Errors") : NULL;
- if (props) {
- pair<bool,const char*> 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<opensaml::GenericResponse&>(request).sendResponse(msg, HTTPResponse::SAML_HTTP_STATUS_FORBIDDEN);
+ if (!pathname.first && props)
+ pathname=props->getString(page);
+
+ if (pathname.first) {
+ ifstream infile(pathname.second);
+ if (infile) {
+ tp.setPropertySet(props);
+ stringstream str;
+ XMLToolingConfig::getConfig().getTemplateEngine()->run(infile, str, tp, tp.getRichException());
+ return request.sendResponse(str);
}
}
+
+ if (!strcmp(page,"access")) {
+ istringstream msg("Access Denied");
+ return request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_FORBIDDEN);
+ }
string errstr = string("sendError could not process error template (") + page + ")";
request.log(SPRequest::SPError, errstr);
}
void SHIBSP_DLLLOCAL clearHeaders(SPRequest& request) {
- // Clear invariant stuff.
- request.clearHeader("Shib-Identity-Provider");
- request.clearHeader("Shib-Authentication-Method");
- request.clearHeader("Shib-AuthnContext-Class");
- request.clearHeader("Shib-AuthnContext-Decl");
- request.clearHeader("Shib-Attributes");
- request.clearHeader("Shib-Application-ID");
-
- // Clear out the list of mapped attributes
- /* TODO: need some kind of master attribute list via the new resolver
- Iterator<IAAP*> provs=dynamic_cast<const IApplication&>(getApplication()).getAAPProviders();
- while (provs.hasNext()) {
- IAAP* aap=provs.next();
- xmltooling::Locker locker(aap);
- Iterator<const IAttributeRule*> rules=aap->getAttributeRules();
- while (rules.hasNext()) {
- const char* header=rules.next()->getHeader();
- if (header)
- request.clearHeader(header);
- }
- }
- */
+ request.clearHeader("Shib-Session-ID", "HTTP_SHIB_SESSION_ID");
+ request.clearHeader("Shib-Identity-Provider", "HTTP_SHIB_IDENTITY_PROVIDER");
+ request.clearHeader("Shib-Authentication-Method", "HTTP_SHIB_AUTHENTICATION_METHOD");
+ request.clearHeader("Shib-AuthnContext-Class", "HTTP_SHIB_AUTHNCONTEXT_CLASS");
+ request.clearHeader("Shib-AuthnContext-Decl", "HTTP_SHIB_AUTHNCONTEXT_DECL");
+ request.clearHeader("Shib-Assertion-Count", "HTTP_SHIB_ASSERTION_COUNT");
+ request.clearHeader("REMOTE_USER", "HTTP_REMOTE_USER");
+ //request.clearHeader("Shib-Application-ID"); handle inside app method
+ request.getApplication().clearAttributeHeaders(request);
}
-
- 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()
{
- //SPConfig::getConfig().ServiceProviderManager.registerFactory(XML_SERVICE_PROVIDER, XMLServiceProviderFactory);
+ SPConfig::getConfig().ServiceProviderManager.registerFactory(XML_SERVICE_PROVIDER, XMLServiceProviderFactory);
}
pair<bool,long> ServiceProvider::doAuthentication(SPRequest& request, bool handler) const
#endif
const Application* app=NULL;
- const char* procState = "Request Processing Error";
string targetURL = request.getRequestURL();
try {
// Fix for secadv 20050901
clearHeaders(request);
- procState = "Session Processing Error";
-
Session* session = NULL;
try {
session = request.getSession();
catch (exception& e) {
request.log(SPRequest::SPWarn, string("error during session lookup: ") + e.what());
// If it's not a retryable session failure, we throw to the outer handler for reporting.
- if (dynamic_cast<RetryableProfileException*>(&e)==NULL)
+ if (dynamic_cast<opensaml::RetryableProfileException*>(&e)==NULL)
throw;
}
return make_pair(true,request.returnOK());
// No session, but we require one. Initiate a new session using the indicated method.
- procState = "Session Initiator Error";
const Handler* initiator=NULL;
if (requireSessionWith.first) {
initiator=app->getSessionInitiatorById(requireSessionWith.second);
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();
+ TemplateParameters tp(&e);
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["errorType"] = "Unexpected Error";
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
const Application* app=NULL;
- const char* procState = "Authorization Processing Error";
string targetURL = request.getRequestURL();
try {
if (settings.second) {
const Session* session = NULL;
try {
- session = request.getSession();
+ session = request.getSession(false);
}
catch (exception& e) {
request.log(SPRequest::SPWarn, string("unable to obtain session to pass to access control provider: ") + e.what());
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();
+ TemplateParameters tp(&e);
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["errorType"] = "Unexpected Error";
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
const Application* app=NULL;
- const char* procState = "Attribute Processing Error";
string targetURL = request.getRequestURL();
try {
const Session* session = NULL;
try {
- session = request.getSession();
+ session = request.getSession(false);
}
catch (exception& e) {
request.log(SPRequest::SPWarn, string("unable to obtain session to export to request: ") + e.what());
// Still no data?
if (!session) {
if (requireSession)
- throw RetryableProfileException("Unable to obtain session to export to request.");
+ throw opensaml::RetryableProfileException("Unable to obtain session to export to request.");
else
return make_pair(false,0); // just bail silently
}
request.setHeader("Shib-Application-ID", app->getId());
+ request.setHeader("Shib-Session-ID", session->getID());
// Export the IdP name and Authn method/context info.
const char* hval = session->getEntityID();
// Maybe export the assertion keys.
pair<bool,bool> exp=settings.first->getBool("exportAssertion");
if (exp.first && exp.second) {
- //setHeader("Shib-Attributes", reinterpret_cast<char*>(serialized));
- // TODO: export lookup URLs to access assertions by ID
- const vector<const char*>& tokens = session->getAssertionIDs();
+ const PropertySet* sessions=app->getPropertySet("Sessions");
+ pair<bool,const char*> exportLocation = sessions ? sessions->getString("exportLocation") : pair<bool,const char*>(false,NULL);
+ if (!exportLocation.first)
+ request.log(SPRequest::SPWarn, "can't export assertions without an exportLocation Sessions property");
+ else {
+ const URLEncoder* encoder = XMLToolingConfig::getConfig().getURLEncoder();
+ string exportName = "Shib-Assertion-00";
+ string baseURL;
+ if (!strncmp(exportLocation.second, "http", 4))
+ baseURL = exportLocation.second;
+ else
+ baseURL = string(request.getHandlerURL(targetURL.c_str())) + exportLocation.second;
+ baseURL = baseURL + "?key=" + session->getID() + "&ID=";
+ const vector<const char*>& tokens = session->getAssertionIDs();
+ vector<const char*>::size_type count = 0;
+ for (vector<const char*>::const_iterator tokenids = tokens.begin(); tokenids!=tokens.end(); ++tokenids) {
+ count++;
+ *(exportName.rbegin()) = '0' + (count%10);
+ *(++exportName.rbegin()) = '0' + (count/10);
+ string fullURL = baseURL + encoder->encode(*tokenids);
+ request.setHeader(exportName.c_str(), fullURL.c_str());
+ }
+ request.setHeader("Shib-Assertion-Count", exportName.c_str() + 15);
+ }
}
// Export the attributes.
- const map<string,const Attribute*>& attributes = session->getAttributes();
- for (map<string,const Attribute*>::const_iterator a = attributes.begin(); a!=attributes.end(); ++a) {
+ bool remoteUserSet = false;
+ const multimap<string,const Attribute*>& attributes = session->getIndexedAttributes();
+ for (multimap<string,const Attribute*>::const_iterator a = attributes.begin(); a!=attributes.end(); ++a) {
const vector<string>& vals = a->second->getSerializedValues();
- if (!strcmp(a->second->getId(), "REMOTE_USER") && !vals.empty())
+
+ // See if this needs to be set as the REMOTE_USER value.
+ if (!remoteUserSet && !vals.empty() && app->getRemoteUserAttributeIds().count(a->first)) {
request.setRemoteUser(vals.front().c_str());
- else {
- string header(request.getSecureHeader(a->second->getId()));
- for (vector<string>::const_iterator v = vals.begin(); v!=vals.end(); ++v) {
- if (!header.empty())
- header += ";";
- string::size_type pos = v->find_first_of(';',string::size_type(0));
- if (pos!=string::npos) {
- string value(*v);
- for (; pos != string::npos; pos = value.find_first_of(';',pos)) {
- value.insert(pos, "\\");
- pos += 2;
- }
- header += value;
- }
- else {
- header += (*v);
+ remoteUserSet = true;
+ }
+
+ // Handle the normal export case.
+ string header(request.getSecureHeader(a->first.c_str()));
+ for (vector<string>::const_iterator v = vals.begin(); v!=vals.end(); ++v) {
+ if (!header.empty())
+ header += ";";
+ string::size_type pos = v->find_first_of(';',string::size_type(0));
+ if (pos!=string::npos) {
+ string value(*v);
+ for (; pos != string::npos; pos = value.find_first_of(';',pos)) {
+ value.insert(pos, "\\");
+ pos += 2;
}
+ header += value;
+ }
+ else {
+ header += (*v);
}
- request.setHeader(a->second->getId(), header.c_str());
}
+ request.setHeader(a->first.c_str(), 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();
+ TemplateParameters tp(&e);
tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?'));
- return make_pair(true,sendError(request, app, "rm", tp));
+ return make_pair(true,sendError(request, app, "session", tp));
}
#ifndef _DEBUG
catch (...) {
TemplateParameters tp;
- tp.m_map["errorType"] = procState;
+ tp.m_map["errorType"] = "Unexpected Error";
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));
+ return make_pair(true,sendError(request, app, "session", tp));
}
#endif
}
#endif
const Application* app=NULL;
- const char* procState = "Shibboleth Handler Error";
string targetURL = request.getRequestURL();
try {
// Make sure this is SSL, if it should be
if ((!handlerSSL.first || handlerSSL.second) && !request.isSecure())
- throw FatalProfileException("Blocked non-SSL access to Shibboleth handler.");
+ throw opensaml::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";
+ throw ConfigurationException("Shibboleth handler invoked at an unconfigured location.");
+
pair<bool,long> hret=handler->run(request);
// Did the handler run successfully?
if (hret.first)
return hret;
- throw BindingException("Configured Shibboleth handler failed to process the request.");
+ throw ConfigurationException("Configured Shibboleth handler failed to process the request.");
}
- catch (MetadataException& e) {
- TemplateParameters tp;
- tp.m_map["errorText"] = e.what();
+ catch (opensaml::saml2md::MetadataException& e) {
+ TemplateParameters tp(&e);
+ tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?'));
// See if a metadata error page is installed.
- const PropertySet* props=app->getPropertySet("Errors");
+ const PropertySet* props=app ? app->getPropertySet("Errors") : NULL;
if (props) {
pair<bool,const char*> 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));
- }
+ if (p.first)
+ return make_pair(true,sendError(request, app, "metadata", tp, true));
}
- 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));
+ return make_pair(true,sendError(request, app, "session", tp, true));
}
catch (exception& e) {
- TemplateParameters tp;
- tp.m_map["errorType"] = procState;
- tp.m_map["errorText"] = e.what();
+ TemplateParameters tp(&e);
tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?'));
- return make_pair(true,sendError(request, app, "session", tp));
+ return make_pair(true,sendError(request, app, "session", tp, true));
}
#ifndef _DEBUG
catch (...) {
TemplateParameters tp;
- tp.m_map["errorType"] = procState;
+ tp.m_map["errorType"] = "Unexpected Error";
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));
+ return make_pair(true,sendError(request, app, "session", tp, true));
}
#endif
}