2 * See the NOTICE file distributed with this work for information
3 * regarding copyright ownership. Licensed under the Apache License,
4 * Version 2.0 (the "License"); you may not use this file except in
5 * compliance with the License. You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
19 * An embeddable component interface to Shibboleth SP attribute processing.
24 #ifdef SHIBRESOLVER_HAVE_GSSAPI_NAMINGEXTS
25 # ifdef SHIBRESOLVER_HAVE_GSSMIT
26 # include <gssapi/gssapi_ext.h>
30 #include <shibsp/exceptions.h>
31 #include <shibsp/Application.h>
32 #include <shibsp/GSSRequest.h>
33 #include <shibsp/SPRequest.h>
34 #include <shibsp/ServiceProvider.h>
35 #include <shibsp/attribute/Attribute.h>
36 #include <shibsp/remoting/ListenerService.h>
38 # include <saml/saml2/metadata/Metadata.h>
39 # include <saml/saml2/metadata/MetadataProvider.h>
40 # include <saml/util/SAMLConstants.h>
41 # include <shibsp/attribute/filtering/AttributeFilter.h>
42 # include <shibsp/attribute/filtering/BasicFilteringContext.h>
43 # include <shibsp/attribute/resolver/AttributeExtractor.h>
44 # include <shibsp/attribute/resolver/AttributeResolver.h>
45 # include <shibsp/attribute/resolver/ResolutionContext.h>
46 # include <shibsp/metadata/MetadataProviderCriteria.h>
48 #include <xmltooling/XMLObjectBuilder.h>
49 #include <xmltooling/XMLToolingConfig.h>
50 #include <xmltooling/impl/AnyElement.h>
51 #include <xmltooling/util/ParserPool.h>
52 #include <xmltooling/util/XMLHelper.h>
53 #include <xercesc/util/Base64.hpp>
55 using namespace shibresolver;
56 using namespace shibsp;
58 using namespace opensaml;
59 using namespace opensaml::saml2md;
61 using namespace xmltooling;
64 namespace shibresolver {
65 class SHIBRESOLVER_DLLLOCAL RemotedResolver : public Remoted {
72 for_each(tokens.begin(), tokens.end(), xmltooling::cleanup<XMLObject>());
73 for_each(inputAttrs.begin(), inputAttrs.end(), xmltooling::cleanup<Attribute>());
74 for_each(resolvedAttrs.begin(), resolvedAttrs.end(), xmltooling::cleanup<Attribute>());
77 vector<const XMLObject*> tokens;
78 vector<Attribute*> inputAttrs;
79 vector<Attribute*> resolvedAttrs;
82 void receive(DDF& in, ostream& out);
84 const Application& app,
86 const XMLCh* protocol,
87 const vector<const XMLObject*>& tokens,
88 const vector<Attribute*>& inputAttrs,
89 vector<Attribute*>& resolvedAttrs
95 AttributeExtractor* extractor,
96 const Application& app,
97 const RoleDescriptor* issuer,
98 const XMLObject& token,
99 vector<Attribute*>& resolvedAttrs
102 const RoleDescriptor* lookup(
103 const Application& app,
105 const char* entityID,
106 const XMLCh* protocol
111 static RemotedResolver g_Remoted;
114 ShibbolethResolver* ShibbolethResolver::create()
116 return new ShibbolethResolver();
119 ShibbolethResolver::ShibbolethResolver() : m_request(NULL), m_sp(NULL)
120 #ifdef SHIBRESOLVER_HAVE_GSSAPI
126 ShibbolethResolver::~ShibbolethResolver()
128 #ifdef SHIBRESOLVER_HAVE_GSSAPI
131 for_each(m_resolvedAttributes.begin(), m_resolvedAttributes.end(), xmltooling::cleanup<Attribute>());
136 void ShibbolethResolver::setRequest(const SPRequest* request)
139 #if defined(SHIBSP_HAVE_GSSAPI) && defined (SHIBRESOLVER_HAVE_GSSAPI)
141 const GSSRequest* gss = dynamic_cast<const GSSRequest*>(request);
143 // TODO: fix API to prevent destruction of contexts
144 gss_ctx_id_t ctx = gss->getGSSContext();
151 void ShibbolethResolver::setApplicationID(const char* appID)
158 void ShibbolethResolver::setIssuer(const char* issuer)
165 void ShibbolethResolver::setProtocol(const XMLCh* protocol)
169 m_protocol = protocol;
172 void ShibbolethResolver::addToken(const XMLObject* token)
175 m_tokens.push_back(token);
178 #ifdef SHIBRESOLVER_HAVE_GSSAPI
179 void ShibbolethResolver::addToken(gss_ctx_id_t* ctx)
186 if (ctx && *ctx != GSS_C_NO_CONTEXT) {
188 gss_buffer_desc contextbuf = GSS_C_EMPTY_BUFFER;
189 OM_uint32 major = gss_export_sec_context(&minor, ctx, &contextbuf);
190 if (major == GSS_S_COMPLETE) {
192 XMLByte* out=Base64::encode(reinterpret_cast<const XMLByte*>(contextbuf.value), contextbuf.length, &len);
195 s.append(reinterpret_cast<char*>(out), len);
196 auto_ptr_XMLCh temp(s.c_str());
197 #ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE
198 XMLString::release(&out);
200 XMLString::release((char**)&out);
202 static const XMLCh _GSSAPI[] = UNICODE_LITERAL_13(G,S,S,A,P,I,C,o,n,t,e,x,t);
203 m_gsswrapper = new AnyElementImpl(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _GSSAPI);
204 m_gsswrapper->setTextContent(temp.get());
207 Category::getInstance(SHIBRESOLVER_LOGCAT).error("error while base64-encoding GSS context");
209 gss_release_buffer(&minor, &contextbuf);
212 Category::getInstance(SHIBRESOLVER_LOGCAT).error("error exporting GSS context");
217 #ifdef SHIBRESOLVER_HAVE_GSSAPI_NAMINGEXTS
218 void ShibbolethResolver::addToken(gss_name_t name)
226 gss_buffer_desc namebuf = GSS_C_EMPTY_BUFFER;
227 OM_uint32 major = gss_export_name_composite(&minor, name, &namebuf);
228 if (major == GSS_S_COMPLETE) {
230 gss_release_buffer(&minor, &namebuf);
233 Category::getInstance(SHIBRESOLVER_LOGCAT).error("error exporting GSS name");
238 void ShibbolethResolver::addToken(const gss_buffer_t contextbuf)
246 XMLByte* out=Base64::encode(reinterpret_cast<const XMLByte*>(contextbuf->value), contextbuf->length, &len);
249 s.append(reinterpret_cast<char*>(out), len);
250 auto_ptr_XMLCh temp(s.c_str());
251 #ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE
252 XMLString::release(&out);
254 XMLString::release((char**)&out);
256 static const XMLCh _GSSAPI[] = UNICODE_LITERAL_10(G,S,S,A,P,I,N,a,m,e);
257 m_gsswrapper = new AnyElementImpl(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _GSSAPI);
258 m_gsswrapper->setTextContent(temp.get());
261 Category::getInstance(SHIBRESOLVER_LOGCAT).error("error while base64-encoding GSS name");
267 void ShibbolethResolver::addAttribute(Attribute* attr)
270 m_inputAttributes.push_back(attr);
273 vector<Attribute*>& ShibbolethResolver::getResolvedAttributes()
275 return m_resolvedAttributes;
278 RequestMapper::Settings ShibbolethResolver::getSettings() const
281 throw ConfigurationException("Request settings not available without supplying SPRequest instance.");
282 return m_request->getRequestSettings();
285 void ShibbolethResolver::resolve()
287 Category& log = Category::getInstance(SHIBRESOLVER_LOGCAT);
288 SPConfig& conf = SPConfig::getConfig();
290 m_sp = conf.getServiceProvider();
296 const Application* app = m_request ? &(m_request->getApplication()) : m_sp->getApplication(m_appID.c_str());
298 throw ConfigurationException("Unable to locate application for resolution.");
300 #ifdef SHIBRESOLVER_HAVE_GSSAPI
302 m_tokens.push_back(m_gsswrapper);
305 if (conf.isEnabled(SPConfig::OutOfProcess)) {
316 // When not out of process, we remote all the message processing.
317 DDF out,in = DDF("org.project-moonshot.shibresolver");
318 DDFJanitor jin(in), jout(out);
319 in.addmember("application_id").string(app->getId());
320 if (!m_issuer.empty())
321 in.addmember("issuer").string(m_issuer.c_str());
322 if (!m_protocol.empty()) {
323 auto_ptr_char prot(m_protocol.c_str());
324 in.addmember("protocol").string(prot.get());
326 if (!m_tokens.empty()) {
327 DDF& tokens = in.addmember("tokens").list();
328 for (vector<const XMLObject*>::const_iterator t = m_tokens.begin(); t != m_tokens.end(); ++t) {
331 tokens.add(DDF(NULL).string(os.str().c_str()));
334 if (!m_inputAttributes.empty()) {
336 DDF& attrs = in.addmember("attributes").list();
337 for (vector<Attribute*>::const_iterator a = m_inputAttributes.begin(); a != m_inputAttributes.end(); ++a) {
338 attr = (*a)->marshall();
343 out = (m_request ? m_request->getServiceProvider() : (*m_sp)).getListenerService()->send(in);
345 Attribute* attribute;
346 DDF attr = out.first();
347 while (!attr.isnull()) {
349 attribute = Attribute::unmarshall(attr);
350 m_resolvedAttributes.push_back(attribute);
351 if (log.isDebugEnabled())
352 log.debug("unmarshalled attribute (ID: %s) with %d value%s",
353 attribute->getId(), attr.first().integer(), attr.first().integer()!=1 ? "s" : "");
355 catch (AttributeException& ex) {
356 const char* id = attr.first().name();
357 log.error("error unmarshalling attribute (ID: %s): %s", id ? id : "none", ex.what());
364 void RemotedResolver::receive(DDF& in, ostream& out)
366 Category& log = Category::getInstance(SHIBRESOLVER_LOGCAT);
369 const char* aid = in["application_id"].string();
370 const Application* app=aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : NULL;
372 // Something's horribly wrong.
373 log.error("couldn't find application (%s) for resolution", aid ? aid : "(missing)");
374 throw ConfigurationException("Unable to locate application for resolution, deleted?");
378 DDFJanitor jout(ret);
382 DDF tlist = in["tokens"];
383 DDF token = tlist.first();
384 while (token.isstring()) {
385 // Parse and bind the document into an XMLObject.
386 istringstream instr(token.string());
387 DOMDocument* doc = XMLToolingConfig::getConfig().getParser().parse(instr);
388 XercesJanitor<DOMDocument> janitor(doc);
389 XMLObject* xmlObject = XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(), true);
390 t.tokens.push_back(xmlObject);
392 token = tlist.next();
395 DDF alist = in["attributes"];
396 Attribute* attribute;
397 DDF attr = alist.first();
398 while (!attr.isnull()) {
399 attribute = Attribute::unmarshall(attr);
400 t.inputAttrs.push_back(attribute);
401 if (log.isDebugEnabled())
402 log.debug("unmarshalled attribute (ID: %s) with %d value%s",
403 attribute->getId(), attr.first().integer(), attr.first().integer()!=1 ? "s" : "");
407 auto_ptr_XMLCh prot(in["protocol"].string());
409 resolve(*app, in["issuer"].string(), prot.get(), t.tokens, t.inputAttrs, t.resolvedAttrs);
411 if (!t.resolvedAttrs.empty()) {
413 for (vector<Attribute*>::const_iterator a = t.resolvedAttrs.begin(); a != t.resolvedAttrs.end(); ++a) {
414 attr = (*a)->marshall();
422 void RemotedResolver::resolve(
423 const Application& app,
425 const XMLCh* protocol,
426 const vector<const XMLObject*>& tokens,
427 const vector<Attribute*>& inputAttrs,
428 vector<Attribute*>& resolvedAttrs
432 Category& log = Category::getInstance(SHIBRESOLVER_LOGCAT);
433 MetadataProvider* m = app.getMetadataProvider(false);
436 const RoleDescriptor* role = NULL;
437 if (issuer && *issuer)
438 role = lookup(app, m, issuer, protocol);
440 vector<const Assertion*> assertions;
442 AttributeExtractor* extractor = app.getAttributeExtractor();
444 Locker extlocker(extractor);
445 // Support metadata-based attributes for only the "top-level" issuer.
447 pair<bool,const char*> mprefix = app.getString("metadataAttributePrefix");
449 log.debug("extracting metadata-derived attributes...");
451 // We pass NULL for "issuer" because the issuer isn't the one asserting metadata-based attributes.
452 extractor->extractAttributes(app, NULL, *role, resolvedAttrs);
453 for (vector<Attribute*>::iterator a = resolvedAttrs.begin(); a != resolvedAttrs.end(); ++a) {
454 vector<string>& ids = (*a)->getAliases();
455 for (vector<string>::iterator id = ids.begin(); id != ids.end(); ++id)
456 *id = mprefix.second + *id;
459 catch (exception& ex) {
460 log.error("caught exception extracting attributes: %s", ex.what());
465 log.debug("extracting pushed attributes...");
466 const RoleDescriptor* role2;
467 for (vector<const XMLObject*>::const_iterator t = tokens.begin(); t != tokens.end(); ++t) {
468 // Save off any assertions for later use by resolver.
470 const Assertion* assertion = dynamic_cast<const Assertion*>(*t);
472 assertions.push_back(assertion);
473 const saml2::Assertion* saml2token = dynamic_cast<const saml2::Assertion*>(assertion);
474 if (saml2token && saml2token->getIssuer() && (saml2token->getIssuer()->getFormat() == NULL ||
475 XMLString::equals(saml2token->getIssuer()->getFormat(), saml2::NameID::ENTITY))) {
476 auto_ptr_char tokenissuer(saml2token->getIssuer()->getName());
477 role2 = lookup(app, m, tokenissuer.get(), protocol);
480 resolve(extractor, app, (role2 ? role2 : role), *(*t), resolvedAttrs);
484 log.warn("no AttributeExtractor plugin installed, check log during startup");
488 AttributeResolver* resolver = app.getAttributeResolver();
490 log.debug("resolving additional attributes...");
492 vector<Attribute*> inputs = inputAttrs;
493 inputs.insert(inputs.end(), resolvedAttrs.begin(), resolvedAttrs.end());
495 Locker locker(resolver);
496 auto_ptr<ResolutionContext> ctx(
497 resolver->createResolutionContext(
499 role ? dynamic_cast<const EntityDescriptor*>(role->getParent()) : NULL,
500 protocol ? protocol : samlconstants::SAML20P_NS,
508 resolver->resolveAttributes(*ctx.get());
509 if (!ctx->getResolvedAttributes().empty())
510 resolvedAttrs.insert(resolvedAttrs.end(), ctx->getResolvedAttributes().begin(), ctx->getResolvedAttributes().end());
513 catch (exception& ex) {
514 log.error("attribute resolution failed: %s", ex.what());
517 throw ConfigurationException("Cannot process request using lite version of shibsp library.");
523 void RemotedResolver::resolve(
524 AttributeExtractor* extractor,
525 const Application& app,
526 const RoleDescriptor* issuer,
527 const XMLObject& token,
528 vector<Attribute*>& resolvedAttrs
531 vector<Attribute*> extractedAttrs;
533 extractor->extractAttributes(app, issuer, token, extractedAttrs);
535 catch (exception& ex) {
536 Category::getInstance(SHIBRESOLVER_LOGCAT).error("caught exception extracting attributes: %s", ex.what());
539 AttributeFilter* filter = app.getAttributeFilter();
540 if (filter && !extractedAttrs.empty()) {
541 BasicFilteringContext fc(app, extractedAttrs, issuer);
542 Locker filtlocker(filter);
544 filter->filterAttributes(fc, extractedAttrs);
546 catch (exception& ex) {
547 Category::getInstance(SHIBRESOLVER_LOGCAT).error("caught exception filtering attributes: %s", ex.what());
548 Category::getInstance(SHIBRESOLVER_LOGCAT).error("dumping extracted attributes due to filtering exception");
549 for_each(extractedAttrs.begin(), extractedAttrs.end(), xmltooling::cleanup<shibsp::Attribute>());
550 extractedAttrs.clear();
554 resolvedAttrs.insert(resolvedAttrs.end(), extractedAttrs.begin(), extractedAttrs.end());
557 const RoleDescriptor* RemotedResolver::lookup(
558 const Application& app, MetadataProvider* m, const char* entityID, const XMLCh* protocol
564 MetadataProviderCriteria idpmc(app, entityID, &IDPSSODescriptor::ELEMENT_QNAME, protocol ? protocol : samlconstants::SAML20P_NS);
566 idpmc.protocol2 = samlconstants::SAML20P_NS;
567 pair<const EntityDescriptor*,const RoleDescriptor*> entity = m->getEntityDescriptor(idpmc);
569 Category::getInstance(SHIBRESOLVER_LOGCAT).warn("unable to locate metadata for provider (%s)", entityID);
571 else if (!entity.second) {
572 MetadataProviderCriteria aamc(
573 app, entityID, &AttributeAuthorityDescriptor::ELEMENT_QNAME, protocol ? protocol : samlconstants::SAML20P_NS
576 aamc.protocol2 = samlconstants::SAML20P_NS;
577 entity = m->getEntityDescriptor(aamc);
578 if (!entity.second) {
579 Category::getInstance(SHIBRESOLVER_LOGCAT).warn("unable to locate compatible IdP or AA role for provider (%s)", entityID);
583 return entity.second;
588 bool ShibbolethResolver::init(unsigned long features, const char* config, bool rethrow)
590 if (features & SPConfig::OutOfProcess) {
592 features = features | SPConfig::AttributeResolution | SPConfig::Metadata | SPConfig::Trust | SPConfig::Credentials;
594 if (!(features & SPConfig::InProcess))
595 features |= SPConfig::Listener;
597 else if (features & SPConfig::InProcess) {
598 features |= SPConfig::Listener;
600 SPConfig::getConfig().setFeatures(features);
601 if (!SPConfig::getConfig().init())
603 if (!SPConfig::getConfig().instantiate(config, rethrow))
609 * Shuts down runtime.
611 * Each process using the library SHOULD call this function exactly once before terminating itself.
613 void ShibbolethResolver::term()
615 SPConfig::getConfig().term();
619 extern "C" int SHIBRESOLVER_EXPORTS xmltooling_extension_init(void*)
621 #ifdef SHIBRESOLVER_SHIBSP_HAS_REMOTING
622 SPConfig& conf = SPConfig::getConfig();
623 if (conf.isEnabled(SPConfig::OutOfProcess) && !conf.isEnabled(SPConfig::InProcess) && conf.isEnabled(SPConfig::Listener))
624 conf.getServiceProvider()->regListener("org.project-moonshot.shibresolver", &g_Remoted);
626 return 0; // signal success
629 extern "C" void SHIBRESOLVER_EXPORTS xmltooling_extension_term()
631 // Factories normally get unregistered during library shutdown, so no work usually required here.