Correct location of TransportOption feature, makes no sense inside policies.
[shibboleth/sp.git] / shibsp / binding / impl / SOAPClient.cpp
index 62b0a71..656a2c8 100644 (file)
  */
 
 #include "internal.h"
+#include "Application.h"
 #include "exceptions.h"
 #include "ServiceProvider.h"
 #include "binding/SOAPClient.h"
 
-#include <log4cpp/Category.hh>
 #include <saml/saml2/metadata/Metadata.h>
 #include <xmltooling/soap/SOAP.h>
 #include <xmltooling/soap/HTTPSOAPTransport.h>
@@ -35,54 +35,41 @@ using namespace shibsp;
 using namespace opensaml::saml2md;
 using namespace xmlsignature;
 using namespace xmltooling;
-using namespace log4cpp;
 using namespace std;
 
-namespace {
-    class SHIBSP_DLLLOCAL _addcert : public binary_function<X509Data*,XSECCryptoX509*,void> {
-    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);
-        }
-    };
-};
-
-SOAPClient::SOAPClient(const Application& application, opensaml::SecurityPolicy& policy)
-    : opensaml::SOAPClient(policy), m_app(application), m_settings(NULL), m_credUse(NULL), m_credResolver(NULL)
+SOAPClient::SOAPClient(SecurityPolicy& policy)
+    : opensaml::SOAPClient(policy), m_app(policy.getApplication()), m_settings(NULL), m_relyingParty(NULL), m_credResolver(NULL)
 {
-    pair<bool,const char*> policyId = m_app.getString("policyId");
-    m_settings = application.getServiceProvider().getPolicySettings(policyId.second);
-    const vector<const opensaml::SecurityPolicyRule*>& rules = application.getServiceProvider().getPolicyRules(policyId.second);
-    for (vector<const opensaml::SecurityPolicyRule*>::const_iterator rule=rules.begin(); rule!=rules.end(); ++rule)
-        policy.addRule(*rule);
-    policy.setMetadataProvider(application.getMetadataProvider());
-    policy.setTrustEngine(application.getTrustEngine());
+    m_settings = m_app.getServiceProvider().getPolicySettings(m_app.getString("policyId").second);
     pair<bool,bool> validate = m_settings->getBool("validate");
     policy.setValidating(validate.first && validate.second);
     setValidating(validate.first && validate.second);
 }
 
-void SOAPClient::send(const soap11::Envelope& env, const KeyInfoSource& peer, const char* endpoint)
+void SOAPClient::send(const soap11::Envelope& env, const char* from, MetadataCredentialCriteria& to, const char* endpoint)
 {
-    if (!m_peer)
-        m_peer = dynamic_cast<const RoleDescriptor*>(&peer);
-    if (m_peer) {
-        const EntityDescriptor* entity = m_peer ? dynamic_cast<const EntityDescriptor*>(m_peer->getParent()) : NULL;
-        m_credUse = entity ? m_app.getCredentialUse(entity) : NULL;
-    }
-    
     // Check for message signing requirements.   
-    if (m_credUse) {
-        pair<bool,bool> flag = m_credUse->getBool("signRequests");
-        if (flag.first && flag.second) {
-            CredentialResolver* cr=NULL;
-            pair<bool,const char*> cred = m_credUse->getString("Signing");
-            if (cred.first && (cr=m_app.getServiceProvider().getCredentialResolver(cred.second))) {
-                // Looks like we're supposed to sign, so check for message.
+    m_relyingParty = m_app.getRelyingParty(dynamic_cast<const EntityDescriptor*>(to.getRole().getParent()));
+    pair<bool,const char*> flag = m_relyingParty->getString("signing");
+    if (flag.first && (!strcmp(flag.second, "true") || !strcmp(flag.second, "back"))) {
+        m_credResolver=m_app.getCredentialResolver();
+        if (m_credResolver) {
+            m_credResolver->lock();
+            // Fill in criteria to use.
+            to.setUsage(Credential::SIGNING_CREDENTIAL);
+            pair<bool,const char*> keyName = m_relyingParty->getString("keyName");
+            if (keyName.first)
+                to.getKeyNames().insert(keyName.second);
+            pair<bool,const XMLCh*> sigalg = m_relyingParty->getXMLString("signingAlg");
+            if (sigalg.first)
+                to.setXMLAlgorithm(sigalg.second);
+            const Credential* cred = m_credResolver->resolve(&to);
+            // Reset criteria back.
+            to.setKeyAlgorithm(NULL);
+            to.setKeySize(0);
+
+            if (cred) {
+                // Check for message.
                 const vector<XMLObject*>& bodies=const_cast<const soap11::Body*>(env.getBody())->getUnknownXMLObjects();
                 if (!bodies.empty()) {
                     opensaml::SignableObject* msg = dynamic_cast<opensaml::SignableObject*>(bodies.front());
@@ -90,32 +77,28 @@ void SOAPClient::send(const soap11::Envelope& env, const KeyInfoSource& peer, co
                         // Build a Signature.
                         Signature* sig = SignatureBuilder::buildSignature();
                         msg->setSignature(sig);
-                        pair<bool,const XMLCh*> alg = m_credUse->getXMLString("sigAlgorithm");
-                        if (alg.first)
-                            sig->setSignatureAlgorithm(alg.second);
-                        Locker locker(cr);
-                        sig->setSigningKey(cr->getKey());
-                    
-                        // Build KeyInfo.
-                        const vector<XSECCryptoX509*>& 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));
-                        }
+                        if (sigalg.first)
+                            sig->setSignatureAlgorithm(sigalg.second);
+                        sigalg = m_relyingParty->getXMLString("digestAlg");
+                        if (sigalg.first)
+                            dynamic_cast<opensaml::ContentReference*>(sig->getContentReference())->setDigestAlgorithm(sigalg.second);
 
                         // Sign it. The marshalling step in the base class should be a no-op.
                         vector<Signature*> sigs(1,sig);
-                        env.marshall((DOMDocument*)NULL,&sigs);
+                        env.marshall((DOMDocument*)NULL,&sigs,cred);
                     }
                 }
             }
+            else {
+                Category::getInstance(SHIBSP_LOGCAT".SOAPClient").error("no signing credential supplied, leaving unsigned.");
+            }
+        }
+        else {
+            Category::getInstance(SHIBSP_LOGCAT".SOAPClient").error("no CredentialResolver available, leaving unsigned.");
         }
     }
     
-    opensaml::SOAPClient::send(env, peer, endpoint);
+    opensaml::SOAPClient::send(env, from, to, endpoint);
 }
 
 void SOAPClient::prepareTransport(SOAPTransport& transport)
