QSslSocket Behaviour Linux vs macOS (possibly LibreSSL incompatibilities?)
-
Hi and welcome to devnet,
What version of Qt are you using ?
Is it self-built ?
Since a few versions, Qt on macOS is built using a backend that uses the Secure Transport framework from Apple.Thank you....
I'm using the latest Qt 5.10 installed via the official online installer. So, no, not self built.
Edit: I might add: The implementation by and large seems rather picky. Adding / using certificates only works when using .pem endoded certificates. While using .crt is no problem in Linux, these result in error 13 (SSLHandshakeError) on the Mac
-
Can you provide a minimal compilable example that shows that behaviour ?
-
I guess this method is where the magic happens:
void PCITcpConnections::accept(qintptr handle, PCITcpConnection *connection) {
QSslSocket *socket = new QSslSocket(this);connect(socket, &QSslSocket::encrypted, connection, &PCITcpConnection::encrypted); connect(socket,&QSslSocket::disconnected, this, &PCITcpConnections::disconnected); connect(socket,static_cast<void (QSslSocket::*)(QAbstractSocket::SocketError)>(&QSslSocket::error),this,&PCITcpConnections::error); const auto cert = QSslCertificate::fromPath("perceptronics_cert.pem", QSsl::Pem, QRegExp::Wildcard); const auto CAcert = QSslCertificate::fromPath("CA_cert.pem", QSsl::Pem, QRegExp::Wildcard); socket->addCaCertificates(CAcert); socket->setPrivateKey("perceptronics_key.pem"); socket->setLocalCertificate(cert.at(0)); socket->setPeerVerifyMode(QSslSocket::VerifyNone); QSslError error(QSslError::SelfSignedCertificate, cert.at(0)); QList<QSslError> expectedSslErrors; expectedSslErrors.append(error); socket->ignoreSslErrors(expectedSslErrors); qDebug() << " ... accepting connection"; if(socket->setSocketDescriptor(handle)) { socket->startServerEncryption(); } else { qWarning() << this << "could not accept connection" << handle; connection->deleteLater(); return; } qDebug() << " Mode: " << socket->mode(); connection->moveToThread(QThread::currentThread()); connection->setSocket(socket); tcpConnections.insert(socket,connection); qDebug() << this << "clients = " << tcpConnections.count(); emit socket->connected();
}
-
I guess this method is where the magic happens:
void PCITcpConnections::accept(qintptr handle, PCITcpConnection *connection) {
QSslSocket *socket = new QSslSocket(this);connect(socket, &QSslSocket::encrypted, connection, &PCITcpConnection::encrypted); connect(socket,&QSslSocket::disconnected, this, &PCITcpConnections::disconnected); connect(socket,static_cast<void (QSslSocket::*)(QAbstractSocket::SocketError)>(&QSslSocket::error),this,&PCITcpConnections::error); const auto cert = QSslCertificate::fromPath("perceptronics_cert.pem", QSsl::Pem, QRegExp::Wildcard); const auto CAcert = QSslCertificate::fromPath("CA_cert.pem", QSsl::Pem, QRegExp::Wildcard); socket->addCaCertificates(CAcert); socket->setPrivateKey("perceptronics_key.pem"); socket->setLocalCertificate(cert.at(0)); socket->setPeerVerifyMode(QSslSocket::VerifyNone); QSslError error(QSslError::SelfSignedCertificate, cert.at(0)); QList<QSslError> expectedSslErrors; expectedSslErrors.append(error); socket->ignoreSslErrors(expectedSslErrors); qDebug() << " ... accepting connection"; if(socket->setSocketDescriptor(handle)) { socket->startServerEncryption(); } else { qWarning() << this << "could not accept connection" << handle; connection->deleteLater(); return; } qDebug() << " Mode: " << socket->mode(); connection->moveToThread(QThread::currentThread()); connection->setSocket(socket); tcpConnections.insert(socket,connection); qDebug() << this << "clients = " << tcpConnections.count(); emit socket->connected();
}
-
I see you are using relative paths which means that these files might not be found at run time. For testing you should put the complete path to each of them.
-
I see you are using relative paths which means that these files might not be found at run time. For testing you should put the complete path to each of them.
@SGaist You are right - however, in the debugger I see the files are loaded. Except maybe the key file.
I noticed something weird I am about to dig into: even though I set the peerVerifyName in my client, the peerVerifyName on the server is empty.Something must be wrong there. Still does not explain why it does work under Linux but fails on the Mac.
I also noticed: on the server the connected state is preserved despite the mentioned errors, whereas on the client a network timeout is experienced after some seconds. -
Might be a silly question but did you explicitly select the OpenSSL backend when compiling Qt ? Also, did you linked to OpenSSL or load at runtime ?
-
Might be a silly question but did you explicitly select the OpenSSL backend when compiling Qt ? Also, did you linked to OpenSSL or load at runtime ?
@SGaist There are no silly questions - just maybe silly answers :-)
1: yes, I set the OpenSSL backend when compiling.
2: I linked.That said this is also something to look into; I am not fully clear how Qt handles SSL and if I did it correctly. Assuming its an SSL issue I wonder why my Mac client can connect to the Linux server without issues.
But in general I agree, this might be the root cause. Unfortunately the error messages are not exactly verbose - it just says "internal SSL error"... could be anything -
- Can you check with otool -L the resulting QtNetwork framework ? Just to ensure that it indeed used your version of OpenSSL and not the one from the system.
Are you using a custom made certificate ?
-
- Can you check with otool -L the resulting QtNetwork framework ? Just to ensure that it indeed used your version of OpenSSL and not the one from the system.
Are you using a custom made certificate ?
@SGaist Here's the otool output:
@rpath/QtNetwork.framework/Versions/5/QtNetwork (compatibility version 5.10.0, current version 5.10.0)
Yes, I use self signed certificates. That is, a self signed CA certificate which in turn have been used to sign the server cert. According to the KeyChain app the certs are ok (ie trusted)
-
It looks incomplete. That looks like just the library id.
I get:
@rpath/QtNetwork.framework/Versions/5/QtNetwork (compatibility version 5.10.0, current version 5.10.1) @rpath/QtCore.framework/Versions/5/QtCore (compatibility version 5.10.0, current version 5.10.1) /System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration (compatibility version 1.0.0, current version 1.0.0) /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0) /System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 58286.31.2) /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1450.15.0) /System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices (compatibility version 1.0.0, current version 822.19.0) /System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration (compatibility version 1.0.0, current version 963.30.1) /usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.11) /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.0.0) /System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork (compatibility version 1.0.0, current version 893.13.1)
-
It looks incomplete. That looks like just the library id.
I get:
@rpath/QtNetwork.framework/Versions/5/QtNetwork (compatibility version 5.10.0, current version 5.10.1) @rpath/QtCore.framework/Versions/5/QtCore (compatibility version 5.10.0, current version 5.10.1) /System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration (compatibility version 1.0.0, current version 1.0.0) /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0) /System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 58286.31.2) /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1450.15.0) /System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices (compatibility version 1.0.0, current version 822.19.0) /System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration (compatibility version 1.0.0, current version 963.30.1) /usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.11) /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.0.0) /System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork (compatibility version 1.0.0, current version 893.13.1)
@SGaist Silly me; I only pasted the one line affecting QtNetwork. Sorry for that. Here we go:
@rpath/QtWidgets.framework/Versions/5/QtWidgets (compatibility version 5.10.0, current version 5.10.0)
@rpath/QtGui.framework/Versions/5/QtGui (compatibility version 5.10.0, current version 5.10.0)
@rpath/QtCore.framework/Versions/5/QtCore (compatibility version 5.10.0, current version 5.10.0)
/System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0)
@rpath/QtNetwork.framework/Versions/5/QtNetwork (compatibility version 5.10.0, current version 5.10.0)
/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/AGL.framework/Versions/A/AGL (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.0.0)fyi: I reverted back to the standard Qt version, ie not using the self compiled one for now. Hence the output above might not be what you expected.
For now, I try to figure out a couple of things (e.g why my peerValidName etc are not set on the socket) and how to load ssl at runtime
-
Indeed, it was the one from your self-compiled Qt that is of interest.
As for loading at run time, you pass the
-openssl-runtime
option. -
Indeed, it was the one from your self-compiled Qt that is of interest.
As for loading at run time, you pass the
-openssl-runtime
option. -
Don't compile all of Qt. For your testing you likely only need qtbase. If more, then build only the modules you need after qtbase (or pass a list of -skip options for all modules you don't use in your application).
-
Don't compile all of Qt. For your testing you likely only need qtbase. If more, then build only the modules you need after qtbase (or pass a list of -skip options for all modules you don't use in your application).
-
What modules are you using for it currently ?
-
So qtbase is enough.
Use out of source builds, so if you want to start from scratch you only have to nuke the build folder and the sources stay clean.
-
So qtbase is enough.
Use out of source builds, so if you want to start from scratch you only have to nuke the build folder and the sources stay clean.
@SGaist edit: after hours of investigation I got it to configure/compile. I had to manually point/symlink the openssl files located in /usr/local/lib and /usr/local/include, respectively, to the directory where openssl actually sits.
As it seems configure bails out for openssl versions north of 1.1.0. For some reason (no idea how this came about) I had openssl 1.1.1-pre-something installed.
Follow up: the private Key is indeed set; however, something is wrong with it because Qt marks it "isNull". That is, I can qDebug it and at first glance it looks fine. Same on Linux.
Follow up 2: I used the code as posted in
https://forum.qt.io/topic/45728/generating-cert-key-during-run-time-for-qsslsocket/7Again - as @Pradeep-P-N reported, the code crashes in the final step (setPrivateKey); that is, the privateKey is null. The said is true on macOS using openssl 1.0.2n
Follow up 3: Under Linux, a valid key is indeed created. However, the key does not seem to be relayed to the server. Keep digging...
Follow up 4: Have not managed to create a key via openssl that could be loaded from disk - neither on Linux nor on the Mac. Not sure what's going on, the private Key just is always null (have tried various formats...). For now solely the code as posted in the linked thread above works under Linux.
For now I suspect main the problem has to do with the linked openssl version on the Mac: Building gives lots of warnings: object file (libcrypto.a) ws built for newer OSX version (10.13) than being linked (10.10).
If I get that correctly this means the openssl lib used to generate the keys was built on 10.13 whereas Qt's version was build on 10.10?