Fix some sloppy code related to policy reuse.
virtual ~ADFSDecoder() {}
XMLObject* decode(string& relayState, const GenericRequest& genericRequest, SecurityPolicy& policy) const;
+
+ protected:
+ void extractMessageDetails(
+ const XMLObject& message, const GenericRequest& req, const XMLCh* protocol, SecurityPolicy& policy
+ ) const {
+ }
};
MessageDecoder* ADFSDecoderFactory(const pair<const DOMElement*,const XMLCh*>& p)
if (!token || !token->getSignature())
throw FatalProfileException("Incoming message did not contain a signed SAML 1.1 assertion.");
- // Run the policy over the assertion. Handles issuer consistency, replay, freshness,
- // and signature verification, assuming the relevant rules are configured.
- policy.evaluate(*token, NULL, m_protocol.get());
+ // Extract message and issuer details from assertion.
+ extractMessageDetails(*token, m_protocol.get(), policy);
+
+ // Run the policy over the assertion. Handles replay, freshness, and
+ // signature verification, assuming the relevant rules are configured.
+ policy.evaluate(*token);
// If no security is in place now, we kick it.
- if (!policy.isSecure())
+ if (!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.
chunkedEncoding="false"
connectTimeout="15" timeout="30"
>
- <Rule type="SAML1Message"/>
- <Rule type="SAML2Message"/>
<Rule type="MessageFlow" checkReplay="true" expires="60"/>
<Rule type="ClientCertAuth" errorFatal="true"/>
<Rule type="XMLSigning" errorFatal="true"/>
}
try {
+ // We're going to insist that the assertion issuer is the same as the peer.
+ // Reset the policy's message bits and extract them from the assertion.
+ policy.reset(true);
+ policy.setMessageID(newtoken->getAssertionID());
+ policy.setIssueInstant(newtoken->getIssueInstantEpoch());
+ policy.setIssuer(newtoken->getIssuer());
policy.evaluate(*newtoken);
- if (!policy.isSecure())
+
+ // Now we can check the security status of the policy.
+ if (!policy.isAuthenticated())
throw SecurityPolicyException("Security of SAML 1.x query result not established.");
+
+ // Lastly, check it over.
saml1::AssertionValidator tokval(application.getAudiences(), time(NULL));
tokval.validateAssertion(*newtoken);
}
}
try {
+ // We're going to insist that the assertion issuer is the same as the peer.
+ // Reset the policy's message bits and extract them from the assertion.
+ policy.reset(true);
+ policy.setMessageID(newtoken->getID());
+ policy.setIssueInstant(newtoken->getIssueInstantEpoch());
+ policy.setIssuer(newtoken->getIssuer());
policy.evaluate(*newtoken);
- if (!policy.isSecure())
+
+ // Now we can check the security status of the policy.
+ if (!policy.isAuthenticated())
throw SecurityPolicyException("Security of SAML 2.0 query result not established.");
+
+ // Lastly, check it over.
saml2::AssertionValidator tokval(application.getAudiences(), time(NULL));
tokval.validateAssertion(*newtoken);
}
throw BindingException("Identity provider returned a SAML error in response to artifact(s).");
}
+ // The SOAP client handles policy evaluation against the SOAP and Response layer,
+ // but no security checking is done here.
return response;
}
BindingException ex("Identity provider returned a SAML error in response to artifact.");
annotateException(&ex, &ssoDescriptor, response->getStatus()); // rethrow
}
+
+ // The SOAP client handles policy evaluation against the SOAP and Response layer,
+ // but no security checking is done here.
return response;
}
const xmltooling::XMLObject& xmlObject
) const=0;
+ /**\r
+ * Extracts policy-relevant assertion details.\r
+ * \r
+ * @param assertion the incoming assertion\r
+ * @param protocol the protocol family in use\r
+ * @param policy SecurityPolicy to provide various components and track message data\r
+ */\r
+ virtual void extractMessageDetails(\r
+ const opensaml::Assertion& assertion, const XMLCh* protocol, opensaml::SecurityPolicy& policy\r
+ ) const;\r
+
/**
* Attempt SSO-initiated attribute resolution using the supplied information,
* including NameID and token extraction and filtering followed by
# include <saml/saml1/core/Assertions.h>
# include <saml/util/CommonDomainCookie.h>
using namespace samlconstants;
+using opensaml::saml2md::EntityDescriptor;
+using opensaml::saml2md::IDPSSODescriptor;
#else
# include "lite/CommonDomainCookie.h"
#endif
return new DummyContext(resolvedAttributes);
return NULL;
}
+
+void AssertionConsumerService::extractMessageDetails(const Assertion& assertion, const XMLCh* protocol, opensaml::SecurityPolicy& policy) const
+{
+ policy.setMessageID(assertion.getID());
+ policy.setIssueInstant(assertion.getIssueInstantEpoch());
+
+ if (XMLString::equals(assertion.getElementQName().getNamespaceURI(), samlconstants::SAML20P_NS)) {
+ const saml2::Assertion* a2 = dynamic_cast<const saml2::Assertion*>(&assertion);
+ if (a2) {
+ m_log.debug("extracting issuer from SAML 2.0 assertion");
+ policy.setIssuer(a2->getIssuer());
+ }
+ }
+ else {
+ const saml1::Assertion* a1 = dynamic_cast<const saml1::Assertion*>(&assertion);
+ if (a1) {
+ m_log.debug("extracting issuer from SAML 1.x assertion");
+ policy.setIssuer(a1->getIssuer());
+ }
+ }
+
+ if (policy.getIssuer() && !policy.getIssuerMetadata() && policy.getMetadataProvider()) {
+ m_log.debug("searching metadata for assertion issuer...");
+ const EntityDescriptor* entity = policy.getMetadataProvider()->getEntityDescriptor(policy.getIssuer()->getName());\r
+ if (entity) {\r
+ m_log.debug("matched assertion issuer against metadata, searching for applicable role...");\r
+ const IDPSSODescriptor* idp=entity->getIDPSSODescriptor(protocol);\r
+ if (idp)\r
+ policy.setIssuerMetadata(idp);\r
+ else if (m_log.isWarnEnabled())\r
+ m_log.warn("unable to find compatible IdP role in metadata");\r
+ }\r
+ else if (m_log.isWarnEnabled()) {\r
+ auto_ptr_char iname(policy.getIssuer()->getName());\r
+ m_log.warn("no metadata found, can't establish identity of issuer (%s)", iname.get());\r
+ }\r
+ }
+}
+
#endif
void AssertionConsumerService::maintainHistory(SPRequest& request, const char* entityID, const char* cookieProps) const
// the focus here is on the assertion content. For SAML 1.x POST,
// all the security comes from the protocol layer, and signing
// the assertion isn't sufficient. So we can check the policy
- // object now and bail if it's not a secure message.
- if (m_post && !policy.isSecure()) {
+ // object now and bail if it's not a secured message.
+ if (m_post && !policy.isAuthenticated()) {
if (policy.getIssuer() && !policy.getIssuerMetadata())
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.isSecure();
+ bool alreadySecured = policy.isAuthenticated();
const Response* response = dynamic_cast<const Response*>(&xmlObject);
if (!response)
if (assertions.empty())
throw FatalProfileException("Incoming message contained no SAML assertions.");
+ pair<bool,int> minor = response->getMinorVersion();
+
// Maintain list of "legit" tokens to feed to SP subsystems.
const AuthenticationStatement* ssoStatement=NULL;
vector<const opensaml::Assertion*> tokens;
try {
// We clear the security flag, so we can tell whether the token was secured on its own.
- policy.setSecure(false);
-
- // Run the policy over the assertion. Handles issuer consistency, replay, freshness,
- // and signature verification, assuming the relevant rules are configured.
+ policy.setAuthenticated(false);
+ policy.reset(true);
+
+ // Extract message bits and re-verify Issuer information.
+ extractMessageDetails(
+ *(*a), (minor.first && minor.second==0) ? samlconstants::SAML10_PROTOCOL_ENUM : samlconstants::SAML11_PROTOCOL_ENUM, policy
+ );
+
+ // Run the policy over the assertion. Handles replay, freshness, and
+ // signature verification, assuming the relevant rules are configured.
policy.evaluate(*(*a));
// If no security is in place now, we kick it.
- if (!alreadySecured && !policy.isSecure()) {
+ if (!alreadySecured && !policy.isAuthenticated()) {
m_log.warn("unable to establish security of assertion");
badtokens.push_back(*a);
continue;
auto_ptr<SAMLArtifact> artobj(SAMLArtifact::parse(artifact.get()));
auto_ptr<XMLObject> payload(artmap->retrieveContent(artobj.get(), issuer.get()));
- if (!policy.isSecure()) {
+ if (!policy.isAuthenticated()) {
m_log.error("request for artifact was unauthenticated, purging the artifact mapping");
return samlError(application, *req, httpResponse, StatusCode::REQUESTER, StatusCode::AUTHN_FAILED, "Unable to authenticate request.");
}
// Remember whether we already established trust.
// None of the SAML 2 bindings require security at the protocol layer.
- bool alreadySecured = policy.isSecure();
+ bool alreadySecured = policy.isAuthenticated();
// Check for errors...this will throw if it's not a successful message.
checkError(&xmlObject);
try {
// We clear the security flag, so we can tell whether the token was secured on its own.
- policy.setSecure(false);
-
- // Run the policy over the assertion. Handles issuer consistency, replay, freshness,
- // and signature verification, assuming the relevant rules are configured.
+ policy.setAuthenticated(false);
+ policy.reset(true);
+
+ // Extract message bits and re-verify Issuer information.
+ extractMessageDetails(*(*a), samlconstants::SAML20P_NS, policy);
+
+ // Run the policy over the assertion. Handles replay, freshness, and
+ // signature verification, assuming the relevant rules are configured.
policy.evaluate(*(*a));
// If no security is in place now, we kick it.
- if (!alreadySecured && !policy.isSecure()) {
+ if (!alreadySecured && !policy.isAuthenticated()) {
m_log.warn("unable to establish security of assertion");
badtokens.push_back(*a);
continue;
try {
// We clear the security flag, so we can tell whether the token was secured on its own.
- policy.setSecure(false);
-
- // Run the policy over the assertion. Handles issuer consistency, replay, freshness,
- // and signature verification, assuming the relevant rules are configured.
+ policy.setAuthenticated(false);
+ policy.reset(true);
+
+ // Extract message bits and re-verify Issuer information.
+ extractMessageDetails(*decrypted, samlconstants::SAML20P_NS, policy);
+
+ // Run the policy over the assertion. Handles replay, freshness, and
+ // signature verification, assuming the relevant rules are configured.
// We have to marshall the object first to ensure signatures can be checked.
+ if (!decrypted->getDOM())
+ decrypted->marshall();
policy.evaluate(*decrypted);
// If no security is in place now, we kick it.
- if (!alreadySecured && !policy.isSecure()) {
+ if (!alreadySecured && !policy.isAuthenticated()) {
m_log.warn("unable to establish security of assertion");
badtokens.push_back(decrypted);
continue;
auto_ptr<XMLObject> msg(m_decoder->decode(relayState, request, policy));
const LogoutRequest* logoutRequest = dynamic_cast<LogoutRequest*>(msg.get());
if (logoutRequest) {
- if (!policy.isSecure())
+ if (!policy.isAuthenticated())
throw SecurityPolicyException("Security of LogoutRequest not established.");
// Message from IdP to logout one or more sessions.
// A LogoutResponse completes an SP-initiated logout sequence.
const LogoutResponse* logoutResponse = dynamic_cast<LogoutResponse*>(msg.get());
if (logoutResponse) {
- if (!policy.isSecure()) {
+ if (!policy.isAuthenticated()) {
SecurityPolicyException ex("Security of LogoutResponse not established.");
if (policy.getIssuerMetadata())
annotateException(&ex, policy.getIssuerMetadata()); // throws it