<!-- Each policy defines a set of rules to use to secure messages. -->
<SecurityPolicies>
- <!-- The predefined policy enforces replay/freshness and permits signing and client TLS. -->
+ <!--
+ The predefined policy enforces replay/freshness, standard
+ condition processing, and permits signing and client TLS.
+ -->
<Policy id="default" validate="false">
- <Rule type="MessageFlow" checkReplay="true" expires="60"/>
- <Rule type="ClientCertAuth" errorFatal="true"/>
- <Rule type="XMLSigning" errorFatal="true"/>
- <Rule type="SimpleSigning" errorFatal="true"/>
+ <PolicyRule type="MessageFlow" checkReplay="true" expires="60"/>
+ <PolicyRule type="Conditions"/>
+ <PolicyRule type="ClientCertAuth" errorFatal="true"/>
+ <PolicyRule type="XMLSigning" errorFatal="true"/>
+ <PolicyRule type="SimpleSigning" errorFatal="true"/>
</Policy>
</SecurityPolicies>
<documentation>Specifies a set of SecurityPolicyRule plugins</documentation>\r
</annotation>\r
<complexType>\r
- <sequence>\r
+ <choice>\r
<element name="Rule" type="conf:PluggableType" minOccurs="1" maxOccurs="unbounded"/>\r
- </sequence>\r
+ <element name="PolicyRule" type="conf:PluggableType" minOccurs="1" maxOccurs="unbounded"/>\r
+ </choice>\r
<attribute name="id" type="conf:string" use="required"/>\r
<attribute name="validate" type="boolean"/>\r
<anyAttribute namespace="##any" processContents="lax"/>\r
bool SAML2Query(QueryContext& ctx) const;
Category& m_log;
+ string m_policyId;
vector<AttributeDesignator*> m_SAML1Designators;
vector<saml2::Attribute*> m_SAML2Designators;
};
return new QueryResolver(e);
}
+ static const XMLCh _policyId[] = UNICODE_LITERAL_8(p,o,l,i,c,y,I,d);
};
QueryResolver::QueryResolver(const DOMElement* e) : m_log(Category::getInstance(SHIBSP_LOGCAT".AttributeResolver.Query"))
xmltooling::NDC ndc("QueryResolver");
#endif
+ const XMLCh* pid = e ? e->getAttributeNS(NULL, _policyId) : NULL;
+ if (pid && *pid) {
+ auto_ptr_char temp(pid);
+ m_policyId = temp.get();
+ }
+
DOMElement* child = XMLHelper::getFirstChildElement(e);
while (child) {
try {
const Application& application = ctx.getApplication();
const PropertySet* relyingParty = application.getRelyingParty(ctx.getEntityDescriptor());
- shibsp::SecurityPolicy policy(application);
+
+ // Locate policy key.
+ const char* policyId = m_policyId.empty() ? application.getString("policyId").second : m_policyId.c_str();
+
+ // Access policy properties.
+ const PropertySet* settings = application.getServiceProvider().getPolicySettings(policyId);
+ pair<bool,bool> validate = settings->getBool("validate");
+
+ shibsp::SecurityPolicy policy(application, NULL, validate.first && validate.second, policyId);
+ policy.getAudiences().push_back(relyingParty->getXMLString("entityID").second);
MetadataCredentialCriteria mcc(*AA);
shibsp::SOAPClient soaper(policy);
}
const Application& application = ctx.getApplication();
- shibsp::SecurityPolicy policy(application);
- MetadataCredentialCriteria mcc(*AA);
- shibsp::SOAPClient soaper(policy);
-
const PropertySet* relyingParty = application.getRelyingParty(ctx.getEntityDescriptor());
+
+ // Locate policy key.
+ const char* policyId = m_policyId.empty() ? application.getString("policyId").second : m_policyId.c_str();
+
+ // Access policy properties.
+ const PropertySet* settings = application.getServiceProvider().getPolicySettings(policyId);
+ pair<bool,bool> validate = settings->getBool("validate");
+
pair<bool,bool> signedAssertions = relyingParty->getBool("requireSignedAssertions");
pair<bool,const char*> encryption = relyingParty->getString("encryption");
+ shibsp::SecurityPolicy policy(application, NULL, validate.first && validate.second, policyId);
+ policy.getAudiences().push_back(relyingParty->getXMLString("entityID").second);
+ MetadataCredentialCriteria mcc(*AA);
+ shibsp::SOAPClient soaper(policy);
+
auto_ptr_XMLCh binding(samlconstants::SAML20_BINDING_SOAP);
saml2p::StatusResponseType* srt=NULL;
const vector<AttributeService*>& endpoints=AA->getAttributeServices();
/*
- * Copyright 2001-2007 Internet2
+ * Copyright 2001-2009 Internet2
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
void generateMetadata(opensaml::saml2md::SPSSODescriptor& role, const char* handlerURL) const;
/**
+ * Returns a SecurityPolicy instance to use for an incoming request.
+ *
+ * <p>Allows handlers to customize the type of policy object their policy rules might require.
+ * <p>The caller <strong>MUST</strong> lock the application's MetadataProvider for the life
+ * of the returned object.
+ *
+ * @param application reference to application receiving message
+ * @param role identifies the role (generally IdP or SP) of the policy peer
+ * @param validate true iff XML parsing should be done with validation
+ * @param policyId identifies policy rules to auto-attach, defaults to the application's set
+ * @return a new policy instance, which the caller is responsible for freeing
+ */
+ virtual opensaml::SecurityPolicy* createSecurityPolicy(
+ const Application& application, const xmltooling::QName* role, bool validate, const char* policyId
+ ) const;
+
+ /**
* Implement protocol-specific handling of the incoming decoded message.
*
* <p>The result of implementing the protocol should be an exception or
Locker metadataLocker(application.getMetadataProvider());
// Create the policy.
- shibsp::SecurityPolicy policy(application, &m_role, validate.first && validate.second, policyId.second);
+ auto_ptr<opensaml::SecurityPolicy> policy(
+ createSecurityPolicy(application, &m_role, validate.first && validate.second, policyId.second)
+ );
string relayState;
try {
// Decode the message and process it in a protocol-specific way.
- auto_ptr<XMLObject> msg(m_decoder->decode(relayState, httpRequest, policy));
+ auto_ptr<XMLObject> msg(m_decoder->decode(relayState, httpRequest, *(policy.get())));
if (!msg.get())
throw BindingException("Failed to decode an SSO protocol response.");
DDF postData = recoverPostData(application, httpRequest, httpResponse, relayState.c_str());
DDFJanitor postjan(postData);
recoverRelayState(application, httpRequest, httpResponse, relayState);
- implementProtocol(application, httpRequest, httpResponse, policy, settings, *msg.get());
+ implementProtocol(application, httpRequest, httpResponse, *(policy.get()), settings, *msg.get());
- auto_ptr_char issuer(policy.getIssuer() ? policy.getIssuer()->getName() : NULL);
+ auto_ptr_char issuer(policy->getIssuer() ? policy->getIssuer()->getName() : NULL);
// History cookie.
if (issuer.get() && *issuer.get())
#ifndef SHIBSP_LITE
-void AssertionConsumerService::generateMetadata(SPSSODescriptor& role, const char* handlerURL) const {
+void AssertionConsumerService::generateMetadata(SPSSODescriptor& role, const char* handlerURL) const
+{
const char* loc = getString("Location").second;
string hurl(handlerURL);
if (*loc != '/')
role.getAssertionConsumerServices().push_back(ep);
}
+opensaml::SecurityPolicy* AssertionConsumerService::createSecurityPolicy(
+ const Application& application, const xmltooling::QName* role, bool validate, const char* policyId
+ ) const
+{
+ return new SecurityPolicy(application, role, validate, policyId);
+}
+
class SHIBSP_DLLLOCAL DummyContext : public ResolutionContext
{
public:
/*
- * Copyright 2001-2007 Internet2
- *
+ * Copyright 2001-2009 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
/**
* SAML1Consumer.cpp
- *
- * SAML 1.x assertion consumer service
+ *
+ * SAML 1.x assertion consumer service
*/
#include "internal.h"
# include "ServiceProvider.h"
# include "SessionCache.h"
# include "attribute/resolver/ResolutionContext.h"
+# include <saml/SAMLConfig.h>
# include <saml/saml1/core/Assertions.h>
# include <saml/saml1/core/Protocols.h>
-# include <saml/saml1/profile/BrowserSSOProfileValidator.h>
# include <saml/saml2/metadata/Metadata.h>
using namespace opensaml::saml1;
using namespace opensaml::saml1p;
#pragma warning( push )
#pragma warning( disable : 4250 )
#endif
-
+
class SHIBSP_DLLLOCAL SAML1Consumer : public AssertionConsumerService
{
public:
SAML1Consumer(const DOMElement* e, const char* appId)
- : AssertionConsumerService(e, appId, Category::getInstance(SHIBSP_LOGCAT".SSO.SAML1")) {
+ : AssertionConsumerService(e, appId, Category::getInstance(SHIBSP_LOGCAT".SSO.SAML1")) {
#ifndef SHIBSP_LITE
+ m_ssoRule = NULL;
m_post = XMLString::equals(getString("Binding").second, samlconstants::SAML1_PROFILE_BROWSER_POST);
+ if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess))
+ m_ssoRule = SAMLConfig::getConfig().SecurityPolicyRuleManager.newPlugin(SAML1BROWSERSSO_POLICY_RULE, e);
#endif
}
- virtual ~SAML1Consumer() {}
-
+ virtual ~SAML1Consumer() {
+#ifndef SHIBSP_LITE
+ delete m_ssoRule;
+#endif
+ }
+
#ifndef SHIBSP_LITE
void generateMetadata(SPSSODescriptor& role, const char* handlerURL) const {
AssertionConsumerService::generateMetadata(role, handlerURL);
const PropertySet* settings,
const XMLObject& xmlObject
) const;
+
bool m_post;
+ SecurityPolicyRule* m_ssoRule;
#endif
};
{
return new SAML1Consumer(p.first, p.second);
}
-
+
+#ifndef SHIBSP_LITE
+ class SHIBSP_DLLLOCAL _rulenamed : std::unary_function<const SecurityPolicyRule*,bool>
+ {
+ public:
+ _rulenamed(const char* name) : m_name(name) {}
+ bool operator()(const SecurityPolicyRule* rule) const {
+ return rule ? !strcmp(m_name, rule->getType()) : false;
+ }
+ private:
+ const char* m_name;
+ };
+#endif
};
#ifndef SHIBSP_LITE
throw MetadataException("Security of SAML 1.x SSO POST response not established.");
throw SecurityPolicyException("Security of SAML 1.x SSO POST response not established.");
}
-
+
// Remember whether we already established trust.
bool alreadySecured = policy.isAuthenticated();
// Saves off error messages potentially helpful for users.
string contextualError;
- // Profile validator.
- time_t now = time(NULL);
- BrowserSSOProfileValidator ssoValidator(application.getRelyingParty(entity)->getXMLString("entityID").second, application.getAudiences(), now);
+ // Ensure the BrowserSSO rule is in the policy set.
+ if (find_if(policy.getRules(), _rulenamed(SAML1BROWSERSSO_POLICY_RULE)) == NULL)
+ policy.getRules().push_back(m_ssoRule);
+
+ // Populate recipient as audience.
+ policy.getAudiences().push_back(application.getRelyingParty(entity)->getXMLString("entityID").second);
+ time_t now = time(NULL);
for (vector<saml1::Assertion*>::const_iterator a = assertions.begin(); a!=assertions.end(); ++a) {
try {
// Skip unsigned assertion?
);
// Run the policy over the assertion. Handles replay, freshness, and
- // signature verification, assuming the relevant rules are configured.
+ // signature verification, assuming the relevant rules are configured,
+ // along with condition and profile enforcement.
policy.evaluate(*(*a));
-
+
// If no security is in place now, we kick it.
if (!alreadySecured && !policy.isAuthenticated())
throw SecurityPolicyException("Unable to establish security of incoming assertion.");
- // Now do profile and core semantic validation to ensure we can use it for SSO.
- ssoValidator.validateAssertion(*(*a));
-
// Track it as a valid token.
tokens.push_back(*a);
/*
- * Copyright 2001-2007 Internet2
+ * Copyright 2001-2009 Internet2
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
# include "ServiceProvider.h"
# include "SessionCache.h"
# include "attribute/resolver/ResolutionContext.h"
+# include <saml/SAMLConfig.h>
# include <saml/saml2/core/Protocols.h>
-# include <saml/saml2/profile/BrowserSSOProfileValidator.h>
# include <saml/saml2/metadata/Metadata.h>
# include <saml/saml2/metadata/MetadataCredentialCriteria.h>
+# include <saml/saml2/profile/SAML2AssertionPolicy.h>
using namespace opensaml::saml2;
using namespace opensaml::saml2p;
using namespace opensaml::saml2md;
public:
SAML2Consumer(const DOMElement* e, const char* appId)
: AssertionConsumerService(e, appId, Category::getInstance(SHIBSP_LOGCAT".SSO.SAML2")) {
+#ifndef SHIBSP_LITE
+ m_ssoRule = NULL;
+ if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess))
+ m_ssoRule = SAMLConfig::getConfig().SecurityPolicyRuleManager.newPlugin(BEARER_POLICY_RULE, e);
+#endif
+ }
+ virtual ~SAML2Consumer() {
+#ifndef SHIBSP_LITE
+ delete m_ssoRule;
+#endif
}
- virtual ~SAML2Consumer() {}
#ifndef SHIBSP_LITE
void generateMetadata(SPSSODescriptor& role, const char* handlerURL) const {
const PropertySet* settings,
const XMLObject& xmlObject
) const;
+
+ SecurityPolicyRule* m_ssoRule;
#endif
};
return new SAML2Consumer(p.first, p.second);
}
+#ifndef SHIBSP_LITE
+ class SHIBSP_DLLLOCAL _rulenamed : std::unary_function<const SecurityPolicyRule*,bool>
+ {
+ public:
+ _rulenamed(const char* name) : m_name(name) {}
+ bool operator()(const SecurityPolicyRule* rule) const {
+ return rule ? !strcmp(m_name, rule->getType()) : false;
+ }
+ private:
+ const char* m_name;
+ };
+#endif
};
#ifndef SHIBSP_LITE
flag = application.getRelyingParty(entity)->getBool("requireSignedAssertions");
}
- time_t now = time(NULL);
- string dest = httpRequest.getRequestURL();
-
// authnskew allows rejection of SSO if AuthnInstant is too old.
const PropertySet* sessionProps = application.getPropertySet("Sessions");
pair<bool,unsigned int> authnskew = sessionProps ? sessionProps->getUnsignedInt("maxTimeSinceAuthn") : pair<bool,unsigned int>(false,0);
// Saves off error messages potentially helpful for users.
string contextualError;
+ // Ensure the Bearer rule is in the policy set.
+ if (find_if(policy.getRules(), _rulenamed(BEARER_POLICY_RULE)) == NULL)
+ policy.getRules().push_back(m_ssoRule);
+
+ // Populate recipient as audience.
+ policy.getAudiences().push_back(application.getRelyingParty(entity)->getXMLString("entityID").second);
+
+ time_t now = time(NULL);
for (vector<saml2::Assertion*>::const_iterator a = assertions.begin(); a!=assertions.end(); ++a) {
try {
// Skip unsigned assertion?
extractMessageDetails(*(*a), samlconstants::SAML20P_NS, policy);
// Run the policy over the assertion. Handles replay, freshness, and
- // signature verification, assuming the relevant rules are configured.
+ // signature verification, assuming the relevant rules are configured,
+ // along with condition and profile enforcement.
policy.evaluate(*(*a));
// If no security is in place now, we kick it.
throw SecurityPolicyException("The incoming assertion was unsigned, violating local security policy.");
}
- // Now do profile and core semantic validation to ensure we can use it for SSO.
- BrowserSSOProfileValidator ssoValidator(
- application.getRelyingParty(entity)->getXMLString("entityID").second, application.getAudiences(), now, dest.substr(0,dest.find('?')).c_str()
- );
- ssoValidator.validateAssertion(*(*a));
-
// Address checking.
- checkAddress(application, httpRequest, ssoValidator.getAddress());
+ SubjectConfirmationData* subcondata = dynamic_cast<SubjectConfirmationData*>(
+ dynamic_cast<SAML2AssertionPolicy&>(policy).getSubjectConfirmation()->getSubjectConfirmationData()
+ );
+ if (subcondata && subcondata->getAddress()) {
+ auto_ptr_char boundip(subcondata->getAddress());
+ checkAddress(application, httpRequest, boundip.get());
+ }
// Track it as a valid token.
tokens.push_back(*a);
extractMessageDetails(*decrypted, samlconstants::SAML20P_NS, policy);
// Run the policy over the assertion. Handles replay, freshness, and
- // signature verification, assuming the relevant rules are configured.
+ // signature verification, assuming the relevant rules are configured,
+ // along with condition and profile enforcement.
// We have to marshall the object first to ensure signatures can be checked.
if (!decrypted->getDOM())
decrypted->marshall();
throw SecurityPolicyException("The decrypted assertion was unsigned, violating local security policy.");
}
- // Now do profile and core semantic validation to ensure we can use it for SSO.
- BrowserSSOProfileValidator ssoValidator(
- application.getRelyingParty(entity)->getXMLString("entityID").second, application.getAudiences(), now, dest.substr(0,dest.find('?')).c_str()
- );
- ssoValidator.validateAssertion(*decrypted);
-
// Address checking.
- checkAddress(application, httpRequest, ssoValidator.getAddress());
+ SubjectConfirmationData* subcondata = dynamic_cast<SubjectConfirmationData*>(
+ dynamic_cast<SAML2AssertionPolicy&>(policy).getSubjectConfirmation()->getSubjectConfirmationData()
+ );
+ if (subcondata && subcondata->getAddress()) {
+ auto_ptr_char boundip(subcondata->getAddress());
+ checkAddress(application, httpRequest, boundip.get());
+ }
// Track it as a valid token.
tokens.push_back(decrypted);
/*
- * Copyright 2001-2007 Internet2
+ * Copyright 2001-2009 Internet2
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
if (cacheex) {
time_t expires = logoutRequest->getNotOnOrAfter() ? logoutRequest->getNotOnOrAfterEpoch() : 0;
cacheex->logout(application, entity, *nameid, &indexes, expires, sessions);
+ m_log.debug("session cache returned %d sessions bound to NameID in logout request", sessions.size());
// Now we actually terminate everything except for the active session,
// if this is front-channel, for notification purposes.
static const XMLCh OutOfProcess[] = UNICODE_LITERAL_12(O,u,t,O,f,P,r,o,c,e,s,s);
static const XMLCh _path[] = UNICODE_LITERAL_4(p,a,t,h);
static const XMLCh Policy[] = UNICODE_LITERAL_6(P,o,l,i,c,y);
+ static const XMLCh PolicyRule[] = UNICODE_LITERAL_10(P,o,l,i,c,y,R,u,l,e);
static const XMLCh _provider[] = UNICODE_LITERAL_8(p,r,o,v,i,d,e,r);
static const XMLCh RelyingParty[] = UNICODE_LITERAL_12(R,e,l,y,i,n,g,P,a,r,t,y);
static const XMLCh _ReplayCache[] = UNICODE_LITERAL_11(R,e,p,l,a,y,C,a,c,h,e);
#ifndef SHIBSP_LITE
nlist=e->getElementsByTagNameNS(samlconstants::SAML20_NS,Audience::LOCAL_NAME);
- if (nlist) {
+ if (nlist && nlist->getLength()) {
log.warn("use of <saml:Audience> elements outside of a Security Policy Rule is deprecated");
for (XMLSize_t i=0; i<nlist->getLength(); i++)
if (nlist->item(i)->getParentNode()->isSameNode(e) && nlist->item(i)->hasChildNodes())
settings->load(child, NULL, &filter);
rules.first = settings.release();
- // Process Rule elements.
- const DOMElement* rule = XMLHelper::getFirstChildElement(child,Rule);
+ // Process PolicyRule elements.
+ const DOMElement* rule = XMLHelper::getFirstChildElement(child,PolicyRule);
while (rule) {
auto_ptr_char type(rule->getAttributeNS(NULL,_type));
try {
catch (exception& ex) {
log.crit("error instantiating policy rule (%s) in policy (%s): %s", type.get(), id.get(), ex.what());
}
- rule = XMLHelper::getNextSiblingElement(rule,Rule);
+ rule = XMLHelper::getNextSiblingElement(rule,PolicyRule);
+ }
+
+ if (rules.second.size() == 0) {
+ // Process Rule elements.
+ log.warn("detected legacy Policy configuration, please convert to new PolicyRule syntax");
+ rule = XMLHelper::getFirstChildElement(child,Rule);
+ while (rule) {
+ auto_ptr_char type(rule->getAttributeNS(NULL,_type));
+ try {
+ rules.second.push_back(samlConf.SecurityPolicyRuleManager.newPlugin(type.get(),rule));
+ }
+ catch (exception& ex) {
+ log.crit("error instantiating policy rule (%s) in policy (%s): %s", type.get(), id.get(), ex.what());
+ }
+ rule = XMLHelper::getNextSiblingElement(rule,Rule);
+ }
+
+ // Manually add a basic Conditions rule.
+ log.info("installing a default Conditions rule in policy (%s) for compatibility with legacy configuration", id.get());
+ rules.second.push_back(samlConf.SecurityPolicyRuleManager.newPlugin(CONDITIONS_POLICY_RULE, NULL));
}
child = XMLHelper::getNextSiblingElement(child,Policy);
#include "metadata/MetadataProviderCriteria.h"
#include "security/SecurityPolicy.h"
-using namespace opensaml::saml2;
using namespace shibsp;
+using namespace opensaml::saml2;
+using namespace std;
SecurityPolicy::SecurityPolicy(const Application& application, const xmltooling::QName* role, bool validate, const char* policyId)
- : opensaml::SecurityPolicy(application.getMetadataProvider(), role, application.getTrustEngine(), validate), m_application(application) {
-
- const std::vector<const opensaml::SecurityPolicyRule*>& rules =
+ : opensaml::SecurityPolicy(application.getMetadataProvider(), role, application.getTrustEngine(), validate), m_application(application) {
+ const vector<const opensaml::SecurityPolicyRule*>& rules =
application.getServiceProvider().getPolicyRules(policyId ? policyId : application.getString("policyId").second);
getRules().assign(rules.begin(), rules.end());
+
+ // Populate audiences.
+ if (application.getAudiences()) {
+ for (vector<const XMLCh*>::const_iterator a = application.getAudiences()->begin(); a != application.getAudiences()->end(); ++a)
+ getAudiences().push_back(*a);
+ }
}
opensaml::saml2md::MetadataProvider::Criteria& SecurityPolicy::getMetadataProviderCriteria() const