Streamline SecurityPolicy methods and rule modification.
Pass a protocol family string through from decoders and clients to policy rules.
Add partial policy reset to avoid spurious message replay errors.
Speed up message extraction rules by skipping incompatible protocols.
const xmltooling::QName* role=NULL,
const xmltooling::TrustEngine* trustEngine=NULL,
bool validate=true
- ) : m_messageQName(NULL), m_messageID(NULL), m_issueInstant(0),
- m_issuer(NULL), m_issuerRole(NULL), m_secure(false), m_matchingPolicy(NULL),
- m_metadata(metadataProvider), m_role(NULL), m_trust(trustEngine), m_validate(validate) {
- if (role)
- m_role = new xmltooling::QName(*role);
- }
-
- /**
- * Constructor for policy using existing rules. The lifetime of the policy rules
- * must be at least as long as the policy object.
- *
- * @param rules reference to array of policy rules to use
- * @param metadataProvider locked MetadataProvider instance
- * @param role identifies the role (generally IdP or SP) of the policy peer
- * @param trustEngine TrustEngine to authenticate policy peer
- * @param validate true iff XML parsing should be done with validation
- */
- SecurityPolicy(
- const std::vector<const SecurityPolicyRule*>& rules,
- const saml2md::MetadataProvider* metadataProvider=NULL,
- const xmltooling::QName* role=NULL,
- const xmltooling::TrustEngine* trustEngine=NULL,
- bool validate=true
- ) : m_messageQName(NULL), m_messageID(NULL), m_issueInstant(0),
- m_issuer(NULL), m_issuerRole(NULL), m_secure(false), m_matchingPolicy(NULL),
- m_rules(rules), m_metadata(metadataProvider), m_role(NULL), m_trust(trustEngine), m_validate(validate) {
+ ) : m_messageID(NULL), m_issueInstant(0), m_issuer(NULL), m_issuerRole(NULL), m_secure(false),
+ m_matchingPolicy(NULL), m_metadata(metadataProvider), m_role(NULL), m_trust(trustEngine), m_validate(validate) {
if (role)
m_role = new xmltooling::QName(*role);
}
}
/**
- * Adds a SecurityPolicyRule to the policy. The lifetime of the policy rule
- * must be at least as long as the policy object.
+ * Gets a mutable array of installed policy rules.
+ *
+ * <p>If adding rules, their lifetime must be at least as long as the policy object.
*
- * @param rule SecurityPolicyRule to add
+ * @return mutable array of rules
*/
- void addRule(const SecurityPolicyRule* rule) {
- m_rules.push_back(rule);
+ std::vector<const SecurityPolicyRule*>& getRules() {
+ return m_rules;
}
/**
*
* @param message the incoming message
* @param request the protocol request
+ * @param protocol the protocol family in use
*
* @throws BindingException raised if the message/request is invalid according to the supplied rules
*/
- void evaluate(const xmltooling::XMLObject& message, const xmltooling::GenericRequest* request=NULL);
+ void evaluate(
+ const xmltooling::XMLObject& message, const xmltooling::GenericRequest* request=NULL, const XMLCh* protocol=NULL
+ );
/**
- * Resets the policy object and clears any per-message state.
+ * Resets the policy object and/or clears any per-message state.
+ *
+ * <p>Resets can be complete (the default) or merely clear the previous message ID and timestamp
+ * when evaluating multiple layers of a message.
+ *
+ * @param messageOnly true iff security and issuer state should be left in place
*/
- void reset();
+ void reset(bool messageOnly=false);
/**
- * Returns the message element/type as determined by the registered policies.
- *
- * @return message element/type as determined by the registered policies
- */
- const xmltooling::QName* getMessageQName() const {
- return m_messageQName;
- }
-
- /**
* Returns the message identifier as determined by the registered policies.
*
* @return message identifier as determined by the registered policies
}
/**
- * Sets the message element/type as determined by the registered policies.
- *
- * @param messageQName message element/type
- */
- void setMessageQName(const xmltooling::QName* messageQName) {
- delete m_messageQName;
- m_messageQName = messageQName ? new xmltooling::QName(*messageQName) : NULL;
- }
-
- /**
* Sets the message identifier as determined by the registered policies.
*
* @param id message identifier
private:
// information extracted from message
- xmltooling::QName* m_messageQName;
XMLCh* m_messageID;
time_t m_issueInstant;
saml2::Issuer* m_issuer;
virtual ~SecurityPolicyRule() {}
/**
+ * Returns the rule's class/type.
+ *
+ * @return the class/type of the object
+ */
+ virtual const char* getType() const=0;
+
+ /**
* Evaluates the rule against the given request and message.
*
+ * <p>An exception will be raised if the message is invalid according to
+ * a policy rule.
+ *
* @param message the incoming message
* @param request the protocol request
+ * @param protocol the protocol family in use
* @param policy SecurityPolicy to provide various components and track message data
- *
- * @throws BindingException raised if the message/request is not acceptable to the policy rule
*/
virtual void evaluate(
- const xmltooling::XMLObject& message, const xmltooling::GenericRequest* request, SecurityPolicy& policy
+ const xmltooling::XMLObject& message,
+ const xmltooling::GenericRequest* request,
+ const XMLCh* protocol,
+ SecurityPolicy& policy
) const=0;
};
ClientCertAuthRule(const DOMElement* e) {}
virtual ~ClientCertAuthRule() {}
- void evaluate(const xmltooling::XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const;
+ const char* getType() const {
+ return CLIENTCERTAUTH_POLICY_RULE;
+ }
+ void evaluate(const XMLObject& message, const GenericRequest* request, const XMLCh* protocol, SecurityPolicy& policy) const;
};
SecurityPolicyRule* SAML_DLLLOCAL ClientCertAuthRuleFactory(const DOMElement* const & e)
}
};
-void ClientCertAuthRule::evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const
+void ClientCertAuthRule::evaluate(
+ const XMLObject& message, const GenericRequest* request, const XMLCh* protocol, SecurityPolicy& policy
+ ) const
{
Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.ClientCertAuth");
MessageFlowRule(const DOMElement* e);
virtual ~MessageFlowRule() {}
- void evaluate(const xmltooling::XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const;
+ const char* getType() const {
+ return MESSAGEFLOW_POLICY_RULE;
+ }
+ void evaluate(const XMLObject& message, const GenericRequest* request, const XMLCh* protocol, SecurityPolicy& policy) const;
private:
bool m_checkReplay;
}
}
-void MessageFlowRule::evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const
+void MessageFlowRule::evaluate(
+ const XMLObject& message, const GenericRequest* request, const XMLCh* protocol, SecurityPolicy& policy
+ ) const
{
Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.MessageFlow");
log.debug("evaluating message flow policy (replay checking %s, expiration %lu)", m_checkReplay ? "on" : "off", m_expires);
NullSecurityRule(const DOMElement* e) : m_log(Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.NullSecurity")) {}
virtual ~NullSecurityRule() {}
- void evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const;
+ const char* getType() const {
+ return NULLSECURITY_POLICY_RULE;
+ }
+ void evaluate(const XMLObject& message, const GenericRequest* request, const XMLCh* protocol, SecurityPolicy& policy) const {
+ m_log.warn("security enforced using NULL policy rule, be sure you know what you're doing");
+ policy.setSecure(true);
+ }
private:
Category& m_log;
return new NullSecurityRule(e);
}
};
-
-void NullSecurityRule::evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const
-{
- m_log.warn("security enforced using NULL policy rule, be sure you know what you're doing");
- policy.setSecure(true);
-}
SecurityPolicy::~SecurityPolicy()
{
- reset();
+ reset(false);
}
-void SecurityPolicy::reset()
+void SecurityPolicy::reset(bool messageOnly)
{
- delete m_messageQName;
XMLString::release(&m_messageID);
- delete m_issuer;
- m_messageQName=NULL;
m_messageID=NULL;
m_issueInstant=0;
- m_issuer=NULL;
- m_issuerRole=NULL;
- m_secure=false;
+ if (!messageOnly) {
+ delete m_issuer;
+ m_issuer=NULL;
+ m_issuerRole=NULL;
+ m_secure=false;
+ }
}
-void SecurityPolicy::evaluate(const XMLObject& message, const GenericRequest* request)
+void SecurityPolicy::evaluate(const XMLObject& message, const GenericRequest* request, const XMLCh* protocol)
{
for (vector<const SecurityPolicyRule*>::const_iterator i=m_rules.begin(); i!=m_rules.end(); ++i)
- (*i)->evaluate(message,request,*this);
+ (*i)->evaluate(message,request,protocol,*this);
}
void SecurityPolicy::setIssuer(const Issuer* issuer)
SimpleSigningRule(const DOMElement* e);
virtual ~SimpleSigningRule() {}
- void evaluate(const xmltooling::XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const;
+ const char* getType() const {
+ return SIMPLESIGNING_POLICY_RULE;
+ }
+ void evaluate(const XMLObject& message, const GenericRequest* request, const XMLCh* protocol, SecurityPolicy& policy) const;
private:
// Appends a raw parameter=value pair to the string.
}
}
-void SimpleSigningRule::evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const
+void SimpleSigningRule::evaluate(
+ const XMLObject& message, const GenericRequest* request, const XMLCh* protocol, SecurityPolicy& policy
+ ) const
{
Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.SimpleSigning");
XMLSigningRule(const DOMElement* e);
virtual ~XMLSigningRule() {}
- void evaluate(const xmltooling::XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const;
+ const char* getType() const {
+ return XMLSIGNING_POLICY_RULE;
+ }
+ void evaluate(const XMLObject& message, const GenericRequest* request, const XMLCh* protocol, SecurityPolicy& policy) const;
private:
bool m_errorsFatal;
}
}
-void XMLSigningRule::evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const
+void XMLSigningRule::evaluate(
+ const XMLObject& message, const GenericRequest* request, const XMLCh* protocol, SecurityPolicy& policy
+ ) const
{
Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.XMLSigning");
SAML1MessageRule(const DOMElement* e) {}
virtual ~SAML1MessageRule() {}
- void evaluate(const xmltooling::XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const;
+ const char* getType() const {
+ return SAML1MESSAGE_POLICY_RULE;
+ }
+ void evaluate(const XMLObject& message, const GenericRequest* request, const XMLCh* protocol, SecurityPolicy& policy) const;
};
SecurityPolicyRule* SAML_DLLLOCAL SAML1MessageRuleFactory(const DOMElement* const & e)
}
};
-void SAML1MessageRule::evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const
+void SAML1MessageRule::evaluate(
+ const XMLObject& message, const GenericRequest* request, const XMLCh* protocol, SecurityPolicy& policy
+ ) const
{
- Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.SAML1Message");
-
- const QName& q = message.getElementQName();
- policy.setMessageQName(&q);
-
+ // Only handle SAML 1.x protocol and SAML 1.x messages.
+ if (!XMLString::equals(protocol, samlconstants::SAML11_PROTOCOL_ENUM) &&
+ !XMLString::equals(protocol, samlconstants::SAML10_PROTOCOL_ENUM))
+ return;
+ const QName& q = message.getElementQName();
if (!XMLString::equals(q.getNamespaceURI(), samlconstants::SAML1P_NS) &&
- !XMLString::equals(q.getNamespaceURI(), samlconstants::SAML1_NS)) {
+ !XMLString::equals(q.getNamespaceURI(), samlconstants::SAML1_NS))
return;
- }
+
+ Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.SAML1Message");
try {
const RootObject& samlRoot = dynamic_cast<const RootObject&>(message);
log.debug("extracting issuer from message");
- const XMLCh* protocol = NULL;
const saml1::Assertion* a = NULL;
// Handle assertions directly.
if (a) {
policy.setIssuer(a->getIssuer());
- pair<bool,int> minor = a->getMinorVersion();
- protocol = (minor.first && minor.second==0) ?
- samlconstants::SAML10_PROTOCOL_ENUM : samlconstants::SAML11_PROTOCOL_ENUM;
}
-
- if (!protocol) {
+ else {
log.warn("issuer identity not extracted");
return;
}
SchemaValidators.validate(xmlObject.get());
// Run through the policy.
- policy.evaluate(*response, &genericRequest);
+ pair<bool,int> minor = response->getMinorVersion();
+ policy.evaluate(
+ *response,
+ &genericRequest,
+ (minor.first && minor.second==0) ? samlconstants::SAML10_PROTOCOL_ENUM : samlconstants::SAML11_PROTOCOL_ENUM
+ );
// Check recipient URL.
auto_ptr_char recipient(response->getRecipient());
if (m_correlate && response->getInResponseTo() && !XMLString::equals(m_correlate, response->getInResponseTo()))
throw SecurityPolicyException("InResponseTo attribute did not correlate with the Request ID.");
- m_soaper.getPolicy().evaluate(*response);
+ m_soaper.getPolicy().reset(true);
+ pair<bool,int> minor = response->getMinorVersion();
+ m_soaper.getPolicy().evaluate(
+ *response,
+ NULL,
+ (minor.first && minor.second==0) ? samlconstants::SAML10_PROTOCOL_ENUM : samlconstants::SAML11_PROTOCOL_ENUM
+ );
if (!m_soaper.getPolicy().isSecure()) {
SecurityPolicyException ex("Security policy could not authenticate the message.");
Request* request = dynamic_cast<Request*>(body->getUnknownXMLObjects().front());
if (request) {
// Run through the policy at two layers.
- policy.evaluate(*env, &genericRequest);
- policy.evaluate(*request, &genericRequest);
+ pair<bool,int> minor = request->getMinorVersion();
+ policy.evaluate(
+ *env,
+ &genericRequest,
+ (minor.first && minor.second==0) ? samlconstants::SAML10_PROTOCOL_ENUM : samlconstants::SAML11_PROTOCOL_ENUM
+ );
+ policy.reset(true);
+ policy.evaluate(
+ *request,
+ &genericRequest,
+ (minor.first && minor.second==0) ? samlconstants::SAML10_PROTOCOL_ENUM : samlconstants::SAML11_PROTOCOL_ENUM
+ );
xmlObject.release();
body->detach(); // frees Envelope
request->detach(); // frees Body
}
}
- throw BindingException("SOAP Envelope did not contain a SAML Request.");
+ throw BindingException("SOAP Envelope did not contain a SAML 1.x Request.");
}
m_artifactResolver->resolve(*(artifact2.get()), dynamic_cast<const SSODescriptorType&>(*roledesc), policy)
);
- // The policy should be enforced against the ArtifactResponse by the resolve step.
+ // The policy should be enforced against the ArtifactResponse by the resolve step.
+ // Reset only the message state.
+ policy.reset(true);
// Extract payload and check that message.
XMLObject* payload = response->getPayload();
- policy.evaluate(*payload, &genericRequest);
+ policy.evaluate(*payload, &genericRequest, samlconstants::SAML20P_NS);
// Return the payload only.
response.release();
SAML2MessageRule(const DOMElement* e) {}
virtual ~SAML2MessageRule() {}
- void evaluate(const xmltooling::XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const;
+ const char* getType() const {
+ return SAML2MESSAGE_POLICY_RULE;
+ }
+ void evaluate(const XMLObject& message, const GenericRequest* request, const XMLCh* protocol, SecurityPolicy& policy) const;
};
SecurityPolicyRule* SAML_DLLLOCAL SAML2MessageRuleFactory(const DOMElement* const & e)
}
};
-void SAML2MessageRule::evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const
+void SAML2MessageRule::evaluate(
+ const XMLObject& message, const GenericRequest* request, const XMLCh* protocol, SecurityPolicy& policy
+ ) const
{
- Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.SAML2Message");
-
- const QName& q = message.getElementQName();
- policy.setMessageQName(&q);
-
+ // Only handle SAML 2.0 protocol and 2.0 messages.
+ if (!XMLString::equals(protocol, samlconstants::SAML20P_NS))
+ return;
+ const QName& q = message.getElementQName();
if (!XMLString::equals(q.getNamespaceURI(), samlconstants::SAML20P_NS)&&
- !XMLString::equals(q.getNamespaceURI(), samlconstants::SAML20_NS)) {
+ !XMLString::equals(q.getNamespaceURI(), samlconstants::SAML20_NS))
return;
- }
+ Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.SAML2Message");
+
try {
const saml2::RootObject& samlRoot = dynamic_cast<const saml2::RootObject&>(message);
policy.setMessageID(samlRoot.getID());
}
log.debug("matched message issuer against metadata, searching for applicable role...");
- const RoleDescriptor* roledesc=entity->getRoleDescriptor(*policy.getRole(), samlconstants::SAML20P_NS);
+ const RoleDescriptor* roledesc=entity->getRoleDescriptor(*policy.getRole(), protocol);
if (!roledesc) {
log.warn("unable to find compatible role (%s) in metadata", policy.getRole()->toString().c_str());
return;
SchemaValidators.validate(xmlObject.get());
// Run through the policy.
- policy.evaluate(*root, &genericRequest);
+ policy.evaluate(*root, &genericRequest, samlconstants::SAML20P_NS);
// Check destination URL.
auto_ptr_char dest(request ? request->getDestination() : response->getDestination());
SchemaValidators.validate(xmlObject.get());
// Run through the policy.
- policy.evaluate(*root, &genericRequest);
+ policy.evaluate(*root, &genericRequest, samlconstants::SAML20P_NS);
// Check destination URL.
auto_ptr_char dest(request ? request->getDestination() : response->getDestination());
if (m_correlate && response->getInResponseTo() && !XMLString::equals(m_correlate, response->getInResponseTo()))
throw SecurityPolicyException("InResponseTo attribute did not correlate with the Request ID.");
- m_soaper.getPolicy().evaluate(*response);
+ m_soaper.getPolicy().reset(true);
+ m_soaper.getPolicy().evaluate(*response, NULL, samlconstants::SAML20P_NS);
if (!m_soaper.getPolicy().isSecure()) {
SecurityPolicyException ex("Security policy could not authenticate the message.");
annotateException(&ex, m_soaper.getPolicy().getIssuerMetadata(), response->getStatus()); // throws it
RequestAbstractType* request = dynamic_cast<RequestAbstractType*>(body->getUnknownXMLObjects().front());
if (request) {
// Run through the policy at two layers.
- policy.evaluate(*env, &genericRequest);
- policy.evaluate(*request, &genericRequest);
+ policy.evaluate(*env, &genericRequest, samlconstants::SAML20P_NS);
+ policy.reset(true);
+ policy.evaluate(*request, &genericRequest, samlconstants::SAML20P_NS);
xmlObject.release();
body->detach(); // frees Envelope
request->detach(); // frees Body
void testSAML1Artifact() {\r
try {\r
QName idprole(samlconstants::SAML20MD_NS, IDPSSODescriptor::LOCAL_NAME);\r
- SecurityPolicy policy(m_rules1, m_metadata, &idprole, m_trust, false);\r
+ SecurityPolicy policy(m_metadata, &idprole, m_trust, false);\r
+ policy.getRules().assign(m_rules1.begin(), m_rules1.end());\r
\r
// Read message to use from file.\r
string path = data_path + "saml1/binding/SAML1Assertion.xml";\r
TSM_ASSERT("Retrieved credential was null", cred!=NULL);\r
response->marshall((DOMDocument*)NULL,&sigs,cred);\r
SchemaValidators.validate(response.get());\r
- policy.evaluate(*(response.get()), this);\r
+ policy.evaluate(*(response.get()), this, samlconstants::SAML11_PROTOCOL_ENUM);\r
return response.release();\r
}\r
\r
void testSAML1POST() {
try {
QName idprole(samlconstants::SAML20MD_NS, IDPSSODescriptor::LOCAL_NAME);
- SecurityPolicy policy(m_rules1, m_metadata, &idprole, m_trust, false);
+ SecurityPolicy policy(m_metadata, &idprole, m_trust, false);
+ policy.getRules().assign(m_rules1.begin(), m_rules1.end());
// Read message to use from file.
string path = data_path + "saml1/binding/SAML1Response.xml";
void testSAML2Artifact() {
try {
QName idprole(samlconstants::SAML20MD_NS, IDPSSODescriptor::LOCAL_NAME);
- SecurityPolicy policy(m_rules2, m_metadata, &idprole, m_trust, false);
+ SecurityPolicy policy(m_metadata, &idprole, m_trust, false);
+ policy.getRules().assign(m_rules2.begin(), m_rules2.end());
// Read message to use from file.
string path = data_path + "saml2/binding/SAML2Response.xml";
sc->setValue(StatusCode::SUCCESS);
response->marshall();
SchemaValidators.validate(response.get());
- policy.evaluate(*(response.get()), this);
+ policy.evaluate(*(response.get()), this, samlconstants::SAML20P_NS);
return response.release();
}
};
void testSAML2POST() {
try {
QName idprole(samlconstants::SAML20MD_NS, IDPSSODescriptor::LOCAL_NAME);
- SecurityPolicy policy(m_rules2, m_metadata, &idprole, m_trust, false);
+ SecurityPolicy policy(m_metadata, &idprole, m_trust, false);
+ policy.getRules().assign(m_rules2.begin(), m_rules2.end());
// Read message to use from file.
string path = data_path + "saml2/binding/SAML2Response.xml";
void testSAML2POSTSimpleSign() {
try {
QName idprole(samlconstants::SAML20MD_NS, IDPSSODescriptor::LOCAL_NAME);
- SecurityPolicy policy(m_rules2, m_metadata, &idprole, m_trust, false);
+ SecurityPolicy policy(m_metadata, &idprole, m_trust, false);
+ policy.getRules().assign(m_rules2.begin(), m_rules2.end());
// Read message to use from file.
string path = data_path + "saml2/binding/SAML2Response.xml";
void testSAML2Redirect() {
try {
QName idprole(samlconstants::SAML20MD_NS, IDPSSODescriptor::LOCAL_NAME);
- SecurityPolicy policy(m_rules2, m_metadata, &idprole, m_trust, false);
+ SecurityPolicy policy(m_metadata, &idprole, m_trust, false);
+ policy.getRules().assign(m_rules2.begin(), m_rules2.end());
// Read message to use from file.
string path = data_path + "saml2/binding/SAML2Response.xml";