X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=shibsp%2Fhandler%2Fimpl%2FMetadataGenerator.cpp;h=b3f9720e21c8bb76d51a29c580b8e161cc2331fc;hb=81b2666016f1ddccdc6d45a78716415677d332ed;hp=092d77b43676aaa385e54d1846eb012bb23b3854;hpb=2410d03c6dac845cbd893cb7966998e1b703d884;p=shibboleth%2Fsp.git diff --git a/shibsp/handler/impl/MetadataGenerator.cpp b/shibsp/handler/impl/MetadataGenerator.cpp index 092d77b..b3f9720 100644 --- a/shibsp/handler/impl/MetadataGenerator.cpp +++ b/shibsp/handler/impl/MetadataGenerator.cpp @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * Copyright 2001-2010 Internet2 + * * 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 @@ -16,7 +16,7 @@ /** * MetadataGenerator.cpp - * + * * Handler for generating "approximate" metadata based on SP configuration. */ @@ -24,11 +24,28 @@ #include "Application.h" #include "exceptions.h" #include "ServiceProvider.h" +#include "SPRequest.h" #include "handler/AbstractHandler.h" #include "handler/RemotedHandler.h" -#include -#include +#ifndef SHIBSP_LITE +# include "metadata/MetadataProviderCriteria.h" +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + using namespace shibsp; #ifndef SHIBSP_LITE @@ -49,7 +66,12 @@ namespace shibsp { class SHIBSP_DLLLOCAL Blocker : public DOMNodeFilter { public: - short acceptNode(const DOMNode* node) const { +#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE + short +#else + FilterAction +#endif + acceptNode(const DOMNode* node) const { return FILTER_REJECT; } }; @@ -66,10 +88,17 @@ namespace shibsp { void receive(DDF& in, ostream& out); private: - pair processMessage(const Application& application, const char* handlerURL, HTTPResponse& httpResponse) const; + pair processMessage( + const Application& application, + const char* handlerURL, + const char* entityID, + HTTPResponse& httpResponse + ) const; set m_acl; #ifndef SHIBSP_LITE + string m_salt; + short m_http,m_https; vector m_bases; #endif }; @@ -87,6 +116,9 @@ namespace shibsp { MetadataGenerator::MetadataGenerator(const DOMElement* e, const char* appId) : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".MetadataGenerator"), &g_Blocker) +#ifndef SHIBSP_LITE + ,m_http(0), m_https(0) +#endif { string address(appId); address += getString("Location").second; @@ -108,6 +140,18 @@ MetadataGenerator::MetadataGenerator(const DOMElement* e, const char* appId) #ifndef SHIBSP_LITE static XMLCh EndpointBase[] = UNICODE_LITERAL_12(E,n,d,p,o,i,n,t,B,a,s,e); + + pair salt = getString("salt"); + if (salt.first) + m_salt = salt.second; + + pair flag = getBool("http"); + if (flag.first) + m_http = flag.second ? 1 : -1; + flag = getBool("https"); + if (flag.first) + m_https = flag.second ? 1 : -1; + e = XMLHelper::getFirstChildElement(e, EndpointBase); while (e) { if (e->hasChildNodes()) { @@ -130,19 +174,21 @@ pair MetadataGenerator::run(SPRequest& request, bool isHandler) const return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_FORBIDDEN)); } } - + try { if (conf.isEnabled(SPConfig::OutOfProcess)) { // When out of process, we run natively and directly process the message. - return processMessage(request.getApplication(), request.getHandlerURL(), request); + return processMessage(request.getApplication(), request.getHandlerURL(), request.getParameter("entityID"), request); } else { // When not out of process, we remote all the message processing. DDF out,in = DDF(m_address.c_str()); in.addmember("application_id").string(request.getApplication().getId()); in.addmember("handler_url").string(request.getHandlerURL()); + if (request.getParameter("entityID")) + in.addmember("entity_id").string(request.getParameter("entityID")); DDFJanitor jin(in), jout(out); - + out=request.getServiceProvider().getListenerService()->send(in); return unwrap(request, out); } @@ -159,7 +205,7 @@ void MetadataGenerator::receive(DDF& in, ostream& out) // Find application. const char* aid=in["application_id"].string(); const char* hurl=in["handler_url"].string(); - const Application* app=aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : NULL; + const Application* app=aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : nullptr; if (!app) { // Something's horribly wrong. m_log.error("couldn't find application (%s) for metadata request", aid ? aid : "(missing)"); @@ -168,47 +214,75 @@ void MetadataGenerator::receive(DDF& in, ostream& out) else if (!hurl) { throw ConfigurationException("Missing handler_url parameter in remoted method call."); } - + // Wrap a response shim. - DDF ret(NULL); + DDF ret(nullptr); DDFJanitor jout(ret); auto_ptr resp(getResponse(ret)); - + // Since we're remoted, the result should either be a throw, a false/0 return, // which we just return as an empty structure, or a response/redirect, // which we capture in the facade and send back. - processMessage(*app, hurl, *resp.get()); + processMessage(*app, hurl, in["entity_id"].string(), *resp.get()); out << ret; } -pair MetadataGenerator::processMessage(const Application& application, const char* handlerURL, HTTPResponse& httpResponse) const +pair MetadataGenerator::processMessage( + const Application& application, const char* handlerURL, const char* entityID, HTTPResponse& httpResponse + ) const { #ifndef SHIBSP_LITE m_log.debug("processing metadata request"); + const PropertySet* relyingParty=nullptr; + if (entityID) { + MetadataProvider* m=application.getMetadataProvider(); + Locker locker(m); + MetadataProviderCriteria mc(application, entityID); + relyingParty = application.getRelyingParty(m->getEntityDescriptor(mc).first); + } + else { + relyingParty = &application; + } + EntityDescriptor* entity; pair prop = getString("template"); if (prop.first) { // Load a template to use for our metadata. - LocalFileInputSource src(getXMLString("template").second); + string templ(prop.second); + XMLToolingConfig::getConfig().getPathResolver()->resolve(templ, PathResolver::XMLTOOLING_CFG_FILE); + auto_ptr_XMLCh widenit(templ.c_str()); + LocalFileInputSource src(widenit.get()); Wrapper4InputSource dsrc(&src,false); DOMDocument* doc=XMLToolingConfig::getConfig().getParser().parse(dsrc); XercesJanitor docjan(doc); auto_ptr xmlobj(XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(), true)); + docjan.release(); entity = dynamic_cast(xmlobj.get()); if (!entity) - throw ConfigurationException("Template file ($1) did not contain an EntityDescriptor", params(1, prop.second)); + throw ConfigurationException("Template file ($1) did not contain an EntityDescriptor", params(1, templ.c_str())); xmlobj.release(); } else { entity = EntityDescriptorBuilder::buildEntityDescriptor(); } + if (!entity->getID()) { + string hashinput = m_salt + relyingParty->getString("entityID").second; + string hashed = '_' + SecurityHelper::doHash("SHA1", hashinput.c_str(), hashinput.length()); + auto_ptr_XMLCh widenit(hashed.c_str()); + entity->setID(widenit.get()); + } + auto_ptr wrapper(entity); pair cache = getUnsignedInt("cacheDuration"); + if (cache.first) { + entity->setCacheDuration(cache.second); + } + cache = getUnsignedInt("validUntil"); if (cache.first) - entity->setValidUntil(time(NULL) + cache.second); - entity->setEntityID(application.getXMLString("entityID").second); + entity->setValidUntil(time(nullptr) + cache.second); + entity->setEntityID(relyingParty->getXMLString("entityID").second); SPSSODescriptor* role; if (entity->getSPSSODescriptors().empty()) { @@ -220,19 +294,36 @@ pair MetadataGenerator::processMessage(const Application& application } // Policy flags. - prop = application.getRelyingParty(NULL)->getString("signing"); + prop = relyingParty->getString("signing"); if (prop.first && (!strcmp(prop.second,"true") || !strcmp(prop.second,"front"))) role->AuthnRequestsSigned(true); - pair flagprop = - application.getServiceProvider().getPolicySettings(application.getString("policyId").second)->getBool("signedAssertions"); + pair flagprop = relyingParty->getBool("requireSignedAssertions"); if (flagprop.first && flagprop.second) role->WantAssertionsSigned(true); + // Ask each handler to generate itself. vector handlers; application.getHandlers(handlers); for (vector::const_iterator h = handlers.begin(); h != handlers.end(); ++h) { if (m_bases.empty()) { - (*h)->generateMetadata(*role, handlerURL); + if (strncmp(handlerURL, "https", 5) == 0) { + if (m_https >= 0) + (*h)->generateMetadata(*role, handlerURL); + if (m_http == 1) { + string temp(handlerURL); + temp.erase(4, 1); + (*h)->generateMetadata(*role, temp.c_str()); + } + } + else { + if (m_http >= 0) + (*h)->generateMetadata(*role, handlerURL); + if (m_https == 1) { + string temp(handlerURL); + temp.insert(temp.begin() + 4, 's'); + (*h)->generateMetadata(*role, temp.c_str()); + } + } } else { for (vector::const_iterator b = m_bases.begin(); b != m_bases.end(); ++b) @@ -244,23 +335,34 @@ pair MetadataGenerator::processMessage(const Application& application if (credResolver) { Locker credLocker(credResolver); CredentialCriteria cc; - cc.setUsage(CredentialCriteria::SIGNING_CREDENTIAL); - vector creds; - credResolver->resolve(creds,&cc); - for (vector::const_iterator c = creds.begin(); c != creds.end(); ++c) { + prop = relyingParty->getString("keyName"); + if (prop.first) + cc.getKeyNames().insert(prop.second); + vector signingcreds,enccreds; + cc.setUsage(Credential::SIGNING_CREDENTIAL); + credResolver->resolve(signingcreds, &cc); + cc.setUsage(Credential::ENCRYPTION_CREDENTIAL); + credResolver->resolve(enccreds, &cc); + + for (vector::const_iterator c = signingcreds.begin(); c != signingcreds.end(); ++c) { KeyInfo* kinfo = (*c)->getKeyInfo(); if (kinfo) { KeyDescriptor* kd = KeyDescriptorBuilder::buildKeyDescriptor(); - kd->setUse(KeyDescriptor::KEYTYPE_SIGNING); kd->setKeyInfo(kinfo); + const XMLCh* use = KeyDescriptor::KEYTYPE_SIGNING; + for (vector::iterator match = enccreds.begin(); match != enccreds.end(); ++match) { + if (*match == *c) { + use = nullptr; + enccreds.erase(match); + break; + } + } + kd->setUse(use); role->getKeyDescriptors().push_back(kd); } } - cc.setUsage(CredentialCriteria::ENCRYPTION_CREDENTIAL); - creds.clear(); - credResolver->resolve(creds,&cc); - for (vector::const_iterator c = creds.begin(); c != creds.end(); ++c) { + for (vector::const_iterator c = enccreds.begin(); c != enccreds.end(); ++c) { KeyInfo* kinfo = (*c)->getKeyInfo(); if (kinfo) { KeyDescriptor* kd = KeyDescriptorBuilder::buildKeyDescriptor(); @@ -271,6 +373,9 @@ pair MetadataGenerator::processMessage(const Application& application } } + // Stream for response. + stringstream s; + // Self-sign it? pair flag = getBool("signing"); if (flag.first && flag.second) { @@ -278,7 +383,7 @@ pair MetadataGenerator::processMessage(const Application& application Locker credLocker(credResolver); // Fill in criteria to use. CredentialCriteria cc; - cc.setUsage(CredentialCriteria::SIGNING_CREDENTIAL); + cc.setUsage(Credential::SIGNING_CREDENTIAL); prop = getString("keyName"); if (prop.first) cc.getKeyNames().insert(prop.second); @@ -289,8 +394,15 @@ pair MetadataGenerator::processMessage(const Application& application const Credential* cred = credResolver->resolve(&cc); if (!cred) throw XMLSecurityException("Unable to obtain signing credential to use."); + + // Pretty-print it first and then read it back in. + stringstream pretty; + XMLHelper::serialize(entity->marshall(), pretty, true); + DOMDocument* prettydoc = XMLToolingConfig::getConfig().getParser().parse(pretty); + auto_ptr prettyentity(XMLObjectBuilder::buildOneFromElement(prettydoc->getDocumentElement(), true)); + Signature* sig = SignatureBuilder::buildSignature(); - entity->setSignature(sig); + dynamic_cast(prettyentity.get())->setSignature(sig); if (sigalg.first) sig->setSignatureAlgorithm(sigalg.second); if (digalg.first) { @@ -298,18 +410,25 @@ pair MetadataGenerator::processMessage(const Application& application if (cr) cr->setDigestAlgorithm(digalg.second); } - - // Sign response while marshalling. + + // Sign while marshalling. vector sigs(1,sig); - entity->marshall((DOMDocument*)NULL,&sigs,cred); + prettyentity->marshall(prettydoc,&sigs,cred); + s << *prettyentity; } + else { + throw FatalProfileException("Can't self-sign metadata, no credential resolver found."); + } + } + else { + // Pretty-print it directly to client. + XMLHelper::serialize(entity->marshall(), s, true); } - stringstream s; - s << *entity; - httpResponse.setContentType("application/samlmetadata+xml"); + prop = getString("mimeType"); + httpResponse.setContentType(prop.first ? prop.second : "application/samlmetadata+xml"); return make_pair(true, httpResponse.sendResponse(s)); #else - return make_pair(false,0); + return make_pair(false,0L); #endif }