55e5f6985aa2e86b14788c700ccbe9607e7226ab
[shibboleth/sp.git] / shib-target / ArtifactMapper.cpp
1 /* 
2  * The Shibboleth License, Version 1. 
3  * Copyright (c) 2002 
4  * University Corporation for Advanced Internet Development, Inc. 
5  * All rights reserved
6  * 
7  * 
8  * Redistribution and use in source and binary forms, with or without 
9  * modification, are permitted provided that the following conditions are met:
10  * 
11  * Redistributions of source code must retain the above copyright notice, this 
12  * list of conditions and the following disclaimer.
13  * 
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.
22  * 
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
28  * 
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.
33  * 
34  * 
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.
48  */
49
50 /* ArtifactMapper.cpp - a ShibTarget-aware SAML artifact->binding mapper
51
52    Scott Cantor
53    2/20/05
54
55    $History:$
56 */
57
58 #include "internal.h"
59
60 using namespace std;
61 using namespace log4cpp;
62 using namespace saml;
63 using namespace shibboleth;
64 using namespace shibtarget;
65
66 SAMLResponse* STArtifactMapper::resolve(SAMLRequest* request)
67 {
68     Category& log=Category::getInstance("shibtarget.ArtifactMapper");
69     
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);
74     if (!entity) {
75         log.error(
76             "metadata lookup failed, unable to determine issuer of artifact (0x%s)",
77             SAMLArtifact::toHex(artifact->getBytes()).c_str()
78             );
79         throw MetadataException("Metadata lookup failed, unable to determine artifact issuer");
80     }
81     
82     auto_ptr_char issuer(entity->getId());
83     log.info("lookup succeeded, artifact issued by (%s)", issuer.get());
84     
85     // Sign it?
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);
92     if (!digestAlg.first)
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);
100             if (cr)
101                 request->sign(cr->getKey(),cr->getCertificates(),signatureAlg.second,digestAlg.second);
102             else
103                 log.error("unable to sign artifact request, specified credential (%s) was not found",signingCred.second);
104         }
105         else
106             log.error("unable to sign SAML 1.0 artifact request, only SAML 1.1 defines signing adequately");
107     }
108
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};
112
113     // Depends on type of artifact.
114     const SAMLArtifactType0001* type1=dynamic_cast<const SAMLArtifactType0001*>(artifact);
115     if (type1) {
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
119             );
120         if (idp) {
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());
127                 if (!binding) {
128                     auto_ptr_char prot(ep->getBinding());
129                     log.warn("skipping binding on unsupported protocol (%s)", prot.get());
130                     continue;
131                 }
132                         try {
133                             response = binding->send(ep->getLocation(),*request,&callCtx);
134                             if (log.isDebugEnabled())
135                                 log.debugStream() << "SAML response from artifact request:\n" << *response << CategoryStream::ENDLINE;
136                             
137                             if (!response->getAssertions().hasNext()) {
138                                 delete response;
139                                 throw FatalProfileException("No SAML assertions returned in response to artifact profile request.");
140                             }
141                             authenticated = callCtx.isAuthenticated() && !XMLString::compareNString(ep->getLocation(),https,6);
142                         }
143                         catch (SAMLException& ex) {
144                                 annotateException(&ex,idp); // rethrows it
145                         }
146             }
147         }
148     }
149     else {
150         const SAMLArtifactType0002* type2=dynamic_cast<const SAMLArtifactType0002*>(artifact);
151         if (type2) {
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
155                 );
156             if (idp) {
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()))
164                         continue;
165                         const SAMLBinding* binding = m_app->getBinding(ep->getBinding());
166                         if (!binding) {
167                             auto_ptr_char prot(ep->getBinding());
168                             log.warn("skipping binding on unsupported protocol (%s)", prot.get());
169                             continue;
170                         }
171                                 try {
172                                     response = binding->send(ep->getLocation(),*request,&callCtx);
173                                     if (log.isDebugEnabled())
174                                         log.debugStream() << "SAML response from artifact request:\n" << *response << CategoryStream::ENDLINE;
175                                     
176                                     if (!response->getAssertions().hasNext()) {
177                                         delete response;
178                                         throw FatalProfileException("No SAML assertions returned in response to artifact profile request.");
179                                     }
180                         authenticated = callCtx.isAuthenticated() && !XMLString::compareNString(ep->getLocation(),https,6);
181                                 }
182                                 catch (SAMLException& ex) {
183                                         annotateException(&ex,idp); // rethrows it
184                                 }
185                 }
186             }
187         }
188         else {
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()) + ")"
192                 );
193         }
194     }
195     
196     if (!response) {
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
200     }
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
206         }
207     }
208     
209     return response;
210 }