summaryrefslogtreecommitdiffstats
path: root/src/network/ssl/qsslsocket_openssl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/ssl/qsslsocket_openssl.cpp')
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp275
1 files changed, 242 insertions, 33 deletions
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index fa26fe8..c297eea 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -68,6 +68,8 @@
PtrCertOpenSystemStoreW QSslSocketPrivate::ptrCertOpenSystemStoreW = 0;
PtrCertFindCertificateInStore QSslSocketPrivate::ptrCertFindCertificateInStore = 0;
PtrCertCloseStore QSslSocketPrivate::ptrCertCloseStore = 0;
+#elif defined(Q_OS_SYMBIAN)
+#include <QtCore/private/qcore_symbian_p.h>
#endif
QT_BEGIN_NAMESPACE
@@ -197,7 +199,7 @@ QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(SSL_CIPHER *ciph
ciph.d->protocol = QSsl::SslV2;
else if (protoString == QLatin1String("TLSv1"))
ciph.d->protocol = QSsl::TlsV1;
-
+
if (descriptionList.at(2).startsWith(QLatin1String("Kx=")))
ciph.d->keyExchangeMethod = descriptionList.at(2).mid(3);
if (descriptionList.at(3).startsWith(QLatin1String("Au=")))
@@ -299,8 +301,20 @@ init_context:
}
// Add all our CAs to this store.
- foreach (const QSslCertificate &caCertificate, q->caCertificates())
+ QList<QSslCertificate> expiredCerts;
+ foreach (const QSslCertificate &caCertificate, q->caCertificates()) {
+ // add expired certs later, so that the
+ // valid ones are used before the expired ones
+ if (! caCertificate.isValid()) {
+ expiredCerts.append(caCertificate);
+ } else {
+ q_X509_STORE_add_cert(ctx->cert_store, (X509 *)caCertificate.handle());
+ }
+ }
+ // now add the expired certs
+ foreach (const QSslCertificate &caCertificate, expiredCerts) {
q_X509_STORE_add_cert(ctx->cert_store, (X509 *)caCertificate.handle());
+ }
// Register a custom callback to get all verification errors.
X509_STORE_set_verify_cb_func(ctx->cert_store, q_X509Callback);
@@ -353,7 +367,7 @@ init_context:
// Set verification depth.
if (configuration.peerVerifyDepth != 0)
q_SSL_CTX_set_verify_depth(ctx, configuration.peerVerifyDepth);
-
+
// Create and initialize SSL session
if (!(ssl = q_SSL_new(ctx))) {
// ### Bad error code
@@ -461,27 +475,12 @@ bool QSslSocketPrivate::ensureLibraryLoaded()
void QSslSocketPrivate::ensureCiphersAndCertsLoaded()
{
+ QMutexLocker locker(openssl_locks()->initLock());
if (s_loadedCiphersAndCerts)
return;
s_loadedCiphersAndCerts = true;
resetDefaultCiphers();
- setDefaultCaCertificates(systemCaCertificates());
-}
-
-/*!
- \internal
-
- Declared static in QSslSocketPrivate, makes sure the SSL libraries have
- been initialized.
-*/
-
-void QSslSocketPrivate::ensureInitialized()
-{
- if (!supportsSsl())
- return;
-
- ensureCiphersAndCertsLoaded();
//load symbols needed to receive certificates from system store
#if defined(Q_OS_MAC)
@@ -503,15 +502,37 @@ void QSslSocketPrivate::ensureInitialized()
#elif defined(Q_OS_WIN)
HINSTANCE hLib = LoadLibraryW(L"Crypt32");
if (hLib) {
+#if defined(Q_OS_WINCE)
+ ptrCertOpenSystemStoreW = (PtrCertOpenSystemStoreW)GetProcAddress(hLib, L"CertOpenStore");
+ ptrCertFindCertificateInStore = (PtrCertFindCertificateInStore)GetProcAddress(hLib, L"CertFindCertificateInStore");
+ ptrCertCloseStore = (PtrCertCloseStore)GetProcAddress(hLib, L"CertCloseStore");
+#else
ptrCertOpenSystemStoreW = (PtrCertOpenSystemStoreW)GetProcAddress(hLib, "CertOpenSystemStoreW");
ptrCertFindCertificateInStore = (PtrCertFindCertificateInStore)GetProcAddress(hLib, "CertFindCertificateInStore");
ptrCertCloseStore = (PtrCertCloseStore)GetProcAddress(hLib, "CertCloseStore");
+#endif
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
+ setDefaultCaCertificates(systemCaCertificates());
+}
+
+/*!
+ \internal
+
+ Declared static in QSslSocketPrivate, makes sure the SSL libraries have
+ been initialized.
+*/
+
+void QSslSocketPrivate::ensureInitialized()
+{
+ if (!supportsSsl())
+ return;
+
+ ensureCiphersAndCertsLoaded();
}
/*!
@@ -547,6 +568,124 @@ void QSslSocketPrivate::resetDefaultCiphers()
setDefaultCiphers(ciphers);
}
+#if defined(Q_OS_SYMBIAN)
+
+QCertificateRetriever::QCertificateRetriever(QCertificateConsumer* parent)
+ : CActive(EPriorityStandard)
+ , certStore(0)
+ , certFilter(0)
+ , consumer(parent)
+ , currentCertificateIndex(0)
+ , certDescriptor(0, 0)
+{
+ CActiveScheduler::Add(this);
+ QT_TRAP_THROWING(certStore = CUnifiedCertStore::NewL(qt_s60GetRFs(), EFalse));
+ QT_TRAP_THROWING(certFilter = CCertAttributeFilter::NewL());
+ certFilter->SetFormat(EX509Certificate);
+}
+
+QCertificateRetriever::~QCertificateRetriever()
+{
+ delete certFilter;
+ delete certStore;
+ Cancel();
+}
+
+void QCertificateRetriever::fetch()
+{
+ certStore->Initialize(iStatus);
+ state = Initializing;
+ SetActive();
+}
+
+void QCertificateRetriever::list()
+{
+ certStore->List(certs, *certFilter, iStatus);
+ state = Listing;
+ SetActive();
+}
+
+void QCertificateRetriever::retrieveNextCertificate()
+{
+ CCTCertInfo* cert = certs[currentCertificateIndex];
+ currentCertificate.resize(cert->Size());
+ certDescriptor.Set((TUint8*)currentCertificate.data(), 0, currentCertificate.size());
+ certStore->Retrieve(*cert, certDescriptor, iStatus);
+ state = RetrievingCertificates;
+ SetActive();
+}
+
+void QCertificateRetriever::RunL()
+{
+ QT_TRYCATCH_LEAVING(run());
+}
+
+void QCertificateRetriever::run()
+{
+ switch (state) {
+ case Initializing:
+ list();
+ break;
+ case Listing:
+ currentCertificateIndex = 0;
+ retrieveNextCertificate();
+ break;
+ case RetrievingCertificates:
+ consumer->addEncodedCertificate(currentCertificate);
+ currentCertificate = QByteArray();
+
+ currentCertificateIndex++;
+
+ if (currentCertificateIndex < certs.Count())
+ retrieveNextCertificate();
+ else
+ consumer->finish();
+ break;
+ }
+}
+
+void QCertificateRetriever::DoCancel()
+{
+ switch (state) {
+ case Initializing:
+ certStore->CancelInitialize();
+ break;
+ case Listing:
+ certStore->CancelList();
+ break;
+ case RetrievingCertificates:
+ certStore->CancelRetrieve();
+ break;
+ }
+}
+
+QCertificateConsumer::QCertificateConsumer(QObject* parent)
+ : QObject(parent)
+ , retriever(0)
+{
+}
+
+QCertificateConsumer::~QCertificateConsumer()
+{
+ delete retriever;
+}
+
+void QCertificateConsumer::finish()
+{
+ delete retriever;
+ retriever = 0;
+ emit finished();
+}
+
+void QCertificateConsumer::start()
+{
+ retriever = new QCertificateRetriever(this);
+ Q_CHECK_PTR(retriever);
+ retriever->fetch();
+}
+
+#endif // defined(Q_OS_SYMBIAN)
+
QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
{
ensureInitialized();
@@ -589,7 +728,15 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
#elif defined(Q_OS_WIN)
if (ptrCertOpenSystemStoreW && ptrCertFindCertificateInStore && ptrCertCloseStore) {
HCERTSTORE hSystemStore;
+#if defined(Q_OS_WINCE)
+ hSystemStore = ptrCertOpenSystemStoreW(CERT_STORE_PROV_SYSTEM_W,
+ 0,
+ 0,
+ CERT_STORE_NO_CRYPT_RELEASE_FLAG|CERT_SYSTEM_STORE_CURRENT_USER,
+ L"ROOT");
+#else
hSystemStore = ptrCertOpenSystemStoreW(0, L"ROOT");
+#endif
if(hSystemStore) {
PCCERT_CONTEXT pc = NULL;
while(1) {
@@ -597,25 +744,41 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
if(!pc)
break;
QByteArray der((const char *)(pc->pbCertEncoded), static_cast<int>(pc->cbCertEncoded));
- QSslCertificate cert(der,QSsl::Der);
+ QSslCertificate cert(der, QSsl::Der);
systemCerts.append(cert);
}
ptrCertCloseStore(hSystemStore, 0);
}
}
-#elif defined(Q_OS_AIX)
- systemCerts.append(QSslCertificate::fromPath(QLatin1String("/var/ssl/certs/*.pem"), QSsl::Pem, QRegExp::Wildcard));
-#elif defined(Q_OS_SOLARIS)
- systemCerts.append(QSslCertificate::fromPath(QLatin1String("/usr/local/ssl/certs/*.pem"), QSsl::Pem, QRegExp::Wildcard));
-#elif defined(Q_OS_HPUX)
- systemCerts.append(QSslCertificate::fromPath(QLatin1String("/opt/openssl/certs/*.pem"), QSsl::Pem, QRegExp::Wildcard));
-#elif defined(Q_OS_LINUX)
+#elif defined(Q_OS_UNIX)
+ systemCerts.append(QSslCertificate::fromPath(QLatin1String("/var/ssl/certs/*.pem"), QSsl::Pem, QRegExp::Wildcard)); // AIX
+ systemCerts.append(QSslCertificate::fromPath(QLatin1String("/usr/local/ssl/certs/*.pem"), QSsl::Pem, QRegExp::Wildcard)); // Solaris
+ systemCerts.append(QSslCertificate::fromPath(QLatin1String("/opt/openssl/certs/*.pem"), QSsl::Pem, QRegExp::Wildcard)); // HP-UX
systemCerts.append(QSslCertificate::fromPath(QLatin1String("/etc/ssl/certs/*.pem"), QSsl::Pem, QRegExp::Wildcard)); // (K)ubuntu, OpenSUSE, Mandriva, ...
systemCerts.append(QSslCertificate::fromPath(QLatin1String("/etc/pki/tls/certs/ca-bundle.crt"), QSsl::Pem)); // Fedora
systemCerts.append(QSslCertificate::fromPath(QLatin1String("/usr/lib/ssl/certs/*.pem"), QSsl::Pem, QRegExp::Wildcard)); // Gentoo, Mandrake
systemCerts.append(QSslCertificate::fromPath(QLatin1String("/usr/share/ssl/*.pem"), QSsl::Pem, QRegExp::Wildcard)); // Centos, Redhat, SuSE
systemCerts.append(QSslCertificate::fromPath(QLatin1String("/usr/local/ssl/*.pem"), QSsl::Pem, QRegExp::Wildcard)); // Normal OpenSSL Tarball
+#elif defined(Q_OS_SYMBIAN)
+ QThread* certThread = new QThread;
+
+ QCertificateConsumer *consumer = new QCertificateConsumer();
+ consumer->moveToThread(certThread);
+ QObject::connect(certThread, SIGNAL(started()), consumer, SLOT(start()));
+ QObject::connect(consumer, SIGNAL(finished()), certThread, SLOT(quit()), Qt::DirectConnection);
+
+ certThread->start();
+ certThread->wait();
+ foreach (const QByteArray &encodedCert, consumer->encodedCertificates()) {
+ QSslCertificate cert(encodedCert, QSsl::Der);
+ if (!cert.isNull())
+ systemCerts.append(cert);
+ }
+
+ delete consumer;
+ delete certThread;
#endif
+
return systemCerts;
}
@@ -661,7 +824,7 @@ void QSslSocketBackendPrivate::transmit()
bool transmitting;
do {
transmitting = false;
-
+
// If the connection is secure, we can transfer data from the write
// buffer (in plain text) to the write BIO through SSL_write.
if (connectionEncrypted && !writeBuffer.isEmpty()) {
@@ -808,8 +971,20 @@ void QSslSocketBackendPrivate::transmit()
#endif
plainSocket->disconnectFromHost();
break;
+ case SSL_ERROR_SYSCALL: // some IO error
+ case SSL_ERROR_SSL: // error in the SSL library
+ // we do not know exactly what the error is, nor whether we can recover from it,
+ // so just return to prevent an endless loop in the outer "while" statement
+ q->setErrorString(QSslSocket::tr("Error while reading: %1").arg(SSL_ERRORSTR()));
+ q->setSocketError(QAbstractSocket::UnknownSocketError);
+ emit q->error(QAbstractSocket::UnknownSocketError);
+ return;
default:
- // ### Handle errors better.
+ // SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT: can only happen with a
+ // BIO_s_connect() or BIO_s_accept(), which we do not call.
+ // SSL_ERROR_WANT_X509_LOOKUP: can only happen with a
+ // SSL_CTX_set_client_cert_cb(), which we do not call.
+ // So this default case should never be triggered.
q->setErrorString(QSslSocket::tr("Error while reading: %1").arg(SSL_ERRORSTR()));
q->setSocketError(QAbstractSocket::UnknownSocketError);
emit q->error(QAbstractSocket::UnknownSocketError);
@@ -943,17 +1118,16 @@ bool QSslSocketBackendPrivate::startHandshake()
QString peerName = (verificationPeerName.isEmpty () ? q->peerName() : verificationPeerName);
QString commonName = configuration.peerCertificate.subjectInfo(QSslCertificate::CommonName);
- QRegExp regexp(commonName, Qt::CaseInsensitive, QRegExp::Wildcard);
- if (!regexp.exactMatch(peerName)) {
+ if (!isMatchingHostname(commonName.toLower(), peerName.toLower())) {
bool matched = false;
foreach (const QString &altName, configuration.peerCertificate
.alternateSubjectNames().values(QSsl::DnsEntry)) {
- regexp.setPattern(altName);
- if (regexp.exactMatch(peerName)) {
+ if (isMatchingHostname(altName.toLower(), peerName.toLower())) {
matched = true;
break;
}
}
+
if (!matched) {
// No matches in common names or alternate names.
QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate);
@@ -1083,4 +1257,39 @@ QList<QSslCertificate> QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates
return certificates;
}
+bool QSslSocketBackendPrivate::isMatchingHostname(const QString &cn, const QString &hostname)
+{
+ int wildcard = cn.indexOf(QLatin1Char('*'));
+
+ // Check this is a wildcard cert, if not then just compare the strings
+ if (wildcard < 0)
+ return cn == hostname;
+
+ int firstCnDot = cn.indexOf(QLatin1Char('.'));
+ int secondCnDot = cn.indexOf(QLatin1Char('.'), firstCnDot+1);
+
+ // Check at least 3 components
+ if ((-1 == secondCnDot) || (secondCnDot+1 >= cn.length()))
+ return false;
+
+ // Check * is last character of 1st component (ie. there's a following .)
+ if (wildcard+1 != firstCnDot)
+ return false;
+
+ // Check only one star
+ if (cn.lastIndexOf(QLatin1Char('*')) != wildcard)
+ return false;
+
+ // Check characters preceding * (if any) match
+ if (wildcard && (hostname.leftRef(wildcard) != cn.leftRef(wildcard)))
+ return false;
+
+ // Check characters following first . match
+ if (hostname.midRef(hostname.indexOf(QLatin1Char('.'))) != cn.midRef(firstCnDot))
+ return false;
+
+ // Ok, I guess this was a wildcard CN and the hostname matches.
+ return true;
+}
+
QT_END_NAMESPACE