summaryrefslogtreecommitdiff
path: root/utils/rbutilqt
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 /utils/rbutilqt
parenta0459de4d5b4bbb062536146cdefaad796480c7c (diff)
downloadrockbox-7a2fdf3fd60a63c1a67986d9f83b321ea3758b9d.tar.gz
rockbox-7a2fdf3fd60a63c1a67986d9f83b321ea3758b9d.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
Diffstat (limited to 'utils/rbutilqt')
-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 @@
20 20
21#include <QNetworkAccessManager> 21#include <QNetworkAccessManager>
22#include <QNetworkRequest> 22#include <QNetworkRequest>
23#include <QSslConfiguration>
23 24
24#include "httpget.h" 25#include "httpget.h"
25#include "Logger.h" 26#include "Logger.h"
@@ -27,6 +28,7 @@
27QString HttpGet::m_globalUserAgent; //< globally set user agent for requests 28QString HttpGet::m_globalUserAgent; //< globally set user agent for requests
28QDir HttpGet::m_globalCache; //< global cach path value for new objects 29QDir HttpGet::m_globalCache; //< global cach path value for new objects
29QNetworkProxy HttpGet::m_globalProxy; 30QNetworkProxy HttpGet::m_globalProxy;
31QList<QSslCertificate> HttpGet::m_acceptedClientCerts;
30 32
31HttpGet::HttpGet(QObject *parent) 33HttpGet::HttpGet(QObject *parent)
32 : QObject(parent), 34 : QObject(parent),
@@ -211,9 +213,30 @@ void HttpGet::startRequest(QUrl url)
211 connect(m_reply, &QNetworkReply::errorOccurred, this, &HttpGet::networkError); 213 connect(m_reply, &QNetworkReply::errorOccurred, this, &HttpGet::networkError);
212#endif 214#endif
213 connect(m_reply, &QNetworkReply::downloadProgress, this, &HttpGet::downloadProgress); 215 connect(m_reply, &QNetworkReply::downloadProgress, this, &HttpGet::downloadProgress);
216 connect(m_reply, &QNetworkReply::sslErrors, this, &HttpGet::gotSslError);
214} 217}
215 218
216 219
220void HttpGet::gotSslError(const QList<QSslError> &errors)
221{
222 LOG_WARNING() << "Got SSL error" << errors;
223
224 // if this is a cert error, and only if we already accepted a remote cert
225 // ignore the error.
226 // This will make QNAM continue the request and finish it.
227 if (errors.size() == 1
228 && errors.at(0).error() == QSslError::UnableToGetLocalIssuerCertificate
229 && m_acceptedClientCerts.contains(m_reply->sslConfiguration().peerCertificate())) {
230 LOG_INFO() << "client cert temporarily trusted by user.";
231 m_reply->ignoreSslErrors();
232 }
233 else {
234 LOG_ERROR() << m_reply->sslConfiguration().peerCertificate().toText();
235 emit sslError(errors.at(0), m_reply->sslConfiguration().peerCertificate());
236 }
237
238}
239
217void HttpGet::networkError(QNetworkReply::NetworkError error) 240void HttpGet::networkError(QNetworkReply::NetworkError error)
218{ 241{
219 LOG_ERROR() << "NetworkError occured:" << error << m_reply->errorString(); 242 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
73 //< set global user agent string 73 //< set global user agent string
74 static void setGlobalUserAgent(const QString& u) 74 static void setGlobalUserAgent(const QString& u)
75 { m_globalUserAgent = u; } 75 { m_globalUserAgent = u; }
76 static void addTrustedPeerCert(QSslCertificate cert)
77 { m_acceptedClientCerts.append(cert);}
76 78
77 public slots: 79 public slots:
78 void abort(void); 80 void abort(void);
@@ -81,14 +83,17 @@ class HttpGet : public QObject
81 void done(QNetworkReply::NetworkError error); 83 void done(QNetworkReply::NetworkError error);
82 void dataReadProgress(int, int); 84 void dataReadProgress(int, int);
83 void headerFinished(void); 85 void headerFinished(void);
86 void sslError(const QSslError& error, const QSslCertificate& peerCert);
84 87
85 private slots: 88 private slots:
86 void requestFinished(QNetworkReply* reply); 89 void requestFinished(QNetworkReply* reply);
87 void startRequest(QUrl url); 90 void startRequest(QUrl url);
88 void downloadProgress(qint64 received, qint64 total); 91 void downloadProgress(qint64 received, qint64 total);
89 void networkError(QNetworkReply::NetworkError error); 92 void networkError(QNetworkReply::NetworkError error);
93 void gotSslError(const QList<QSslError> &errors);
90 94
91 private: 95 private:
96 static QList<QSslCertificate> m_acceptedClientCerts;
92 static QString m_globalUserAgent; 97 static QString m_globalUserAgent;
93 static QNetworkProxy m_globalProxy; 98 static QNetworkProxy m_globalProxy;
94 QNetworkAccessManager m_mgr; 99 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()
205 // try to get the current build information 205 // try to get the current build information
206 daily = new HttpGet(this); 206 daily = new HttpGet(this);
207 connect(daily, &HttpGet::done, this, &RbUtilQt::downloadDone); 207 connect(daily, &HttpGet::done, this, &RbUtilQt::downloadDone);
208 connect(daily, &HttpGet::sslError, this, &RbUtilQt::sslError);
208 connect(qApp, &QGuiApplication::lastWindowClosed, daily, &HttpGet::abort); 209 connect(qApp, &QGuiApplication::lastWindowClosed, daily, &HttpGet::abort);
209 daily->setCache(false); 210 daily->setCache(false);
210 ui.statusbar->showMessage(tr("Downloading build information, please wait ...")); 211 ui.statusbar->showMessage(tr("Downloading build information, please wait ..."));
@@ -213,10 +214,49 @@ void RbUtilQt::downloadInfo()
213 daily->getFile(QUrl(PlayerBuildInfo::instance()->value(PlayerBuildInfo::BuildInfoUrl).toString())); 214 daily->getFile(QUrl(PlayerBuildInfo::instance()->value(PlayerBuildInfo::BuildInfoUrl).toString()));
214} 215}
215 216
217void RbUtilQt::sslError(const QSslError& error, const QSslCertificate& peerCert)
218{
219 LOG_WARNING() << "sslError" << (int)error.error();
220 // On Rockbox Utility start we always try to get the build info first.
221 // Thus we can use that to catch potential certificate errors.
222 // If the user accepts the certificate we'll have HttpGet ignore all cert
223 // errors for the exact certificate we got during this first request.
224 // Thus we don't need to handle cert errors later anymore.
225 if (error.error() == QSslError::UnableToGetLocalIssuerCertificate) {
226 QMessageBox mb(this);
227 mb.setWindowTitle(tr("Certificate error"));
228 mb.setIcon(QMessageBox::Warning);
229 mb.setText(tr("%1\n\n"
230 "Issuer: %2\n"
231 "Subject: %3\n"
232 "Valid since: %4\n"
233 "Valid until: %5\n\n"
234 "Temporarily trust certificate?")
235 .arg(error.errorString())
236 .arg(peerCert.issuerInfo(QSslCertificate::Organization).at(0))
237 .arg(peerCert.subjectDisplayName())
238 .arg(peerCert.effectiveDate().toString())
239 .arg(peerCert.expiryDate().toString())
240 );
241 mb.setDetailedText(peerCert.toText());
242 mb.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
243
244 auto r = mb.exec();
245 if (r == QMessageBox::Yes) {
246 HttpGet::addTrustedPeerCert(peerCert);
247 downloadInfo();
248 }
249 else {
250 downloadDone(QNetworkReply::OperationCanceledError);
251 }
252 }
253}
254
216 255
217void RbUtilQt::downloadDone(QNetworkReply::NetworkError error) 256void RbUtilQt::downloadDone(QNetworkReply::NetworkError error)
218{ 257{
219 if(error != QNetworkReply::NoError) { 258 if(error != QNetworkReply::NoError
259 && error != QNetworkReply::SslHandshakeFailedError) {
220 LOG_INFO() << "network error:" << daily->errorString(); 260 LOG_INFO() << "network error:" << daily->errorString();
221 ui.statusbar->showMessage(tr("Can't get version information!")); 261 ui.statusbar->showMessage(tr("Can't get version information!"));
222 QMessageBox::critical(this, tr("Network error"), 262 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
72 bool m_auto; 72 bool m_auto;
73 73
74 private slots: 74 private slots:
75 void sslError(const QSslError& error, const QSslCertificate& peerCert);
75 void shutdown(void); 76 void shutdown(void);
76 void about(void); 77 void about(void);
77 void help(void); 78 void help(void);