49a3e38908731a8cfae42baaa76cc846c40f9c8d
[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 "exceptions.h"
25 #include "ServiceProvider.h"
26 #include "binding/SOAPClient.h"
27
28 #include <log4cpp/Category.hh>
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 log4cpp;
39 using namespace std;
40
41 SOAPClient::SOAPClient(const Application& application, opensaml::SecurityPolicy& policy)
42     : opensaml::SOAPClient(policy), m_app(application), m_settings(NULL), m_relyingParty(NULL), m_credResolver(NULL)
43 {
44     pair<bool,const char*> policyId = m_app.getString("policyId");
45     m_settings = application.getServiceProvider().getPolicySettings(policyId.second);
46     const vector<const opensaml::SecurityPolicyRule*>& rules = application.getServiceProvider().getPolicyRules(policyId.second);
47     for (vector<const opensaml::SecurityPolicyRule*>::const_iterator rule=rules.begin(); rule!=rules.end(); ++rule)
48         policy.addRule(*rule);
49     policy.setMetadataProvider(application.getMetadataProvider());
50     policy.setTrustEngine(application.getTrustEngine());
51     pair<bool,bool> validate = m_settings->getBool("validate");
52     policy.setValidating(validate.first && validate.second);
53     setValidating(validate.first && validate.second);
54 }
55
56 void SOAPClient::send(const soap11::Envelope& env, MetadataCredentialCriteria& peer, const char* endpoint)
57 {
58     // Check for message signing requirements.   
59     m_relyingParty = m_app.getRelyingParty(dynamic_cast<const EntityDescriptor*>(peer.getRole().getParent()));
60     pair<bool,bool> flag = m_relyingParty->getBool("signRequests");
61     if (flag.first && flag.second) {
62         m_credResolver=m_app.getCredentialResolver();
63         if (m_credResolver) {
64             m_credResolver->lock();
65             // Fill in criteria to use.
66             peer.setUsage(CredentialCriteria::SIGNING_CREDENTIAL);
67             pair<bool,const char*> algcrit = m_relyingParty->getString("sigAlgorithm");
68             if (algcrit.first)
69                 peer.setKeyAlgorithm(algcrit.second);
70             const Credential* cred = m_credResolver->resolve(&peer);
71             peer.setKeyAlgorithm(NULL);
72
73             if (cred) {
74                 // Check for message.
75                 const vector<XMLObject*>& bodies=const_cast<const soap11::Body*>(env.getBody())->getUnknownXMLObjects();
76                 if (!bodies.empty()) {
77                     opensaml::SignableObject* msg = dynamic_cast<opensaml::SignableObject*>(bodies.front());
78                     if (msg) {
79                         // Build a Signature.
80                         Signature* sig = SignatureBuilder::buildSignature();
81                         msg->setSignature(sig);
82                         pair<bool,const XMLCh*> alg = m_relyingParty->getXMLString("sigAlgorithm");
83                         if (alg.first)
84                             sig->setSignatureAlgorithm(alg.second);
85
86                         // Sign it. The marshalling step in the base class should be a no-op.
87                         vector<Signature*> sigs(1,sig);
88                         env.marshall((DOMDocument*)NULL,&sigs,cred);
89                     }
90                 }
91             }
92             else {
93                 Category::getInstance(SHIBSP_LOGCAT".SOAPClient").error("no signing credential supplied, leaving unsigned.");
94             }
95         }
96         else {
97             Category::getInstance(SHIBSP_LOGCAT".SOAPClient").error("no CredentialResolver available, leaving unsigned.");
98         }
99     }
100     
101     opensaml::SOAPClient::send(env, peer, endpoint);
102 }
103
104 void SOAPClient::prepareTransport(SOAPTransport& transport)
105 {
106 #ifdef _DEBUG
107     xmltooling::NDC("prepareTransport");
108 #endif
109     Category& log=Category::getInstance(SHIBSP_LOGCAT".SOAPClient");
110     log.debug("prepping SOAP transport for use by application (%s)", m_app.getId());
111
112     pair<bool,bool> flag = m_settings->getBool("requireConfidentiality");
113     if ((!flag.first || flag.second) && !transport.isConfidential())
114         throw opensaml::BindingException("Transport confidentiality required, but not available."); 
115
116     flag = m_settings->getBool("validate");
117     setValidating(flag.first && flag.second);
118     flag = m_settings->getBool("requireTransportAuth");
119     forceTransportAuthentication(!flag.first || flag.second);
120
121     opensaml::SOAPClient::prepareTransport(transport);
122
123     pair<bool,const char*> authType=m_relyingParty->getString("authType");
124     if (!authType.first || !strcmp(authType.second,"TLS")) {
125         if (!m_credResolver) {
126             m_credResolver = m_app.getCredentialResolver();
127             if (m_credResolver)
128                 m_credResolver->lock();
129         }
130         if (m_credResolver) {
131             m_criteria->setUsage(CredentialCriteria::TLS_CREDENTIAL);
132             const Credential* cred = m_credResolver->resolve(m_criteria);
133             if (cred) {
134                 if (!transport.setCredential(cred))
135                     log.error("failed to load Credential into SOAPTransport");
136             }
137             else {
138                 log.error("no TLS credential supplied");
139             }
140         }
141         else {
142             log.error("no CredentialResolver available for TLS");
143         }
144     }
145     else {
146         SOAPTransport::transport_auth_t type=SOAPTransport::transport_auth_none;
147         pair<bool,const char*> username=m_relyingParty->getString("authUsername");
148         pair<bool,const char*> password=m_relyingParty->getString("authPassword");
149         if (!username.first || !password.first)
150             log.error("transport authType (%s) specified but authUsername or authPassword was missing", authType.second);
151         else if (!strcmp(authType.second,"basic"))
152             type = SOAPTransport::transport_auth_basic;
153         else if (!strcmp(authType.second,"digest"))
154             type = SOAPTransport::transport_auth_digest;
155         else if (!strcmp(authType.second,"ntlm"))
156             type = SOAPTransport::transport_auth_ntlm;
157         else if (!strcmp(authType.second,"gss"))
158             type = SOAPTransport::transport_auth_gss;
159         else
160             log.error("unknown authType (%s) specified for RelyingParty", authType.second);
161         if (type > SOAPTransport::transport_auth_none) {
162             if (transport.setAuth(type,username.second,password.second))
163                 log.debug("configured for transport authentication (method=%s, username=%s)", authType.second, username.second);
164             else
165                 log.error("failed to configure transport authentication (method=%s)", authType.second);
166         }
167     }
168     
169     transport.setConnectTimeout(m_settings->getUnsignedInt("connectTimeout").second);
170     transport.setTimeout(m_settings->getUnsignedInt("timeout").second);
171
172     HTTPSOAPTransport* http = dynamic_cast<HTTPSOAPTransport*>(&transport);
173     if (http) {
174         flag = m_settings->getBool("chunkedEncoding");
175         http->useChunkedEncoding(!flag.first || flag.second);
176         http->setRequestHeader("Shibboleth", PACKAGE_VERSION);
177     }
178 }
179
180 void SOAPClient::reset()
181 {
182     m_relyingParty = NULL;
183     if (m_credResolver)
184         m_credResolver->unlock();
185     m_credResolver = NULL;
186     opensaml::SOAPClient::reset();
187 }