#include "config.h"
#include "CurlSSLHandle.h"
#if USE(CURL)
#if USE(CF)
#if OS(WINDOWS)
#include "WebCoreBundleWin.h"
#endif
#include <wtf/RetainPtr.h>
#endif
#if NEED_OPENSSL_THREAD_SUPPORT && OS(WINDOWS)
#include <wtf/Threading.h>
#endif
namespace WebCore {
CurlSSLHandle::CurlSSLHandle()
{
auto caCertPath = getCACertPathEnv();
if (!caCertPath.isEmpty())
setCACertPath(WTFMove(caCertPath));
#if NEED_OPENSSL_THREAD_SUPPORT
ThreadSupport::setup();
#endif
}
String CurlSSLHandle::getCACertPathEnv()
{
char* envPath = getenv("CURL_CA_BUNDLE_PATH");
if (envPath)
return String(envPath);
#if USE(CF)
CFBundleRef webKitBundleRef = webKitBundle();
if (webKitBundleRef) {
RetainPtr<CFURLRef> certURLRef = adoptCF(CFBundleCopyResourceURL(webKitBundleRef, CFSTR("cacert"), CFSTR("pem"), CFSTR("certificates")));
if (certURLRef) {
char path[MAX_PATH];
if (CFURLGetFileSystemRepresentation(certURLRef.get(), false, reinterpret_cast<UInt8*>(path), MAX_PATH) && *path)
return String(path);
}
}
#endif
return String();
}
void CurlSSLHandle::setCACertPath(String&& caCertPath)
{
RELEASE_ASSERT(!caCertPath.isEmpty());
m_caCertInfo = WTFMove(caCertPath);
}
void CurlSSLHandle::setCACertData(CertificateInfo::Certificate&& caCertData)
{
RELEASE_ASSERT(!caCertData.isEmpty());
m_caCertInfo = WTFMove(caCertData);
}
void CurlSSLHandle::clearCACertInfo()
{
m_caCertInfo = WTF::Monostate { };
}
void CurlSSLHandle::setHostAllowsAnyHTTPSCertificate(const String& hostName)
{
LockHolder mutex(m_mutex);
m_allowedHosts.set(hostName, Vector<CertificateInfo::Certificate> { });
}
bool CurlSSLHandle::isAllowedHTTPSCertificateHost(const String& hostName)
{
LockHolder mutex(m_mutex);
auto it = m_allowedHosts.find(hostName);
return (it != m_allowedHosts.end());
}
bool CurlSSLHandle::canIgnoredHTTPSCertificate(const String& hostName, const Vector<CertificateInfo::Certificate>& certificates)
{
LockHolder mutex(m_mutex);
auto found = m_allowedHosts.find(hostName);
if (found == m_allowedHosts.end())
return false;
auto& value = found->value;
if (value.isEmpty()) {
value = certificates;
return true;
}
return std::equal(certificates.begin(), certificates.end(), value.begin());
}
void CurlSSLHandle::setClientCertificateInfo(const String& hostName, const String& certificate, const String& key)
{
LockHolder mutex(m_mutex);
m_allowedClientHosts.set(hostName, ClientCertificate { certificate, key });
}
std::optional<CurlSSLHandle::ClientCertificate> CurlSSLHandle::getSSLClientCertificate(const String& hostName)
{
LockHolder mutex(m_mutex);
auto it = m_allowedClientHosts.find(hostName);
if (it == m_allowedClientHosts.end())
return std::nullopt;
return it->value;
}
#if NEED_OPENSSL_THREAD_SUPPORT
void CurlSSLHandle::ThreadSupport::setup()
{
static std::once_flag onceFlag;
std::call_once(onceFlag, [] {
singleton();
});
}
CurlSSLHandle::ThreadSupport::ThreadSupport()
{
CRYPTO_set_locking_callback(lockingCallback);
#if OS(WINDOWS)
CRYPTO_THREADID_set_callback(threadIdCallback);
#endif
}
void CurlSSLHandle::ThreadSupport::lockingCallback(int mode, int type, const char*, int)
{
RELEASE_ASSERT(type >= 0 && type < CRYPTO_NUM_LOCKS);
auto& locker = ThreadSupport::singleton();
if (mode & CRYPTO_LOCK)
locker.lock(type);
else
locker.unlock(type);
}
#if OS(WINDOWS)
void CurlSSLHandle::ThreadSupport::threadIdCallback(CRYPTO_THREADID* threadId)
{
CRYPTO_THREADID_set_numeric(threadId, static_cast<unsigned long>(Thread::currentID()));
}
#endif
#endif
}
#endif