2 * Copyright 2001-2006 Internet2
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * SAML2ArtifactDecoder.cpp
20 * SAML 2.0 Artifact binding message decoder
24 #include "exceptions.h"
25 #include "saml/binding/SAMLArtifact.h"
26 #include "saml2/core/Protocols.h"
27 #include "saml2/metadata/Metadata.h"
28 #include "saml2/metadata/MetadataProvider.h"
29 #include "saml2/binding/SAML2Artifact.h"
30 #include "saml2/binding/SAML2ArtifactDecoder.h"
31 #include "security/X509TrustEngine.h"
33 #include <log4cpp/Category.hh>
34 #include <xmltooling/util/NDC.h>
35 #include <xmltooling/util/ReplayCache.h>
37 using namespace opensaml::saml2md;
38 using namespace opensaml::saml2p;
39 using namespace opensaml::saml2;
40 using namespace opensaml;
41 using namespace xmlsignature;
42 using namespace xmltooling;
43 using namespace log4cpp;
48 MessageDecoder* SAML_DLLLOCAL SAML2ArtifactDecoderFactory(const DOMElement* const & e)
50 return new SAML2ArtifactDecoder(e);
55 SAML2ArtifactDecoder::SAML2ArtifactDecoder(const DOMElement* e) {}
57 SAML2ArtifactDecoder::~SAML2ArtifactDecoder() {}
59 XMLObject* SAML2ArtifactDecoder::decode(
61 const RoleDescriptor*& issuer,
63 const HTTPRequest& httpRequest,
64 const MetadataProvider* metadataProvider,
66 const opensaml::TrustEngine* trustEngine
70 xmltooling::NDC ndc("decode");
72 Category& log = Category::getInstance(SAML_LOGCAT".MessageDecoder.SAML2Artifact");
74 log.debug("validating input");
75 const char* SAMLart = httpRequest.getParameter("SAMLart");
78 const char* state = httpRequest.getParameter("RelayState");
82 if (!m_artifactResolver || !metadataProvider)
83 throw BindingException("Artifact binding requires ArtifactResolver and MetadataProvider implementations be supplied.");
85 // Import the artifact.
86 SAMLArtifact* artifact=NULL;
87 ReplayCache* replayCache = XMLToolingConfig::getConfig().getReplayCache();
89 log.debug("processing encoded artifact (%s)", SAMLart);
93 if (!replayCache->check("SAML2Artifact", SAMLart, time(NULL) + (2*XMLToolingConfig::getConfig().clock_skew_secs))) {
94 log.error("replay detected of artifact (%s)", SAMLart);
95 throw BindingException("Rejecting replayed artifact ($1).", params(1,SAMLart));
99 log.warn("replay cache was not provided, this is a serious security risk!");
101 artifact = SAMLArtifact::parse(SAMLart);
103 catch (ArtifactException&) {
104 log.error("error parsing artifact (%s)", SAMLart);
109 auto_ptr<SAML2Artifact> artifact2(dynamic_cast<SAML2Artifact*>(artifact));
110 if (!artifact2.get()) {
111 throw BindingException("Artifact binding requires SAML 2.0 artifact.");
116 issuerTrusted = false;
117 log.debug("attempting to determine source of artifact...");
118 const EntityDescriptor* provider=metadataProvider->getEntityDescriptor(artifact);
121 "metadata lookup failed, unable to determine issuer of artifact (0x%s)",
122 SAMLArtifact::toHex(artifact->getBytes()).c_str()
124 throw BindingException("Metadata lookup failed, unable to determine artifact issuer.");
127 if (log.isDebugEnabled()) {
128 auto_ptr_char issuer(provider->getEntityID());
129 log.debug("lookup succeeded, artifact issued by (%s)", issuer.get());
132 log.debug("attempting to find artifact issuing role...");
133 issuer=provider->getRoleDescriptor(*role, SAMLConstants::SAML20P_NS);
134 if (!issuer || !dynamic_cast<const SSODescriptorType*>(issuer)) {
135 log.error("unable to find compatible SAML role (%s) in metadata", role->toString().c_str());
136 BindingException ex("Unable to find compatible metadata role for artifact issuer.");
137 annotateException(&ex,provider); // throws it
141 auto_ptr<ArtifactResponse> response(
142 m_artifactResolver->resolve(
145 dynamic_cast<const SSODescriptorType&>(*issuer),
146 dynamic_cast<const X509TrustEngine*>(trustEngine)
150 // Check Issuer of outer message.
151 if (!issuerMatches(response->getIssuer(), provider->getEntityID())) {
152 log.error("issuer of ArtifactResponse did not match source of artifact");
153 throw BindingException("Issuer of ArtifactResponse did not match source of artifact.");
156 // Extract payload and check that Issuer.
157 XMLObject* payload = response->getPayload();
158 RequestAbstractType* req = NULL;
159 StatusResponseType* res = dynamic_cast<StatusResponseType*>(payload);
161 req = dynamic_cast<RequestAbstractType*>(payload);
163 throw BindingException("ArtifactResponse payload was not a recognized SAML 2.0 protocol message.");
165 if (!issuerMatches(res ? res->getIssuer() : req->getIssuer(), provider->getEntityID())) {
166 log.error("issuer of ArtifactResponse payload did not match source of artifact");
167 throw BindingException("Issuer of ArtifactResponse payload did not match source of artifact.");
170 // Check payload freshness.
171 time_t now = time(NULL);
172 if ((res ? res->getIssueInstant() : req->getIssueInstant())->getEpoch() < now-(2*XMLToolingConfig::getConfig().clock_skew_secs))
173 throw BindingException("Detected expired ArtifactResponse payload.");
177 auto_ptr_char mid(res ? res->getID() : req->getID());
178 if (!replayCache->check("SAML2ArtifactPayload", mid.get(), now + (2*XMLToolingConfig::getConfig().clock_skew_secs))) {
179 log.error("replay detected of ArtifactResponse payload message ID (%s)", mid.get());
180 throw BindingException("Rejecting replayed ArtifactResponse payload ($1).", params(1,mid.get()));
186 if (response->getSignature()) {
187 issuerTrusted = trustEngine->validate(*(response->getSignature()), *issuer, metadataProvider->getKeyResolver());
188 if (!issuerTrusted) {
189 log.error("unable to verify signature on ArtifactResponse message with supplied trust engine");
190 throw BindingException("Message signature failed verification.");
193 Signature* sig = (res ? res->getSignature() : req->getSignature());
195 issuerTrusted = trustEngine->validate(*sig, *issuer, metadataProvider->getKeyResolver());
196 if (!issuerTrusted) {
197 log.error("unable to verify signature on ArtifactResponse payload with supplied trust engine");
198 throw BindingException("Message signature failed verification.");
203 if (!issuerTrusted) {
204 log.warn("unable to verify integrity of ArtifactResponse message or payload, leaving untrusted");
207 // Return the payload only.
212 catch (XMLToolingException& ex) {
213 annotateException(&ex,issuer,false);
218 bool SAML2ArtifactDecoder::issuerMatches(const Issuer* messageIssuer, const XMLCh* expectedIssuer) const
220 if (messageIssuer && messageIssuer->getName()) {
221 if (messageIssuer->getFormat() && !XMLString::equals(messageIssuer->getFormat(), NameIDType::ENTITY))
223 else if (!XMLString::equals(expectedIssuer, messageIssuer->getName()))