From 2f17e23247c6b032bb5b5965a029c7c7a4ce58be Mon Sep 17 00:00:00 2001 From: cantor Date: Sat, 13 Mar 2010 20:42:04 +0000 Subject: [PATCH] https://bugs.internet2.edu/jira/browse/CPPXT-56 git-svn-id: https://svn.middleware.georgetown.edu/cpp-xmltooling/branches/REL_1@723 de75baf8-a10c-0410-a50a-987c0e22f00f --- xmltooling/soap/impl/CURLSOAPTransport.cpp | 94 +++++++++++++++++++----------- xmltooling/util/CurlURLInputStream.cpp | 44 ++++++++++++-- xmltooling/util/CurlURLInputStream.h | 10 ++++ 3 files changed, 109 insertions(+), 39 deletions(-) diff --git a/xmltooling/soap/impl/CURLSOAPTransport.cpp b/xmltooling/soap/impl/CURLSOAPTransport.cpp index e7a1b1e..de49f99 100644 --- a/xmltooling/soap/impl/CURLSOAPTransport.cpp +++ b/xmltooling/soap/impl/CURLSOAPTransport.cpp @@ -72,7 +72,8 @@ namespace xmltooling { #ifndef XMLTOOLING_NO_XMLSEC m_cred(NULL), m_trustEngine(NULL), m_peerResolver(NULL), m_mandatory(false), #endif - m_ssl_callback(NULL), m_ssl_userptr(NULL), m_chunked(true), m_authenticated(false), m_cacheTag(NULL) { + m_openssl_ops(SSL_OP_ALL|SSL_OP_NO_SSLv2), m_ssl_callback(NULL), m_ssl_userptr(NULL), + m_chunked(true), m_authenticated(false), m_cacheTag(NULL) { m_handle = g_CURLPool->get(addr); curl_easy_setopt(m_handle,CURLOPT_URL,addr.m_endpoint); curl_easy_setopt(m_handle,CURLOPT_CONNECTTIMEOUT,15); @@ -152,35 +153,7 @@ 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 setProviderOption(const char* provider, const char* option, const char* value); void send(istream& in) { send(&in); @@ -233,6 +206,7 @@ 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; @@ -408,6 +382,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, 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 +} + const vector& CURLSOAPTransport::getResponseHeader(const char* name) const { static vector emptyVector; @@ -642,13 +670,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 diff --git a/xmltooling/util/CurlURLInputStream.cpp b/xmltooling/util/CurlURLInputStream.cpp index e2d44c9..92ac8b1 100644 --- a/xmltooling/util/CurlURLInputStream.cpp +++ b/xmltooling/util/CurlURLInputStream.cpp @@ -43,7 +43,8 @@ using namespace xercesc; using namespace std; namespace { - static const XMLCh _CURL[] = UNICODE_LITERAL_4(C,U,R,L); + static const XMLCh _CURL[] = UNICODE_LITERAL_4(C,U,R,L); + static const XMLCh _OpenSSL[] = UNICODE_LITERAL_7(O,p,e,n,S,S,L); static const XMLCh _option[] = UNICODE_LITERAL_6(o,p,t,i,o,n); static const XMLCh _provider[] = UNICODE_LITERAL_8(p,r,o,v,i,d,e,r); static const XMLCh TransportOption[] = UNICODE_LITERAL_15(T,r,a,n,s,p,o,r,t,O,p,t,i,o,n); @@ -54,13 +55,15 @@ namespace { // callback to invoke a caller-defined SSL callback CURLcode ssl_ctx_callback(CURL* curl, SSL_CTX* ssl_ctx, void* userptr) { - // Manually disable SSLv2 so we're not dependent on libcurl to do it. + CurlURLInputStream* str = reinterpret_cast(userptr); + + // 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, str->getOpenSSLOps()|SSL_OP_NO_TICKET); #else - SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2); + SSL_CTX_set_options(ssl_ctx, str->getOpenSSLOps()); #endif return CURLE_OK; @@ -103,6 +106,7 @@ namespace { CurlURLInputStream::CurlURLInputStream(const char* url, string* cacheTag) : fLog(logging::Category::getInstance(XMLTOOLING_LOGCAT".libcurl.InputStream")) , fCacheTag(cacheTag) + , fOpenSSLOps(SSL_OP_ALL|SSL_OP_NO_SSLv2) , fURL(url ? url : "") , fMulti(0) , fEasy(0) @@ -125,6 +129,7 @@ CurlURLInputStream::CurlURLInputStream(const char* url, string* cacheTag) CurlURLInputStream::CurlURLInputStream(const XMLCh* url, string* cacheTag) : fLog(logging::Category::getInstance(XMLTOOLING_LOGCAT".libcurl.InputStream")) , fCacheTag(cacheTag) + , fOpenSSLOps(SSL_OP_ALL|SSL_OP_NO_SSLv2) , fMulti(0) , fEasy(0) , fHeaders(0) @@ -150,6 +155,7 @@ CurlURLInputStream::CurlURLInputStream(const XMLCh* url, string* cacheTag) CurlURLInputStream::CurlURLInputStream(const DOMElement* e, string* cacheTag) : fLog(logging::Category::getInstance(XMLTOOLING_LOGCAT".libcurl.InputStream")) , fCacheTag(cacheTag) + , fOpenSSLOps(SSL_OP_ALL|SSL_OP_NO_SSLv2) , fMulti(0) , fEasy(0) , fHeaders(0) @@ -233,6 +239,7 @@ void CurlURLInputStream::init(const DOMElement* e) // Install SSL callback. curl_easy_setopt(fEasy, CURLOPT_SSL_CTX_FUNCTION, ssl_ctx_callback); + curl_easy_setopt(fEasy, CURLOPT_SSL_CTX_DATA, this); fError[0] = 0; curl_easy_setopt(fEasy, CURLOPT_ERRORBUFFER, fError); @@ -260,7 +267,32 @@ void CurlURLInputStream::init(const DOMElement* e) bool success; DOMElement* child = XMLHelper::getLastChildElement(e, TransportOption); while (child) { - if (child->hasChildNodes() && XMLString::equals(child->getAttributeNS(NULL,_provider), _CURL)) { + if (child->hasChildNodes() && XMLString::equals(child->getAttributeNS(NULL,_provider), _OpenSSL)) { + auto_ptr_char option(child->getAttributeNS(NULL,_option)); + auto_ptr_char value(child->getFirstChild()->getNodeValue()); + if (option.get() && value.get() && !strcmp(option.get(), "SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION") && + (*value.get()=='1' || *value.get()=='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) + fOpenSSLOps |= SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION; + success = true; +#elif (OPENSSL_VERSION_NUMBER > 0x009080bfL) + success = false; +#else + success = true; +#endif + } + else { + success = false; + } + if (!success) + fLog.error("failed to set OpenSSL transport option (%s)", option.get()); + } + else if (child->hasChildNodes() && XMLString::equals(child->getAttributeNS(NULL,_provider), _CURL)) { auto_ptr_char option(child->getAttributeNS(NULL,_option)); auto_ptr_char value(child->getFirstChild()->getNodeValue()); if (option.get() && *option.get() && value.get() && *value.get()) { @@ -289,7 +321,7 @@ void CurlURLInputStream::init(const DOMElement* e) } #endif if (!success) - fLog.error("failed to set transport option (%s)", option.get()); + fLog.error("failed to set CURL transport option (%s)", option.get()); } } child = XMLHelper::getPreviousSiblingElement(child, TransportOption); diff --git a/xmltooling/util/CurlURLInputStream.h b/xmltooling/util/CurlURLInputStream.h index a2be81e..3adf169 100644 --- a/xmltooling/util/CurlURLInputStream.h +++ b/xmltooling/util/CurlURLInputStream.h @@ -92,6 +92,15 @@ namespace xmltooling { xsecsize_t readBytes(XMLByte* const toFill, const xsecsize_t maxToRead); + /** + * Access the OpenSSL context options in place for this object. + * + * @return bitmask suitable for use with SSL_CTX_set_options + */ + int getOpenSSLOps() const { + return fOpenSSLOps; + } + private : CurlURLInputStream(const CurlURLInputStream&); CurlURLInputStream& operator=(const CurlURLInputStream&); @@ -107,6 +116,7 @@ namespace xmltooling { std::string* fCacheTag; std::string fURL; std::vector fSavedOptions; + int fOpenSSLOps; CURLM* fMulti; CURL* fEasy; -- 2.1.4