From: cantor Date: Wed, 14 Feb 2007 05:30:12 +0000 (+0000) Subject: Glue SOAP client to SP config, expand policy settings. X-Git-Tag: 2.4~1009 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=shibboleth%2Fsp.git;a=commitdiff_plain;h=b06dd43f8b3d2bf1abf63dd6b3396b94ff75d029 Glue SOAP client to SP config, expand policy settings. git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/trunk@2168 cb58f699-b61c-0410-a6fe-9272a202ed29 --- diff --git a/.cdtproject b/.cdtproject index c794717..79144f7 100644 --- a/.cdtproject +++ b/.cdtproject @@ -8,12 +8,10 @@ - - - - - + + + @@ -21,6 +19,10 @@ + + + + diff --git a/configs/shibboleth.xml.in b/configs/shibboleth.xml.in index b41fda3..e24d5c3 100644 --- a/configs/shibboleth.xml.in +++ b/configs/shibboleth.xml.in @@ -106,7 +106,7 @@ Resource requests are mapped in the Local section into an applicationId that points into to this section. --> - - - - - + + + + diff --git a/schemas/shibboleth-spconfig-2.0.xsd b/schemas/shibboleth-spconfig-2.0.xsd index 87d9bc0..4054f2e 100644 --- a/schemas/shibboleth-spconfig-2.0.xsd +++ b/schemas/shibboleth-spconfig-2.0.xsd @@ -50,8 +50,8 @@ - - + + @@ -70,7 +70,7 @@ - + @@ -91,7 +91,7 @@ - + @@ -108,7 +108,7 @@ - + @@ -142,7 +142,7 @@ - + @@ -152,7 +152,7 @@ - + @@ -178,8 +178,8 @@ - - + + @@ -194,14 +194,14 @@ - - - + + + - + @@ -243,11 +243,11 @@ - - - - - + + + + + @@ -283,7 +283,7 @@ - + @@ -295,8 +295,8 @@ - - + + @@ -312,7 +312,7 @@ - + @@ -333,7 +333,8 @@ - + + @@ -352,8 +353,9 @@ - - + + + @@ -381,17 +383,16 @@ - - - - - - - - - - - + + + + + + + + + + @@ -405,11 +406,10 @@ - - - - - + + + + @@ -423,24 +423,23 @@ - - - - - - - + + + + + + + - - - - - - + + + + + @@ -450,8 +449,8 @@ - - + + @@ -518,10 +517,16 @@ + + + + + + + - diff --git a/shibsp/Makefile.am b/shibsp/Makefile.am index 33b0e17..819e749a 100644 --- a/shibsp/Makefile.am +++ b/shibsp/Makefile.am @@ -9,6 +9,8 @@ libshibspincludedir = $(includedir)/shibsp attrincludedir = $(includedir)/shibsp/attribute +bindincludedir = $(includedir)/shibsp/binding + mdincludedir = $(includedir)/shibsp/metadata remincludedir = $(includedir)/shibsp/remoting @@ -40,6 +42,9 @@ attrinclude_HEADERS = \ attribute/ScopedAttribute.h \ attribute/SimpleAttribute.h +bindinclude_HEADERS = \ + binding/SOAPClient.h + mdinclude_HEADERS = \ metadata/MetadataExt.h @@ -68,6 +73,7 @@ libshibsp_la_SOURCES = \ SessionCache.cpp \ SPConfig.cpp \ attribute/Attribute.cpp \ + binding/impl/SOAPClient.cpp \ impl/RemotedSessionCache.cpp \ impl/StorageServiceSessionCache.cpp \ impl/XMLAccessControl.cpp \ diff --git a/shibsp/ServiceProvider.h b/shibsp/ServiceProvider.h index d4026fe..79cac91 100644 --- a/shibsp/ServiceProvider.h +++ b/shibsp/ServiceProvider.h @@ -102,12 +102,20 @@ namespace shibsp { virtual xmlsignature::CredentialResolver* getCredentialResolver(const char* id) const=0; /** + * Returns the security policy settings for an identified policy. + * + * @param id identifies the policy to return + * @return a PropertySet + */ + virtual const PropertySet* getPolicySettings(const char* id) const=0; + + /** * Returns the security policy rules for an identified policy. * - * @param id identifies the policy rules to return, or NULL for the default policy + * @param id identifies the policy to return * @return an array of policy rules */ - virtual std::vector& getPolicyRules(const char* id=NULL) const=0; + virtual const std::vector& getPolicyRules(const char* id) const=0; /** * Returns a RequestMapper instance. diff --git a/shibsp/binding/SOAPClient.h b/shibsp/binding/SOAPClient.h new file mode 100644 index 0000000..f19af64 --- /dev/null +++ b/shibsp/binding/SOAPClient.h @@ -0,0 +1,84 @@ +/* + * Copyright 2001-2007 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 + * + * 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. + */ + +/** + * @file shibsp/binding/SOAPClient.h + * + * Specialized SOAPClient for SP environment. + */ + +#ifndef __shibsp_soap11client_h__ +#define __shibsp_soap11client_h__ + +#include +#include + +namespace shibsp { + + /** + * Specialized SOAPClient for SP environment. + */ + class SHIBSP_API SOAPClient : public opensaml::SOAPClient + { + public: + /** + * Creates a SOAP client instance for an Application to use. + * + * @param application reference to Application + * @param policy reference to (empty) SecurityPolicy to apply + */ + SOAPClient(const Application& application, opensaml::SecurityPolicy& policy); + + virtual ~SOAPClient() { + if (m_credResolver) + m_credResolver->unlock(); + } + + /** + * Override handles message signing for SAML payloads. + * + * @param env SOAP envelope to send + * @param peer peer to send message to, expressed in TrustEngine terms + * @param endpoint URL of endpoint to recieve message + */ + void send(const soap11::Envelope& env, const xmltooling::KeyInfoSource& peer, const char* endpoint); + + void reset(); + + protected: + /** + * Override prepares transport by applying policy settings from Application. + * + * @param transport reference to transport layer + */ + void prepareTransport(const xmltooling::SOAPTransport& transport); + + /** Application supplied to client. */ + const Application& m_app; + + /** Properties associated with the Application's security policy. */ + const PropertySet* m_settings; + + /** CredentialUse properties, set after transport prep. */ + const PropertySet* m_credUse; + + /** Locked CredentialResolver for transport, set after transport prep. */ + xmlsignature::CredentialResolver* m_credResolver; + }; + +}; + +#endif /* __shibsp_soap11client_h__ */ diff --git a/shibsp/binding/impl/SOAPClient.cpp b/shibsp/binding/impl/SOAPClient.cpp new file mode 100644 index 0000000..7e9ec74 --- /dev/null +++ b/shibsp/binding/impl/SOAPClient.cpp @@ -0,0 +1,200 @@ +/* + * Copyright 2001-2007 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 + * + * 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. + */ + +/** + * SOAPClient.cpp + * + * Specialized SOAPClient for SP environment. + */ + +#include "internal.h" +#include "exceptions.h" +#include "ServiceProvider.h" +#include "binding/SOAPClient.h" + +#include +#include +#include +#include +#include + +using namespace shibsp; +using namespace opensaml::saml2md; +using namespace xmlsignature; +using namespace xmltooling; +using namespace log4cpp; +using namespace std; + +SOAPClient::SOAPClient(const Application& application, opensaml::SecurityPolicy& policy) + : opensaml::SOAPClient(policy), m_app(application), m_settings(NULL), m_credUse(NULL), m_credResolver(NULL) +{ + SPConfig& conf = SPConfig::getConfig(); + pair policyId = m_app.getString("policyId"); + m_settings = conf.getServiceProvider()->getPolicySettings(policyId.second); + const vector& rules = conf.getServiceProvider()->getPolicyRules(policyId.second); + for (vector::const_iterator rule=rules.begin(); rule!=rules.end(); ++rule) + policy.addRule(*rule); + policy.setMetadataProvider(application.getMetadataProvider()); + policy.setTrustEngine(application.getTrustEngine()); +} + +namespace { + class SHIBSP_DLLLOCAL _addcert : public binary_function { + public: + void operator()(X509Data* bag, XSECCryptoX509* cert) const { + safeBuffer& buf=cert->getDEREncodingSB(); + X509Certificate* x=X509CertificateBuilder::buildX509Certificate(); + x->setValue(buf.sbStrToXMLCh()); + bag->getX509Certificates().push_back(x); + } + }; +}; + +void SOAPClient::send(const soap11::Envelope& env, const KeyInfoSource& peer, const char* endpoint) +{ + if (!m_peer) + m_peer = dynamic_cast(&peer); + + if (m_peer) { + const EntityDescriptor* entity = m_peer ? dynamic_cast(m_peer->getParent()) : NULL; + m_credUse = entity ? m_app.getCredentialUse(entity) : NULL; + } + + // Check for message signing requirements. + if (m_credUse) { + pair flag = m_credUse->getBool("signRequests"); + if (flag.first && flag.second) { + CredentialResolver* cr=NULL; + pair cred = m_credUse->getString("Signing"); + if (cred.first && (cr==SPConfig::getConfig().getServiceProvider()->getCredentialResolver(cred.second))) { + // Looks like we're supposed to sign, so check for message. + const vector& bodies=const_cast(env.getBody())->getUnknownXMLObjects(); + if (!bodies.empty()) { + opensaml::SignableObject* msg = dynamic_cast(bodies.front()); + if (msg) { + // Build a Signature. + Signature* sig = SignatureBuilder::buildSignature(); + msg->setSignature(sig); + pair alg = m_credUse->getXMLString("sigAlgorithm"); + if (alg.first) + sig->setSignatureAlgorithm(alg.second); + Locker locker(cr); + sig->setSigningKey(cr->getKey()); + + // Build KeyInfo. + const vector& certs = cr->getCertificates(); + if (!certs.empty()) { + KeyInfo* keyInfo=KeyInfoBuilder::buildKeyInfo(); + sig->setKeyInfo(keyInfo); + X509Data* x509Data=X509DataBuilder::buildX509Data(); + keyInfo->getX509Datas().push_back(x509Data); + for_each(certs.begin(),certs.end(),bind1st(_addcert(),x509Data)); + } + + // Sign it. The marshalling step in the base class should be a no-op. + vector sigs(1,sig); + env.marshall((DOMDocument*)NULL,&sigs); + } + } + } + } + } + + opensaml::SOAPClient::send(env, peer, endpoint); +} + +void SOAPClient::prepareTransport(const SOAPTransport& transport) +{ +#ifdef _DEBUG + xmltooling::NDC("prepareTransport"); +#endif + Category& log=Category::getInstance(SHIBSP_LOGCAT".SOAPClient"); + log.debug("prepping SOAP transport for use by application (%s)", m_app.getId()); + + pair flag = m_settings->getBool("requireConfidentiality"); + if ((!flag.first || flag.second) && !transport.isConfidential()) + throw opensaml::BindingException("Transport confidentiality required, but not available."); + + flag = m_settings->getBool("validate"); + setValidating(flag.first && flag.second); + flag = m_settings->getBool("requireTransportAuth"); + forceTransportAuthentication(!flag.first || flag.second); + + opensaml::SOAPClient::prepareTransport(transport); + + if (!m_credUse) { + const EntityDescriptor* entity = m_peer ? dynamic_cast(m_peer->getParent()) : NULL; + m_credUse = entity ? m_app.getCredentialUse(entity) : NULL; + } + if (m_credUse) { + pair authType=m_credUse->getString("authType"); + if (authType.first) { + SOAPTransport::transport_auth_t type=SOAPTransport::transport_auth_none; + pair username=m_credUse->getString("authUsername"); + pair password=m_credUse->getString("authPassword"); + if (!username.first || !password.first) + log.error("transport authType (%s) specified but authUsername or authPassword was missing", authType.second); + else if (!strcmp(authType.second,"basic")) + type = SOAPTransport::transport_auth_basic; + else if (!strcmp(authType.second,"digest")) + type = SOAPTransport::transport_auth_digest; + else if (!strcmp(authType.second,"ntlm")) + type = SOAPTransport::transport_auth_ntlm; + else if (!strcmp(authType.second,"gss")) + type = SOAPTransport::transport_auth_gss; + else + log.error("unknown authType (%s) specified in CredentialUse element", authType.second); + if (type > SOAPTransport::transport_auth_none) { + if (transport.setAuth(type,username.second,password.second)) + log.debug("configured for transport authentication (method=%s, username=%s)", authType.second, username.second); + else + log.error("failed to configure transport authentication (method=%s)", authType.second); + } + } + + authType = m_credUse->getString("TLS"); + if (authType.first) { + m_credResolver = SPConfig::getConfig().getServiceProvider()->getCredentialResolver(authType.second); + if (m_credResolver) { + m_credResolver->lock(); + if (!transport.setCredentialResolver(m_credResolver)) { + m_credResolver->unlock(); + m_credResolver = NULL; + log.error("failed to load CredentialResolver into SOAPTransport"); + } + } + else { + log.error("unable to access CredentialResolver (%s)", authType.second); + } + } + } + + transport.setConnectTimeout(m_settings->getUnsignedInt("connectTimeout").second); + transport.setTimeout(m_settings->getUnsignedInt("timeout").second); + + const HTTPSOAPTransport* http = dynamic_cast(&transport); + if (http) + http->setRequestHeader("Shibboleth", PACKAGE_VERSION); +} + +void SOAPClient::reset() +{ + m_credUse = NULL; + if (m_credResolver) + m_credResolver->unlock(); + m_credResolver = NULL; + opensaml::SOAPClient::reset(); +} \ No newline at end of file diff --git a/shibsp/impl/XMLServiceProvider.cpp b/shibsp/impl/XMLServiceProvider.cpp index 600c3bd..426462b 100644 --- a/shibsp/impl/XMLServiceProvider.cpp +++ b/shibsp/impl/XMLServiceProvider.cpp @@ -155,8 +155,7 @@ namespace { RequestMapper* m_requestMapper; map m_appmap; map m_credResolverMap; - map< string,vector > m_policyMap; - string m_policyDefault; + map< string,pair< PropertySet*,vector > > m_policyMap; // Provides filter to exclude special config elements. short acceptNode(const DOMNode* node) const; @@ -249,11 +248,17 @@ namespace { return NULL; } - vector& getPolicyRules(const char* id=NULL) const { - if (!id) - id = m_impl->m_policyDefault.c_str(); - if (m_impl->m_policyMap.count(id)) - return m_impl->m_policyMap[id]; + const PropertySet* getPolicySettings(const char* id) const { + map > >::const_iterator i = m_impl->m_policyMap.find(id); + if (i!=m_impl->m_policyMap.end()) + return i->second.first; + throw ConfigurationException("Security Policy ($1) not found, check element.", params(1,id)); + } + + const vector& getPolicyRules(const char* id) const { + map > >::const_iterator i = m_impl->m_policyMap.find(id); + if (i!=m_impl->m_policyMap.end()) + return i->second.second; throw ConfigurationException("Security Policy ($1) not found, check element.", params(1,id)); } @@ -277,7 +282,6 @@ namespace { static const XMLCh Applications[] = UNICODE_LITERAL_12(A,p,p,l,i,c,a,t,i,o,n,s); static const XMLCh Credentials[] = UNICODE_LITERAL_11(C,r,e,d,e,n,t,i,a,l,s); static const XMLCh CredentialUse[] = UNICODE_LITERAL_13(C,r,e,d,e,n,t,i,a,l,U,s,e); - static const XMLCh _default[] = UNICODE_LITERAL_7(d,e,f,a,u,l,t); static const XMLCh fatal[] = UNICODE_LITERAL_5(f,a,t,a,l); static const XMLCh _Handler[] = UNICODE_LITERAL_7(H,a,n,d,l,e,r); static const XMLCh _id[] = UNICODE_LITERAL_2(i,d); @@ -303,6 +307,16 @@ namespace { static const XMLCh _MetadataProvider[] = UNICODE_LITERAL_16(M,e,t,a,d,a,t,a,P,r,o,v,i,d,e,r); static const XMLCh _path[] = UNICODE_LITERAL_4(p,a,t,h); static const XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e); + + class SHIBSP_DLLLOCAL PolicyNodeFilter : public DOMNodeFilter + { + public: + short acceptNode(const DOMNode* node) const { + if (XMLHelper::isNodeNamed(node,shibspconstants::SHIB2SPCONFIG_NS,Rule)) + return FILTER_REJECT; + return FILTER_ACCEPT; + } + }; }; namespace shibsp { @@ -940,17 +954,20 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o // Load security policies. child = XMLHelper::getLastChildElement(e,SecurityPolicies); if (child) { - auto_ptr_char def(child->getAttributeNS(NULL,_default)); - m_policyDefault = def.get(); + PolicyNodeFilter filter; child = XMLHelper::getFirstChildElement(child,Policy); while (child) { auto_ptr_char id(child->getAttributeNS(NULL,_id)); - vector& rules = m_policyMap[id.get()]; + pair< PropertySet*,vector >& rules = m_policyMap[id.get()]; + rules.first = NULL; + auto_ptr settings(new DOMPropertySet()); + settings->load(child, log, &filter); + rules.first = settings.release(); const DOMElement* rule = XMLHelper::getFirstChildElement(child,Rule); while (rule) { auto_ptr_char type(rule->getAttributeNS(NULL,_type)); try { - rules.push_back(samlConf.SecurityPolicyRuleManager.newPlugin(type.get(),rule)); + rules.second.push_back(samlConf.SecurityPolicyRuleManager.newPlugin(type.get(),rule)); } catch (exception& ex) { log.crit("error instantiating policy rule (%s) in policy (%s): %s", type.get(), id.get(), ex.what()); @@ -959,8 +976,6 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o } child = XMLHelper::getNextSiblingElement(child,Policy); } - if (!m_policyMap.count(m_policyDefault)) - throw ConfigurationException("Default security policy ($1) not found in conf:SecurityPolicies element.", params(1,m_policyDefault.c_str())); } // Load the default application. This actually has a fixed ID of "default". ;-) @@ -1000,8 +1015,10 @@ XMLConfigImpl::~XMLConfigImpl() { for_each(m_appmap.begin(),m_appmap.end(),cleanup_pair()); for_each(m_credResolverMap.begin(),m_credResolverMap.end(),cleanup_pair()); - for (map< string,vector >::iterator i=m_policyMap.begin(); i!=m_policyMap.end(); ++i) - for_each(i->second.begin(), i->second.end(), xmltooling::cleanup()); + for (map< string,pair > >::iterator i=m_policyMap.begin(); i!=m_policyMap.end(); ++i) { + delete i->second.first; + for_each(i->second.second.begin(), i->second.second.end(), xmltooling::cleanup()); + } delete m_requestMapper; if (m_document) m_document->release(); diff --git a/shibsp/shibsp.vcproj b/shibsp/shibsp.vcproj index fdce281..d640797 100644 --- a/shibsp/shibsp.vcproj +++ b/shibsp/shibsp.vcproj @@ -60,7 +60,7 @@ /> + + + + + + + + + + #include #include +#include #include #include @@ -127,7 +128,7 @@ int main(int argc,char* argv[]) } ServiceProvider* sp=conf.getServiceProvider(); - xmltooling::Locker locker(sp); + sp->lock(); try { const Application* app=sp->getApplication(a_param); @@ -157,12 +158,11 @@ int main(int argc,char* argv[]) else throw MetadataException("No AttributeAuthority role found in metadata."); - QName role(samlconstants::SAML20P_NS, AttributeAuthorityDescriptor::LOCAL_NAME); - SecurityPolicy policy(sp->getPolicyRules(), m, &role, app->getTrustEngine()); + SecurityPolicy policy; + shibsp::SOAPClient soaper(*app,policy); if (ver == v20) { auto_ptr_XMLCh binding(samlconstants::SAML20_BINDING_SOAP); - SAML2SOAPClient soaper(policy,true); opensaml::saml2p::StatusResponseType* srt=NULL; const vector& endpoints=AA->getAttributeServices(); for (vector::const_iterator ep=endpoints.begin(); !srt && ep!=endpoints.end(); ++ep) { @@ -181,10 +181,9 @@ int main(int argc,char* argv[]) subject->setNameID(nameid); query->setSubject(subject); query->setIssuer(iss); - auto_ptr wrapper(query); - soaper.sendSAML(query, *AA, loc.get()); - wrapper.release(); // freed by SOAP client - srt = soaper.receiveSAML(); + SAML2SOAPClient client(soaper); + client.sendSAML(query, *AA, loc.get()); + srt = client.receiveSAML(); } catch (exception& ex) { cerr << ex.what() << endl; @@ -206,7 +205,6 @@ int main(int argc,char* argv[]) } else { auto_ptr_XMLCh binding(samlconstants::SAML1_BINDING_SOAP); - SAML1SOAPClient soaper(policy,true); const opensaml::saml1p::Response* response=NULL; const vector& endpoints=AA->getAttributeServices(); for (vector::const_iterator ep=endpoints.begin(); !response && ep!=endpoints.end(); ++ep) { @@ -225,10 +223,9 @@ int main(int argc,char* argv[]) query->setSubject(subject); query->setResource(issuer.get()); request->setMinorVersion(ver==v11 ? 1 : 0); - auto_ptr wrapper(request); - soaper.sendSAML(request, *AA, loc.get()); - wrapper.release(); // freed by SOAP client - response = soaper.receiveSAML(); + SAML1SOAPClient client(soaper); + client.sendSAML(request, *AA, loc.get()); + response = client.receiveSAML(); } catch (exception& ex) { cerr << ex.what() << endl; @@ -252,6 +249,7 @@ int main(int argc,char* argv[]) cerr << ex.what() << endl; } + sp->unlock(); conf.term(); return 0; }