2 * Copyright 2001-2007 Internet2
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 * Specialized SOAPClient for SP environment.
24 #include "exceptions.h"
25 #include "ServiceProvider.h"
26 #include "binding/SOAPClient.h"
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>
34 using namespace shibsp;
35 using namespace opensaml::saml2md;
36 using namespace xmlsignature;
37 using namespace xmltooling;
38 using namespace log4cpp;
42 class SHIBSP_DLLLOCAL _addcert : public binary_function<X509Data*,XSECCryptoX509*,void> {
44 void operator()(X509Data* bag, XSECCryptoX509* cert) const {
45 safeBuffer& buf=cert->getDEREncodingSB();
46 X509Certificate* x=X509CertificateBuilder::buildX509Certificate();
47 x->setValue(buf.sbStrToXMLCh());
48 bag->getX509Certificates().push_back(x);
53 SOAPClient::SOAPClient(const Application& application, opensaml::SecurityPolicy& policy)
54 : opensaml::SOAPClient(policy), m_app(application), m_settings(NULL), m_credUse(NULL), m_credResolver(NULL)
56 SPConfig& conf = SPConfig::getConfig();
57 pair<bool,const char*> policyId = m_app.getString("policyId");
58 m_settings = conf.getServiceProvider()->getPolicySettings(policyId.second);
59 const vector<const opensaml::SecurityPolicyRule*>& rules = conf.getServiceProvider()->getPolicyRules(policyId.second);
60 for (vector<const opensaml::SecurityPolicyRule*>::const_iterator rule=rules.begin(); rule!=rules.end(); ++rule)
61 policy.addRule(*rule);
62 policy.setMetadataProvider(application.getMetadataProvider());
63 policy.setTrustEngine(application.getTrustEngine());
66 void SOAPClient::send(const soap11::Envelope& env, const KeyInfoSource& peer, const char* endpoint)
69 m_peer = dynamic_cast<const RoleDescriptor*>(&peer);
72 const EntityDescriptor* entity = m_peer ? dynamic_cast<const EntityDescriptor*>(m_peer->getParent()) : NULL;
73 m_credUse = entity ? m_app.getCredentialUse(entity) : NULL;
76 // Check for message signing requirements.
78 pair<bool,bool> flag = m_credUse->getBool("signRequests");
79 if (flag.first && flag.second) {
80 CredentialResolver* cr=NULL;
81 pair<bool,const char*> cred = m_credUse->getString("Signing");
82 if (cred.first && (cr=SPConfig::getConfig().getServiceProvider()->getCredentialResolver(cred.second))) {
83 // Looks like we're supposed to sign, so check for message.
84 const vector<XMLObject*>& bodies=const_cast<const soap11::Body*>(env.getBody())->getUnknownXMLObjects();
85 if (!bodies.empty()) {
86 opensaml::SignableObject* msg = dynamic_cast<opensaml::SignableObject*>(bodies.front());
89 Signature* sig = SignatureBuilder::buildSignature();
90 msg->setSignature(sig);
91 pair<bool,const XMLCh*> alg = m_credUse->getXMLString("sigAlgorithm");
93 sig->setSignatureAlgorithm(alg.second);
95 sig->setSigningKey(cr->getKey());
98 const vector<XSECCryptoX509*>& certs = cr->getCertificates();
100 KeyInfo* keyInfo=KeyInfoBuilder::buildKeyInfo();
101 sig->setKeyInfo(keyInfo);
102 X509Data* x509Data=X509DataBuilder::buildX509Data();
103 keyInfo->getX509Datas().push_back(x509Data);
104 for_each(certs.begin(),certs.end(),bind1st(_addcert(),x509Data));
107 // Sign it. The marshalling step in the base class should be a no-op.
108 vector<Signature*> sigs(1,sig);
109 env.marshall((DOMDocument*)NULL,&sigs);
116 opensaml::SOAPClient::send(env, peer, endpoint);
119 void SOAPClient::prepareTransport(SOAPTransport& transport)
122 xmltooling::NDC("prepareTransport");
124 Category& log=Category::getInstance(SHIBSP_LOGCAT".SOAPClient");
125 log.debug("prepping SOAP transport for use by application (%s)", m_app.getId());
127 pair<bool,bool> flag = m_settings->getBool("requireConfidentiality");
128 if ((!flag.first || flag.second) && !transport.isConfidential())
129 throw opensaml::BindingException("Transport confidentiality required, but not available.");
131 flag = m_settings->getBool("validate");
132 setValidating(flag.first && flag.second);
133 flag = m_settings->getBool("requireTransportAuth");
134 forceTransportAuthentication(!flag.first || flag.second);
136 opensaml::SOAPClient::prepareTransport(transport);
139 const EntityDescriptor* entity = m_peer ? dynamic_cast<const EntityDescriptor*>(m_peer->getParent()) : NULL;
140 m_credUse = entity ? m_app.getCredentialUse(entity) : NULL;
143 pair<bool,const char*> authType=m_credUse->getString("authType");
144 if (authType.first) {
145 SOAPTransport::transport_auth_t type=SOAPTransport::transport_auth_none;
146 pair<bool,const char*> username=m_credUse->getString("authUsername");
147 pair<bool,const char*> password=m_credUse->getString("authPassword");
148 if (!username.first || !password.first)
149 log.error("transport authType (%s) specified but authUsername or authPassword was missing", authType.second);
150 else if (!strcmp(authType.second,"basic"))
151 type = SOAPTransport::transport_auth_basic;
152 else if (!strcmp(authType.second,"digest"))
153 type = SOAPTransport::transport_auth_digest;
154 else if (!strcmp(authType.second,"ntlm"))
155 type = SOAPTransport::transport_auth_ntlm;
156 else if (!strcmp(authType.second,"gss"))
157 type = SOAPTransport::transport_auth_gss;
159 log.error("unknown authType (%s) specified in CredentialUse element", authType.second);
160 if (type > SOAPTransport::transport_auth_none) {
161 if (transport.setAuth(type,username.second,password.second))
162 log.debug("configured for transport authentication (method=%s, username=%s)", authType.second, username.second);
164 log.error("failed to configure transport authentication (method=%s)", authType.second);
168 authType = m_credUse->getString("TLS");
169 if (authType.first) {
170 m_credResolver = SPConfig::getConfig().getServiceProvider()->getCredentialResolver(authType.second);
171 if (m_credResolver) {
172 m_credResolver->lock();
173 if (!transport.setCredentialResolver(m_credResolver)) {
174 m_credResolver->unlock();
175 m_credResolver = NULL;
176 log.error("failed to load CredentialResolver into SOAPTransport");
180 log.error("unable to access CredentialResolver (%s)", authType.second);
185 transport.setConnectTimeout(m_settings->getUnsignedInt("connectTimeout").second);
186 transport.setTimeout(m_settings->getUnsignedInt("timeout").second);
188 HTTPSOAPTransport* http = dynamic_cast<HTTPSOAPTransport*>(&transport);
190 flag = m_settings->getBool("chunkedEncoding");
191 http->useChunkedEncoding(!flag.first || flag.second);
192 http->setRequestHeader("Shibboleth", PACKAGE_VERSION);
196 void SOAPClient::reset()
200 m_credResolver->unlock();
201 m_credResolver = NULL;
202 opensaml::SOAPClient::reset();