X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=xmltooling%2Fsoap%2Fimpl%2FCURLSOAPTransport.cpp;h=60bc1c674c847cc81c406c88098efe2aa3e64cda;hb=dfe7b58aba11d21415249530add576e811417191;hp=460a62e40c494017a1bfc0e2b0b96ae58583d208;hpb=baf2e0386f3a071f88f2443662cfa55e61176563;p=shibboleth%2Fcpp-xmltooling.git diff --git a/xmltooling/soap/impl/CURLSOAPTransport.cpp b/xmltooling/soap/impl/CURLSOAPTransport.cpp index 460a62e..60bc1c6 100644 --- a/xmltooling/soap/impl/CURLSOAPTransport.cpp +++ b/xmltooling/soap/impl/CURLSOAPTransport.cpp @@ -1,23 +1,27 @@ -/* - * Copyright 2001-2009 Internet2 +/** + * Licensed to the University Corporation for Advanced Internet + * Development, Inc. (UCAID) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * UCAID licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the + * License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. */ /** * CURLSOAPTransport.cpp * - * libcurl-based SOAPTransport implementation + * libcurl-based SOAPTransport implementation. */ #include "internal.h" @@ -61,24 +65,25 @@ namespace xmltooling { Category& m_log; }; - static XMLTOOL_DLLLOCAL CURLPool* g_CURLPool = NULL; + static XMLTOOL_DLLLOCAL CURLPool* g_CURLPool = nullptr; class XMLTOOL_DLLLOCAL CURLSOAPTransport : public HTTPSOAPTransport, public OpenSSLSOAPTransport { public: 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), + m_handle(nullptr), m_headers(nullptr), #ifndef XMLTOOLING_NO_XMLSEC - m_cred(NULL), m_trustEngine(NULL), m_peerResolver(NULL), m_mandatory(false), + m_cred(nullptr), m_trustEngine(nullptr), m_peerResolver(nullptr), m_mandatory(false), #endif - m_ssl_callback(NULL), m_ssl_userptr(NULL), m_chunked(true), m_authenticated(false) { + m_openssl_ops(SSL_OP_ALL|SSL_OP_NO_SSLv2), m_ssl_callback(nullptr), m_ssl_userptr(nullptr), + m_chunked(true), m_authenticated(false), m_cacheTag(nullptr) { 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_USERPWD,nullptr); 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"); @@ -86,8 +91,9 @@ namespace xmltooling { virtual ~CURLSOAPTransport() { curl_slist_free_all(m_headers); - curl_easy_setopt(m_handle,CURLOPT_ERRORBUFFER,NULL); - curl_easy_setopt(m_handle,CURLOPT_PRIVATE,m_authenticated ? "secure" : NULL); // Save off security "state". + curl_easy_setopt(m_handle, CURLOPT_USERAGENT, nullptr); + curl_easy_setopt(m_handle, CURLOPT_ERRORBUFFER, nullptr); + curl_easy_setopt(m_handle, CURLOPT_PRIVATE, m_authenticated ? "secure" : nullptr); // Save off security "state". g_CURLPool->put(m_sender.c_str(), m_peerName.c_str(), m_endpoint.c_str(), m_handle); } @@ -103,35 +109,35 @@ namespace xmltooling { 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); + bool setAuth(transport_auth_t authType, const char* username=nullptr, const char* password=nullptr); bool setVerifyHost(bool verify) { return (curl_easy_setopt(m_handle,CURLOPT_SSL_VERIFYHOST,verify ? 2 : 0)==CURLE_OK); } #ifndef XMLTOOLING_NO_XMLSEC - bool setCredential(const Credential* cred=NULL) { + bool setCredential(const Credential* cred=nullptr) { const OpenSSLCredential* down = dynamic_cast(cred); if (!down) { - m_cred = NULL; - return (cred==NULL); + m_cred = nullptr; + return (cred==nullptr); } m_cred = down; return true; } bool setTrustEngine( - const X509TrustEngine* trustEngine=NULL, - const CredentialResolver* peerResolver=NULL, - CredentialCriteria* criteria=NULL, + const X509TrustEngine* trustEngine=nullptr, + const CredentialResolver* peerResolver=nullptr, + CredentialCriteria* criteria=nullptr, bool mandatory=true ) { const OpenSSLTrustEngine* down = dynamic_cast(trustEngine); if (!down) { - m_trustEngine = NULL; - m_peerResolver = NULL; - m_criteria = NULL; - return (trustEngine==NULL); + m_trustEngine = nullptr; + m_peerResolver = nullptr; + m_criteria = nullptr; + return (trustEngine==nullptr); } m_trustEngine = down; m_peerResolver = peerResolver; @@ -147,41 +153,25 @@ namespace xmltooling { 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(strtol(option, NULL, 10)); - if (opt < CURLOPTTYPE_OBJECTPOINT) - return (curl_easy_setopt(m_handle, opt, strtol(value, NULL, 10)) == CURLE_OK); -#ifdef CURLOPTTYPE_OFF_T - else if (opt < CURLOPTTYPE_OFF_T) { - if (value) - m_saved_options.push_back(value); - return (curl_easy_setopt(m_handle, opt, value ? m_saved_options.back().c_str() : NULL) == CURLE_OK); - } -# ifdef HAVE_CURL_OFF_T - else if (sizeof(curl_off_t) == sizeof(long)) - return (curl_easy_setopt(m_handle, opt, strtol(value, NULL, 10)) == CURLE_OK); -# else - else if (sizeof(off_t) == sizeof(long)) - return (curl_easy_setopt(m_handle, opt, strtol(value, NULL, 10)) == CURLE_OK); -# endif - return false; -#else - else { - if (value) - m_saved_options.push_back(value); - return (curl_easy_setopt(m_handle, opt, value ? m_saved_options.back().c_str() : NULL) == CURLE_OK); - } -#endif + bool followRedirects(bool follow, unsigned int maxRedirs) { + return ( + curl_easy_setopt(m_handle, CURLOPT_FOLLOWLOCATION, (follow ? 1 : 0)) == CURLE_OK && + curl_easy_setopt(m_handle, CURLOPT_MAXREDIRS, (follow ? maxRedirs : 0)) == CURLE_OK + ); } + bool setCacheTag(string* cacheTag) { + m_cacheTag = cacheTag; + return true; + } + + bool setProviderOption(const char* provider, const char* option, const char* value); + void send(istream& in) { send(&in); } - void send(istream* in=NULL); + void send(istream* in=nullptr); istream& receive() { return m_stream; @@ -196,6 +186,7 @@ namespace xmltooling { } string getContentType() const; + long getStatusCode() const; bool setRequestHeader(const char* name, const char* val) { string temp(name); @@ -206,7 +197,7 @@ namespace xmltooling { const vector& getResponseHeader(const char* val) const; - bool setSSLCallback(ssl_ctx_callback_fn fn, void* userptr=NULL) { + bool setSSLCallback(ssl_ctx_callback_fn fn, void* userptr=nullptr) { m_ssl_callback=fn; m_ssl_userptr=userptr; return true; @@ -218,6 +209,7 @@ namespace xmltooling { CURL* m_handle; stringstream m_stream; struct curl_slist* m_headers; + string m_useragent; map > m_response_headers; vector m_saved_options; #ifndef XMLTOOLING_NO_XMLSEC @@ -227,10 +219,12 @@ namespace xmltooling { CredentialCriteria* m_criteria; bool m_mandatory; #endif + int m_openssl_ops; ssl_ctx_callback_fn m_ssl_callback; void* m_ssl_userptr; bool m_chunked; bool m_authenticated; + string* m_cacheTag; 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); @@ -253,13 +247,6 @@ namespace xmltooling { } }; -void xmltooling::registerSOAPTransports() -{ - XMLToolingConfig& conf=XMLToolingConfig::getConfig(); - conf.SOAPTransportManager.registerFactory("http", CURLSOAPTransportFactory); - conf.SOAPTransportManager.registerFactory("https", CURLSOAPTransportFactory); -} - void xmltooling::initSOAPTransports() { g_CURLPool=new CURLPool(); @@ -268,36 +255,7 @@ void xmltooling::initSOAPTransports() void xmltooling::termSOAPTransports() { delete g_CURLPool; - g_CURLPool = NULL; -} - -SOAPTransport::SOAPTransport() -{ -} - -SOAPTransport::~SOAPTransport() -{ -} - -bool SOAPTransport::setProviderOption(const char* provider, const char* option, const char* value) -{ - return false; -} - -HTTPSOAPTransport::HTTPSOAPTransport() -{ -} - -HTTPSOAPTransport::~HTTPSOAPTransport() -{ -} - -OpenSSLSOAPTransport::OpenSSLSOAPTransport() -{ -} - -OpenSSLSOAPTransport::~OpenSSLSOAPTransport() -{ + g_CURLPool = nullptr; } CURLPool::~CURLPool() @@ -345,18 +303,17 @@ CURL* CURLPool::get(const SOAPTransport::Address& addr) // Create a new connection and set non-varying options. CURL* handle=curl_easy_init(); if (!handle) - return NULL; + return nullptr; 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_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_CAINFO,NULL); + curl_easy_setopt(handle,CURLOPT_CAINFO,nullptr); curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,&curl_header_hook); curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,&curl_write_hook); curl_easy_setopt(handle,CURLOPT_DEBUGFUNCTION,&curl_debug_hook); - return handle; } @@ -374,7 +331,7 @@ void CURLPool::put(const char* from, const char* to, const char* endpoint, CURL* else i->second.push_back(handle); - CURL* killit=NULL; + CURL* killit=nullptr; if (++m_size > 256) { // Kick a handle out from the back of the bus. while (true) { @@ -406,7 +363,7 @@ bool CURLSOAPTransport::setAuth(transport_auth_t authType, const char* username, if (authType==transport_auth_none) { if (curl_easy_setopt(m_handle,CURLOPT_HTTPAUTH,0)!=CURLE_OK) return false; - return (curl_easy_setopt(m_handle,CURLOPT_USERPWD,NULL)==CURLE_OK); + return (curl_easy_setopt(m_handle,CURLOPT_USERPWD,nullptr)==CURLE_OK); } long flag=0; switch (authType) { @@ -422,6 +379,60 @@ bool CURLSOAPTransport::setAuth(transport_auth_t authType, const char* username, return (curl_easy_setopt(m_handle,CURLOPT_USERPWD,m_simplecreds.c_str())==CURLE_OK); } +bool CURLSOAPTransport::setProviderOption(const char* provider, const char* option, const char* value) +{ + if (!provider || !option || !value) { + return false; + } + else if (!strcmp(provider, "OpenSSL")) { + if (!strcmp(option, "SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION") && (*value=='1' || *value=='t')) { + // If the new option to enable buggy rengotiation is available, set it. + // Otherwise, signal false if this is newer than 0.9.8k, because that + // means it's 0.9.8l, which blocks renegotiation, and therefore will + // not honor this request. Older versions are buggy, so behave as though + // the flag was set anyway, so we signal true. +#if defined(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION) + m_openssl_ops |= SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION; + return true; +#elif (OPENSSL_VERSION_NUMBER > 0x009080bfL) + return false; +#else + return true; +#endif + } + return false; + } + else if (strcmp(provider, "CURL")) { + return false; + } + + // For libcurl, the option is an enum and the value type depends on the option. + CURLoption opt = static_cast(strtol(option, nullptr, 10)); + if (opt < CURLOPTTYPE_OBJECTPOINT) + return (curl_easy_setopt(m_handle, opt, strtol(value, nullptr, 10)) == CURLE_OK); +#ifdef CURLOPTTYPE_OFF_T + else if (opt < CURLOPTTYPE_OFF_T) { + if (value) + m_saved_options.push_back(value); + return (curl_easy_setopt(m_handle, opt, value ? m_saved_options.back().c_str() : nullptr) == CURLE_OK); + } +# ifdef HAVE_CURL_OFF_T + else if (sizeof(curl_off_t) == sizeof(long)) + return (curl_easy_setopt(m_handle, opt, strtol(value, nullptr, 10)) == CURLE_OK); +# else + else if (sizeof(off_t) == sizeof(long)) + return (curl_easy_setopt(m_handle, opt, strtol(value, nullptr, 10)) == CURLE_OK); +# endif + return false; +#else + else { + if (value) + m_saved_options.push_back(value); + return (curl_easy_setopt(m_handle, opt, value ? m_saved_options.back().c_str() : nullptr) == CURLE_OK); + } +#endif +} + const vector& CURLSOAPTransport::getResponseHeader(const char* name) const { static vector emptyVector; @@ -444,11 +455,19 @@ const vector& CURLSOAPTransport::getResponseHeader(const char* name) con string CURLSOAPTransport::getContentType() const { - char* content_type=NULL; + char* content_type=nullptr; curl_easy_getinfo(m_handle,CURLINFO_CONTENT_TYPE,&content_type); return content_type ? content_type : ""; } +long CURLSOAPTransport::getStatusCode() const +{ + long code=200; + if (curl_easy_getinfo(m_handle, CURLINFO_RESPONSE_CODE, &code) != CURLE_OK) + code = 200; + return code; +} + void CURLSOAPTransport::send(istream* in) { #ifdef _DEBUG @@ -482,12 +501,14 @@ void CURLSOAPTransport::send(istream* in) msg.append(buf,in->gcount()); } curl_easy_setopt(m_handle,CURLOPT_POST,1); - curl_easy_setopt(m_handle,CURLOPT_READFUNCTION,NULL); + curl_easy_setopt(m_handle,CURLOPT_READFUNCTION,nullptr); curl_easy_setopt(m_handle,CURLOPT_POSTFIELDS,msg.c_str()); curl_easy_setopt(m_handle,CURLOPT_POSTFIELDSIZE,msg.length()); } else { curl_easy_setopt(m_handle,CURLOPT_HTTPGET,1); + curl_easy_setopt(m_handle,CURLOPT_FOLLOWLOCATION,1); + curl_easy_setopt(m_handle,CURLOPT_MAXREDIRS,6); } char curl_errorbuf[CURL_ERROR_SIZE]; @@ -496,6 +517,24 @@ void CURLSOAPTransport::send(istream* in) if (log_curl.isDebugEnabled()) curl_easy_setopt(m_handle,CURLOPT_VERBOSE,1); + // Check for cache tag. + if (m_cacheTag && !m_cacheTag->empty()) { + string hdr("If-None-Match: "); + hdr += *m_cacheTag; + m_headers = curl_slist_append(m_headers, hdr.c_str()); + } + + m_useragent = XMLToolingConfig::getConfig().user_agent; + if (!m_useragent.empty()) { + curl_version_info_data* curlver = curl_version_info(CURLVERSION_NOW); + m_useragent += " libcurl/"; + if (curlver) + m_useragent = m_useragent + curlver->version + ' ' + curlver->ssl_version; + else + m_useragent = m_useragent + LIBCURL_VERSION + ' ' + OPENSSL_VERSION_TEXT; + curl_easy_setopt(m_handle, CURLOPT_USERAGENT, m_useragent.c_str()); + } + // Set request headers. curl_easy_setopt(m_handle,CURLOPT_HTTPHEADER,m_headers); @@ -509,14 +548,14 @@ void CURLSOAPTransport::send(istream* in) // Restore security "state". Necessary because the callback only runs // when handshakes occur. Even new TCP connections won't execute it. - char* priv=NULL; + char* priv=nullptr; curl_easy_getinfo(m_handle,CURLINFO_PRIVATE,&priv); if (priv) m_authenticated=true; } else { - curl_easy_setopt(m_handle,CURLOPT_SSL_CTX_FUNCTION,NULL); - curl_easy_setopt(m_handle,CURLOPT_SSL_CTX_DATA,NULL); + curl_easy_setopt(m_handle,CURLOPT_SSL_CTX_FUNCTION,nullptr); + curl_easy_setopt(m_handle,CURLOPT_SSL_CTX_DATA,nullptr); } // Make the call. @@ -526,6 +565,13 @@ void CURLSOAPTransport::send(istream* in) string("CURLSOAPTransport failed while contacting SOAP endpoint (") + m_endpoint + "): " + (curl_errorbuf[0] ? curl_errorbuf : "no further information available")); } + + // Check for outgoing cache tag. + if (m_cacheTag) { + const vector& tags = getResponseHeader("ETag"); + if (!tags.empty()) + *m_cacheTag = tags.front(); + } } // callback to buffer headers from server @@ -606,7 +652,7 @@ int xmltooling::verify_callback(X509_STORE_CTX* x509_ctx, void* arg) if (ctx->m_criteria) { ctx->m_criteria->setUsage(Credential::TLS_CREDENTIAL); // Bypass name check (handled for us by curl). - ctx->m_criteria->setPeerName(NULL); + ctx->m_criteria->setPeerName(nullptr); success = ctx->m_trustEngine->validate(x509_ctx->cert,x509_ctx->untrusted,*(ctx->m_peerResolver),ctx->m_criteria); } else { @@ -617,8 +663,19 @@ int xmltooling::verify_callback(X509_STORE_CTX* x509_ctx, void* arg) } 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 + log.error("supplied TrustEngine failed to validate SSL/TLS server certificate:"); + if (x509_ctx->cert) { + BIO* b = BIO_new(BIO_s_mem()); + X509_print(b, x509_ctx->cert); + BUF_MEM* bptr = nullptr; + BIO_get_mem_ptr(b, &bptr); + if (bptr && bptr->length > 0) { + string s(bptr->data, bptr->length); + log.error(s); + } + BIO_free(b); + } + x509_ctx->error = X509_V_ERR_APPLICATION_VERIFICATION; // generic error, check log for plugin specifics ctx->setAuthenticated(false); return ctx->m_mandatory ? 0 : 1; } @@ -634,13 +691,13 @@ CURLcode xmltooling::xml_ssl_ctx_callback(CURL* curl, SSL_CTX* ssl_ctx, void* us { CURLSOAPTransport* conf = reinterpret_cast(userptr); - // Manually disable SSLv2 so we're not dependent on libcurl to do it. + // Default flags manually disable SSLv2 so we're not dependent on libcurl to do it. // Also disable the ticket option where implemented, since this breaks a variety // of servers. Newer libcurl also does this for us. #ifdef SSL_OP_NO_TICKET - SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_TICKET); + SSL_CTX_set_options(ssl_ctx, conf->m_openssl_ops|SSL_OP_NO_TICKET); #else - SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2); + SSL_CTX_set_options(ssl_ctx, conf->m_openssl_ops); #endif #ifndef XMLTOOLING_NO_XMLSEC @@ -648,7 +705,7 @@ CURLcode xmltooling::xml_ssl_ctx_callback(CURL* curl, SSL_CTX* ssl_ctx, void* us conf->m_cred->attach(ssl_ctx); if (conf->m_trustEngine) { - SSL_CTX_set_verify(ssl_ctx,SSL_VERIFY_PEER,NULL); + SSL_CTX_set_verify(ssl_ctx,SSL_VERIFY_PEER,nullptr); #if (OPENSSL_VERSION_NUMBER >= 0x00907000L) // With 0.9.7, we can pass a callback argument directly. SSL_CTX_set_cert_verify_callback(ssl_ctx,verify_callback,userptr); @@ -656,7 +713,7 @@ CURLcode xmltooling::xml_ssl_ctx_callback(CURL* curl, SSL_CTX* ssl_ctx, void* us // With 0.9.6, there's no argument, so we're going to use a really embarrassing hack and // stuff the argument in the depth property where it will get copied to the context object // that's handed to the callback. - SSL_CTX_set_cert_verify_callback(ssl_ctx,reinterpret_cast(verify_callback),NULL); + SSL_CTX_set_cert_verify_callback(ssl_ctx,reinterpret_cast(verify_callback),nullptr); SSL_CTX_set_verify_depth(ssl_ctx,reinterpret_cast(userptr)); #endif }