-/*
- * The Shibboleth License, Version 1.
- * Copyright (c) 2002
- * University Corporation for Advanced Internet Development, Inc.
- * All rights reserved
+/*
+ * Copyright 2001-2005 Internet2
*
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution, if any, must include
- * the following acknowledgment: "This product includes software developed by
- * the University Corporation for Advanced Internet Development
- * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement
- * may appear in the software itself, if and wherever such third-party
- * acknowledgments normally appear.
- *
- * Neither the name of Shibboleth nor the names of its contributors, nor
- * Internet2, nor the University Corporation for Advanced Internet Development,
- * Inc., nor UCAID may be used to endorse or promote products derived from this
- * software without specific prior written permission. For written permission,
- * please contact shibboleth@shibboleth.org
- *
- * Products derived from this software may not be called Shibboleth, Internet2,
- * UCAID, or the University Corporation for Advanced Internet Development, nor
- * may Shibboleth appear in their name, without prior written permission of the
- * University Corporation for Advanced Internet Development.
- *
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
- * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK
- * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE.
- * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY
- * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
/* ArtifactMapper.cpp - a ShibTarget-aware SAML artifact->binding mapper
*/
#include "internal.h"
+#include <saml/binding/SAMLArtifact.h>
-#include <log4cpp/Category.hh>
-
-using namespace std;
-using namespace log4cpp;
-using namespace saml;
-using namespace shibboleth;
+using namespace shibsp;
using namespace shibtarget;
+using namespace saml;
+using namespace opensaml::saml2md;
+using namespace xmltooling;
+using namespace log4cpp;
+using namespace std;
-SAMLBrowserProfile::ArtifactMapper::ArtifactMapperResponse STArtifactMapper::map(const SAMLArtifact* artifact)
+SAMLResponse* STArtifactMapper::resolve(SAMLRequest* request)
{
Category& log=Category::getInstance("shibtarget.ArtifactMapper");
// First do a search for the issuer.
- const IEntityDescriptor* entity=m_metadata.lookup(artifact);
+ SAMLArtifact* artifact=request->getArtifacts().next();
+ auto_ptr<opensaml::SAMLArtifact> os2art(opensaml::SAMLArtifact::parse(artifact->encode().c_str()));
+
+ MetadataProvider* m=m_app->getMetadataProvider();
+ const EntityDescriptor* entity=m->getEntityDescriptor(os2art.get());
if (!entity) {
log.error(
"metadata lookup failed, unable to determine issuer of artifact (0x%s)",
- SAMLArtifact::toHex(artifact->getBytes()).c_str()
+ opensaml::SAMLArtifact::toHex(artifact->getBytes()).c_str()
);
- throw MetadataException("ArtifactMapper::map() metadata lookup failed, unable to determine artifact issuer");
+ throw MetadataException("Metadata lookup failed, unable to determine artifact issuer");
}
- SAMLBrowserProfile::ArtifactMapper::ArtifactMapperResponse amr;
- auto_ptr_char issuer(entity->getId());
+ xmltooling::auto_ptr_char issuer(entity->getEntityID());
log.info("lookup succeeded, artifact issued by (%s)", issuer.get());
- amr.source=issuer.get();
- const IPropertySet* credUse=m_app->getCredentialUse(entity);
+ // Sign it?
+ const PropertySet* credUse=m_app->getCredentialUse(entity);
+ pair<bool,bool> signRequest=credUse ? credUse->getBool("signRequest") : make_pair(false,false);
+ pair<bool,const char*> signatureAlg=credUse ? credUse->getString("signatureAlg") : pair<bool,const char*>(false,NULL);
+ if (!signatureAlg.first)
+ signatureAlg.second=URI_ID_RSA_SHA1;
+ pair<bool,const char*> digestAlg=credUse ? credUse->getString("digestAlg") : pair<bool,const char*>(false,NULL);
+ if (!digestAlg.first)
+ digestAlg.second=URI_ID_SHA1;
+ pair<bool,bool> signedResponse=credUse ? credUse->getBool("signedResponse") : make_pair(false,false);
+ pair<bool,const char*> signingCred=credUse ? credUse->getString("Signing") : pair<bool,const char*>(false,NULL);
+ if (signRequest.first && signRequest.second && signingCred.first) {
+ if (request->getMinorVersion()==1) {
+ shibboleth::Credentials creds(ShibTargetConfig::getConfig().getINI()->getCredentialsProviders());
+ const shibboleth::ICredResolver* cr=creds.lookup(signingCred.second);
+ if (cr)
+ request->sign(cr->getKey(),cr->getCertificates(),signatureAlg.second,digestAlg.second);
+ else
+ log.error("unable to sign artifact request, specified credential (%s) was not found",signingCred.second);
+ }
+ else
+ log.error("unable to sign SAML 1.0 artifact request, only SAML 1.1 defines signing adequately");
+ }
+
+ SAMLResponse* response = NULL;
+ bool authenticated = false;
+ static const XMLCh https[] = {chLatin_h, chLatin_t, chLatin_t, chLatin_p, chLatin_s, chColon, chNull};
// Depends on type of artifact.
const SAMLArtifactType0001* type1=dynamic_cast<const SAMLArtifactType0001*>(artifact);
if (type1) {
- // With type 01, any endpoint will do. Try SAML 1.1 first.
- const IIDPSSODescriptor* idp=entity->getIDPSSODescriptor(saml::XML::SAML11_PROTOCOL_ENUM);
+ // With type 01, any endpoint will do.
+ const IDPSSODescriptor* idp=entity->getIDPSSODescriptor(
+ request->getMinorVersion()==1 ? samlconstants::SAML11_PROTOCOL_ENUM : samlconstants::SAML10_PROTOCOL_ENUM
+ );
if (idp) {
- const IEndpointManager* mgr=idp->getArtifactResolutionServiceManager();
- Iterator<const IEndpoint*> eps=mgr ? mgr->getEndpoints() : EMPTY(const IEndpoint*);
- while (eps.hasNext()) {
- const IEndpoint* ep=eps.next();
- amr.binding = m_app->getBinding(ep->getBinding());
- if (amr.binding) {
- auto_ptr_char loc(ep->getLocation());
- amr.endpoint = loc.get();
- amr.callCtx = new ShibHTTPHook::ShibHTTPHookCallContext(credUse ? credUse->getString("TLS").second : NULL,idp);
- return amr;
+ ShibHTTPHook::ShibHTTPHookCallContext callCtx(credUse,idp);
+ const vector<ArtifactResolutionService*>& endpoints=idp->getArtifactResolutionServices();
+ for (vector<ArtifactResolutionService*>::const_iterator ep=endpoints.begin(); !response && ep!=endpoints.end(); ++ep) {
+ const SAMLBinding* binding = m_app->getBinding((*ep)->getBinding());
+ if (!binding) {
+ xmltooling::auto_ptr_char prot((*ep)->getBinding());
+ log.warn("skipping binding on unsupported protocol (%s)", prot.get());
+ continue;
}
- }
- }
-
- // No compatible 1.1 binding, try 1.0...
- idp=entity->getIDPSSODescriptor(saml::XML::SAML10_PROTOCOL_ENUM);
- if (idp) {
- const IEndpointManager* mgr=idp->getArtifactResolutionServiceManager();
- Iterator<const IEndpoint*> eps=mgr ? mgr->getEndpoints() : EMPTY(const IEndpoint*);
- while (eps.hasNext()) {
- const IEndpoint* ep=eps.next();
- amr.binding = m_app->getBinding(ep->getBinding());
- if (amr.binding) {
- auto_ptr_char loc(ep->getLocation());
- amr.endpoint = loc.get();
- amr.callCtx = new ShibHTTPHook::ShibHTTPHookCallContext(credUse ? credUse->getString("TLS").second : NULL,idp);
- return amr;
+ try {
+ response = binding->send((*ep)->getLocation(),*request,&callCtx);
+ if (log.isDebugEnabled())
+ log.debugStream() << "SAML response from artifact request:\n" << *response << CategoryStream::ENDLINE;
+
+ if (!response->getAssertions().hasNext()) {
+ delete response;
+ throw FatalProfileException("No SAML assertions returned in response to artifact profile request.");
+ }
+ authenticated = callCtx.isAuthenticated() && !XMLString::compareNString((*ep)->getLocation(),https,6);
+ }
+ catch (XMLToolingException& ex) {
+ annotateException(&ex,idp); // rethrows it
+ }
+ catch (exception& ex) {
+ opensaml::BindingException ex2(ex.what());
+ annotateException(&ex2,idp); // rethrows it
}
}
}
else {
const SAMLArtifactType0002* type2=dynamic_cast<const SAMLArtifactType0002*>(artifact);
if (type2) {
- // With type 02, we have to find the matching location. Try SAML 1.1 first.
- const IIDPSSODescriptor* idp=entity->getIDPSSODescriptor(saml::XML::SAML11_PROTOCOL_ENUM);
- if (idp) {
- const IEndpointManager* mgr=idp->getArtifactResolutionServiceManager();
- Iterator<const IEndpoint*> eps=mgr ? mgr->getEndpoints() : EMPTY(const IEndpoint*);
- while (eps.hasNext()) {
- const IEndpoint* ep=eps.next();
- auto_ptr_char loc(ep->getLocation());
- if (!strcmp(loc.get(),type2->getSourceLocation())) {
- amr.binding = m_app->getBinding(ep->getBinding());
- if (amr.binding) {
- amr.endpoint = loc.get();
- amr.callCtx = new ShibHTTPHook::ShibHTTPHookCallContext(credUse ? credUse->getString("TLS").second : NULL,idp);
- return amr;
- }
- }
- }
- }
-
- // No match for 1.1, try 1.0...
- idp=entity->getIDPSSODescriptor(saml::XML::SAML10_PROTOCOL_ENUM);
+ // With type 02, we have to find the matching location.
+ const IDPSSODescriptor* idp=entity->getIDPSSODescriptor(
+ request->getMinorVersion()==1 ? samlconstants::SAML11_PROTOCOL_ENUM : samlconstants::SAML10_PROTOCOL_ENUM
+ );
if (idp) {
- const IEndpointManager* mgr=idp->getArtifactResolutionServiceManager();
- Iterator<const IEndpoint*> eps=mgr ? mgr->getEndpoints() : EMPTY(const IEndpoint*);
- while (eps.hasNext()) {
- const IEndpoint* ep=eps.next();
- auto_ptr_char loc(ep->getLocation());
- if (!strcmp(loc.get(),type2->getSourceLocation())) {
- amr.binding = m_app->getBinding(ep->getBinding());
- if (amr.binding) {
- amr.endpoint = loc.get();
- amr.callCtx = new ShibHTTPHook::ShibHTTPHookCallContext(credUse ? credUse->getString("TLS").second : NULL,idp);
- return amr;
- }
+ ShibHTTPHook::ShibHTTPHookCallContext callCtx(credUse,idp);
+ const vector<ArtifactResolutionService*>& endpoints=idp->getArtifactResolutionServices();
+ for (vector<ArtifactResolutionService*>::const_iterator ep=endpoints.begin(); !response && ep!=endpoints.end(); ++ep) {
+ xmltooling::auto_ptr_char loc((*ep)->getLocation());
+ if (strcmp(loc.get(),type2->getSourceLocation()))
+ continue;
+ const SAMLBinding* binding = m_app->getBinding((*ep)->getBinding());
+ if (!binding) {
+ xmltooling::auto_ptr_char prot((*ep)->getBinding());
+ log.warn("skipping binding on unsupported protocol (%s)", prot.get());
+ continue;
+ }
+ try {
+ response = binding->send((*ep)->getLocation(),*request,&callCtx);
+ if (log.isDebugEnabled())
+ log.debugStream() << "SAML response from artifact request:\n" << *response << CategoryStream::ENDLINE;
+
+ if (!response->getAssertions().hasNext()) {
+ delete response;
+ throw FatalProfileException("No SAML assertions returned in response to artifact profile request.");
+ }
+ authenticated = callCtx.isAuthenticated() && !XMLString::compareNString((*ep)->getLocation(),https,6);
+ }
+ catch (XMLToolingException& ex) {
+ annotateException(&ex,idp); // rethrows it
+ }
+ catch (exception& ex) {
+ opensaml::BindingException ex2(ex.what());
+ annotateException(&ex2,idp); // rethrows it
}
}
}
}
else {
log.error("unrecognized artifact type (0x%s)", SAMLArtifact::toHex(artifact->getTypeCode()).c_str());
- throw UnsupportedExtensionException(
- string("ArtifactMapper::map() unrecognized artifact type (0x") + SAMLArtifact::toHex(artifact->getTypeCode()) + ")"
+ throw xmltooling::UnknownExtensionException(
+ string("Received unrecognized artifact type (0x") + SAMLArtifact::toHex(artifact->getTypeCode()) + ")"
);
}
}
- log.error("unable to locate acceptable binding endpoint to resolve artifact");
- MetadataException ex("Unable to locate acceptable binding endpoint to resolve artifact.");
- annotateException(ex,entity,false);
- throw ex;
+ if (!response) {
+ log.error("unable to locate acceptable binding/endpoint to resolve artifact");
+ MetadataException ex("Unable to locate acceptable binding/endpoint to resolve artifact.");
+ annotateException(&ex,entity); // throws it
+ }
+ else if (!response->isSigned()) {
+ if (!authenticated || (signedResponse.first && signedResponse.second)) {
+ log.error("unsigned response obtained, but it must be signed.");
+ XMLSecurityException ex("Unable to obtain a signed response from artifact request.");
+ annotateException(&ex,entity); // throws it
+ }
+ }
+
+ return response;
}