@@ -126,80 +109,87 @@ void SOAPClient::prepareTransport(SOAPTransport& transport)
     Category& log=Category::getInstance(SHIBSP_LOGCAT".SOAPClient");
     log.debug("prepping SOAP transport for use by application (%s)", m_app.getId());
 
-    pair<bool,bool> flag = m_settings->getBool("requireConfidentiality");
+    pair<bool,bool> flag = m_relyingParty->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");
+    flag = m_relyingParty->getBool("requireTransportAuth");
     forceTransportAuthentication(!flag.first || flag.second);
 
     opensaml::SOAPClient::prepareTransport(transport);
 
-    if (!m_credUse) {
-        const EntityDescriptor* entity = m_peer ? dynamic_cast<const EntityDescriptor*>(m_peer->getParent()) : NULL;
-        m_credUse = entity ? m_app.getCredentialUse(entity) : NULL;
-    }
-    if (m_credUse) {
-        pair<bool,const char*> authType=m_credUse->getString("authType");
-        if (authType.first) {
-            SOAPTransport::transport_auth_t type=SOAPTransport::transport_auth_none;
-            pair<bool,const char*> username=m_credUse->getString("authUsername");
-            pair<bool,const char*> 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 = m_app.getServiceProvider().getCredentialResolver(authType.second);
-            if (m_credResolver) {
+    pair<bool,const char*> authType=m_relyingParty->getString("authType");
+    if (!authType.first || !strcmp(authType.second,"TLS")) {
+        if (!m_credResolver) {
+            m_credResolver = m_app.getCredentialResolver();
+            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");
-                }
+        }
+        if (m_credResolver) {
+            m_criteria->setUsage(Credential::TLS_CREDENTIAL);
+            authType = m_relyingParty->getString("keyName");
+            if (authType.first)
+                m_criteria->getKeyNames().insert(authType.second);
+            const Credential* cred = m_credResolver->resolve(m_criteria);
+            if (cred) {
+                if (!transport.setCredential(cred))
+                    log.error("failed to load Credential into SOAPTransport");
             }
             else {
-                log.error("unable to access CredentialResolver (%s)", authType.second);
+                log.error("no TLS credential supplied");
             }
         }
-    } 
-
-    transport.setConnectTimeout(m_settings->getUnsignedInt("connectTimeout").second);
-    transport.setTimeout(m_settings->getUnsignedInt("timeout").second);
+        else {
+            log.error("no CredentialResolver available for TLS");
+        }
+    }
+    else {
+        SOAPTransport::transport_auth_t type=SOAPTransport::transport_auth_none;
+        pair<bool,const char*> username=m_relyingParty->getString("authUsername");
+        pair<bool,const char*> password=m_relyingParty->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 if (strcmp(authType.second,"none"))
+            log.error("unknown authType (%s) specified for RelyingParty", 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);
+        }
+    }
+    
+    pair<bool,unsigned int> timeout = m_relyingParty->getUnsignedInt("connectTimeout"); 
+    transport.setConnectTimeout(timeout.first ? timeout.second : 10);
+    timeout = m_relyingParty->getUnsignedInt("timeout");
+    transport.setTimeout(timeout.first ? timeout.second : 20);
+    m_app.getServiceProvider().setTransportOptions(transport);
 
     HTTPSOAPTransport* http = dynamic_cast<HTTPSOAPTransport*>(&transport);
     if (http) {
-        flag = m_settings->getBool("chunkedEncoding");
-        http->useChunkedEncoding(!flag.first || flag.second);
-        http->setRequestHeader("Shibboleth", PACKAGE_VERSION);
+        flag = m_relyingParty->getBool("chunkedEncoding");
+        http->useChunkedEncoding(flag.first && flag.second);
+        http->setRequestHeader("User-Agent", PACKAGE_NAME);
+        http->setRequestHeader(PACKAGE_NAME, PACKAGE_VERSION);
     }
 }
 
 void SOAPClient::reset()
 {
-    m_credUse = NULL;
+    m_relyingParty = NULL;
     if (m_credResolver)
         m_credResolver->unlock();
     m_credResolver = NULL;
     opensaml::SOAPClient::reset();
-}
\ No newline at end of file
+}
+