summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDominik Riebeling <Dominik.Riebeling@gmail.com>2022-03-19 16:54:27 +0100
committerDominik Riebeling <Dominik.Riebeling@gmail.com>2022-03-19 16:57:41 +0100
commit7a2fdf3fd60a63c1a67986d9f83b321ea3758b9d (patch)
treeb01734a734d75b2507df4a0538dba388d4f201b7
parenta0459de4d5b4bbb062536146cdefaad796480c7c (diff)
downloadrockbox-7a2fdf3fd6.tar.gz
rockbox-7a2fdf3fd6.zip
rbutil: Handle SSL certificate errors on first request.
Qt uses the systems certificate store. On old(er) systems the root certificate might not be present, so checking the certificate from the rockbox.org server might fail. On startup we try to download the build-info file. If this fails with a certificate error allow the user to temporarily accept the rockbox.org certificate for all successive requests. Change-Id: I459e12d53286aaedea4db659d90a5e057c56801f
-rw-r--r--utils/rbutilqt/base/httpget.cpp23
-rw-r--r--utils/rbutilqt/base/httpget.h5
-rw-r--r--utils/rbutilqt/rbutilqt.cpp42
-rw-r--r--utils/rbutilqt/rbutilqt.h1
4 files changed, 70 insertions, 1 deletions
diff --git a/utils/rbutilqt/base/httpget.cpp b/utils/rbutilqt/base/httpget.cpp
index fb74514e73..0cd9236209 100644
--- a/utils/rbutilqt/base/httpget.cpp
+++ b/utils/rbutilqt/base/httpget.cpp
@@ -20,6 +20,7 @@
#include <QNetworkAccessManager>
#include <QNetworkRequest>
+#include <QSslConfiguration>
#include "httpget.h"
#include "Logger.h"
@@ -27,6 +28,7 @@
QString HttpGet::m_globalUserAgent; //< globally set user agent for requests
QDir HttpGet::m_globalCache; //< global cach path value for new objects
QNetworkProxy HttpGet::m_globalProxy;
+QList<QSslCertificate> HttpGet::m_acceptedClientCerts;
HttpGet::HttpGet(QObject *parent)
: QObject(parent),
@@ -211,9 +213,30 @@ void HttpGet::startRequest(QUrl url)
connect(m_reply, &QNetworkReply::errorOccurred, this, &HttpGet::networkError);
#endif
connect(m_reply, &QNetworkReply::downloadProgress, this, &HttpGet::downloadProgress);
+ connect(m_reply, &QNetworkReply::sslErrors, this, &HttpGet::gotSslError);
}
+void HttpGet::gotSslError(const QList<QSslError> &errors)
+{
+ LOG_WARNING() << "Got SSL error" << errors;
+
+ // if this is a cert error, and only if we already accepted a remote cert
+ // ignore the error.
+ // This will make QNAM continue the request and finish it.
+ if (errors.size() == 1
+ && errors.at(0).error() == QSslError::UnableToGetLocalIssuerCertificate
+ && m_acceptedClientCerts.contains(m_reply->sslConfiguration().peerCertificate())) {
+ LOG_INFO() << "client cert temporarily trusted by user.";
+ m_reply->ignoreSslErrors();
+ }
+ else {
+ LOG_ERROR() << m_reply->sslConfiguration().peerCertificate().toText();
+ emit sslError(errors.at(0), m_reply->sslConfiguration().peerCertificate());
+ }
+
+}
+
void HttpGet::networkError(QNetworkReply::NetworkError error)
{
LOG_ERROR() << "NetworkError occured:" << error << m_reply->errorString();
diff --git a/utils/rbutilqt/base/httpget.h b/utils/rbutilqt/base/httpget.h
index 443a606e6d..fb5b920b47 100644
--- a/utils/rbutilqt/base/httpget.h
+++ b/utils/rbutilqt/base/httpget.h
@@ -73,6 +73,8 @@ class HttpGet : public QObject
//< set global user agent string
static void setGlobalUserAgent(const QString& u)
{ m_globalUserAgent = u; }
+ static void addTrustedPeerCert(QSslCertificate cert)
+ { m_acceptedClientCerts.append(cert);}
public slots:
void abort(void);
@@ -81,14 +83,17 @@ class HttpGet : public QObject
void done(QNetworkReply::NetworkError error);
void dataReadProgress(int, int);
void headerFinished(void);
+ void sslError(const QSslError& error, const QSslCertificate& peerCert);
private slots:
void requestFinished(QNetworkReply* reply);
void startRequest(QUrl url);
void downloadProgress(qint64 received, qint64 total);
void networkError(QNetworkReply::NetworkError error);
+ void gotSslError(const QList<QSslError> &errors);
private:
+ static QList<QSslCertificate> m_acceptedClientCerts;
static QString m_globalUserAgent;
static QNetworkProxy m_globalProxy;
QNetworkAccessManager m_mgr;
diff --git a/utils/rbutilqt/rbutilqt.cpp b/utils/rbutilqt/rbutilqt.cpp
index 6d0da3390f..680303859e 100644
--- a/utils/rbutilqt/rbutilqt.cpp
+++ b/utils/rbutilqt/rbutilqt.cpp
@@ -205,6 +205,7 @@ void RbUtilQt::downloadInfo()
// try to get the current build information
daily = new HttpGet(this);
connect(daily, &HttpGet::done, this, &RbUtilQt::downloadDone);
+ connect(daily, &HttpGet::sslError, this, &RbUtilQt::sslError);
connect(qApp, &QGuiApplication::lastWindowClosed, daily, &HttpGet::abort);
daily->setCache(false);
ui.statusbar->showMessage(tr("Downloading build information, please wait ..."));
@@ -213,10 +214,49 @@ void RbUtilQt::downloadInfo()
daily->getFile(QUrl(PlayerBuildInfo::instance()->value(PlayerBuildInfo::BuildInfoUrl).toString()));
}
+void RbUtilQt::sslError(const QSslError& error, const QSslCertificate& peerCert)
+{
+ LOG_WARNING() << "sslError" << (int)error.error();
+ // On Rockbox Utility start we always try to get the build info first.
+ // Thus we can use that to catch potential certificate errors.
+ // If the user accepts the certificate we'll have HttpGet ignore all cert
+ // errors for the exact certificate we got during this first request.
+ // Thus we don't need to handle cert errors later anymore.
+ if (error.error() == QSslError::UnableToGetLocalIssuerCertificate) {
+ QMessageBox mb(this);
+ mb.setWindowTitle(tr("Certificate error"));
+ mb.setIcon(QMessageBox::Warning);
+ mb.setText(tr("%1\n\n"
+ "Issuer: %2\n"
+ "Subject: %3\n"
+ "Valid since: %4\n"
+ "Valid until: %5\n\n"
+ "Temporarily trust certificate?")
+ .arg(error.errorString())
+ .arg(peerCert.issuerInfo(QSslCertificate::Organization).at(0))
+ .arg(peerCert.subjectDisplayName())
+ .arg(peerCert.effectiveDate().toString())
+ .arg(peerCert.expiryDate().toString())
+ );
+ mb.setDetailedText(peerCert.toText());
+ mb.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+
+ auto r = mb.exec();
+ if (r == QMessageBox::Yes) {
+ HttpGet::addTrustedPeerCert(peerCert);
+ downloadInfo();
+ }
+ else {
+ downloadDone(QNetworkReply::OperationCanceledError);
+ }
+ }
+}
+
void RbUtilQt::downloadDone(QNetworkReply::NetworkError error)
{
- if(error != QNetworkReply::NoError) {
+ if(error != QNetworkReply::NoError
+ && error != QNetworkReply::SslHandshakeFailedError) {
LOG_INFO() << "network error:" << daily->errorString();
ui.statusbar->showMessage(tr("Can't get version information!"));
QMessageBox::critical(this, tr("Network error"),
diff --git a/utils/rbutilqt/rbutilqt.h b/utils/rbutilqt/rbutilqt.h
index c507317fa2..9caa8a1267 100644
--- a/utils/rbutilqt/rbutilqt.h
+++ b/utils/rbutilqt/rbutilqt.h
@@ -72,6 +72,7 @@ class RbUtilQt : public QMainWindow
bool m_auto;
private slots:
+ void sslError(const QSslError& error, const QSslCertificate& peerCert);
void shutdown(void);
void about(void);
void help(void);