/*
- * Copyright 2001-2007 Internet2
- *
+ * Copyright 2001-2010 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
/**
* SAML2NameIDMgmt.cpp
- *
+ *
* Handles SAML 2.0 NameID management protocol messages.
*/
#include "exceptions.h"
#include "Application.h"
#include "ServiceProvider.h"
+#include "SPRequest.h"
#include "handler/AbstractHandler.h"
#include "handler/RemotedHandler.h"
#include "util/SPConstants.h"
#ifndef SHIBSP_LITE
# include "SessionCache.h"
# include "security/SecurityPolicy.h"
+# include "security/SecurityPolicyProvider.h"
# include "util/TemplateParameters.h"
# include <fstream>
+# include <saml/exceptions.h>
# include <saml/SAMLConfig.h>
# include <saml/saml2/core/Protocols.h>
# include <saml/saml2/metadata/EndpointManager.h>
+# include <saml/saml2/metadata/Metadata.h>
# include <saml/saml2/metadata/MetadataCredentialCriteria.h>
# include <xmltooling/util/URLEncoder.h>
using namespace opensaml::saml2;
using namespace opensaml::saml2p;
using namespace opensaml::saml2md;
using namespace opensaml;
+#else
+# include "lite/SAMLConstants.h"
#endif
using namespace shibsp;
#pragma warning( push )
#pragma warning( disable : 4250 )
#endif
-
+
class SHIBSP_DLLLOCAL SAML2NameIDMgmt : public AbstractHandler, public RemotedHandler
{
public:
}
#endif
}
-
+
void receive(DDF& in, ostream& out);
pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
return "ManageNameIDService";
}
#endif
+ const XMLCh* getProtocolFamily() const {
+ return samlconstants::SAML20P_NS;
+ }
private:
- pair<bool,long> doRequest(
- const Application& application, const char* session_id, const HTTPRequest& httpRequest, HTTPResponse& httpResponse
- ) const;
+ pair<bool,long> doRequest(const Application& application, const HTTPRequest& httpRequest, HTTPResponse& httpResponse) const;
#ifndef SHIBSP_LITE
- bool stronglyMatches(const XMLCh* idp, const XMLCh* sp, const saml2::NameID& n1, const saml2::NameID& n2) const;
bool notifyBackChannel(const Application& application, const char* requestURL, const NameID& nameid, const NewID* newid) const;
pair<bool,long> sendResponse(
bool front
) const;
- QName m_role;
+ xmltooling::QName m_role;
MessageDecoder* m_decoder;
XMLCh* m_outgoing;
vector<const XMLCh*> m_bindings;
SAML2NameIDMgmt::SAML2NameIDMgmt(const DOMElement* e, const char* appId)
: AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".NameIDMgmt.SAML2"))
#ifndef SHIBSP_LITE
- ,m_role(samlconstants::SAML20MD_NS, IDPSSODescriptor::LOCAL_NAME), m_decoder(NULL), m_outgoing(NULL)
+ ,m_role(samlconstants::SAML20MD_NS, IDPSSODescriptor::LOCAL_NAME), m_decoder(nullptr), m_outgoing(nullptr)
#endif
{
#ifndef SHIBSP_LITE
SAMLConfig& conf = SAMLConfig::getConfig();
// Handle incoming binding.
- m_decoder = conf.MessageDecoderManager.newPlugin(getString("Binding").second,make_pair(e,shibspconstants::SHIB2SPCONFIG_NS));
+ m_decoder = conf.MessageDecoderManager.newPlugin(
+ getString("Binding").second, pair<const DOMElement*,const XMLCh*>(e,shibspconstants::SHIB2SPCONFIG_NS)
+ );
m_decoder->setArtifactResolver(SPConfig::getConfig().getArtifactResolver());
if (m_decoder->isUserAgentPresent()) {
m_bindings.push_back(start);
try {
auto_ptr_char b(start);
- MessageEncoder * encoder = conf.MessageEncoderManager.newPlugin(b.get(),make_pair(e,shibspconstants::SHIB2SPCONFIG_NS));
- m_encoders[start] = encoder;
- m_log.debug("supporting outgoing front-channel binding (%s)", b.get());
+ MessageEncoder * encoder = conf.MessageEncoderManager.newPlugin(
+ b.get(), pair<const DOMElement*,const XMLCh*>(e,shibspconstants::SHIB2SPCONFIG_NS)
+ );
+ if (encoder->isUserAgentPresent() && XMLString::equals(getProtocolFamily(), encoder->getProtocolFamily())) {
+ m_encoders[start] = encoder;
+ m_log.debug("supporting outgoing binding (%s)", b.get());
+ }
+ else {
+ delete encoder;
+ m_log.warn("skipping outgoing binding (%s), not a SAML 2.0 front-channel mechanism", b.get());
+ }
}
catch (exception& ex) {
m_log.error("error building MessageEncoder: %s", ex.what());
}
else {
MessageEncoder* encoder = conf.MessageEncoderManager.newPlugin(
- getString("Binding").second,make_pair(e,shibspconstants::SHIB2SPCONFIG_NS)
+ getString("Binding").second, pair<const DOMElement*,const XMLCh*>(e,shibspconstants::SHIB2SPCONFIG_NS)
);
- m_encoders.insert(pair<const XMLCh*,MessageEncoder*>(NULL, encoder));
+ m_encoders.insert(pair<const XMLCh*,MessageEncoder*>(nullptr, encoder));
}
}
#endif
pair<bool,long> SAML2NameIDMgmt::run(SPRequest& request, bool isHandler) const
{
- // Get the session_id in case this is a front-channel request.
- pair<string,const char*> shib_cookie = request.getApplication().getCookieNameProps("_shibsession_");
- const char* session_id = request.getCookie(shib_cookie.first.c_str());
-
SPConfig& conf = SPConfig::getConfig();
if (conf.isEnabled(SPConfig::OutOfProcess)) {
// When out of process, we run natively and directly process the message.
- return doRequest(request.getApplication(), session_id, request, request);
+ return doRequest(request.getApplication(), request, request);
}
else {
// When not out of process, we remote all the message processing.
- DDF out,in = wrap(request, NULL, true);
+ vector<string> headers(1,"Cookie");
+ DDF out,in = wrap(request, &headers, true);
DDFJanitor jin(in), jout(out);
- if (session_id)
- in.addmember("session_id").string(session_id);
out=request.getServiceProvider().getListenerService()->send(in);
return unwrap(request, out);
}
{
// Find application.
const char* aid=in["application_id"].string();
- const Application* app=aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : NULL;
+ const Application* app=aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : nullptr;
if (!app) {
// Something's horribly wrong.
m_log.error("couldn't find application (%s) for NameID mgmt", aid ? aid : "(missing)");
throw ConfigurationException("Unable to locate application for NameID mgmt, deleted?");
}
-
+
// Unpack the request.
- DDF ret(NULL);
- DDFJanitor jout(ret);
auto_ptr<HTTPRequest> req(getRequest(in));
+
+ // Wrap a response shim.
+ DDF ret(nullptr);
+ DDFJanitor jout(ret);
auto_ptr<HTTPResponse> resp(getResponse(ret));
-
+
// Since we're remoted, the result should either be a throw, which we pass on,
// a false/0 return, which we just return as an empty structure, or a response/redirect,
// which we capture in the facade and send back.
- doRequest(*app, in["session_id"].string(), *req.get(), *resp.get());
+ doRequest(*app, *req.get(), *resp.get());
out << ret;
}
pair<bool,long> SAML2NameIDMgmt::doRequest(
- const Application& application, const char* session_id, const HTTPRequest& request, HTTPResponse& response
+ const Application& application, const HTTPRequest& request, HTTPResponse& response
) const
{
#ifndef SHIBSP_LITE
pair<bool,const char*> policyId = getString("policyId", m_configNS.get()); // namespace-qualified if inside handler element
if (!policyId.first)
policyId = application.getString("policyId"); // unqualified in Application(s) element
-
- // Access policy properties.
- const PropertySet* settings = application.getServiceProvider().getPolicySettings(policyId.second);
- pair<bool,bool> validate = settings->getBool("validate");
// Lock metadata for use by policy.
Locker metadataLocker(application.getMetadataProvider());
// Create the policy.
- shibsp::SecurityPolicy policy(application, &m_role, validate.first && validate.second);
-
+ auto_ptr<SecurityPolicy> policy(
+ application.getServiceProvider().getSecurityPolicyProvider()->createSecurityPolicy(application, &m_role, policyId.second)
+ );
+
// Decode the message.
string relayState;
- auto_ptr<XMLObject> msg(m_decoder->decode(relayState, request, policy));
+ auto_ptr<XMLObject> msg(m_decoder->decode(relayState, request, *policy.get()));
const ManageNameIDRequest* mgmtRequest = dynamic_cast<ManageNameIDRequest*>(msg.get());
if (mgmtRequest) {
- if (!policy.isAuthenticated())
+ if (!policy->isAuthenticated())
throw SecurityPolicyException("Security of ManageNameIDRequest not established.");
// Message from IdP to change or terminate a NameID.
-
+
// If this is front-channel, we have to have a session_id to use already.
- if (m_decoder->isUserAgentPresent() && !session_id) {
+ string session_id = cache->active(application, request);
+ if (m_decoder->isUserAgentPresent() && session_id.empty()) {
m_log.error("no active session");
return sendResponse(
mgmtRequest->getID(),
StatusCode::REQUESTER, StatusCode::UNKNOWN_PRINCIPAL, "No active session found in request.",
relayState.c_str(),
- policy.getIssuerMetadata(),
+ policy->getIssuerMetadata(),
application,
response,
true
);
}
+ EntityDescriptor* entity = policy->getIssuerMetadata() ? dynamic_cast<EntityDescriptor*>(policy->getIssuerMetadata()->getParent()) : nullptr;
+
bool ownedName = false;
NameID* nameid = mgmtRequest->getNameID();
if (!nameid) {
else {
Locker credlocker(cr);
auto_ptr<MetadataCredentialCriteria> mcc(
- policy.getIssuerMetadata() ? new MetadataCredentialCriteria(*policy.getIssuerMetadata()) : NULL
+ policy->getIssuerMetadata() ? new MetadataCredentialCriteria(*policy->getIssuerMetadata()) : nullptr
);
try {
- auto_ptr<XMLObject> decryptedID(encname->decrypt(*cr,application.getXMLString("entityID").second,mcc.get()));
+ auto_ptr<XMLObject> decryptedID(
+ encname->decrypt(*cr,application.getRelyingParty(entity)->getXMLString("entityID").second,mcc.get())
+ );
nameid = dynamic_cast<NameID*>(decryptedID.get());
if (nameid) {
ownedName = true;
mgmtRequest->getID(),
StatusCode::REQUESTER, StatusCode::UNKNOWN_PRINCIPAL, "NameID not found in request.",
relayState.c_str(),
- policy.getIssuerMetadata(),
+ policy->getIssuerMetadata(),
application,
response,
m_decoder->isUserAgentPresent()
);
}
- auto_ptr<NameID> namewrapper(ownedName ? nameid : NULL);
+ auto_ptr<NameID> namewrapper(ownedName ? nameid : nullptr);
// For a front-channel request, we have to match the information in the request
// against the current session.
- EntityDescriptor* entity = policy.getIssuerMetadata() ? dynamic_cast<EntityDescriptor*>(policy.getIssuerMetadata()->getParent()) : NULL;
- if (session_id) {
- if (!cache->matches(session_id, entity, *nameid, NULL, application)) {
+ if (!session_id.empty()) {
+ if (!cache->matches(application, request, entity, *nameid, nullptr)) {
return sendResponse(
mgmtRequest->getID(),
StatusCode::REQUESTER, StatusCode::REQUEST_DENIED, "Active session did not match NameID mgmt request.",
relayState.c_str(),
- policy.getIssuerMetadata(),
+ policy->getIssuerMetadata(),
application,
response,
true
// Determine what's happening...
bool ownedNewID = false;
- NewID* newid = NULL;
+ NewID* newid = nullptr;
if (!mgmtRequest->getTerminate()) {
// Better be a NewID in there.
newid = mgmtRequest->getNewID();
else {
Locker credlocker(cr);
auto_ptr<MetadataCredentialCriteria> mcc(
- policy.getIssuerMetadata() ? new MetadataCredentialCriteria(*policy.getIssuerMetadata()) : NULL
+ policy->getIssuerMetadata() ? new MetadataCredentialCriteria(*policy->getIssuerMetadata()) : nullptr
);
try {
- auto_ptr<XMLObject> decryptedID(encnewid->decrypt(*cr,application.getXMLString("entityID").second,mcc.get()));
+ auto_ptr<XMLObject> decryptedID(
+ encnewid->decrypt(*cr,application.getRelyingParty(entity)->getXMLString("entityID").second,mcc.get())
+ );
newid = dynamic_cast<NewID*>(decryptedID.get());
if (newid) {
ownedNewID = true;
m_log.error("NewID not found in request");
return sendResponse(
mgmtRequest->getID(),
- StatusCode::REQUESTER, NULL, "NewID not found in request.",
+ StatusCode::REQUESTER, nullptr, "NewID not found in request.",
relayState.c_str(),
- policy.getIssuerMetadata(),
+ policy->getIssuerMetadata(),
application,
response,
m_decoder->isUserAgentPresent()
}
}
- auto_ptr<NewID> newwrapper(ownedNewID ? newid : NULL);
+ auto_ptr<NewID> newwrapper(ownedNewID ? newid : nullptr);
// TODO: maybe support in-place modification of sessions?
/*
m_log.error("error while logging out matching sessions: %s", ex.what());
return sendResponse(
logoutRequest->getID(),
- StatusCode::RESPONDER, NULL, ex.what(),
+ StatusCode::RESPONDER, nullptr, ex.what(),
relayState.c_str(),
policy.getIssuerMetadata(),
application,
return sendResponse(
mgmtRequest->getID(),
worked ? StatusCode::SUCCESS : StatusCode::RESPONDER,
- NULL,
- NULL,
+ nullptr,
+ nullptr,
relayState.c_str(),
- policy.getIssuerMetadata(),
+ policy->getIssuerMetadata(),
application,
response,
m_decoder->isUserAgentPresent()
*/
FatalProfileException ex("Incoming message was not a samlp:ManageNameIDRequest.");
- if (policy.getIssuerMetadata())
- annotateException(&ex, policy.getIssuerMetadata()); // throws it
+ if (policy->getIssuerMetadata())
+ annotateException(&ex, policy->getIssuerMetadata()); // throws it
ex.raise();
- return make_pair(false,0); // never happen, satisfies compiler
+ return make_pair(false,0L); // never happen, satisfies compiler
#else
throw ConfigurationException("Cannot process NameID mgmt message using lite version of shibsp library.");
#endif
#ifndef SHIBSP_LITE
-bool SAML2NameIDMgmt::stronglyMatches(const XMLCh* idp, const XMLCh* sp, const saml2::NameID& n1, const saml2::NameID& n2) const
-{
- if (!XMLString::equals(n1.getName(), n2.getName()))
- return false;
-
- const XMLCh* s1 = n1.getFormat();
- const XMLCh* s2 = n2.getFormat();
- if (!s1 || !*s1)
- s1 = saml2::NameID::UNSPECIFIED;
- if (!s2 || !*s2)
- s2 = saml2::NameID::UNSPECIFIED;
- if (!XMLString::equals(s1,s2))
- return false;
-
- s1 = n1.getNameQualifier();
- s2 = n2.getNameQualifier();
- if (!s1 || !*s1)
- s1 = idp;
- if (!s2 || !*s2)
- s2 = idp;
- if (!XMLString::equals(s1,s2))
- return false;
-
- s1 = n1.getSPNameQualifier();
- s2 = n2.getSPNameQualifier();
- if (!s1 || !*s1)
- s1 = sp;
- if (!s2 || !*s2)
- s2 = sp;
- if (!XMLString::equals(s1,s2))
- return false;
-
- return true;
-}
-
pair<bool,long> SAML2NameIDMgmt::sendResponse(
const XMLCh* requestID,
const XMLCh* code,
) const
{
// Get endpoint and encoder to use.
- const EndpointType* ep = NULL;
- const MessageEncoder* encoder = NULL;
+ const EndpointType* ep = nullptr;
+ const MessageEncoder* encoder = nullptr;
if (front) {
const IDPSSODescriptor* idp = dynamic_cast<const IDPSSODescriptor*>(role);
for (vector<const XMLCh*>::const_iterator b = m_bindings.begin(); idp && b!=m_bindings.end(); ++b) {
}
Issuer* issuer = IssuerBuilder::buildIssuer();
nim->setIssuer(issuer);
- issuer->setName(application.getXMLString("entityID").second);
+ issuer->setName(application.getRelyingParty(dynamic_cast<EntityDescriptor*>(role->getParent()))->getXMLString("entityID").second);
fillStatus(*nim.get(), code, subcode, msg);
auto_ptr_char dest(nim->getDestination());
#include <xmltooling/impl/AnyElement.h>
#include <xmltooling/soap/SOAP.h>
#include <xmltooling/soap/SOAPClient.h>
+#include <xmltooling/soap/HTTPSOAPTransport.h>
using namespace soap11;
namespace {
static const XMLCh NameIDNotification[] = UNICODE_LITERAL_18(N,a,m,e,I,D,N,o,t,i,f,i,c,a,t,i,o,n);
private:
void prepareTransport(SOAPTransport& transport) {
transport.setVerifyHost(false);
+ HTTPSOAPTransport* http = dynamic_cast<HTTPSOAPTransport*>(&transport);
+ if (http) {
+ http->useChunkedEncoding(false);
+ http->setRequestHeader("User-Agent", PACKAGE_NAME);
+ http->setRequestHeader(PACKAGE_NAME, PACKAGE_VERSION);
+ }
}
};
};