2 * The Shibboleth License, Version 1.
4 * University Corporation for Advanced Internet Development, Inc.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
11 * Redistributions of source code must retain the above copyright notice, this
12 * list of conditions and the following disclaimer.
14 * Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution, if any, must include
17 * the following acknowledgment: "This product includes software developed by
18 * the University Corporation for Advanced Internet Development
19 * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement
20 * may appear in the software itself, if and wherever such third-party
21 * acknowledgments normally appear.
23 * Neither the name of Shibboleth nor the names of its contributors, nor
24 * Internet2, nor the University Corporation for Advanced Internet Development,
25 * Inc., nor UCAID may be used to endorse or promote products derived from this
26 * software without specific prior written permission. For written permission,
27 * please contact shibboleth@shibboleth.org
29 * Products derived from this software may not be called Shibboleth, Internet2,
30 * UCAID, or the University Corporation for Advanced Internet Development, nor
31 * may Shibboleth appear in their name, without prior written permission of the
32 * University Corporation for Advanced Internet Development.
35 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
36 * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
38 * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK
39 * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE.
40 * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY
41 * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT,
42 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
43 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
44 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
47 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50 /* ArtifactMapper.cpp - a ShibTarget-aware SAML artifact->binding mapper
61 using namespace log4cpp;
63 using namespace shibboleth;
64 using namespace shibtarget;
66 SAMLResponse* STArtifactMapper::resolve(SAMLRequest* request)
68 Category& log=Category::getInstance("shibtarget.ArtifactMapper");
70 // First do a search for the issuer.
71 SAMLArtifact* artifact=request->getArtifacts().next();
72 Metadata m(m_app->getMetadataProviders());
73 const IEntityDescriptor* entity=m.lookup(artifact);
76 "metadata lookup failed, unable to determine issuer of artifact (0x%s)",
77 SAMLArtifact::toHex(artifact->getBytes()).c_str()
79 throw MetadataException("Metadata lookup failed, unable to determine artifact issuer");
82 auto_ptr_char issuer(entity->getId());
83 log.info("lookup succeeded, artifact issued by (%s)", issuer.get());
86 const IPropertySet* credUse=m_app->getCredentialUse(entity);
87 pair<bool,bool> signRequest=credUse ? credUse->getBool("signRequest") : make_pair(false,false);
88 pair<bool,const char*> signatureAlg=credUse ? credUse->getString("signatureAlg") : pair<bool,const char*>(false,NULL);
89 if (!signatureAlg.first)
90 signatureAlg.second=URI_ID_RSA_SHA1;
91 pair<bool,const char*> digestAlg=credUse ? credUse->getString("digestAlg") : pair<bool,const char*>(false,NULL);
93 digestAlg.second=URI_ID_SHA1;
94 pair<bool,bool> signedResponse=credUse ? credUse->getBool("signedResponse") : make_pair(false,false);
95 pair<bool,const char*> signingCred=credUse ? credUse->getString("Signing") : pair<bool,const char*>(false,NULL);
96 if (signRequest.first && signRequest.second && signingCred.first) {
97 if (request->getMinorVersion()==1) {
98 Credentials creds(ShibTargetConfig::getConfig().getINI()->getCredentialsProviders());
99 const ICredResolver* cr=creds.lookup(signingCred.second);
101 request->sign(cr->getKey(),cr->getCertificates(),signatureAlg.second,digestAlg.second);
103 log.error("unable to sign artifact request, specified credential (%s) was not found",signingCred.second);
106 log.error("unable to sign SAML 1.0 artifact request, only SAML 1.1 defines signing adequately");
109 SAMLResponse* response = NULL;
110 bool authenticated = false;
111 static const XMLCh https[] = {chLatin_h, chLatin_t, chLatin_t, chLatin_p, chLatin_s, chColon, chNull};
113 // Depends on type of artifact.
114 const SAMLArtifactType0001* type1=dynamic_cast<const SAMLArtifactType0001*>(artifact);
116 // With type 01, any endpoint will do.
117 const IIDPSSODescriptor* idp=entity->getIDPSSODescriptor(
118 request->getMinorVersion()==1 ? saml::XML::SAML11_PROTOCOL_ENUM : saml::XML::SAML10_PROTOCOL_ENUM
121 ShibHTTPHook::ShibHTTPHookCallContext callCtx(credUse,idp);
122 const IEndpointManager* mgr=idp->getArtifactResolutionServiceManager();
123 Iterator<const IEndpoint*> eps=mgr ? mgr->getEndpoints() : EMPTY(const IEndpoint*);
124 while (!response && eps.hasNext()) {
125 const IEndpoint* ep=eps.next();
126 const SAMLBinding* binding = m_app->getBinding(ep->getBinding());
128 auto_ptr_char prot(ep->getBinding());
129 log.warn("skipping binding on unsupported protocol (%s)", prot.get());
133 response = binding->send(ep->getLocation(),*request,&callCtx);
134 if (log.isDebugEnabled())
135 log.debugStream() << "SAML response from artifact request:\n" << *response << CategoryStream::ENDLINE;
137 if (!response->getAssertions().hasNext()) {
139 throw FatalProfileException("No SAML assertions returned in response to artifact profile request.");
141 authenticated = callCtx.isAuthenticated() && !XMLString::compareNString(ep->getLocation(),https,6);
143 catch (SAMLException& ex) {
144 annotateException(&ex,idp); // rethrows it
150 const SAMLArtifactType0002* type2=dynamic_cast<const SAMLArtifactType0002*>(artifact);
152 // With type 02, we have to find the matching location.
153 const IIDPSSODescriptor* idp=entity->getIDPSSODescriptor(
154 request->getMinorVersion()==1 ? saml::XML::SAML11_PROTOCOL_ENUM : saml::XML::SAML10_PROTOCOL_ENUM
157 ShibHTTPHook::ShibHTTPHookCallContext callCtx(credUse,idp);
158 const IEndpointManager* mgr=idp->getArtifactResolutionServiceManager();
159 Iterator<const IEndpoint*> eps=mgr ? mgr->getEndpoints() : EMPTY(const IEndpoint*);
160 while (eps.hasNext()) {
161 const IEndpoint* ep=eps.next();
162 auto_ptr_char loc(ep->getLocation());
163 if (strcmp(loc.get(),type2->getSourceLocation()))
165 const SAMLBinding* binding = m_app->getBinding(ep->getBinding());
167 auto_ptr_char prot(ep->getBinding());
168 log.warn("skipping binding on unsupported protocol (%s)", prot.get());
172 response = binding->send(ep->getLocation(),*request,&callCtx);
173 if (log.isDebugEnabled())
174 log.debugStream() << "SAML response from artifact request:\n" << *response << CategoryStream::ENDLINE;
176 if (!response->getAssertions().hasNext()) {
178 throw FatalProfileException("No SAML assertions returned in response to artifact profile request.");
180 authenticated = callCtx.isAuthenticated() && !XMLString::compareNString(ep->getLocation(),https,6);
182 catch (SAMLException& ex) {
183 annotateException(&ex,idp); // rethrows it
189 log.error("unrecognized artifact type (0x%s)", SAMLArtifact::toHex(artifact->getTypeCode()).c_str());
190 throw UnsupportedExtensionException(
191 string("Received unrecognized artifact type (0x") + SAMLArtifact::toHex(artifact->getTypeCode()) + ")"
197 log.error("unable to locate acceptable binding/endpoint to resolve artifact");
198 MetadataException ex("Unable to locate acceptable binding/endpoint to resolve artifact.");
199 annotateException(&ex,entity); // throws it
201 else if (!response->isSigned()) {
202 if (!authenticated || (signedResponse.first && signedResponse.second)) {
203 log.error("unsigned response obtained, but it must be signed.");
204 TrustException ex("Unable to obtain a signed response from artifact request.");
205 annotateException(&ex,entity); // throws it