2 * Licensed to the University Corporation for Advanced Internet
3 * Development, Inc. (UCAID) under one or more contributor license
4 * agreements. See the NOTICE file distributed with this work for
5 * additional information regarding copyright ownership.
7 * UCAID licenses this file to you under the Apache License,
8 * Version 2.0 (the "License"); you may not use this file except
9 * in compliance with the License. You may obtain a copy of the
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17 * either express or implied. See the License for the specific
18 * language governing permissions and limitations under the License.
24 * Specialized SOAPClient for SP environment.
28 #include "Application.h"
29 #include "ServiceProvider.h"
30 #include "binding/SOAPClient.h"
31 #include "security/SecurityPolicy.h"
33 #include <saml/exceptions.h>
34 #include <saml/saml2/metadata/Metadata.h>
35 #include <saml/saml2/metadata/MetadataCredentialCriteria.h>
36 #include <saml/signature/ContentReference.h>
37 #include <xmltooling/security/Credential.h>
38 #include <xmltooling/signature/Signature.h>
39 #include <xmltooling/soap/SOAP.h>
40 #include <xmltooling/soap/HTTPSOAPTransport.h>
41 #include <xmltooling/util/NDC.h>
43 using namespace shibsp;
44 using namespace opensaml::saml2md;
45 using namespace xmlsignature;
46 using namespace xmltooling;
49 SOAPClient::SOAPClient(SecurityPolicy& policy)
50 : opensaml::SOAPClient(policy), m_app(policy.getApplication()), m_relyingParty(nullptr), m_credResolver(nullptr)
54 SOAPClient::~SOAPClient()
57 m_credResolver->unlock();
60 void SOAPClient::send(const soap11::Envelope& env, const char* from, MetadataCredentialCriteria& to, const char* endpoint)
62 // Check for message signing requirements.
63 m_relyingParty = m_app.getRelyingParty(dynamic_cast<const EntityDescriptor*>(to.getRole().getParent()));
64 pair<bool,const char*> flag = m_relyingParty->getString("signing");
65 if (flag.first && (!strcmp(flag.second, "true") || !strcmp(flag.second, "back"))) {
66 m_credResolver=m_app.getCredentialResolver();
68 m_credResolver->lock();
69 const Credential* cred = nullptr;
71 // Fill in criteria to use.
72 to.setUsage(Credential::SIGNING_CREDENTIAL);
73 pair<bool,const char*> keyName = m_relyingParty->getString("keyName");
75 to.getKeyNames().insert(keyName.second);
77 // Check for an explicit algorithm, in which case resolve a credential directly.
78 pair<bool,const XMLCh*> sigalg = m_relyingParty->getXMLString("signingAlg");
80 to.setXMLAlgorithm(sigalg.second);
81 cred = m_credResolver->resolve(&to);
84 // Prefer credential based on peer's requirements.
85 pair<const SigningMethod*,const Credential*> p = to.getRole().getSigningMethod(*m_credResolver, to);
87 sigalg = make_pair(true, p.first->getAlgorithm());
92 // Reset criteria back.
97 const vector<XMLObject*>& bodies=const_cast<const soap11::Body*>(env.getBody())->getUnknownXMLObjects();
98 if (!bodies.empty()) {
99 opensaml::SignableObject* msg = dynamic_cast<opensaml::SignableObject*>(bodies.front());
101 // Build a Signature.
102 Signature* sig = SignatureBuilder::buildSignature();
103 msg->setSignature(sig);
105 sig->setSignatureAlgorithm(sigalg.second);
106 sigalg = m_relyingParty->getXMLString("digestAlg");
108 const DigestMethod* dm = to.getRole().getDigestMethod();
110 sigalg = make_pair(true, dm->getAlgorithm());
113 dynamic_cast<opensaml::ContentReference*>(sig->getContentReference())->setDigestAlgorithm(sigalg.second);
115 // Sign it. The marshalling step in the base class should be a no-op.
116 vector<Signature*> sigs(1,sig);
117 env.marshall((DOMDocument*)nullptr,&sigs,cred);
122 Category::getInstance(SHIBSP_LOGCAT".SOAPClient").warn("no signing credential resolved, leaving message unsigned");
126 Category::getInstance(SHIBSP_LOGCAT".SOAPClient").warn("no CredentialResolver available, leaving unsigned");
130 opensaml::SOAPClient::send(env, from, to, endpoint);
133 void SOAPClient::prepareTransport(SOAPTransport& transport)
136 xmltooling::NDC("prepareTransport");
138 Category& log=Category::getInstance(SHIBSP_LOGCAT".SOAPClient");
139 log.debug("prepping SOAP transport for use by application (%s)", m_app.getId());
141 pair<bool,bool> flag = m_relyingParty->getBool("requireConfidentiality");
142 if ((!flag.first || flag.second) && !transport.isConfidential())
143 throw opensaml::BindingException("Transport confidentiality required, but not available.");
145 setValidating(getPolicy().getValidating());
146 flag = m_relyingParty->getBool("requireTransportAuth");
147 forceTransportAuthentication(!flag.first || flag.second);
149 opensaml::SOAPClient::prepareTransport(transport);
151 pair<bool,const char*> authType=m_relyingParty->getString("authType");
152 if (!authType.first || !strcmp(authType.second,"TLS")) {
153 if (!m_credResolver) {
154 m_credResolver = m_app.getCredentialResolver();
156 m_credResolver->lock();
158 if (m_credResolver) {
159 m_criteria->setUsage(Credential::TLS_CREDENTIAL);
160 authType = m_relyingParty->getString("keyName");
162 m_criteria->getKeyNames().insert(authType.second);
163 const Credential* cred = m_credResolver->resolve(m_criteria);
164 m_criteria->getKeyNames().clear();
166 if (!transport.setCredential(cred))
167 log.error("failed to load Credential into SOAPTransport");
170 log.error("no TLS credential supplied");
174 log.error("no CredentialResolver available for TLS");
178 SOAPTransport::transport_auth_t type=SOAPTransport::transport_auth_none;
179 pair<bool,const char*> username=m_relyingParty->getString("authUsername");
180 pair<bool,const char*> password=m_relyingParty->getString("authPassword");
181 if (!username.first || !password.first)
182 log.error("transport authType (%s) specified but authUsername or authPassword was missing", authType.second);
183 else if (!strcmp(authType.second,"basic"))
184 type = SOAPTransport::transport_auth_basic;
185 else if (!strcmp(authType.second,"digest"))
186 type = SOAPTransport::transport_auth_digest;
187 else if (!strcmp(authType.second,"ntlm"))
188 type = SOAPTransport::transport_auth_ntlm;
189 else if (!strcmp(authType.second,"gss"))
190 type = SOAPTransport::transport_auth_gss;
191 else if (strcmp(authType.second,"none"))
192 log.error("unknown authType (%s) specified for RelyingParty", authType.second);
193 if (type > SOAPTransport::transport_auth_none) {
194 if (transport.setAuth(type,username.second,password.second))
195 log.debug("configured for transport authentication (method=%s, username=%s)", authType.second, username.second);
197 log.error("failed to configure transport authentication (method=%s)", authType.second);
201 pair<bool,unsigned int> timeout = m_relyingParty->getUnsignedInt("connectTimeout");
202 transport.setConnectTimeout(timeout.first ? timeout.second : 10);
203 timeout = m_relyingParty->getUnsignedInt("timeout");
204 transport.setTimeout(timeout.first ? timeout.second : 20);
205 m_app.getServiceProvider().setTransportOptions(transport);
207 HTTPSOAPTransport* http = dynamic_cast<HTTPSOAPTransport*>(&transport);
209 flag = m_relyingParty->getBool("chunkedEncoding");
210 http->useChunkedEncoding(flag.first && flag.second);
211 http->setRequestHeader(PACKAGE_NAME, PACKAGE_VERSION);
215 void SOAPClient::reset()
217 m_relyingParty = nullptr;
219 m_credResolver->unlock();
220 m_credResolver = nullptr;
221 opensaml::SOAPClient::reset();