From 1d5fda59e4996860bb2480ad52e29b2160d04725 Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Wed, 18 Feb 2009 16:12:48 +0000 Subject: [PATCH] Prepare policy API for mix-in subclassing. Allow policy rules to distinguish success from ignorance. --- saml/binding/SecurityPolicy.h | 28 ++++++++++- saml/binding/SecurityPolicyRule.h | 9 +++- saml/binding/impl/ClientCertAuthRule.cpp | 17 ++++--- saml/binding/impl/MessageFlowRule.cpp | 12 +++-- saml/binding/impl/NullSecurityRule.cpp | 5 +- saml/binding/impl/SecurityPolicy.cpp | 22 +++++++-- saml/binding/impl/SimpleSigningRule.cpp | 23 ++++----- saml/binding/impl/XMLSigningRule.cpp | 17 ++++--- saml/saml2/metadata/MetadataProvider.h | 85 ++++++++++++++++++-------------- 9 files changed, 140 insertions(+), 78 deletions(-) diff --git a/saml/binding/SecurityPolicy.h b/saml/binding/SecurityPolicy.h index 7f879ec..72d2464 100644 --- a/saml/binding/SecurityPolicy.h +++ b/saml/binding/SecurityPolicy.h @@ -1,5 +1,5 @@ /* - * 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. @@ -91,6 +91,9 @@ namespace opensaml { * Returns a reference to a MetadataProvider::Criteria instance suitable for use with the * installed MetadataProvider. * + *

The object will be cleared/reset when returned, so do not mutate it and then + * call the method again before using it. + * * @return reference to a MetadataProvider::Criteria instance */ virtual saml2md::MetadataProvider::Criteria& getMetadataProviderCriteria() const; @@ -152,6 +155,17 @@ namespace opensaml { } /** + * Sets a MetadataProvider::Criteria instance suitable for use with the + * installed MetadataProvider. + * + *

The policy will take ownership of the criteria object when this + * method completes. + * + * @param criteria a MetadataProvider::Criteria instance, or NULL + */ + void setMetadataProviderCriteria(saml2md::MetadataProvider::Criteria* criteria); + + /** * Sets a peer role element/type for to the policy. * * @param role the peer role element/type or NULL @@ -211,7 +225,17 @@ namespace opensaml { * * @param messageOnly true iff security and issuer state should be left in place */ - void reset(bool messageOnly=false); + virtual void reset(bool messageOnly=false); + + /** + * Resets the policy object and/or clears any per-message state for only this specific class. + * + *

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(bool messageOnly=false); /** * Returns the message identifier as determined by the registered policies. diff --git a/saml/binding/SecurityPolicyRule.h b/saml/binding/SecurityPolicyRule.h index f8d11e8..ae17ba4 100644 --- a/saml/binding/SecurityPolicyRule.h +++ b/saml/binding/SecurityPolicyRule.h @@ -1,5 +1,5 @@ /* - * 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. @@ -54,12 +54,17 @@ namespace opensaml { * *

An exception will be raised if the message is invalid according to * a policy rule. + * + *

The return value is used to indicate whether a message was ignored or + * successfully processed. A false value signals that the rule wasn't successful + * but was also not unsuccessful, because the rule was inapplicable to the message. * * @param message the incoming message * @param request the protocol request * @param policy SecurityPolicy to provide various components and track message data + * @return indicator as to whether a message was understood and processed */ - virtual void evaluate( + virtual bool evaluate( const xmltooling::XMLObject& message, const xmltooling::GenericRequest* request, SecurityPolicy& policy diff --git a/saml/binding/impl/ClientCertAuthRule.cpp b/saml/binding/impl/ClientCertAuthRule.cpp index a083baf..840c242 100644 --- a/saml/binding/impl/ClientCertAuthRule.cpp +++ b/saml/binding/impl/ClientCertAuthRule.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -47,7 +47,7 @@ namespace opensaml { const char* getType() const { return CLIENTCERTAUTH_POLICY_RULE; } - void evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const; + bool evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const; private: bool m_errorFatal; @@ -69,27 +69,27 @@ ClientCertAuthRule::ClientCertAuthRule(const DOMElement* e) : m_errorFatal(false } } -void ClientCertAuthRule::evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const +bool ClientCertAuthRule::evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const { Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.ClientCertAuth"); if (!request) - return; + return false; if (!policy.getIssuerMetadata()) { log.debug("ignoring message, no issuer metadata supplied"); - return; + return false; } const X509TrustEngine* x509trust; if (!(x509trust=dynamic_cast(policy.getTrustEngine()))) { log.debug("ignoring message, no X509TrustEngine supplied"); - return; + return false; } const std::vector& chain = request->getClientCertificates(); if (chain.empty()) - return; + return false; // Set up criteria object, including peer name to enforce cert name checking. MetadataCredentialCriteria cc(*(policy.getIssuerMetadata())); @@ -101,9 +101,10 @@ void ClientCertAuthRule::evaluate(const XMLObject& message, const GenericRequest if (m_errorFatal) throw SecurityPolicyException("Client certificate supplied, but could not be verified."); log.error("unable to verify certificate chain with supplied trust engine"); - return; + return false; } log.debug("client certificate verified against message issuer"); policy.setAuthenticated(true); + return true; } diff --git a/saml/binding/impl/MessageFlowRule.cpp b/saml/binding/impl/MessageFlowRule.cpp index 45722fa..f371f1f 100644 --- a/saml/binding/impl/MessageFlowRule.cpp +++ b/saml/binding/impl/MessageFlowRule.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -43,7 +43,7 @@ namespace opensaml { const char* getType() const { return MESSAGEFLOW_POLICY_RULE; } - void evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const; + bool evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const; private: bool m_checkReplay; @@ -72,7 +72,7 @@ MessageFlowRule::MessageFlowRule(const DOMElement* e) } } -void MessageFlowRule::evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const +bool MessageFlowRule::evaluate(const XMLObject& message, const GenericRequest* request, 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); @@ -100,12 +100,12 @@ void MessageFlowRule::evaluate(const XMLObject& message, const GenericRequest* r if (m_checkReplay) { const XMLCh* id = policy.getMessageID(); if (!id || !*id) - return; + return false; ReplayCache* replayCache = XMLToolingConfig::getConfig().getReplayCache(); if (!replayCache) { log.warn("no ReplayCache available, skipping requested replay check"); - return; + return false; } auto_ptr_char temp(id); @@ -113,5 +113,7 @@ void MessageFlowRule::evaluate(const XMLObject& message, const GenericRequest* r log.error("replay detected of message ID (%s)", temp.get()); throw SecurityPolicyException("Rejecting replayed message ID ($1).", params(1,temp.get())); } + return true; } + return false; } diff --git a/saml/binding/impl/NullSecurityRule.cpp b/saml/binding/impl/NullSecurityRule.cpp index 74c3fd6..0fe25ce 100644 --- a/saml/binding/impl/NullSecurityRule.cpp +++ b/saml/binding/impl/NullSecurityRule.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -40,9 +40,10 @@ namespace opensaml { const char* getType() const { return NULLSECURITY_POLICY_RULE; } - void evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const { + bool 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.setAuthenticated(true); + return true; } private: diff --git a/saml/binding/impl/SecurityPolicy.cpp b/saml/binding/impl/SecurityPolicy.cpp index 02812a4..af93a86 100644 --- a/saml/binding/impl/SecurityPolicy.cpp +++ b/saml/binding/impl/SecurityPolicy.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -53,16 +53,21 @@ SecurityPolicy::IssuerMatchingPolicy SecurityPolicy::m_defaultMatching; SecurityPolicy::~SecurityPolicy() { - reset(false); + XMLString::release(&m_messageID); + delete m_metadataCriteria; + delete m_issuer; } void SecurityPolicy::reset(bool messageOnly) { + _reset(); +} + +void SecurityPolicy::_reset(bool messageOnly) +{ XMLString::release(&m_messageID); m_messageID=NULL; m_issueInstant=0; - delete m_metadataCriteria; - m_metadataCriteria=NULL; if (!messageOnly) { delete m_issuer; m_issuer=NULL; @@ -75,9 +80,18 @@ MetadataProvider::Criteria& SecurityPolicy::getMetadataProviderCriteria() const { if (!m_metadataCriteria) m_metadataCriteria=new MetadataProvider::Criteria(); + else + m_metadataCriteria->reset(); return *m_metadataCriteria; } +void SecurityPolicy::setMetadataProviderCriteria(saml2md::MetadataProvider::Criteria* criteria) +{ + if (m_metadataCriteria) + delete m_metadataCriteria; + m_metadataCriteria=criteria; +} + void SecurityPolicy::evaluate(const XMLObject& message, const GenericRequest* request) { for (vector::const_iterator i=m_rules.begin(); i!=m_rules.end(); ++i) diff --git a/saml/binding/impl/SimpleSigningRule.cpp b/saml/binding/impl/SimpleSigningRule.cpp index 4974d05..c16dd7e 100644 --- a/saml/binding/impl/SimpleSigningRule.cpp +++ b/saml/binding/impl/SimpleSigningRule.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -52,7 +52,7 @@ namespace opensaml { const char* getType() const { return SIMPLESIGNING_POLICY_RULE; } - void evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const; + bool evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const; private: // Appends a raw parameter=value pair to the string. @@ -92,33 +92,33 @@ SimpleSigningRule::SimpleSigningRule(const DOMElement* e) : m_errorFatal(false) } } -void SimpleSigningRule::evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const +bool SimpleSigningRule::evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const { Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.SimpleSigning"); if (!policy.getIssuerMetadata()) { log.debug("ignoring message, no issuer metadata supplied"); - return; + return false; } const SignatureTrustEngine* sigtrust; if (!(sigtrust=dynamic_cast(policy.getTrustEngine()))) { log.debug("ignoring message, no SignatureTrustEngine supplied"); - return; + return false; } const HTTPRequest* httpRequest = dynamic_cast(request); if (!request || !httpRequest) - return; + return false; const char* signature = request->getParameter("Signature"); if (!signature) - return; + return false; const char* sigAlgorithm = request->getParameter("SigAlg"); if (!sigAlgorithm) { log.error("SigAlg parameter not found, no way to verify the signature"); - return; + return false; } string input; @@ -152,7 +152,7 @@ void SimpleSigningRule::evaluate(const XMLObject& message, const GenericRequest* XMLByte* decoded=Base64::decode(reinterpret_cast(pch),&x); if (!decoded) { log.warn("unable to decode base64 in POST binding message"); - return; + return false; } input = string("SAMLRequest=") + reinterpret_cast(decoded); #ifdef OPENSAML_XERCESC_HAS_XMLBYTE_RELEASE @@ -166,7 +166,7 @@ void SimpleSigningRule::evaluate(const XMLObject& message, const GenericRequest* XMLByte* decoded=Base64::decode(reinterpret_cast(pch),&x); if (!decoded) { log.warn("unable to decode base64 in POST binding message"); - return; + return false; } input = string("SAMLResponse=") + reinterpret_cast(decoded); #ifdef OPENSAML_XERCESC_HAS_XMLBYTE_RELEASE @@ -223,9 +223,10 @@ void SimpleSigningRule::evaluate(const XMLObject& message, const GenericRequest* log.error("unable to verify message signature with supplied trust engine"); if (m_errorFatal) throw SecurityPolicyException("Message was signed, but signature could not be verified."); - return; + return false; } log.debug("signature verified against message issuer"); policy.setAuthenticated(true); + return true; } diff --git a/saml/binding/impl/XMLSigningRule.cpp b/saml/binding/impl/XMLSigningRule.cpp index 047de86..5ebf1e2 100644 --- a/saml/binding/impl/XMLSigningRule.cpp +++ b/saml/binding/impl/XMLSigningRule.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -50,7 +50,7 @@ namespace opensaml { const char* getType() const { return XMLSIGNING_POLICY_RULE; } - void evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const; + bool evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const; private: bool m_errorFatal; @@ -72,24 +72,24 @@ XMLSigningRule::XMLSigningRule(const DOMElement* e) : m_errorFatal(false) } } -void XMLSigningRule::evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const +bool XMLSigningRule::evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const { Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.XMLSigning"); if (!policy.getIssuerMetadata()) { log.debug("ignoring message, no issuer metadata supplied"); - return; + return false; } const SignatureTrustEngine* sigtrust; if (!(sigtrust=dynamic_cast(policy.getTrustEngine()))) { log.debug("ignoring message, no SignatureTrustEngine supplied"); - return; + return false; } const SignableObject* signable = dynamic_cast(&message); if (!signable || !signable->getSignature()) - return; + return false; log.debug("validating signature profile"); try { @@ -100,7 +100,7 @@ void XMLSigningRule::evaluate(const XMLObject& message, const GenericRequest* re log.error("signature profile failed to validate: %s", ve.what()); if (m_errorFatal) throw; - return; + return false; } // Set up criteria object. @@ -110,9 +110,10 @@ void XMLSigningRule::evaluate(const XMLObject& message, const GenericRequest* re log.error("unable to verify message signature with supplied trust engine"); if (m_errorFatal) throw SecurityPolicyException("Message was signed, but signature could not be verified."); - return; + return false; } log.debug("signature verified against message issuer"); policy.setAuthenticated(true); + return true; } diff --git a/saml/saml2/metadata/MetadataProvider.h b/saml/saml2/metadata/MetadataProvider.h index beced6e..1f9e2be 100644 --- a/saml/saml2/metadata/MetadataProvider.h +++ b/saml/saml2/metadata/MetadataProvider.h @@ -1,6 +1,6 @@ /* * Copyright 2001-2007 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 @@ -16,7 +16,7 @@ /** * @file saml/saml2/metadata/MetadataProvider.h - * + * * Supplies an individual source of metadata. */ @@ -29,7 +29,7 @@ #include namespace opensaml { - + class SAML_API SAMLArtifact; namespace saml2md { @@ -47,7 +47,7 @@ namespace opensaml { /** * Supplies an individual source of metadata. - * + * * The source can be a local file, remote service, or the result of a * dynamic lookup, can include local caching, etc. Providers * MUST be locked before any lookup operations. @@ -58,37 +58,37 @@ namespace opensaml { protected: /** * Constructor. - * + * * If a DOM is supplied, a set of default logic will be used to identify * and build MetadataFilter plugins and install them into the provider. - * + * * The following XML content is supported: - * + * *

- * + * * XML namespaces are ignored in the processing of these elements. - * + * * @param e DOM to supply configuration for provider */ MetadataProvider(const xercesc::DOMElement* e=NULL); - + public: /** * Destructor will delete any installed filters. */ virtual ~MetadataProvider(); - + /** * Adds a metadata filter to apply to any resolved metadata. Will not be applied * to metadata that is already loaded. - * + * * @param newFilter metadata filter to add */ virtual void addMetadataFilter(MetadataFilter* newFilter) { @@ -97,7 +97,7 @@ namespace opensaml { /** * Removes a metadata filter. The caller must delete the filter if necessary. - * + * * @param oldFilter metadata filter to remove * @return the old filter */ @@ -110,7 +110,7 @@ namespace opensaml { } return NULL; } - + /** * Should be called after instantiating provider and adding filters, but before * performing any lookup operations. Allows the provider to defer initialization @@ -119,23 +119,23 @@ namespace opensaml { * this method so as to report/log any errors that would affect later processing. */ virtual void init()=0; - + /** * Gets the entire metadata tree, after the registered filter has been applied. * The caller MUST unlock the provider when finished with the data. - * + * * @return the entire metadata tree */ virtual const xmltooling::XMLObject* getMetadata() const=0; - + /** * Gets the metadata for a given group of entities. If a valid group is returned, * the resolver will be left in a locked state. The caller MUST unlock the * resolver when finished with the group. - * + * * @param name the name of the group * @param requireValidMetadata indicates whether the metadata for the group must be valid/current - * + * * @return the group's metadata or NULL if there is no metadata or no valid metadata */ virtual const EntitiesDescriptor* getEntitiesDescriptor(const XMLCh* name, bool requireValidMetadata=true) const; @@ -144,10 +144,10 @@ namespace opensaml { * Gets the metadata for a given group of entities. If a valid group is returned, * the resolver will be left in a locked state. The caller MUST unlock the * resolver when finished with the group. - * + * * @param name the name of the group * @param requireValidMetadata indicates whether the metadata for the group must be valid/current - * + * * @return the group's metadata or NULL if there is no metadata or no valid metadata */ virtual const EntitiesDescriptor* getEntitiesDescriptor(const char* name, bool requireValidMetadata=true) const=0; @@ -161,10 +161,10 @@ namespace opensaml { */ Criteria() : entityID_unicode(NULL), entityID_ascii(NULL), artifact(NULL), role(NULL), protocol(NULL), protocol2(NULL), validOnly(true) { } - + /** * Constructor. - * + * * @param id entityID to lookup * @param q element/type of role, if any * @param prot protocol support constant, if any @@ -173,10 +173,10 @@ namespace opensaml { Criteria(const XMLCh* id, const xmltooling::QName* q=NULL, const XMLCh* prot=NULL, bool valid=true) : entityID_unicode(id), entityID_ascii(NULL), artifact(NULL), role(q), protocol(prot), protocol2(NULL), validOnly(valid) { } - + /** * Constructor. - * + * * @param id entityID to lookup * @param q element/type of role, if any * @param prot protocol support constant, if any @@ -188,7 +188,7 @@ namespace opensaml { /** * Constructor. - * + * * @param a artifact to lookup * @param q element/type of role, if any * @param prot protocol support constant, if any @@ -197,9 +197,22 @@ namespace opensaml { Criteria(const SAMLArtifact* a, const xmltooling::QName* q=NULL, const XMLCh* prot=NULL, bool valid=true) : entityID_unicode(NULL), entityID_ascii(NULL), artifact(a), role(q), protocol(prot), protocol2(NULL), validOnly(valid) { } - + virtual ~Criteria() {} - + + /** + * Restores the object to its default state. + */ + virtual void reset() { + entityID_unicode=NULL; + entityID_ascii=NULL; + artifact=NULL; + role=NULL; + protocol=NULL; + protocol2=NULL; + validOnly=true; + } + /** Unique ID of entity. */ const XMLCh* entityID_unicode; /** Unique ID of entity. */ @@ -215,14 +228,14 @@ namespace opensaml { /** Controls whether stale metadata is ignored. */ bool validOnly; }; - + /** * Gets entity metadata based on supplied criteria. If a valid entity is returned, * the provider will be left in a locked state. The caller MUST unlock the * provider when finished with the entity. - * + * * @param criteria lookup criteria - * + * * @return the entity's metadata (and optionally a role) or NULL if there is no qualifying metadata */ virtual std::pair getEntityDescriptor(const Criteria& criteria) const=0; @@ -230,7 +243,7 @@ namespace opensaml { protected: /** * Applies any installed filters to a metadata instance. - * + * * @param xmlObject the metadata to be filtered */ void doFilters(xmltooling::XMLObject& xmlObject) const; @@ -247,7 +260,7 @@ namespace opensaml { * Registers MetadataProvider classes into the runtime. */ void SAML_API registerMetadataProviders(); - + /** MetadataProvider based on local or remote XML file */ #define XML_METADATA_PROVIDER "XML" -- 2.1.4