ad3d3cb4e27af3f7ff210eb99debba4cdc9de48a
[shibboleth/sp.git] / shibsp / binding / impl / SOAPClient.cpp
1 /*
2  *  Copyright 2001-2007 Internet2
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /**
18  * SOAPClient.cpp
19  * 
20  * Specialized SOAPClient for SP environment.
21  */
22
23 #include "internal.h"
24 #include "Application.h"
25 #include "exceptions.h"
26 #include "ServiceProvider.h"
27 #include "binding/SOAPClient.h"
28
29 #include <saml/saml2/metadata/Metadata.h>
30 #include <xmltooling/soap/SOAP.h>
31 #include <xmltooling/soap/HTTPSOAPTransport.h>
32 #include <xmltooling/util/NDC.h>
33
34 using namespace shibsp;
35 using namespace opensaml::saml2md;
36 using namespace xmlsignature;
37 using namespace xmltooling;
38 using namespace std;
39
40 SOAPClient::SOAPClient(SecurityPolicy& policy)
41     : opensaml::SOAPClient(policy), m_app(policy.getApplication()), m_settings(NULL), m_relyingParty(NULL), m_credResolver(NULL)
42 {
43     m_settings = m_app.getServiceProvider().getPolicySettings(m_app.getString("policyId").second);
44     pair<bool,bool> validate = m_settings->getBool("validate");
45     policy.setValidating(validate.first && validate.second);
46     setValidating(validate.first && validate.second);
47 }
48
49 void SOAPClient::send(const soap11::Envelope& env, const char* from, MetadataCredentialCriteria& to, const char* endpoint)
50 {
51     // Check for message signing requirements.   
52     m_relyingParty = m_app.getRelyingParty(dynamic_cast<const EntityDescriptor*>(to.getRole().getParent()));
53     pair<bool,const char*> flag = m_relyingParty->getString("signing");
54     if (flag.first && (!strcmp(flag.second, "true") || !strcmp(flag.second, "back"))) {
55         m_credResolver=m_app.getCredentialResolver();
56         if (m_credResolver) {
57             m_credResolver->lock();
58             // Fill in criteria to use.
59             to.setUsage(Credential::SIGNING_CREDENTIAL);
60             pair<bool,const char*> keyName = m_relyingParty->getString("keyName");
61             if (keyName.first)
62                 to.getKeyNames().insert(keyName.second);
63             pair<bool,const XMLCh*> sigalg = m_relyingParty->getXMLString("signingAlg");
64             if (sigalg.first)
65                 to.setXMLAlgorithm(sigalg.second);
66             const Credential* cred = m_credResolver->resolve(&to);
67             // Reset criteria back.
68             to.setKeyAlgorithm(NULL);
69             to.setKeySize(0);
70             to.getKeyNames().clear();
71
72             if (cred) {
73                 // Check for message.
74                 const vector<XMLObject*>& bodies=const_cast<const soap11::Body*>(env.getBody())->getUnknownXMLObjects();
75                 if (!bodies.empty()) {
76                     opensaml::SignableObject* msg = dynamic_cast<opensaml::SignableObject*>(bodies.front());
77                     if (msg) {
78                         // Build a Signature.
79                         Signature* sig = SignatureBuilder::buildSignature();
80                         msg->setSignature(sig);
81                         if (sigalg.first)
82                             sig->setSignatureAlgorithm(sigalg.second);
83                         sigalg = m_relyingParty->getXMLString("digestAlg");
84                         if (sigalg.first)
85                             dynamic_cast<opensaml::ContentReference*>(sig->getContentReference())->setDigestAlgorithm(sigalg.second);
86
87                         // Sign it. The marshalling step in the base class should be a no-op.
88                         vector<Signature*> sigs(1,sig);
89                         env.marshall((DOMDocument*)NULL,&sigs,cred);
90                     }
91                 }
92             }
93             else {
94                 Category::getInstance(SHIBSP_LOGCAT".SOAPClient").error("no signing credential supplied, leaving unsigned.");
95             }
96         }
97         else {
98             Category::getInstance(SHIBSP_LOGCAT".SOAPClient").error("no CredentialResolver available, leaving unsigned.");
99         }
100     }
101     
102     opensaml::SOAPClient::send(env, from, to, endpoint);
103 }
104
105 void SOAPClient::prepareTransport(SOAPTransport& transport)
106 {
107 #ifdef _DEBUG
108     xmltooling::NDC("prepareTransport");
109 #endif
110     Category& log=Category::getInstance(SHIBSP_LOGCAT".SOAPClient");
111     log.debug("prepping SOAP transport for use by application (%s)", m_app.getId());
112
113     pair<bool,bool> flag = m_relyingParty->getBool("requireConfidentiality");
114     if ((!flag.first || flag.second) && !transport.isConfidential())
115         throw opensaml::BindingException("Transport confidentiality required, but not available."); 
116
117     flag = m_settings->getBool("validate");
118     setValidating(flag.first && flag.second);
119     flag = m_relyingParty->getBool("requireTransportAuth");
120     forceTransportAuthentication(!flag.first || flag.second);
121
122     opensaml::SOAPClient::prepareTransport(transport);
123
124     pair<bool,const char*> authType=m_relyingParty->getString("authType");
125     if (!authType.first || !strcmp(authType.second,"TLS")) {
126         if (!m_credResolver) {
127             m_credResolver = m_app.getCredentialResolver();
128             if (m_credResolver)
129                 m_credResolver->lock();
130         }
131         if (m_credResolver) {
132             m_criteria->setUsage(Credential::TLS_CREDENTIAL);
133             authType = m_relyingParty->getString("keyName");
134             if (authType.first)
135                 m_criteria->getKeyNames().insert(authType.second);
136             const Credential* cred = m_credResolver->resolve(m_criteria);
137             m_criteria->getKeyNames().clear();
138             if (cred) {
139                 if (!transport.setCredential(cred))
140                     log.error("failed to load Credential into SOAPTransport");
141             }
142             else {
143                 log.error("no TLS credential supplied");
144             }
145         }
146         else {
147             log.error("no CredentialResolver available for TLS");
148         }
149     }
150     else {
151         SOAPTransport::transport_auth_t type=SOAPTransport::transport_auth_none;
152         pair<bool,const char*> username=m_relyingParty->getString("authUsername");
153         pair<bool,const char*> password=m_relyingParty->getString("authPassword");
154         if (!username.first || !password.first)
155             log.error("transport authType (%s) specified but authUsername or authPassword was missing", authType.second);
156         else if (!strcmp(authType.second,"basic"))
157             type = SOAPTransport::transport_auth_basic;
158         else if (!strcmp(authType.second,"digest"))
159             type = SOAPTransport::transport_auth_digest;
160         else if (!strcmp(authType.second,"ntlm"))
161             type = SOAPTransport::transport_auth_ntlm;
162         else if (!strcmp(authType.second,"gss"))
163             type = SOAPTransport::transport_auth_gss;
164         else if (strcmp(authType.second,"none"))
165             log.error("unknown authType (%s) specified for RelyingParty", authType.second);
166         if (type > SOAPTransport::transport_auth_none) {
167             if (transport.setAuth(type,username.second,password.second))
168                 log.debug("configured for transport authentication (method=%s, username=%s)", authType.second, username.second);
169             else
170                 log.error("failed to configure transport authentication (method=%s)", authType.second);
171         }
172     }
173     
174     pair<bool,unsigned int> timeout = m_relyingParty->getUnsignedInt("connectTimeout"); 
175     transport.setConnectTimeout(timeout.first ? timeout.second : 10);
176     timeout = m_relyingParty->getUnsignedInt("timeout");
177     transport.setTimeout(timeout.first ? timeout.second : 20);
178     m_app.getServiceProvider().setTransportOptions(transport);
179
180     HTTPSOAPTransport* http = dynamic_cast<HTTPSOAPTransport*>(&transport);
181     if (http) {
182         flag = m_relyingParty->getBool("chunkedEncoding");
183         http->useChunkedEncoding(flag.first && flag.second);
184         http->setRequestHeader("User-Agent", PACKAGE_NAME);
185         http->setRequestHeader(PACKAGE_NAME, PACKAGE_VERSION);
186     }
187 }
188
189 void SOAPClient::reset()
190 {
191     m_relyingParty = NULL;
192     if (m_credResolver)
193         m_credResolver->unlock();
194     m_credResolver = NULL;
195     opensaml::SOAPClient::reset();
196 }
197