From 9a982779eabc4fafafe18dc9ad1b44fb2425563c Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Wed, 19 May 2010 18:47:42 +0200 Subject: make QSslSocket::systemCaCertificates() use system certs before, we were shipping our own bundle of CA certificates. Now we read the certificates from the system CA store. Patch-by: Zeno Albisser and Peter Hartmann Reviewed-by: Thiago Task-number: QTBUG-8833 Task-number: QT-3361 --- src/network/ssl/qsslsocket.cpp | 8 +-- src/network/ssl/qsslsocket_openssl.cpp | 126 ++++++++++++++++++++++++++++----- src/network/ssl/qsslsocket_p.h | 24 +++++++ 3 files changed, 138 insertions(+), 20 deletions(-) diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index 0918539..a8c602a 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -1329,8 +1329,8 @@ void QSslSocket::setDefaultCaCertificates(const QList &certific /*! Returns the current default CA certificate database. This database is originally set to your system's default CA certificate database. - If no system default database is found, Qt will provide its own - default database. You can override the default CA certificate database + If no system default database is found, an empty database will be + returned. You can override the default CA certificate database with your own CA certificate database using setDefaultCaCertificates(). Each SSL socket's CA certificate database is initialized to the @@ -1344,8 +1344,8 @@ QList QSslSocket::defaultCaCertificates() } /*! - This function provides a default CA certificate database - shipped together with Qt. The CA certificate database + This function provides the CA certificate database + provided by the operating system. The CA certificate database returned by this function is used to initialize the database returned by defaultCaCertificates(). You can replace that database with your own with setDefaultCaCertificates(). diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 050fb1b..36a0cc7 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -57,12 +57,18 @@ #include #include #include - -static void initNetworkResources() -{ - // Initialize resources - Q_INIT_RESOURCE(network); -} +#include // for loading the security lib for the CA store + +#if defined(Q_OS_MAC) +#define kSecTrustSettingsDomainSystem 2 // so we do not need to include the header file + PtrSecCertificateGetData QSslSocketPrivate::ptrSecCertificateGetData = 0; + PtrSecTrustSettingsCopyCertificates QSslSocketPrivate::ptrSecTrustSettingsCopyCertificates = 0; + PtrSecTrustCopyAnchorCertificates QSslSocketPrivate::ptrSecTrustCopyAnchorCertificates = 0; +#elif defined(Q_OS_WIN) + PtrCertOpenSystemStoreW QSslSocketPrivate::ptrCertOpenSystemStoreW = 0; + PtrCertFindCertificateInStore QSslSocketPrivate::ptrCertFindCertificateInStore = 0; + PtrCertCloseStore QSslSocketPrivate::ptrCertCloseStore = 0; +#endif QT_BEGIN_NAMESPACE @@ -406,9 +412,6 @@ bool QSslSocketPrivate::ensureInitialized() if (!q_initialized) { q_initialized = true; - // Initialize resources - initNetworkResources(); - // Initialize OpenSSL. q_CRYPTO_set_id_callback(id_function); q_CRYPTO_set_locking_callback(locking_function); @@ -448,6 +451,36 @@ bool QSslSocketPrivate::ensureInitialized() resetDefaultCiphers(); setDefaultCaCertificates(systemCaCertificates()); } + + //load symbols needed to receive certificates from system store +#if defined(Q_OS_MAC) + QLibrary securityLib("/System/Library/Frameworks/Security.framework/Versions/Current/Security"); + if (securityLib.load()) { + ptrSecCertificateGetData = (PtrSecCertificateGetData) securityLib.resolve("SecCertificateGetData"); + if (!ptrSecCertificateGetData) + qWarning("could not resolve symbols in security library"); // should never happen + + ptrSecTrustSettingsCopyCertificates = (PtrSecTrustSettingsCopyCertificates) securityLib.resolve("SecTrustSettingsCopyCertificates"); + if (!ptrSecTrustSettingsCopyCertificates) { // method was introduced in Leopard, use legacy method if it's not there + ptrSecTrustCopyAnchorCertificates = (PtrSecTrustCopyAnchorCertificates) securityLib.resolve("SecTrustCopyAnchorCertificates"); + if (!ptrSecTrustCopyAnchorCertificates) + qWarning("could not resolve symbols in security library"); // should never happen + } + } else { + qWarning("could not load security library"); + } +#elif defined(Q_OS_WIN) + HINSTANCE hLib = LoadLibraryW(L"Crypt32"); + if (hLib) { + ptrCertOpenSystemStoreW = (PtrCertOpenSystemStoreW)GetProcAddress(hLib, "CertOpenSystemStoreW"); + ptrCertFindCertificateInStore = (PtrCertFindCertificateInStore)GetProcAddress(hLib, "CertFindCertificateInStore"); + ptrCertCloseStore = (PtrCertCloseStore)GetProcAddress(hLib, "CertCloseStore"); + if (!ptrCertOpenSystemStoreW || !ptrCertFindCertificateInStore || !ptrCertCloseStore) + qWarning("could not resolve symbols in crypt32 library"); // should never happen + } else { + qWarning("could not load crypt32 library"); // should never happen + } +#endif return true; } @@ -486,13 +519,74 @@ void QSslSocketPrivate::resetDefaultCiphers() QList QSslSocketPrivate::systemCaCertificates() { - // Qt provides a default bundle of certificates - QFile caBundle(QLatin1String(":/trolltech/network/ssl/qt-ca-bundle.crt")); - if (caBundle.open(QIODevice::ReadOnly | QIODevice::Text)) - return QSslCertificate::fromDevice(&caBundle); - - // Unreachable; return no bundle. - return QList(); + ensureInitialized(); + QList systemCerts; +#if defined(Q_OS_MAC) + CFArrayRef cfCerts; + OSStatus status = 1; + + OSStatus SecCertificateGetData ( + SecCertificateRef certificate, + CSSM_DATA_PTR data + ); + + if (ptrSecCertificateGetData) { + if (ptrSecTrustSettingsCopyCertificates) + status = ptrSecTrustSettingsCopyCertificates(kSecTrustSettingsDomainSystem, &cfCerts); + else if (ptrSecTrustCopyAnchorCertificates) + status = ptrSecTrustCopyAnchorCertificates(&cfCerts); + if (!status) { + CFIndex size = CFArrayGetCount(cfCerts); + for (CFIndex i = 0; i < size; ++i) { + SecCertificateRef cfCert = (SecCertificateRef)CFArrayGetValueAtIndex(cfCerts, i); + CSSM_DATA data; + CSSM_DATA_PTR dataPtr = &data; + if (ptrSecCertificateGetData(cfCert, dataPtr)) { + qWarning("error retrieving a CA certificate from the system store"); + } else { + int len = data.Length; + char *rawData = reinterpret_cast(data.Data); + QByteArray rawCert(rawData, len); + systemCerts.append(QSslCertificate::fromData(rawCert, QSsl::Der)); + } + } + } + else { + // no detailed error handling here + qWarning("could not retrieve system CA certificates"); + } + } +#elif defined(Q_OS_WIN) + if (ptrCertOpenSystemStoreW && ptrCertFindCertificateInStore && ptrCertCloseStore) { + HCERTSTORE hSystemStore; + hSystemStore = ptrCertOpenSystemStoreW(0, L"ROOT"); + if(hSystemStore) { + PCCERT_CONTEXT pc = NULL; + while(1) { + pc = ptrCertFindCertificateInStore( hSystemStore, X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, pc); + if(!pc) + break; + QByteArray der((const char *)(pc->pbCertEncoded), static_cast(pc->cbCertEncoded)); + QSslCertificate cert(der,QSsl::Der); + systemCerts.append(cert); + } + ptrCertCloseStore(hSystemStore, 0); + } + } +#elif defined(Q_OS_AIX) + systemCerts.append(QSslCertificate::fromPath("/var/ssl/certs/*.pem", QSsl::Pem, QRegExp::Wildcard)); +#elif defined(Q_OS_SOLARIS) + systemCerts.append(QSslCertificate::fromPath("/usr/local/ssl/certs/*.pem", QSsl::Pem, QRegExp::Wildcard)); +#elif defined(Q_OS_HPUX) + systemCerts.append(QSslCertificate::fromPath("/opt/openssl/certs/*.pem", QSsl::Pem, QRegExp::Wildcard)); +#elif defined(Q_OS_LINUX) + systemCerts.append(QSslCertificate::fromPath("/etc/ssl/certs/*.pem", QSsl::Pem, QRegExp::Wildcard)); // (K)ubuntu, OpenSUSE, Mandriva, ... + systemCerts.append(QSslCertificate::fromPath("/etc/pki/tls/certs/ca-bundle.crt", QSsl::Pem)); // Fedora + systemCerts.append(QSslCertificate::fromPath("/usr/lib/ssl/certs/*.pem", QSsl::Pem, QRegExp::Wildcard)); // Gentoo, Mandrake + systemCerts.append(QSslCertificate::fromPath("/usr/share/ssl/*.pem", QSsl::Pem, QRegExp::Wildcard)); // Centos, Redhat, SuSE + systemCerts.append(QSslCertificate::fromPath("/usr/local/ssl/*.pem", QSsl::Pem, QRegExp::Wildcard)); // Normal OpenSSL Tarball +#endif + return systemCerts; } void QSslSocketBackendPrivate::startClientEncryption() diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h index 8e22664..cf1786c 100644 --- a/src/network/ssl/qsslsocket_p.h +++ b/src/network/ssl/qsslsocket_p.h @@ -66,6 +66,20 @@ QT_BEGIN_NAMESPACE +#if defined(Q_OS_MAC) +#include +#include + typedef OSStatus (*PtrSecCertificateGetData)(SecCertificateRef, CSSM_DATA_PTR); + typedef OSStatus (*PtrSecTrustSettingsCopyCertificates)(int, CFArrayRef*); + typedef OSStatus (*PtrSecTrustCopyAnchorCertificates)(CFArrayRef*); +#elif defined(Q_OS_WIN) + typedef HCERTSTORE (WINAPI *PtrCertOpenSystemStoreW)(HCRYPTPROV_LEGACY, LPCWSTR); + typedef PCCERT_CONTEXT (WINAPI *PtrCertFindCertificateInStore)(HCERTSTORE, DWORD, DWORD, DWORD, const void*, PCCERT_CONTEXT); + typedef BOOL (WINAPI *PtrCertCloseStore)(HCERTSTORE, DWORD); +#endif + + + class QSslSocketPrivate : public QTcpSocketPrivate { Q_DECLARE_PUBLIC(QSslSocket) @@ -106,6 +120,16 @@ public: static void addDefaultCaCertificate(const QSslCertificate &cert); static void addDefaultCaCertificates(const QList &certs); +#if defined(Q_OS_MAC) + static PtrSecCertificateGetData ptrSecCertificateGetData; + static PtrSecTrustSettingsCopyCertificates ptrSecTrustSettingsCopyCertificates; + static PtrSecTrustCopyAnchorCertificates ptrSecTrustCopyAnchorCertificates; +#elif defined(Q_OS_WIN) + static PtrCertOpenSystemStoreW ptrCertOpenSystemStoreW; + static PtrCertFindCertificateInStore ptrCertFindCertificateInStore; + static PtrCertCloseStore ptrCertCloseStore; +#endif + // The socket itself, including private slots. QTcpSocket *plainSocket; void createPlainSocket(QIODevice::OpenMode openMode); -- cgit v0.12