X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=shibsp%2Fhandler%2Fimpl%2FMetadataGenerator.cpp;h=7cafe7d48cd9608bad7dde83c9171eb4b15d2534;hb=a0af8ed86ab481af4605daf5cf837fdaa4544bdd;hp=ca5c9263338b018dfaaa3b7db71589b9c6f0cee1;hpb=83a02e10dec5f273b3ae0042bfc35470cc664a24;p=shibboleth%2Fsp.git diff --git a/shibsp/handler/impl/MetadataGenerator.cpp b/shibsp/handler/impl/MetadataGenerator.cpp index ca5c926..7cafe7d 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,29 @@ #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 "attribute/resolver/AttributeExtractor.h" +# 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 +67,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; } }; @@ -60,17 +83,41 @@ namespace shibsp { { public: MetadataGenerator(const DOMElement* e, const char* appId); - virtual ~MetadataGenerator() {} + virtual ~MetadataGenerator() { +#ifndef SHIBSP_LITE + delete m_uiinfo; + delete m_org; + delete m_entityAttrs; + for_each(m_contacts.begin(), m_contacts.end(), xmltooling::cleanup()); + for_each(m_formats.begin(), m_formats.end(), xmltooling::cleanup()); + for_each(m_reqAttrs.begin(), m_reqAttrs.end(), xmltooling::cleanup()); + for_each(m_attrConsumers.begin(), m_attrConsumers.end(), xmltooling::cleanup()); +#endif + } pair run(SPRequest& request, bool isHandler=true) const; 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; + UIInfo* m_uiinfo; + Organization* m_org; + EntityAttributes* m_entityAttrs; + vector m_contacts; + vector m_formats; + vector m_reqAttrs; + vector m_attrConsumers; #endif }; @@ -87,6 +134,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), m_uiinfo(nullptr), m_org(nullptr), m_entityAttrs(nullptr) +#endif { string address(appId); address += getString("Location").second; @@ -108,14 +158,92 @@ 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); - e = XMLHelper::getFirstChildElement(e, EndpointBase); + + 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); while (e) { - if (e->hasChildNodes()) { + if (XMLString::equals(e->getLocalName(), EndpointBase) && e->hasChildNodes()) { auto_ptr_char base(e->getFirstChild()->getNodeValue()); if (base.get() && *base.get()) m_bases.push_back(base.get()); } - e = XMLHelper::getNextSiblingElement(e, EndpointBase); + else { + // Try and parse the object. + auto_ptr child(XMLObjectBuilder::buildOneFromElement(const_cast(e))); + ContactPerson* cp = dynamic_cast(child.get()); + if (cp) { + child.release(); + m_contacts.push_back(cp); + } + else { + NameIDFormat* nif = dynamic_cast(child.get()); + if (nif) { + child.release(); + m_formats.push_back(nif); + } + else { + RequestedAttribute* req = dynamic_cast(child.get()); + if (req) { + child.release(); + m_reqAttrs.push_back(req); + } + else { + AttributeConsumingService* acs = dynamic_cast(child.get()); + if (acs) { + child.release(); + m_attrConsumers.push_back(acs); + } + else { + UIInfo* info = dynamic_cast(child.get()); + if (info) { + if (!m_uiinfo) { + child.release(); + m_uiinfo = info; + } + else { + m_log.warn("skipping duplicate UIInfo element"); + } + } + else { + Organization* org = dynamic_cast(child.get()); + if (org) { + if (!m_org) { + child.release(); + m_org = org; + } + else { + m_log.warn("skipping duplicate Organization element"); + } + } + else { + EntityAttributes* ea = dynamic_cast(child.get()); + if (ea) { + if (!m_entityAttrs) { + child.release(); + m_entityAttrs = ea; + } + else { + m_log.warn("skipping duplicate EntityAttributes element"); + } + } + } + } + } + } + } + } + } + e = XMLHelper::getNextSiblingElement(e); } #endif } @@ -127,22 +255,24 @@ pair MetadataGenerator::run(SPRequest& request, bool isHandler) const if (!m_acl.empty() && m_acl.count(request.getRemoteAddr()) == 0) { m_log.error("request for metadata blocked from invalid address (%s)", request.getRemoteAddr().c_str()); istringstream msg("Metadata Request Blocked"); - return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_UNAUTHORIZED)); + 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 +289,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 +298,87 @@ 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); + + if (m_org && !entity->getOrganization()) + entity->setOrganization(m_org->cloneOrganization()); + + for (vector::const_iterator cp = m_contacts.begin(); cp != m_contacts.end(); ++cp) + entity->getContactPersons().push_back((*cp)->cloneContactPerson()); + + if (m_entityAttrs) { + if (!entity->getExtensions()) + entity->setExtensions(ExtensionsBuilder::buildExtensions()); + entity->getExtensions()->getUnknownXMLObjects().push_back(m_entityAttrs->cloneEntityAttributes()); + } SPSSODescriptor* role; if (entity->getSPSSODescriptors().empty()) { @@ -219,20 +389,69 @@ pair MetadataGenerator::processMessage(const Application& application role = entity->getSPSSODescriptors().front(); } + for (vector::const_iterator nif = m_formats.begin(); nif != m_formats.end(); ++nif) + role->getNameIDFormats().push_back((*nif)->cloneNameIDFormat()); + + if (m_uiinfo) { + if (!role->getExtensions()) + role->setExtensions(ExtensionsBuilder::buildExtensions()); + role->getExtensions()->getUnknownXMLObjects().push_back(m_uiinfo->cloneUIInfo()); + } + + for (vector::const_iterator acs = m_attrConsumers.begin(); acs != m_attrConsumers.end(); ++acs) + role->getAttributeConsumingServices().push_back((*acs)->cloneAttributeConsumingService()); + + if (!m_reqAttrs.empty()) { + int index = 1; + const vector& svcs = const_cast(role)->getAttributeConsumingServices(); + for (vector::const_iterator s =svcs.begin(); s != svcs.end(); ++s) { + pair i = (*s)->getIndex(); + if (i.first && index == i.second) + index = i.second + 1; + } + AttributeConsumingService* svc = AttributeConsumingServiceBuilder::buildAttributeConsumingService(); + role->getAttributeConsumingServices().push_back(svc); + svc->setIndex(index); + ServiceName* sn = ServiceNameBuilder::buildServiceName(); + svc->getServiceNames().push_back(sn); + sn->setName(entity->getEntityID()); + static const XMLCh english[] = UNICODE_LITERAL_2(e,n); + sn->setLang(english); + for (vector::const_iterator req = m_reqAttrs.begin(); req != m_reqAttrs.end(); ++req) + svc->getRequestedAttributes().push_back((*req)->cloneRequestedAttribute()); + } + // 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) @@ -240,27 +459,44 @@ pair MetadataGenerator::processMessage(const Application& application } } + AttributeExtractor* extractor = application.getAttributeExtractor(); + if (extractor) { + Locker extlocker(extractor); + extractor->generateMetadata(*role); + } + CredentialResolver* credResolver=application.getCredentialResolver(); if (credResolver) { Locker credLocker(credResolver); CredentialCriteria cc; + prop = relyingParty->getString("keyName"); + if (prop.first) + cc.getKeyNames().insert(prop.second); + vector signingcreds,enccreds; cc.setUsage(Credential::SIGNING_CREDENTIAL); - vector creds; - credResolver->resolve(creds,&cc); - for (vector::const_iterator c = creds.begin(); c != creds.end(); ++c) { + 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(Credential::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 +507,9 @@ pair MetadataGenerator::processMessage(const Application& application } } + // Stream for response. + stringstream s; + // Self-sign it? pair flag = getBool("signing"); if (flag.first && flag.second) { @@ -289,8 +528,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,16 +544,23 @@ 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,0L);