X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=shibsp%2FServiceProvider.cpp;h=3ac5236055fb77aad116eaf417142f93b525331c;hb=c51bfd77603cf0ddb0b5e374c35586a8435895d6;hp=a5d84f5eb4895b6f5e014f66379e9d5cf95829a6;hpb=b105e8c270938e3f02d1fa7a3c52541b2642415f;p=shibboleth%2Fcpp-sp.git diff --git a/shibsp/ServiceProvider.cpp b/shibsp/ServiceProvider.cpp index a5d84f5..3ac5236 100644 --- a/shibsp/ServiceProvider.cpp +++ b/shibsp/ServiceProvider.cpp @@ -37,6 +37,7 @@ #include #include +#include #ifndef SHIBSP_LITE # include # include @@ -94,6 +95,7 @@ namespace shibsp { redirectErrors = props->getString("redirectErrors"); if (redirectErrors.first) { string loc(redirectErrors.second); + request.absolutize(loc); loc = loc + '?' + tp.toQueryString(); return request.sendRedirect(loc.c_str()); } @@ -147,6 +149,7 @@ namespace shibsp { void SHIBSP_DLLLOCAL clearHeaders(SPRequest& request) { const Application& app = request.getApplication(); + app.clearHeader(request, "Shib-Cookie-Name", "HTTP_SHIB_COOKIE_NAME"); app.clearHeader(request, "Shib-Session-ID", "HTTP_SHIB_SESSION_ID"); app.clearHeader(request, "Shib-Session-Index", "HTTP_SHIB_SESSION_INDEX"); app.clearHeader(request, "Shib-Identity-Provider", "HTTP_SHIB_IDENTITY_PROVIDER"); @@ -185,24 +188,24 @@ SecurityPolicyProvider* ServiceProvider::getSecurityPolicyProvider(bool required Remoted* ServiceProvider::regListener(const char* address, Remoted* listener) { - Remoted* ret=nullptr; - map::const_iterator i=m_listenerMap.find(address); - if (i!=m_listenerMap.end()) - ret=i->second; - m_listenerMap[address]=listener; - Category::getInstance(SHIBSP_LOGCAT".ServiceProvider").info("registered remoted message endpoint (%s)",address); + Remoted* ret = nullptr; + map::const_iterator i = m_listenerMap.find(address); + if (i != m_listenerMap.end()) + ret = i->second; + m_listenerMap[address] = listener; + Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider").info("registered remoted message endpoint (%s)",address); return ret; } bool ServiceProvider::unregListener(const char* address, Remoted* current, Remoted* restore) { - map::const_iterator i=m_listenerMap.find(address); - if (i!=m_listenerMap.end() && i->second==current) { + map::const_iterator i = m_listenerMap.find(address); + if (i != m_listenerMap.end() && i->second == current) { if (restore) - m_listenerMap[address]=restore; + m_listenerMap[address] = restore; else m_listenerMap.erase(address); - Category::getInstance(SHIBSP_LOGCAT".ServiceProvider").info("unregistered remoted message endpoint (%s)",address); + Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider").info("unregistered remoted message endpoint (%s)",address); return true; } return false; @@ -210,8 +213,8 @@ bool ServiceProvider::unregListener(const char* address, Remoted* current, Remot Remoted* ServiceProvider::lookupListener(const char *address) const { - map::const_iterator i=m_listenerMap.find(address); - return (i==m_listenerMap.end()) ? nullptr : i->second; + map::const_iterator i = m_listenerMap.find(address); + return (i == m_listenerMap.end()) ? nullptr : i->second; } pair ServiceProvider::doAuthentication(SPRequest& request, bool handler) const @@ -219,9 +222,9 @@ pair ServiceProvider::doAuthentication(SPRequest& request, bool handl #ifdef _DEBUG xmltooling::NDC ndc("doAuthentication"); #endif - Category& log = Category::getInstance(SHIBSP_LOGCAT".ServiceProvider"); + Category& log = Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider"); - const Application* app=nullptr; + const Application* app = nullptr; string targetURL = request.getRequestURL(); try { @@ -248,7 +251,7 @@ pair ServiceProvider::doAuthentication(SPRequest& request, bool handl else { TemplateParameters tp; tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); - return make_pair(true,sendError(log, request, app, "ssl", tp, false)); + return make_pair(true, sendError(log, request, app, "ssl", tp, false)); } } } @@ -259,29 +262,24 @@ pair ServiceProvider::doAuthentication(SPRequest& request, bool handl // 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 (boost::contains(targetURL, handlerURL)) { if (handler) return doHandler(request); else return make_pair(true, request.returnOK()); } - // Three settings dictate how to proceed. + // These settings dictate how to proceed. pair authType = settings.first->getString("authType"); pair requireSession = settings.first->getBool("requireSession"); pair requireSessionWith = settings.first->getString("requireSessionWith"); - - string lcAuthType; - if (authType.first) { - while (*authType.second) - lcAuthType += tolower(*authType.second++); - } + pair requireLogoutWith = settings.first->getString("requireLogoutWith"); // If no session is required AND the AuthType (an Apache-derived concept) isn't recognized, // 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 && - (!authType.first || m_authTypes.find(lcAuthType) == m_authTypes.end())) + (!authType.first || m_authTypes.find(boost::to_lower_copy(string(authType.second))) == m_authTypes.end())) return make_pair(true, request.returnDecline()); // Fix for secadv 20050901 @@ -289,19 +287,44 @@ pair ServiceProvider::doAuthentication(SPRequest& request, bool handl Session* session = nullptr; try { - session = request.getSession(); + session = request.getSession(true, false, false); // don't cache it } catch (exception& e) { log.warn("error during session lookup: %s", e.what()); // If it's not a retryable session failure, we throw to the outer handler for reporting. - if (dynamic_cast(&e)==nullptr) + if (dynamic_cast(&e) == nullptr) throw; } - if (!session) { + Locker slocker(session, false); // pop existing lock on exit + if (session) { + // Check for logout interception. + if (requireLogoutWith.first) { + // Check for a completion parameter on the query string. + const char* qstr = request.getQueryString(); + if (!qstr || !strstr(qstr, "shiblogoutdone=1")) { + // First leg of circuit, so we redirect to the logout endpoint specified with this URL as a return location. + string selfurl = request.getRequestURL(); + if (qstr) + selfurl += '&'; + else + selfurl += '?'; + selfurl += "shiblogoutdone=1"; + string loc = requireLogoutWith.second; + request.absolutize(loc); + if (loc.find('?') != string::npos) + loc += '&'; + else + loc += '?'; + loc += "return=" + XMLToolingConfig::getConfig().getURLEncoder()->encode(selfurl.c_str()); + return make_pair(true, request.sendRedirect(loc.c_str())); + } + } + } + else { // No session. Maybe that's acceptable? if ((!requireSession.first || !requireSession.second) && !requireSessionWith.first) - return make_pair(true,request.returnOK()); + return make_pair(true, request.returnOK()); // No session, but we require one. Initiate a new session using the indicated method. const SessionInitiator* initiator=nullptr; @@ -309,7 +332,7 @@ pair ServiceProvider::doAuthentication(SPRequest& request, bool handl initiator=app->getSessionInitiatorById(requireSessionWith.second); if (!initiator) { throw ConfigurationException( - "No session initiator found with id ($1), check requireSessionWith command.", params(1,requireSessionWith.second) + "No session initiator found with id ($1), check requireSessionWith command.", params(1, requireSessionWith.second) ); } } @@ -319,10 +342,15 @@ pair ServiceProvider::doAuthentication(SPRequest& request, bool handl throw ConfigurationException("No default session initiator found, check configuration."); } - return initiator->run(request,false); + // Dispatch to SessionInitiator. This MUST handle the request, or we want to fail here. + // Used to fall through into doExport, but this is a cleaner exit path. + pair ret = initiator->run(request, false); + if (ret.first) + return ret; + throw ConfigurationException("Session initiator did not handle request for a new session, check configuration."); } - request.setAuthType(lcAuthType.c_str()); + request.setAuthType(authType.second); // We're done. Everything is okay. Nothing to report. Nothing to do.. // Let the caller decide how to proceed. @@ -333,7 +361,7 @@ pair ServiceProvider::doAuthentication(SPRequest& request, bool handl request.log(SPRequest::SPError, e.what()); TemplateParameters tp(&e); tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); - return make_pair(true,sendError(log, request, app, "session", tp)); + return make_pair(true, sendError(log, request, app, "session", tp)); } } @@ -342,9 +370,11 @@ pair ServiceProvider::doAuthorization(SPRequest& request) const #ifdef _DEBUG xmltooling::NDC ndc("doAuthorization"); #endif - Category& log = Category::getInstance(SHIBSP_LOGCAT".ServiceProvider"); + Category& log = Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider"); - const Application* app=nullptr; + const Application* app = nullptr; + Session* session = nullptr; + Locker slocker; string targetURL = request.getRequestURL(); try { @@ -356,57 +386,52 @@ pair ServiceProvider::doAuthorization(SPRequest& request) const pair requireSession = settings.first->getBool("requireSession"); pair requireSessionWith = settings.first->getString("requireSessionWith"); - string lcAuthType; - if (authType.first) { - while (*authType.second) - lcAuthType += tolower(*authType.second++); - } - // If no session is required AND the AuthType (an Apache-derived concept) isn't recognized, // 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 && - (!authType.first || m_authTypes.find(lcAuthType) == m_authTypes.end())) + (!authType.first || m_authTypes.find(boost::to_lower_copy(string(authType.second))) == m_authTypes.end())) return make_pair(true, request.returnDecline()); // Do we have an access control plugin? if (settings.second) { - const Session* session = nullptr; try { - session = request.getSession(false); + session = request.getSession(false, false, false); // ignore timeout and do not cache + if (session) + slocker.assign(session, false); // assign to lock popper } catch (exception& e) { log.warn("unable to obtain session to pass to access control provider: %s", e.what()); } Locker acllock(settings.second); - switch (settings.second->authorized(request,session)) { + switch (settings.second->authorized(request, session)) { case AccessControl::shib_acl_true: log.debug("access control provider granted access"); - return make_pair(true,request.returnOK()); + return make_pair(true, request.returnOK()); case AccessControl::shib_acl_false: { log.warn("access control provider denied access"); - TemplateParameters tp; + TemplateParameters tp(nullptr, nullptr, session); tp.m_map["requestURL"] = targetURL; - return make_pair(true,sendError(log, request, app, "access", tp, false)); + return make_pair(true, sendError(log, request, app, "access", tp, false)); } default: // Use the "DECLINE" interface to signal we don't know what to do. - return make_pair(true,request.returnDecline()); + return make_pair(true, request.returnDecline()); } } else { - return make_pair(true,request.returnDecline()); + return make_pair(true, request.returnDecline()); } } catch (exception& e) { request.log(SPRequest::SPError, e.what()); - TemplateParameters tp(&e); + TemplateParameters tp(&e, nullptr, session); tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); - return make_pair(true,sendError(log, request, app, "access", tp)); + return make_pair(true, sendError(log, request, app, "access", tp)); } } @@ -415,18 +440,21 @@ pair ServiceProvider::doExport(SPRequest& request, bool requireSessio #ifdef _DEBUG xmltooling::NDC ndc("doExport"); #endif - Category& log = Category::getInstance(SHIBSP_LOGCAT".ServiceProvider"); + Category& log = Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider"); - const Application* app=nullptr; + const Application* app = nullptr; + Session* session = nullptr; + Locker slocker; string targetURL = request.getRequestURL(); try { RequestMapper::Settings settings = request.getRequestSettings(); app = &(request.getApplication()); - const Session* session = nullptr; try { - session = request.getSession(false); + session = request.getSession(false, false, false); // ignore timeout and do not cache + if (session) + slocker.assign(session, false); // assign to lock popper } catch (exception& e) { log.warn("unable to obtain session to export to request: %s", e.what()); @@ -440,7 +468,7 @@ pair ServiceProvider::doExport(SPRequest& request, bool requireSessio if (requireSession) throw opensaml::RetryableProfileException("Unable to obtain session to export to request."); else - return make_pair(false,0L); // just bail silently + return make_pair(false, 0L); // just bail silently } pair enc = settings.first->getString("encoding"); @@ -476,10 +504,17 @@ pair ServiceProvider::doExport(SPRequest& request, bool requireSessio app->setHeader(request, "Shib-Session-Index", hval); } + // Check for export of algorithmically-derived portion of cookie names. + stdvars = settings.first->getBool("exportCookie"); + if (stdvars.first && stdvars.second) { + pair cookieprops = app->getCookieNameProps(nullptr); + app->setHeader(request, "Shib-Cookie-Name", cookieprops.first.c_str()); + } + // Maybe export the assertion keys. - pair exp=settings.first->getBool("exportAssertion"); + pair exp = settings.first->getBool("exportAssertion"); if (exp.first && exp.second) { - const PropertySet* sessions=app->getPropertySet("Sessions"); + const PropertySet* sessions = app->getPropertySet("Sessions"); pair exportLocation = sessions ? sessions->getString("exportLocation") : pair(false,nullptr); if (!exportLocation.first) log.warn("can't export assertions without an exportLocation Sessions property"); @@ -506,12 +541,12 @@ pair ServiceProvider::doExport(SPRequest& request, bool requireSessio // Export the attributes. const multimap& attributes = session->getIndexedAttributes(); - for (multimap::const_iterator a = attributes.begin(); a!=attributes.end(); ++a) { + for (multimap::const_iterator a = attributes.begin(); a != attributes.end(); ++a) { if (a->second->isInternal()) continue; string header(app->getSecureHeader(request, a->first.c_str())); const vector& vals = a->second->getSerializedValues(); - for (vector::const_iterator v = vals.begin(); v!=vals.end(); ++v) { + for (vector::const_iterator v = vals.begin(); v != vals.end(); ++v) { if (!header.empty()) header += ";"; if (enc.first) { @@ -519,10 +554,10 @@ pair ServiceProvider::doExport(SPRequest& request, bool requireSessio header += encoder->encode(v->c_str()); } else { - string::size_type pos = v->find_first_of(';',string::size_type(0)); - if (pos!=string::npos) { + 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)) { + for (; pos != string::npos; pos = value.find_first_of(';', pos)) { value.insert(pos, "\\"); pos += 2; } @@ -559,9 +594,9 @@ pair ServiceProvider::doExport(SPRequest& request, bool requireSessio } catch (exception& e) { request.log(SPRequest::SPError, e.what()); - TemplateParameters tp(&e); + TemplateParameters tp(&e, nullptr, session); tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); - return make_pair(true,sendError(log, request, app, "session", tp)); + return make_pair(true, sendError(log, request, app, "session", tp)); } } @@ -570,9 +605,9 @@ pair ServiceProvider::doHandler(SPRequest& request) const #ifdef _DEBUG xmltooling::NDC ndc("doHandler"); #endif - Category& log = Category::getInstance(SHIBSP_LOGCAT".ServiceProvider"); + Category& log = Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider"); - const Application* app=nullptr; + const Application* app = nullptr; string targetURL = request.getRequestURL(); try { @@ -604,20 +639,20 @@ pair ServiceProvider::doHandler(SPRequest& request) const } } - const char* handlerURL=request.getHandlerURL(targetURL.c_str()); + 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)) + if (!boost::contains(targetURL, handlerURL)) return make_pair(true, request.returnDecline()); - const PropertySet* sessionProps=app->getPropertySet("Sessions"); + 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"); + pair handlerSSL = sessionProps->getBool("handlerSSL"); // Make sure this is SSL, if it should be if ((!handlerSSL.first || handlerSSL.second) && !request.isSecure()) @@ -625,11 +660,11 @@ pair ServiceProvider::doHandler(SPRequest& request) const // 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)); + const Handler* handler = app->getHandler(targetURL.c_str() + strlen(handlerURL)); if (!handler) throw ConfigurationException("Shibboleth handler invoked at an unconfigured location."); - pair hret=handler->run(request); + pair hret = handler->run(request); // Did the handler run successfully? if (hret.first) @@ -639,9 +674,16 @@ pair ServiceProvider::doHandler(SPRequest& request) const } catch (exception& e) { request.log(SPRequest::SPError, e.what()); - TemplateParameters tp(&e); - tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); + Session* session = nullptr; + try { + session = request.getSession(false, true, false); // do not cache + } + catch (exception&) { + } + Locker slocker(session, false); // pop existing lock on exit + TemplateParameters tp(&e, nullptr, session); + tp.m_map["requestURL"] = targetURL.substr(0, targetURL.find('?')); tp.m_request = &request; - return make_pair(true,sendError(log, request, app, "session", tp)); + return make_pair(true, sendError(log, request, app, "session", tp)); } }