2 * Copyright 2001-2007 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.
17 /* ArtifactMapper.cpp - a ShibTarget-aware SAML artifact->binding mapper
26 #include <shibsp/SPConfig.h>
27 #include <saml/binding/SAMLArtifact.h>
29 using namespace shibsp;
30 using namespace shibtarget;
32 using namespace opensaml::saml2md;
33 using namespace xmltooling;
34 using namespace log4cpp;
36 using xmlsignature::CredentialResolver;
38 SAMLResponse* STArtifactMapper::resolve(SAMLRequest* request)
40 Category& log=Category::getInstance("shibtarget.ArtifactMapper");
42 // First do a search for the issuer.
43 SAMLArtifact* artifact=request->getArtifacts().next();
44 auto_ptr<opensaml::SAMLArtifact> os2art(opensaml::SAMLArtifact::parse(artifact->encode().c_str()));
46 MetadataProvider* m=m_app->getMetadataProvider();
47 const EntityDescriptor* entity=m->getEntityDescriptor(os2art.get());
50 "metadata lookup failed, unable to determine issuer of artifact (0x%s)",
51 opensaml::SAMLArtifact::toHex(artifact->getBytes()).c_str()
53 throw MetadataException("Metadata lookup failed, unable to determine artifact issuer");
56 xmltooling::auto_ptr_char issuer(entity->getEntityID());
57 log.info("lookup succeeded, artifact issued by (%s)", issuer.get());
60 const PropertySet* credUse=m_app->getCredentialUse(entity);
61 pair<bool,bool> signRequest=credUse ? credUse->getBool("signRequest") : make_pair(false,false);
62 pair<bool,const char*> signatureAlg=credUse ? credUse->getString("signatureAlg") : pair<bool,const char*>(false,NULL);
63 if (!signatureAlg.first)
64 signatureAlg.second=URI_ID_RSA_SHA1;
65 pair<bool,const char*> digestAlg=credUse ? credUse->getString("digestAlg") : pair<bool,const char*>(false,NULL);
67 digestAlg.second=URI_ID_SHA1;
68 pair<bool,bool> signedResponse=credUse ? credUse->getBool("signedResponse") : make_pair(false,false);
69 pair<bool,const char*> signingCred=credUse ? credUse->getString("Signing") : pair<bool,const char*>(false,NULL);
70 if (signRequest.first && signRequest.second && signingCred.first) {
71 if (request->getMinorVersion()==1) {
72 CredentialResolver* cr=SPConfig::getConfig().getServiceProvider()->getCredentialResolver(signingCred.second);
74 xmltooling::Locker locker(cr);
75 request->sign(cr->getKey(),cr->getCertificates(),signatureAlg.second,digestAlg.second);
78 log.error("unable to sign artifact request, specified credential (%s) was not found",signingCred.second);
81 log.error("unable to sign SAML 1.0 artifact request, only SAML 1.1 defines signing adequately");
84 SAMLResponse* response = NULL;
85 bool authenticated = false;
86 static const XMLCh https[] = {chLatin_h, chLatin_t, chLatin_t, chLatin_p, chLatin_s, chColon, chNull};
88 // Depends on type of artifact.
89 const SAMLArtifactType0001* type1=dynamic_cast<const SAMLArtifactType0001*>(artifact);
91 // With type 01, any endpoint will do.
92 const IDPSSODescriptor* idp=entity->getIDPSSODescriptor(
93 request->getMinorVersion()==1 ? samlconstants::SAML11_PROTOCOL_ENUM : samlconstants::SAML10_PROTOCOL_ENUM
96 ShibHTTPHook::ShibHTTPHookCallContext callCtx(credUse,idp);
97 const vector<ArtifactResolutionService*>& endpoints=idp->getArtifactResolutionServices();
98 for (vector<ArtifactResolutionService*>::const_iterator ep=endpoints.begin(); !response && ep!=endpoints.end(); ++ep) {
99 const SAMLBinding* binding = m_app->getBinding((*ep)->getBinding());
101 xmltooling::auto_ptr_char prot((*ep)->getBinding());
102 log.warn("skipping binding on unsupported protocol (%s)", prot.get());
106 response = binding->send((*ep)->getLocation(),*request,&callCtx);
107 if (log.isDebugEnabled())
108 log.debugStream() << "SAML response from artifact request:\n" << *response << CategoryStream::ENDLINE;
110 if (!response->getAssertions().hasNext()) {
112 throw FatalProfileException("No SAML assertions returned in response to artifact profile request.");
114 authenticated = callCtx.isAuthenticated() && !XMLString::compareNString((*ep)->getLocation(),https,6);
116 catch (XMLToolingException& ex) {
117 annotateException(&ex,idp); // rethrows it
119 catch (exception& ex) {
120 opensaml::BindingException ex2(ex.what());
121 annotateException(&ex2,idp); // rethrows it
127 const SAMLArtifactType0002* type2=dynamic_cast<const SAMLArtifactType0002*>(artifact);
129 // With type 02, we have to find the matching location.
130 const IDPSSODescriptor* idp=entity->getIDPSSODescriptor(
131 request->getMinorVersion()==1 ? samlconstants::SAML11_PROTOCOL_ENUM : samlconstants::SAML10_PROTOCOL_ENUM
134 ShibHTTPHook::ShibHTTPHookCallContext callCtx(credUse,idp);
135 const vector<ArtifactResolutionService*>& endpoints=idp->getArtifactResolutionServices();
136 for (vector<ArtifactResolutionService*>::const_iterator ep=endpoints.begin(); !response && ep!=endpoints.end(); ++ep) {
137 xmltooling::auto_ptr_char loc((*ep)->getLocation());
138 if (strcmp(loc.get(),type2->getSourceLocation()))
140 const SAMLBinding* binding = m_app->getBinding((*ep)->getBinding());
142 xmltooling::auto_ptr_char prot((*ep)->getBinding());
143 log.warn("skipping binding on unsupported protocol (%s)", prot.get());
147 response = binding->send((*ep)->getLocation(),*request,&callCtx);
148 if (log.isDebugEnabled())
149 log.debugStream() << "SAML response from artifact request:\n" << *response << CategoryStream::ENDLINE;
151 if (!response->getAssertions().hasNext()) {
153 throw FatalProfileException("No SAML assertions returned in response to artifact profile request.");
155 authenticated = callCtx.isAuthenticated() && !XMLString::compareNString((*ep)->getLocation(),https,6);
157 catch (XMLToolingException& ex) {
158 annotateException(&ex,idp); // rethrows it
160 catch (exception& ex) {
161 opensaml::BindingException ex2(ex.what());
162 annotateException(&ex2,idp); // rethrows it
168 log.error("unrecognized artifact type (0x%s)", SAMLArtifact::toHex(artifact->getTypeCode()).c_str());
169 throw xmltooling::UnknownExtensionException(
170 string("Received unrecognized artifact type (0x") + SAMLArtifact::toHex(artifact->getTypeCode()) + ")"
176 log.error("unable to locate acceptable binding/endpoint to resolve artifact");
177 MetadataException ex("Unable to locate acceptable binding/endpoint to resolve artifact.");
178 annotateException(&ex,entity); // throws it
180 else if (!response->isSigned()) {
181 if (!authenticated || (signedResponse.first && signedResponse.second)) {
182 log.error("unsigned response obtained, but it must be signed.");
183 XMLSecurityException ex("Unable to obtain a signed response from artifact request.");
184 annotateException(&ex,entity); // throws it