diff --git a/CMakeLists.txt b/CMakeLists.txt index be7cad3f..c37c28de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,6 +158,7 @@ set(SQLB_RESOURCES src/icons/icons.qrc src/translations/flags/flags.qrc src/translations/translations.qrc + src/certs/CaCerts.qrc ) set(SQLB_MISC diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 572a9c55..5f73ce93 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -1692,6 +1692,9 @@ void MainWindow::reloadSettings() // Hide or show the File → Remote menu as needed QAction *remoteMenuAction = ui->menuRemote->menuAction(); remoteMenuAction->setVisible(Settings::getSettingsValue("MainWindow", "remotemenu").toBool()); + + // Update the remote database connection settings + m_remoteDb.reloadSettings(); } void MainWindow::httpresponse(QNetworkReply *reply) diff --git a/src/RemoteDatabase.cpp b/src/RemoteDatabase.cpp index 87d61a1c..ca6604ff 100644 --- a/src/RemoteDatabase.cpp +++ b/src/RemoteDatabase.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "RemoteDatabase.h" #include "version.h" @@ -12,7 +13,8 @@ RemoteDatabase::RemoteDatabase() : m_manager(new QNetworkAccessManager), m_currentReply(nullptr) { - // TODO Set up SSL configuration here + // Load settings and set up some more stuff while doing so + reloadSettings(); // TODO Add support for proxies here @@ -27,6 +29,40 @@ RemoteDatabase::~RemoteDatabase() delete m_manager; } +void RemoteDatabase::reloadSettings() +{ + // Set up SSL configuration + m_sslConfiguration = QSslConfiguration::defaultConfiguration(); + m_sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyPeer); + + // Load CA certs from resource file + QDir dirCaCerts(":/certs"); + QStringList caCertsList = dirCaCerts.entryList(); + QList caCerts; + foreach(const QString& caCertName, caCertsList) + { + QFile fileCaCert(":/certs/" + caCertName); + fileCaCert.open(QFile::ReadOnly); + caCerts.push_back(QSslCertificate(&fileCaCert)); + fileCaCert.close(); + } + m_sslConfiguration.setCaCertificates(caCerts); + + // Load client cert + QFile fileClientCert("client.crt"); + fileClientCert.open(QFile::ReadOnly); + QSslCertificate clientCert(&fileClientCert); + fileClientCert.close(); + m_sslConfiguration.setLocalCertificate(clientCert); + + // Load private key + QFile fileClientKey("client.key"); + fileClientKey.open(QFile::ReadOnly); + QSslKey clientKey(&fileClientKey, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, "password"); + fileClientKey.close(); + m_sslConfiguration.setPrivateKey(clientKey); +} + void RemoteDatabase::fetchDatabase(const QString& url) { // Check if network is accessible. If not, abort right here @@ -41,7 +77,10 @@ void RemoteDatabase::fetchDatabase(const QString& url) request.setUrl(url); request.setRawHeader("User-Agent", QString("%1 %2").arg(qApp->organizationName()).arg(APP_VERSION).toUtf8()); - // TODO Set SSL configuration here + // Set SSL configuration when trying to access a file via the HTTPS protocol + bool https = QUrl(url).scheme().compare("https", Qt::CaseInsensitive) == 0; + if(https) + request.setSslConfiguration(m_sslConfiguration); // Fetch database and save pending reply. Note that we're only supporting one active download here at the moment. m_currentReply = m_manager->get(request); @@ -55,9 +94,12 @@ void RemoteDatabase::fetchDatabase(const QString& url) connect(m_currentReply, &QNetworkReply::downloadProgress, this, &RemoteDatabase::updateProgress); } -void RemoteDatabase::gotEncrypted(QNetworkReply* /*reply*/) +void RemoteDatabase::gotEncrypted(QNetworkReply* reply) { - // TODO Check SSL configuration here and abort reply if it's not good + // Verify the server's certificate using our CA certs. If it's not good, abort the reply here + auto verificationErrors = reply->sslConfiguration().peerCertificate().verify(m_sslConfiguration.caCertificates()); + if(!(verificationErrors.size() == 0 || (verificationErrors.size() == 1 && verificationErrors.at(0).error() == QSslError::SelfSignedCertificate))) + reply->abort(); } void RemoteDatabase::gotReply(QNetworkReply* reply) @@ -93,6 +135,25 @@ void RemoteDatabase::gotReply(QNetworkReply* reply) void RemoteDatabase::gotError(QNetworkReply* reply, const QList& errors) { + // Are there any errors in here that aren't about self-signed certificates and non-matching hostnames? + // TODO What about the hostname mismatch? Can we remove that from the list of ignored errors later? + bool serious_errors = false; + foreach(const QSslError& error, errors) + { + if(error.error() != QSslError::SelfSignedCertificate && error.error() != QSslError::HostNameMismatch) + { + serious_errors = true; + break; + } + } + + // Just stop the error checking here and accept the reply if there were no 'serious' errors + if(!serious_errors) + { + reply->ignoreSslErrors(errors); + return; + } + // Build an error message and short it to the user QString message = tr("Error opening remote database file from %1.\n%2").arg(reply->url().toString()).arg(errors.at(0).errorString()); QMessageBox::warning(0, qApp->applicationName(), message); diff --git a/src/RemoteDatabase.h b/src/RemoteDatabase.h index 954ef5c1..90a71dd0 100644 --- a/src/RemoteDatabase.h +++ b/src/RemoteDatabase.h @@ -3,6 +3,7 @@ #include #include +#include class QNetworkAccessManager; class QString; @@ -17,6 +18,8 @@ public: RemoteDatabase(); virtual ~RemoteDatabase(); + void reloadSettings(); + void fetchDatabase(const QString& url); signals: @@ -31,6 +34,7 @@ private: QNetworkAccessManager* m_manager; QProgressDialog m_progress; QNetworkReply* m_currentReply; + QSslConfiguration m_sslConfiguration; }; #endif diff --git a/src/certs/CaCerts.qrc b/src/certs/CaCerts.qrc new file mode 100644 index 00000000..250d468b --- /dev/null +++ b/src/certs/CaCerts.qrc @@ -0,0 +1,5 @@ + + + mlnt.crt + + diff --git a/src/certs/mlnt.crt b/src/certs/mlnt.crt new file mode 100644 index 00000000..08ea1204 --- /dev/null +++ b/src/certs/mlnt.crt @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFXTCCA0WgAwIBAgIJAJYKIz1dqTZBMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQwHhcNMTYxMDE3MTMyOTIwWhcNMTcxMDE3MTMyOTIwWjBF +MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEAx/sltVmjQ6YxsnCLK2CQYtGuD4/EsKmePYMaUwCZEHhIdAF2E8cBaMa7 +hJFehrQiITsFb74ZQih6d97OO0MzIf8Ox+IN5GZMBtunoqHrQu6zgBrCaJT8/BBX +xJIESl9tGQ3UhsZWW6DL5ds6Fl6N/OUtT8j+TIfi+mtCgYT9eF7Qj9eV+HggSXHy +fqw37UGDodNbphk/MLQwX447keq7qJIDNQq65O6LGNrGae5c3Z4daLHHj5tYOX7a +Kd/7Tnv8sX3nUQArJfOpPgmmGQnrt8oDSjAEP4INa63AVUrsz9FIbtJ+ULgTyqW5 +Ud6orUZ9XMuaFXzVlgKRF/PjTiN9PmKByDz08MaUa4y2xMw7Nr2aSnEHObbBRq9k +slnFjlFFVd0jiysg/uUtGkmitOhzMoO5fjEXiPxLI9cweafRJFfqAauHKZ12swAQ ++a/Caws/9WU7lYElW2oxav2+P71BecwpmcRr5KcPYgM2lhNJDdQEy91Z8cr0m/aQ +glIyxs6CAXPPZlJwSknwP94eg3nXvA7Xr2xPBpPL9CC/08T9wJiZCopAaCnoGYyg +x2RdSKvRBESgLvh5FBhLQUYyKGuRwfIKoe3Use+mlaYFZMFPqxR2H9GdI/oeEOoe +zFUBgcP/HGu9/0SJAmFpgEKqTGoCabfrB2VFJV2BZV2vsO8mCWUCAwEAAaNQME4w +HQYDVR0OBBYEFMwuK3aJWDr4sRawyaexyzYPUaKKMB8GA1UdIwQYMBaAFMwuK3aJ +WDr4sRawyaexyzYPUaKKMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIB +AI4REr1jWlSHkNWDyRjuyKNc2jrAD+IhaI54pfB1lNwXVN4drgOU8d1k5d11Gqxd +Gaht8iwlKBCa2hiNX52585F6DsrhW8lXpc2pwLigbmle/+uf3jJ+6sa7DVzPiJI8 +wToGDxwKZ4qKNW0Z12Kvgt0rJtLcmmrk+91A8AicT3cBe6WLa9opcAXjcx9+QRvA +qs/Wc9krAPmm/G58gmNI5KitdZpPcSkqx9sPsTV9d5CYgUNuY2f6P1RA+1q8LU6i +tBDehpHtrHfXZCGSUv9HlaEwdVFU6L7CTZg8OMbg+d2Eo/nJQwf9ofvk8MUf4bMG +u44hBHfLGo77v7T9v++dSgN3BcydxNt+28bY69v8Lh43SIbuFs1neE9Bx3LwbLw8 +ry9lxHFUVah0fyUEA+gxM5GqmtKUcm9CSFkL6lkpYYhz/87z9hyBysQ8icVJclBu +EAXbus9xMcNjOCACJDNoLWOT9WI2sW5X3LXBl7GB4XuyTV5Dx3SvA8liA7KLun2z +v1jBiHIoQ5aiYW4bPqZ+G8oRMnrc7jU5jhpFq0CIbJ5f4bnB44JSRn/gqU9uSK5Y +9QOX+TA1BUBvUy9DuUgioDr+09IHLOXygVPTNfYVD2ozZCDLAtPFLE57NqcOceqG +I6bkjp+skDgkgJKWsbiB7ba03c9sD091b+k+tBSDHtJU +-----END CERTIFICATE----- diff --git a/src/client.crt b/src/client.crt new file mode 100644 index 00000000..24d773c4 --- /dev/null +++ b/src/client.crt @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFFTCCAv0CAQEwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMCQVUxEzARBgNV +BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0 +ZDAeFw0xNjEwMTcxNDU4NTNaFw0xNzEwMTcxNDU4NTNaMFwxCzAJBgNVBAYTAkFV +MRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRz +IFB0eSBMdGQxFTATBgNVBAMMDFRlc3QgY2VydCAjMTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBALU9e88zfZ52lZZXK4PpNEitP9xOf3o7prZsFnRLTGqT +HUB69ZKeJcXrEoMi6Etl6Uek9Q246J2VKNfrORxMoCaukBh4WE2aj9YPFSdQPjEs +y9+YM3wbGIFfeuhAMuoNWm+opulfDEbYoZkPGIN0ouos9BYdjygkZoPBauGcWOky +PO7Peh5gMyuq23idtiHuLxtbh4K8WGNAgUQBrL5jRrZcblTBErH+G6XoQqvX4SF/ +TyPqMiD+aj8oVVLBLhWN9C5qYeMltj8CIno8ko1P8PP5KH1rsUaj3UeisGh1rJPw +Tvss9LeEwgcoZcIV2aeRrqHn/XX9VgzCVzIcOMcCUqc9LHsamJjqCs1c0s+ZsHgm +NlR/xzEQNf817fNOBhpDO+ujf2z/qHmubL3pQVQyo+pmDJm7EZ4BcuwG8CnulptR +yJZTWio8pOC9J/R8dci3cyrVLoxgayQdENFrqoKBkRVWGwKbu1OfRAo4hTSjSCIS +UqfAib0Qj/YZrM7flH3U3S1+FXoLjcch1S/btvxVaXG7DhsoH/X3ql7Mnjw0tXJp +Fjlqlf9uD12FJH8Bc3OiSnJnE0sH3VGrW5Zb53SuCKM+r18WrtzWyv9k9EE0gICo +uCwL4AVmSAdU1C8pEymW0RBVqzAEvuEE0ZkhxCXm8ZJBgcuATj0KAjj4r7OMdb+Z +AgMBAAEwDQYJKoZIhvcNAQELBQADggIBAEmM9OAIhUsqk/EI3x4qPDRq16VAARnA +bYebzQ44z4eGgOSIbLGXn+aLCwgYjpsjFCeDjKmy1SRWNFuUCT3afcQGkHXM9g4n +qAVWSEnCDSSwghdPJokPw3peUNzb/cx46kqz7gAUpTW1q3rMAxvTF4igPBsls9fQ +JM1U6daHi8EYl2NXWE4BDQMEDeq92pzEI9rFxka0l7ZkjbZETtz9BktvpSGGhQmx +KryO0g4+4WoJO4RmXY087rWZD3B9cZGJI363legq7TC1qr69076i+cd0q1qQnj73 +E05KBtgfHQJtEL3+pQwYU4XU210FoJ/uFA8crE2LgHcPjb6NwZchyNETjXeqm9mv +7U9hGQC8Cq9rllwFtJ4Y2XU6iXYIXcK0Z6Qk82fkAgegDejmft8XqObOyS+pr6Zu +JlhOjUSxr6D/TJOkxp1DtfTFqrvtq0iFMtsagGl7weLlfv0t0QXy1vbVNFvaWfgC +qejq/JrHzsjbamuKAjpmoFv34MwGDt05GVVx7+Qavk/bo/jYXyBlWJpV8GJN9vMi +0Zg3RyX05s2KJWX4PQs2ZncYAKOIZEhKT3LuhqE+eak+++GPt1ul/yNeEDGUIzQW +6B1/tAaQZhoY2plZ1grrnyFr9BhOXLylL/X/Rr2Pw1huexa/K/Gbp+w2rr/3/zop +ZoUymJ60DOFt +-----END CERTIFICATE----- diff --git a/src/client.key b/src/client.key new file mode 100644 index 00000000..78028ff9 --- /dev/null +++ b/src/client.key @@ -0,0 +1,54 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,FAC500C30C704082 + +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 +-----END RSA PRIVATE KEY----- diff --git a/src/src.pro b/src/src.pro index 9a25dfbd..f17fe874 100644 --- a/src/src.pro +++ b/src/src.pro @@ -87,7 +87,8 @@ SOURCES += \ RESOURCES += icons/icons.qrc \ translations/flags/flags.qrc \ - translations/translations.qrc + translations/translations.qrc \ + certs/CaCerts.qrc FORMS += \ MainWindow.ui \