02e1b0640c118b142815b8dea24f6483da9cdd9a
[shibboleth/cpp-sp.git] / shib-target / ArtifactMapper.cpp
1 /*
2  *  Copyright 2001-2005 Internet2
3  * 
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /* ArtifactMapper.cpp - a ShibTarget-aware SAML artifact->binding mapper
18
19    Scott Cantor
20    2/20/05
21
22    $History:$
23 */
24
25 #include "internal.h"
26
27 using namespace shibsp;
28 using namespace shibtarget;
29 using namespace shibboleth;
30 using namespace saml;
31 using namespace log4cpp;
32 using namespace std;
33
34 SAMLResponse* STArtifactMapper::resolve(SAMLRequest* request)
35 {
36     Category& log=Category::getInstance("shibtarget.ArtifactMapper");
37     
38     // First do a search for the issuer.
39     SAMLArtifact* artifact=request->getArtifacts().next();
40     Metadata m(m_app->getMetadataProviders());
41     const IEntityDescriptor* entity=m.lookup(artifact);
42     if (!entity) {
43         log.error(
44             "metadata lookup failed, unable to determine issuer of artifact (0x%s)",
45             SAMLArtifact::toHex(artifact->getBytes()).c_str()
46             );
47         throw MetadataException("Metadata lookup failed, unable to determine artifact issuer");
48     }
49     
50     auto_ptr_char issuer(entity->getId());
51     log.info("lookup succeeded, artifact issued by (%s)", issuer.get());
52     
53     // Sign it?
54     const PropertySet* credUse=m_app->getCredentialUse(entity);
55     pair<bool,bool> signRequest=credUse ? credUse->getBool("signRequest") : make_pair(false,false);
56     pair<bool,const char*> signatureAlg=credUse ? credUse->getString("signatureAlg") : pair<bool,const char*>(false,NULL);
57     if (!signatureAlg.first)
58         signatureAlg.second=URI_ID_RSA_SHA1;
59     pair<bool,const char*> digestAlg=credUse ? credUse->getString("digestAlg") : pair<bool,const char*>(false,NULL);
60     if (!digestAlg.first)
61         digestAlg.second=URI_ID_SHA1;
62     pair<bool,bool> signedResponse=credUse ? credUse->getBool("signedResponse") : make_pair(false,false);
63     pair<bool,const char*> signingCred=credUse ? credUse->getString("Signing") : pair<bool,const char*>(false,NULL);
64     if (signRequest.first && signRequest.second && signingCred.first) {
65         if (request->getMinorVersion()==1) {
66             Credentials creds(ShibTargetConfig::getConfig().getINI()->getCredentialsProviders());
67             const ICredResolver* cr=creds.lookup(signingCred.second);
68             if (cr)
69                 request->sign(cr->getKey(),cr->getCertificates(),signatureAlg.second,digestAlg.second);
70             else
71                 log.error("unable to sign artifact request, specified credential (%s) was not found",signingCred.second);
72         }
73         else
74             log.error("unable to sign SAML 1.0 artifact request, only SAML 1.1 defines signing adequately");
75     }
76
77         SAMLResponse* response = NULL;
78         bool authenticated = false;
79     static const XMLCh https[] = {chLatin_h, chLatin_t, chLatin_t, chLatin_p, chLatin_s, chColon, chNull};
80
81     // Depends on type of artifact.
82     const SAMLArtifactType0001* type1=dynamic_cast<const SAMLArtifactType0001*>(artifact);
83     if (type1) {
84         // With type 01, any endpoint will do.
85         const IIDPSSODescriptor* idp=entity->getIDPSSODescriptor(
86             request->getMinorVersion()==1 ? saml::XML::SAML11_PROTOCOL_ENUM : saml::XML::SAML10_PROTOCOL_ENUM
87             );
88         if (idp) {
89                     ShibHTTPHook::ShibHTTPHookCallContext callCtx(credUse,idp);
90             const IEndpointManager* mgr=idp->getArtifactResolutionServiceManager();
91             Iterator<const IEndpoint*> eps=mgr ? mgr->getEndpoints() : EMPTY(const IEndpoint*);
92             while (!response && eps.hasNext()) {
93                 const IEndpoint* ep=eps.next();
94                 const SAMLBinding* binding = m_app->getBinding(ep->getBinding());
95                 if (!binding) {
96                     auto_ptr_char prot(ep->getBinding());
97                     log.warn("skipping binding on unsupported protocol (%s)", prot.get());
98                     continue;
99                 }
100                         try {
101                             response = binding->send(ep->getLocation(),*request,&callCtx);
102                             if (log.isDebugEnabled())
103                                 log.debugStream() << "SAML response from artifact request:\n" << *response << CategoryStream::ENDLINE;
104                             
105                             if (!response->getAssertions().hasNext()) {
106                                 delete response;
107                                 throw FatalProfileException("No SAML assertions returned in response to artifact profile request.");
108                             }
109                             authenticated = callCtx.isAuthenticated() && !XMLString::compareNString(ep->getLocation(),https,6);
110                         }
111                         catch (SAMLException& ex) {
112                                 annotateException(&ex,idp); // rethrows it
113                         }
114             }
115         }
116     }
117     else {
118         const SAMLArtifactType0002* type2=dynamic_cast<const SAMLArtifactType0002*>(artifact);
119         if (type2) {
120             // With type 02, we have to find the matching location.
121             const IIDPSSODescriptor* idp=entity->getIDPSSODescriptor(
122                 request->getMinorVersion()==1 ? saml::XML::SAML11_PROTOCOL_ENUM : saml::XML::SAML10_PROTOCOL_ENUM
123                 );
124             if (idp) {
125                     ShibHTTPHook::ShibHTTPHookCallContext callCtx(credUse,idp);
126                 const IEndpointManager* mgr=idp->getArtifactResolutionServiceManager();
127                 Iterator<const IEndpoint*> eps=mgr ? mgr->getEndpoints() : EMPTY(const IEndpoint*);
128                 while (eps.hasNext()) {
129                     const IEndpoint* ep=eps.next();
130                     auto_ptr_char loc(ep->getLocation());
131                     if (strcmp(loc.get(),type2->getSourceLocation()))
132                         continue;
133                         const SAMLBinding* binding = m_app->getBinding(ep->getBinding());
134                         if (!binding) {
135                             auto_ptr_char prot(ep->getBinding());
136                             log.warn("skipping binding on unsupported protocol (%s)", prot.get());
137                             continue;
138                         }
139                                 try {
140                                     response = binding->send(ep->getLocation(),*request,&callCtx);
141                                     if (log.isDebugEnabled())
142                                         log.debugStream() << "SAML response from artifact request:\n" << *response << CategoryStream::ENDLINE;
143                                     
144                                     if (!response->getAssertions().hasNext()) {
145                                         delete response;
146                                         throw FatalProfileException("No SAML assertions returned in response to artifact profile request.");
147                                     }
148                         authenticated = callCtx.isAuthenticated() && !XMLString::compareNString(ep->getLocation(),https,6);
149                                 }
150                                 catch (SAMLException& ex) {
151                                         annotateException(&ex,idp); // rethrows it
152                                 }
153                 }
154             }
155         }
156         else {
157             log.error("unrecognized artifact type (0x%s)", SAMLArtifact::toHex(artifact->getTypeCode()).c_str());
158             throw UnsupportedExtensionException(
159                 string("Received unrecognized artifact type (0x") + SAMLArtifact::toHex(artifact->getTypeCode()) + ")"
160                 );
161         }
162     }
163     
164     if (!response) {
165             log.error("unable to locate acceptable binding/endpoint to resolve artifact");
166             MetadataException ex("Unable to locate acceptable binding/endpoint to resolve artifact.");
167             annotateException(&ex,entity); // throws it
168     }
169     else if (!response->isSigned()) {
170         if (!authenticated || (signedResponse.first && signedResponse.second)) {
171                 log.error("unsigned response obtained, but it must be signed.");
172                 TrustException ex("Unable to obtain a signed response from artifact request.");
173                     annotateException(&ex,entity); // throws it
174         }
175     }
176     
177     return response;
178 }