Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 5919704

Browse files
committedMay 11, 2022
Remove legacy ssl error handling
1 parent 4a929de commit 5919704

18 files changed

+126
-88
lines changed
 

‎changelog/unreleased/9655

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Change: Rewrote ssl error handling
2+
3+
We rewrote the way we handle ssl errors.
4+
5+
https://github.com/owncloud/client/issues/9655

‎src/cmd/cmd.cpp

+36-22
Original file line numberDiff line numberDiff line change
@@ -553,30 +553,44 @@ int main(int argc, char **argv)
553553

554554
ctx.account->setUrl(ctx.credentialFreeUrl);
555555

556-
// Perform a call to get the capabilities.
557-
auto capabilitiesJob = new JsonApiJob(ctx.account, QStringLiteral("ocs/v1.php/cloud/capabilities"), {}, {}, nullptr);
558-
QObject::connect(capabilitiesJob, &JsonApiJob::finishedSignal, qApp, [capabilitiesJob, ctx] {
559-
auto caps = capabilitiesJob->data().value("ocs").toObject().value("data").toObject().value("capabilities").toObject();
560-
qDebug() << "Server capabilities" << caps;
561-
ctx.account->setCapabilities(caps.toVariantMap());
562-
ctx.account->setServerVersion(caps["core"].toObject()["status"].toObject()["version"].toString());
563-
564-
if (capabilitiesJob->reply()->error() != QNetworkReply::NoError) {
565-
qFatal("Error connecting to server");
566-
}
567-
568-
auto userJob = new JsonApiJob(ctx.account, QLatin1String("ocs/v1.php/cloud/user"), {}, {}, nullptr);
569-
QObject::connect(userJob, &JsonApiJob::finishedSignal, qApp, [userJob, ctx] {
570-
const QJsonObject data = userJob->data().value("ocs").toObject().value("data").toObject();
571-
ctx.account->setDavUser(data.value("id").toString());
572-
ctx.account->setDavDisplayName(data.value("display-name").toString());
556+
auto *checkServer = new CheckServerJob(ctx.account, nullptr);
557+
QObject::connect(checkServer, &CheckServerJob::instanceFound, [ctx] {
558+
// Perform a call to get the capabilities.
559+
auto *capabilitiesJob = new JsonApiJob(ctx.account, QStringLiteral("ocs/v1.php/cloud/capabilities"), {}, {}, nullptr);
560+
QObject::connect(capabilitiesJob, &JsonApiJob::finishedSignal, qApp, [capabilitiesJob, ctx] {
561+
auto caps = capabilitiesJob->data().value("ocs").toObject().value("data").toObject().value("capabilities").toObject();
562+
qDebug() << "Server capabilities" << caps;
563+
ctx.account->setCapabilities(caps.toVariantMap());
564+
ctx.account->setServerVersion(caps["core"].toObject()["status"].toObject()["version"].toString());
565+
566+
if (capabilitiesJob->reply()->error() != QNetworkReply::NoError) {
567+
qFatal("Error connecting to server");
568+
}
573569

574-
// much lower age than the default since this utility is usually made to be run right after a change in the tests
575-
SyncEngine::minimumFileAgeForUpload = std::chrono::milliseconds(0);
576-
sync(ctx);
570+
auto userJob = new JsonApiJob(ctx.account, QLatin1String("ocs/v1.php/cloud/user"), {}, {}, nullptr);
571+
QObject::connect(userJob, &JsonApiJob::finishedSignal, qApp, [userJob, ctx] {
572+
const QJsonObject data = userJob->data().value("ocs").toObject().value("data").toObject();
573+
ctx.account->setDavUser(data.value("id").toString());
574+
ctx.account->setDavDisplayName(data.value("display-name").toString());
575+
576+
// much lower age than the default since this utility is usually made to be run right after a change in the tests
577+
SyncEngine::minimumFileAgeForUpload = std::chrono::milliseconds(0);
578+
sync(ctx);
579+
});
580+
userJob->start();
577581
});
578-
userJob->start();
582+
capabilitiesJob->start();
583+
});
584+
QObject::connect(checkServer, &CheckServerJob::instanceNotFound, [ctx] {
585+
qFatal("Failed to resolve %s.", qPrintable(ctx.account->url().toString()));
579586
});
580-
capabilitiesJob->start();
587+
QObject::connect(checkServer, &CheckServerJob::timedOut, [ctx] {
588+
qFatal("Looking up %s timed out.", qPrintable(ctx.account->url().toString()));
589+
});
590+
QObject::connect(checkServer, &CheckServerJob::sslErrors, [ctx] {
591+
qFatal(APPLICATION_EXECUTABLE "cmd currently does not support untrusted ssl certificates.");
592+
});
593+
594+
checkServer->start();
581595
return app.exec();
582596
}

‎src/gui/accountsettings.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,6 @@ Folder *AccountSettings::selectedFolder() const
220220
void AccountSettings::slotToggleSignInState()
221221
{
222222
if (_accountState->isSignedOut()) {
223-
_accountState->account()->resetRejectedCertificates();
224223
_accountState->signIn();
225224
} else {
226225
_accountState->signOutByUi();

‎src/gui/accountstate.cpp

+48-26
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#include "configfile.h"
2020
#include "creds/abstractcredentials.h"
2121
#include "creds/httpcredentials.h"
22+
#include "gui/settingsdialog.h"
23+
#include "gui/tlserrordialog.h"
2224
#include "logger.h"
2325
#include "settingsdialog.h"
2426
#include "theme.h"
@@ -262,6 +264,10 @@ void AccountState::checkConnectivity(bool blockJobs)
262264
if (isSignedOut() || _waitingForNewCredentials) {
263265
return;
264266
}
267+
if (_tlsDialog) {
268+
qCDebug(lcAccountState) << "Skip checkConnectivity, waiting for tls dialog";
269+
return;
270+
}
265271

266272
if (_connectionValidator && blockJobs && !_queueGuard.queue()->isBlocked()) {
267273
// abort already running non blocking validator
@@ -278,22 +284,18 @@ void AccountState::checkConnectivity(bool blockJobs)
278284
if (!account()->credentials()->wasFetched()) {
279285
_waitingForNewCredentials = true;
280286
account()->credentials()->fetchFromKeychain();
281-
return;
282-
}
283-
// we are not properly setup yet
284-
if (!account()->hasCapabilities()) {
285-
return;
286287
}
287-
288-
// IF the account is connected the connection check can be skipped
289-
// if the last successful etag check job is not so long ago.
290-
const auto pta = account()->capabilities().remotePollInterval();
291-
const auto polltime = duration_cast<seconds>(ConfigFile().remotePollInterval(pta));
292-
const auto elapsed = _timeOfLastETagCheck.secsTo(QDateTime::currentDateTimeUtc());
293-
if (!blockJobs && isConnected() && _timeOfLastETagCheck.isValid()
294-
&& elapsed <= polltime.count()) {
295-
qCDebug(lcAccountState) << account()->displayName() << "The last ETag check succeeded within the last " << polltime.count() << "s (" << elapsed << "s). No connection check needed!";
296-
return;
288+
if (account()->hasCapabilities()) {
289+
// IF the account is connected the connection check can be skipped
290+
// if the last successful etag check job is not so long ago.
291+
const auto pta = account()->capabilities().remotePollInterval();
292+
const auto polltime = duration_cast<seconds>(ConfigFile().remotePollInterval(pta));
293+
const auto elapsed = _timeOfLastETagCheck.secsTo(QDateTime::currentDateTimeUtc());
294+
if (!blockJobs && isConnected() && _timeOfLastETagCheck.isValid()
295+
&& elapsed <= polltime.count()) {
296+
qCDebug(lcAccountState) << account()->displayName() << "The last ETag check succeeded within the last " << polltime.count() << "s (" << elapsed << "s). No connection check needed!";
297+
return;
298+
}
297299
}
298300

299301
if (blockJobs) {
@@ -302,6 +304,30 @@ void AccountState::checkConnectivity(bool blockJobs)
302304
_connectionValidator = new ConnectionValidator(account());
303305
connect(_connectionValidator, &ConnectionValidator::connectionResult,
304306
this, &AccountState::slotConnectionValidatorResult);
307+
308+
connect(_connectionValidator, &ConnectionValidator::sslErrors, this, [blockJobs, this](const QList<QSslError> &errors) {
309+
if (!_tlsDialog) {
310+
_tlsDialog = new TlsErrorDialog(errors, _account->url().host(), ocApp()->gui()->settingsDialog());
311+
_tlsDialog->setAttribute(Qt::WA_DeleteOnClose);
312+
QSet<QSslCertificate> certs;
313+
certs.reserve(errors.size());
314+
for (const auto &error : errors) {
315+
certs << error.certificate();
316+
}
317+
connect(_tlsDialog, &TlsErrorDialog::accepted, _tlsDialog, [certs, blockJobs, this]() {
318+
_account->addApprovedCerts(certs);
319+
_tlsDialog.clear();
320+
_waitingForNewCredentials = false;
321+
checkConnectivity(blockJobs);
322+
});
323+
connect(_tlsDialog, &TlsErrorDialog::rejected, this, [certs, this]() {
324+
setState(SignedOut);
325+
});
326+
327+
_tlsDialog->show();
328+
}
329+
ocApp()->gui()->raiseDialog(_tlsDialog);
330+
});
305331
if (isConnected()) {
306332
// Use a small authed propfind as a minimal ping when we're
307333
// already connected.
@@ -313,16 +339,12 @@ void AccountState::checkConnectivity(bool blockJobs)
313339
}
314340
} else {
315341
// Check the server and then the auth.
316-
317-
// Let's try this for all OS and see if it fixes the Qt issues we have on Linux #4720 #3888 #4051
318-
// There seems to be a bug in Qt on Windows where QNAM sometimes stops
319-
// working correctly after the computer woke up from sleep. See #2895 #2899
320-
// and #2973.
321-
// As an attempted workaround, reset the QNAM regularly if the account is
322-
// disconnected.
323-
account()->clearCookieJar();
324-
account()->resetAccessManager();
325-
_connectionValidator->checkServerAndUpdate();
342+
if (_waitingForNewCredentials) {
343+
_connectionValidator->checkServer();
344+
} else {
345+
account()->clearCookieJar();
346+
_connectionValidator->checkServerAndUpdate();
347+
}
326348
}
327349
}
328350

@@ -380,7 +402,7 @@ void AccountState::slotConnectionValidatorResult(ConnectionValidator::Status sta
380402
slotInvalidCredentials();
381403
break;
382404
case ConnectionValidator::SslError:
383-
setState(SignedOut);
405+
// handled with the tlsDialog
384406
break;
385407
case ConnectionValidator::ServiceUnavailable:
386408
_timeSinceMaintenanceOver.invalidate();

‎src/gui/accountstate.h

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ namespace OCC {
3434

3535
class AccountState;
3636
class Account;
37+
class TlsErrorDialog;
3738

3839
/**
3940
* @brief Extra info about an ownCloud server account.
@@ -169,6 +170,7 @@ protected Q_SLOTS:
169170
QDateTime _timeOfLastETagCheck;
170171
QPointer<ConnectionValidator> _connectionValidator;
171172
QPointer<UpdateUrlDialog> _updateUrlDialog;
173+
QPointer<TlsErrorDialog> _tlsDialog;
172174

173175
/**
174176
* Starts counting when the server starts being back up after 503 or

‎src/gui/connectionvalidator.cpp

+8-1
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@
2222
#include "account.h"
2323
#include "clientproxy.h"
2424
#include "connectionvalidator.h"
25+
#include "creds/abstractcredentials.h"
2526
#include "networkjobs.h"
2627
#include "networkjobs/jsonjob.h"
2728
#include "theme.h"
28-
#include <creds/abstractcredentials.h>
29+
#include "tlserrordialog.h"
2930

3031
using namespace std::chrono_literals;
3132

@@ -98,6 +99,8 @@ void ConnectionValidator::systemProxyLookupDone(const QNetworkProxy &proxy)
9899
// The actual check
99100
void ConnectionValidator::slotCheckServerAndAuth()
100101
{
102+
// ensure we receive ssl errors
103+
_account->resetAccessManager();
101104
CheckServerJob *checkJob = new CheckServerJob(_account, this);
102105
checkJob->setClearCookies(_clearCookies);
103106
checkJob->setTimeout(timeoutToUse);
@@ -108,6 +111,7 @@ void ConnectionValidator::slotCheckServerAndAuth()
108111
_errors.append(tr("timeout"));
109112
reportResult(Timeout);
110113
});
114+
connect(checkJob, &CheckServerJob::sslErrors, this, &ConnectionValidator::sslErrors);
111115
checkJob->start();
112116
}
113117

@@ -235,6 +239,7 @@ void ConnectionValidator::checkServerCapabilities()
235239
{
236240
// The main flow now needs the capabilities
237241
auto *job = new JsonApiJob(_account, QStringLiteral("ocs/v2.php/cloud/capabilities"), {}, {}, this);
242+
job->setAuthenticationJob(true);
238243
job->setTimeout(timeoutToUse);
239244

240245
QObject::connect(job, &JsonApiJob::finishedSignal, this, [job, this] {
@@ -257,6 +262,7 @@ void ConnectionValidator::fetchUser()
257262
{
258263
auto *job = new JsonApiJob(_account, QLatin1String("ocs/v2.php/cloud/user"), {}, {}, this);
259264
job->setTimeout(timeoutToUse);
265+
job->setAuthenticationJob(true);
260266
QObject::connect(job, &JsonApiJob::finishedSignal, this, [job, this] {
261267
const QString user = job->data().value("ocs").toObject().value("data").toObject().value("id").toString();
262268
if (!user.isEmpty()) {
@@ -275,6 +281,7 @@ void ConnectionValidator::fetchUser()
275281
#ifndef TOKEN_AUTH_ONLY
276282
if (capabilities.isValid() && capabilities.avatarsAvailable()) {
277283
auto *job = new AvatarJob(_account, _account->davUser(), 128, this);
284+
job->setAuthenticationJob(true);
278285
job->setTimeout(20s);
279286
QObject::connect(job, &AvatarJob::avatarPixmap, this, &ConnectionValidator::slotAvatarImage);
280287
job->start();

‎src/gui/connectionvalidator.h

+2
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ public slots:
112112
signals:
113113
void connectionResult(ConnectionValidator::Status status, const QStringList &errors);
114114

115+
void sslErrors(const QList<QSslError> &errors);
116+
115117
protected slots:
116118
/// Checks authentication only.
117119
void checkAuthentication();

‎src/gui/newwizard/jobs/resolveurljobfactory.cpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
#include "accessmanager.h"
1818
#include "common/utility.h"
19+
#include "gui/application.h"
20+
#include "gui/owncloudgui.h"
21+
#include "gui/settingsdialog.h"
1922
#include "gui/tlserrordialog.h"
2023
#include "gui/updateurldialog.h"
2124

@@ -100,7 +103,7 @@ CoreJob *ResolveUrlJobFactory::startJob(const QUrl &url)
100103
connect(reply, &QNetworkReply::finished, job, makeFinishedHandler(reply));
101104

102105
connect(reply, &QNetworkReply::sslErrors, reply, [reply, req, job, makeFinishedHandler, nam = nam()](const QList<QSslError> &errors) mutable {
103-
auto *tlsErrorDialog = new TlsErrorDialog(errors, reply->url().host());
106+
auto *tlsErrorDialog = new TlsErrorDialog(errors, reply->url().host(), ocApp()->gui()->settingsDialog());
104107

105108
reply->setProperty(abortedBySslErrorHandlerC, true);
106109
reply->abort();
@@ -118,6 +121,7 @@ CoreJob *ResolveUrlJobFactory::startJob(const QUrl &url)
118121
});
119122

120123
tlsErrorDialog->show();
124+
ocApp()->gui()->raiseDialog(tlsErrorDialog);
121125
});
122126

123127
makeRequest();

‎src/gui/owncloudgui.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -960,7 +960,6 @@ void ownCloudGui::slotUpdateProgress(Folder *folder, const ProgressInfo &progres
960960
void ownCloudGui::slotLogin()
961961
{
962962
if (auto account = qvariant_cast<AccountStatePtr>(sender()->property(propertyAccountC))) {
963-
account->account()->resetRejectedCertificates();
964963
account->signIn();
965964
} else {
966965
for (const auto &a : AccountManager::instance()->accounts()) {

‎src/gui/tlserrordialog.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class TlsErrorDialog : public QDialog
2828
Q_OBJECT
2929

3030
public:
31-
explicit TlsErrorDialog(const QList<QSslError> &sslErrors, const QString &host, QWidget *parent = nullptr);
31+
explicit TlsErrorDialog(const QList<QSslError> &sslErrors, const QString &host, QWidget *parent);
3232
~TlsErrorDialog() override;
3333

3434
private:

‎src/libsync/abstractnetworkjob.cpp

-3
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,6 @@ void AbstractNetworkJob::adoptRequest(QPointer<QNetworkReply> reply)
180180
void AbstractNetworkJob::slotFinished()
181181
{
182182
_finished = true;
183-
if (_reply->error() == QNetworkReply::SslHandshakeFailedError) {
184-
qCWarning(lcNetworkJob) << "SslHandshakeFailedError:" << errorString() << ": can be caused by a webserver wanting SSL client certificates";
185-
}
186183
if (_reply->error() != QNetworkReply::NoError) {
187184
if (_account->jobQueue()->retry(this)) {
188185
qCDebug(lcNetworkJob) << "Queuing: " << _reply->url() << " for retry";

‎src/libsync/accessmanager.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,14 @@ QNetworkReply *AccessManager::createRequest(QNetworkAccessManager::Operation op,
7878
newRequest.setAttribute(QNetworkRequest::Http2AllowedAttribute, http2EnabledEnv);
7979
}
8080

81-
// for some reason, passing an empty list causes the default chain to be removed
82-
// this behavior does not match the documentation
8381
auto sslConfiguration = newRequest.sslConfiguration();
8482

8583
sslConfiguration.setSslOption(QSsl::SslOptionDisableSessionTickets, false);
8684
sslConfiguration.setSslOption(QSsl::SslOptionDisableSessionSharing, false);
8785
sslConfiguration.setSslOption(QSsl::SslOptionDisableSessionPersistence, false);
8886
if (!_customTrustedCaCertificates.isEmpty()) {
87+
// for some reason, passing an empty list causes the default chain to be removed
88+
// this behavior does not match the documentation
8989
sslConfiguration.addCaCertificates({ _customTrustedCaCertificates.begin(), _customTrustedCaCertificates.end() });
9090
}
9191
newRequest.setSslConfiguration(sslConfiguration);

‎src/libsync/account.cpp

+2-7
Original file line numberDiff line numberDiff line change
@@ -269,17 +269,12 @@ void Account::setApprovedCerts(const QList<QSslCertificate> &certs)
269269
_am->setCustomTrustedCaCertificates(_approvedCerts);
270270
}
271271

272-
void Account::addApprovedCerts(const QList<QSslCertificate> &certs)
272+
void Account::addApprovedCerts(const QSet<QSslCertificate> &certs)
273273
{
274-
_approvedCerts.unite({ certs.begin(), certs.end() });
274+
_approvedCerts.unite(certs);
275275
_am->setCustomTrustedCaCertificates(_approvedCerts);
276276
}
277277

278-
void Account::resetRejectedCertificates()
279-
{
280-
_rejectedCertificates.clear();
281-
}
282-
283278
void Account::setUrl(const QUrl &url)
284279
{
285280
_url = url;

0 commit comments

Comments
 (0)
Please sign in to comment.