#include "internal.h"
#include "exceptions.h"
+#include "logging.h"
+#include "security/CredentialCriteria.h"
#include "security/OpenSSLTrustEngine.h"
-#include "signature/OpenSSLCredentialResolver.h"
+#include "security/OpenSSLCredential.h"
#include "soap/HTTPSOAPTransport.h"
#include "soap/OpenSSLSOAPTransport.h"
#include "util/NDC.h"
#include <list>
#include <curl/curl.h>
-#include <log4cpp/Category.hh>
#include <openssl/x509_vfy.h>
-using namespace xmlsignature;
+using namespace xmltooling::logging;
using namespace xmltooling;
-using namespace log4cpp;
using namespace std;
namespace xmltooling {
class XMLTOOL_DLLLOCAL CURLPool
{
public:
- CURLPool() : m_size(256), m_lock(Mutex::create()),
+ CURLPool() : m_size(0), m_lock(Mutex::create()),
m_log(Category::getInstance(XMLTOOLING_LOGCAT".SOAPTransport.CURLPool")) {}
~CURLPool();
- CURL* get(const string& to, const char* endpoint);
- void put(const string& to, const char* endpoint, CURL* handle);
+ CURL* get(const SOAPTransport::Address& addr);
+ void put(const char* from, const char* to, const char* endpoint, CURL* handle);
private:
typedef map<string,vector<CURL*> > poolmap_t;
class XMLTOOL_DLLLOCAL CURLSOAPTransport : public HTTPSOAPTransport, public OpenSSLSOAPTransport
{
public:
- CURLSOAPTransport(const KeyInfoSource& peer, const char* endpoint)
- : m_peer(peer), m_endpoint(endpoint), m_handle(NULL), m_headers(NULL),
+ CURLSOAPTransport(const Address& addr)
+ : m_sender(addr.m_from ? addr.m_from : ""), m_peerName(addr.m_to ? addr.m_to : ""), m_endpoint(addr.m_endpoint),
+ m_handle(NULL), m_headers(NULL),
#ifndef XMLTOOLING_NO_XMLSEC
- m_credResolver(NULL), m_trustEngine(NULL), m_mandatory(false), m_keyResolver(NULL),
+ m_cred(NULL), m_trustEngine(NULL), m_peerResolver(NULL), m_mandatory(false),
#endif
- m_ssl_callback(NULL), m_ssl_userptr(NULL), m_secure(false) {
- m_handle = g_CURLPool->get(peer.getName(), endpoint);
- curl_easy_setopt(m_handle,CURLOPT_URL,endpoint);
+ m_ssl_callback(NULL), m_ssl_userptr(NULL), m_chunked(true), m_authenticated(false) {
+ m_handle = g_CURLPool->get(addr);
+ curl_easy_setopt(m_handle,CURLOPT_URL,addr.m_endpoint);
curl_easy_setopt(m_handle,CURLOPT_CONNECTTIMEOUT,15);
curl_easy_setopt(m_handle,CURLOPT_TIMEOUT,30);
curl_easy_setopt(m_handle,CURLOPT_HTTPAUTH,0);
curl_easy_setopt(m_handle,CURLOPT_USERPWD,NULL);
+ curl_easy_setopt(m_handle,CURLOPT_SSL_VERIFYHOST,2);
curl_easy_setopt(m_handle,CURLOPT_HEADERDATA,this);
m_headers=curl_slist_append(m_headers,"Content-Type: text/xml");
- m_headers=curl_slist_append(m_headers,"Transport-Encoding: chunked");
}
virtual ~CURLSOAPTransport() {
curl_slist_free_all(m_headers);
curl_easy_setopt(m_handle,CURLOPT_ERRORBUFFER,NULL);
- curl_easy_setopt(m_handle,CURLOPT_PRIVATE,m_secure ? "secure" : NULL); // Save off security "state".
- g_CURLPool->put(m_peer.getName(), m_endpoint.c_str(), m_handle);
+ curl_easy_setopt(m_handle,CURLOPT_PRIVATE,m_authenticated ? "secure" : NULL); // Save off security "state".
+ g_CURLPool->put(m_sender.c_str(), m_peerName.c_str(), m_endpoint.c_str(), m_handle);
}
bool isConfidential() const {
return m_endpoint.find("https")==0;
}
- bool setConnectTimeout(long timeout) const {
+ bool setConnectTimeout(long timeout) {
return (curl_easy_setopt(m_handle,CURLOPT_CONNECTTIMEOUT,timeout)==CURLE_OK);
}
- bool setTimeout(long timeout) const {
+ bool setTimeout(long timeout) {
return (curl_easy_setopt(m_handle,CURLOPT_TIMEOUT,timeout)==CURLE_OK);
}
- bool setAuth(transport_auth_t authType, const char* username=NULL, const char* password=NULL) const;
+ bool setAuth(transport_auth_t authType, const char* username=NULL, const char* password=NULL);
+
+ bool setVerifyHost(bool verify) {
+ return (curl_easy_setopt(m_handle,CURLOPT_SSL_VERIFYHOST,verify ? 2 : 0)==CURLE_OK);
+ }
#ifndef XMLTOOLING_NO_XMLSEC
- bool setCredentialResolver(const CredentialResolver* credResolver) const {
- const OpenSSLCredentialResolver* down = dynamic_cast<const OpenSSLCredentialResolver*>(credResolver);
+ bool setCredential(const Credential* cred=NULL) {
+ const OpenSSLCredential* down = dynamic_cast<const OpenSSLCredential*>(cred);
if (!down) {
- m_credResolver = NULL;
- return (credResolver==NULL);
+ m_cred = NULL;
+ return (cred==NULL);
}
- m_credResolver = down;
+ m_cred = down;
return true;
}
- bool setTrustEngine(const X509TrustEngine* trustEngine, bool mandatory=true, const KeyResolver* keyResolver=NULL) const {
+ bool setTrustEngine(
+ const X509TrustEngine* trustEngine=NULL,
+ const CredentialResolver* peerResolver=NULL,
+ CredentialCriteria* criteria=NULL,
+ bool mandatory=true
+ ) {
const OpenSSLTrustEngine* down = dynamic_cast<const OpenSSLTrustEngine*>(trustEngine);
if (!down) {
m_trustEngine = NULL;
- m_keyResolver = NULL;
+ m_peerResolver = NULL;
+ m_criteria = NULL;
return (trustEngine==NULL);
}
m_trustEngine = down;
- m_keyResolver = keyResolver;
+ m_peerResolver = peerResolver;
+ m_criteria = criteria;
m_mandatory = mandatory;
return true;
}
#endif
+ bool useChunkedEncoding(bool chunked=true) {
+ m_chunked = chunked;
+ return true;
+ }
+
+ bool setProviderOption(const char* provider, const char* option, const char* value) {
+ if (!provider || strcmp(provider, "CURL"))
+ return false;
+ // For libcurl, the option is an enum and the value type depends on the option.
+ CURLoption opt = static_cast<CURLoption>(strtol(option, NULL, 10));
+ if (opt < CURLOPTTYPE_OBJECTPOINT)
+ return (curl_easy_setopt(m_handle, opt, strtol(value, NULL, 10)) == CURLE_OK);
+ else if (opt < CURLOPTTYPE_OFF_T)
+ return (curl_easy_setopt(m_handle, opt, value) == CURLE_OK);
+ else if (sizeof(curl_off_t) == sizeof(long))
+ return (curl_easy_setopt(m_handle, opt, strtol(value, NULL, 10)) == CURLE_OK);
+ return false;
+ }
+
void send(istream& in);
istream& receive() {
return m_stream;
}
- bool isSecure() const {
- return m_secure;
+ bool isAuthenticated() const {
+ return m_authenticated;
}
- void setSecure(bool secure) {
- m_secure = secure;
+ void setAuthenticated(bool auth) {
+ m_authenticated = auth;
}
string getContentType() const;
- bool setRequestHeader(const char* name, const char* val) const {
+ bool setRequestHeader(const char* name, const char* val) {
string temp(name);
temp=temp + ": " + val;
m_headers=curl_slist_append(m_headers,temp.c_str());
const vector<string>& getResponseHeader(const char* val) const;
- bool setSSLCallback(ssl_ctx_callback_fn fn, void* userptr=NULL) const {
+ bool setSSLCallback(ssl_ctx_callback_fn fn, void* userptr=NULL) {
m_ssl_callback=fn;
m_ssl_userptr=userptr;
return true;
private:
// per-call state
- const KeyInfoSource& m_peer;
- string m_endpoint;
+ string m_sender,m_peerName,m_endpoint,m_simplecreds;
CURL* m_handle;
stringstream m_stream;
- mutable struct curl_slist* m_headers;
+ struct curl_slist* m_headers;
map<string,vector<string> > m_response_headers;
#ifndef XMLTOOLING_NO_XMLSEC
- mutable const OpenSSLCredentialResolver* m_credResolver;
- mutable const OpenSSLTrustEngine* m_trustEngine;
- mutable bool m_mandatory;
- mutable const KeyResolver* m_keyResolver;
+ const OpenSSLCredential* m_cred;
+ const OpenSSLTrustEngine* m_trustEngine;
+ const CredentialResolver* m_peerResolver;
+ CredentialCriteria* m_criteria;
+ bool m_mandatory;
#endif
- mutable ssl_ctx_callback_fn m_ssl_callback;
- mutable void* m_ssl_userptr;
- bool m_secure;
+ ssl_ctx_callback_fn m_ssl_callback;
+ void* m_ssl_userptr;
+ bool m_chunked;
+ bool m_authenticated;
friend size_t XMLTOOL_DLLLOCAL curl_header_hook(void* ptr, size_t size, size_t nmemb, void* stream);
friend CURLcode XMLTOOL_DLLLOCAL xml_ssl_ctx_callback(CURL* curl, SSL_CTX* ssl_ctx, void* userptr);
int XMLTOOL_DLLLOCAL verify_callback(X509_STORE_CTX* x509_ctx, void* arg);
#endif
- SOAPTransport* CURLSOAPTransportFactory(const pair<const KeyInfoSource*,const char*>& dest)
+ SOAPTransport* CURLSOAPTransportFactory(const SOAPTransport::Address& addr)
{
- return new CURLSOAPTransport(*dest.first, dest.second);
+ return new CURLSOAPTransport(addr);
}
};
delete m_lock;
}
-CURL* CURLPool::get(const string& to, const char* endpoint)
+CURL* CURLPool::get(const SOAPTransport::Address& addr)
{
#ifdef _DEBUG
xmltooling::NDC("get");
#endif
- m_log.debug("getting connection handle to %s", endpoint);
+ m_log.debug("getting connection handle to %s", addr.m_endpoint);
+ string key(addr.m_endpoint);
+ if (addr.m_from)
+ key = key + '|' + addr.m_from;
+ if (addr.m_to)
+ key = key + '|' + addr.m_to;
m_lock->lock();
- poolmap_t::iterator i=m_bindingMap.find(to + "|" + endpoint);
+ poolmap_t::iterator i=m_bindingMap.find(key);
if (i!=m_bindingMap.end()) {
// Move this pool to the front of the list.
curl_easy_setopt(handle,CURLOPT_NOPROGRESS,1);
curl_easy_setopt(handle,CURLOPT_NOSIGNAL,1);
curl_easy_setopt(handle,CURLOPT_FAILONERROR,1);
- curl_easy_setopt(handle,CURLOPT_SSLVERSION,3);
+ curl_easy_setopt(handle,CURLOPT_SSLVERSION,CURL_SSLVERSION_SSLv3);
+ curl_easy_setopt(handle,CURLOPT_SSL_CIPHER_LIST,"ALL:!aNULL:!LOW:!EXPORT:!SSLv2");
// Verification of the peer is via TrustEngine only.
curl_easy_setopt(handle,CURLOPT_SSL_VERIFYPEER,0);
- curl_easy_setopt(handle,CURLOPT_SSL_VERIFYHOST,2);
curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,&curl_header_hook);
- curl_easy_setopt(handle,CURLOPT_READFUNCTION,&curl_read_hook);
curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,&curl_write_hook);
curl_easy_setopt(handle,CURLOPT_DEBUGFUNCTION,&curl_debug_hook);
return handle;
}
-void CURLPool::put(const string& to, const char* endpoint, CURL* handle)
+void CURLPool::put(const char* from, const char* to, const char* endpoint, CURL* handle)
{
- string key = to + "|" + endpoint;
+ string key(endpoint);
+ if (from)
+ key = key + '|' + from;
+ if (to)
+ key = key + '|' + to;
m_lock->lock();
poolmap_t::iterator i=m_bindingMap.find(key);
if (i==m_bindingMap.end())
}
}
-bool CURLSOAPTransport::setAuth(transport_auth_t authType, const char* username, const char* password) const
+bool CURLSOAPTransport::setAuth(transport_auth_t authType, const char* username, const char* password)
{
if (authType==transport_auth_none) {
if (curl_easy_setopt(m_handle,CURLOPT_HTTPAUTH,0)!=CURLE_OK)
}
if (curl_easy_setopt(m_handle,CURLOPT_HTTPAUTH,flag)!=CURLE_OK)
return false;
- string creds = string(username ? username : "") + ':' + (password ? password : "");
- return (curl_easy_setopt(m_handle,CURLOPT_USERPWD,creds.c_str())==CURLE_OK);
+ m_simplecreds = string(username ? username : "") + ':' + (password ? password : "");
+ return (curl_easy_setopt(m_handle,CURLOPT_USERPWD,m_simplecreds.c_str())==CURLE_OK);
}
const vector<string>& CURLSOAPTransport::getResponseHeader(const char* name) const
Category& log=Category::getInstance(XMLTOOLING_LOGCAT".SOAPTransport");
Category& log_curl=Category::getInstance(XMLTOOLING_LOGCAT".libcurl");
+ string msg;
+
// By this time, the handle has been prepared with the URL to use and the
// caller should have executed any set functions to manipulate it.
// Setup standard per-call curl properties.
- curl_easy_setopt(m_handle,CURLOPT_POST,1);
- curl_easy_setopt(m_handle,CURLOPT_READDATA,&in);
- curl_easy_setopt(m_handle,CURLOPT_FILE,&m_stream);
curl_easy_setopt(m_handle,CURLOPT_DEBUGDATA,&log_curl);
+ curl_easy_setopt(m_handle,CURLOPT_FILE,&m_stream);
+ curl_easy_setopt(m_handle,CURLOPT_POST,1);
+ if (m_chunked) {
+ m_headers=curl_slist_append(m_headers,"Transfer-Encoding: chunked");
+ curl_easy_setopt(m_handle,CURLOPT_READFUNCTION,&curl_read_hook);
+ curl_easy_setopt(m_handle,CURLOPT_READDATA,&in);
+ }
+ else {
+ char buf[1024];
+ while (in) {
+ in.read(buf,1024);
+ msg.append(buf,in.gcount());
+ }
+ curl_easy_setopt(m_handle,CURLOPT_READFUNCTION,NULL);
+ curl_easy_setopt(m_handle,CURLOPT_POSTFIELDS,msg.c_str());
+ curl_easy_setopt(m_handle,CURLOPT_POSTFIELDSIZE,msg.length());
+ }
char curl_errorbuf[CURL_ERROR_SIZE];
curl_errorbuf[0]=0;
if (log_curl.isDebugEnabled())
curl_easy_setopt(m_handle,CURLOPT_VERBOSE,1);
- // Set request headers (possibly appended by hooks).
+ // Set request headers.
curl_easy_setopt(m_handle,CURLOPT_HTTPHEADER,m_headers);
- if (m_ssl_callback || m_credResolver || m_trustEngine) {
+#ifndef XMLTOOLING_NO_XMLSEC
+ if (m_ssl_callback || m_cred || m_trustEngine) {
+#else
+ if (m_ssl_callback) {
+#endif
curl_easy_setopt(m_handle,CURLOPT_SSL_CTX_FUNCTION,xml_ssl_ctx_callback);
curl_easy_setopt(m_handle,CURLOPT_SSL_CTX_DATA,this);
char* priv=NULL;
curl_easy_getinfo(m_handle,CURLINFO_PRIVATE,&priv);
if (priv)
- m_secure=true;
+ m_authenticated=true;
}
else {
curl_easy_setopt(m_handle,CURLOPT_SSL_CTX_FUNCTION,NULL);
// *ptr is actually a logging object
if (!ptr) return 0;
CategoryStream log=reinterpret_cast<Category*>(ptr)->debugStream();
- for (char* ch=data; len && (isprint(*ch) || isspace(*ch)); len--)
+ for (unsigned char* ch=(unsigned char*)data; len && (isprint(*ch) || isspace(*ch)); len--)
log << *ch++;
- log << CategoryStream::ENDLINE;
return 0;
}
);
#endif
- // Bypass name check (handled for us by curl).
- if (!ctx->m_trustEngine->validate(x509_ctx->cert,x509_ctx->untrusted,ctx->m_peer,false,ctx->m_keyResolver)) {
+ bool success=false;
+ if (ctx->m_criteria) {
+ ctx->m_criteria->setUsage(Credential::TLS_CREDENTIAL);
+ // Bypass name check (handled for us by curl).
+ ctx->m_criteria->setPeerName(NULL);
+ success = ctx->m_trustEngine->validate(x509_ctx->cert,x509_ctx->untrusted,*(ctx->m_peerResolver),ctx->m_criteria);
+ }
+ else {
+ // Bypass name check (handled for us by curl).
+ CredentialCriteria cc;
+ cc.setUsage(Credential::TLS_CREDENTIAL);
+ success = ctx->m_trustEngine->validate(x509_ctx->cert,x509_ctx->untrusted,*(ctx->m_peerResolver),&cc);
+ }
+
+ if (!success) {
log.error("supplied TrustEngine failed to validate SSL/TLS server certificate");
x509_ctx->error=X509_V_ERR_APPLICATION_VERIFICATION; // generic error, check log for plugin specifics
- ctx->setSecure(false);
+ ctx->setAuthenticated(false);
return ctx->m_mandatory ? 0 : 1;
}
// Signal success. Hopefully it doesn't matter what's actually in the structure now.
- ctx->setSecure(true);
+ ctx->setAuthenticated(true);
return 1;
}
#endif
CURLSOAPTransport* conf = reinterpret_cast<CURLSOAPTransport*>(userptr);
#ifndef XMLTOOLING_NO_XMLSEC
- if (conf->m_credResolver)
- conf->m_credResolver->attach(ssl_ctx);
+ if (conf->m_cred)
+ conf->m_cred->attach(ssl_ctx);
if (conf->m_trustEngine) {
SSL_CTX_set_verify(ssl_ctx,SSL_VERIFY_PEER,NULL);