mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-27 09:30:13 +03:00
E2EE. Fix freeze on metadata checksum validation.
Signed-off-by: alex-z <blackslayer4@gmail.com>
This commit is contained in:
parent
c1bfd8b133
commit
90e3a37a20
7 changed files with 42 additions and 38 deletions
|
@ -41,6 +41,7 @@ constexpr auto caCertsKeyC = "CaCertificates";
|
||||||
constexpr auto accountsC = "Accounts";
|
constexpr auto accountsC = "Accounts";
|
||||||
constexpr auto versionC = "version";
|
constexpr auto versionC = "version";
|
||||||
constexpr auto serverVersionC = "serverVersion";
|
constexpr auto serverVersionC = "serverVersion";
|
||||||
|
constexpr auto skipE2eeMetadataChecksumValidationC = "skipE2eeMetadataChecksumValidation";
|
||||||
constexpr auto generalC = "General";
|
constexpr auto generalC = "General";
|
||||||
|
|
||||||
constexpr auto dummyAuthTypeC = "dummy";
|
constexpr auto dummyAuthTypeC = "dummy";
|
||||||
|
@ -286,6 +287,11 @@ void AccountManager::saveAccountHelper(Account *acc, QSettings &settings, bool s
|
||||||
settings.setValue(QLatin1String(davUserC), acc->_davUser);
|
settings.setValue(QLatin1String(davUserC), acc->_davUser);
|
||||||
settings.setValue(QLatin1String(displayNameC), acc->_displayName);
|
settings.setValue(QLatin1String(displayNameC), acc->_displayName);
|
||||||
settings.setValue(QLatin1String(serverVersionC), acc->_serverVersion);
|
settings.setValue(QLatin1String(serverVersionC), acc->_serverVersion);
|
||||||
|
if (!acc->_skipE2eeMetadataChecksumValidation) {
|
||||||
|
settings.remove(QLatin1String(skipE2eeMetadataChecksumValidationC));
|
||||||
|
} else {
|
||||||
|
settings.setValue(QLatin1String(skipE2eeMetadataChecksumValidationC), acc->_skipE2eeMetadataChecksumValidation);
|
||||||
|
}
|
||||||
|
|
||||||
if (acc->_credentials) {
|
if (acc->_credentials) {
|
||||||
if (saveCredentials) {
|
if (saveCredentials) {
|
||||||
|
@ -385,6 +391,7 @@ AccountPtr AccountManager::loadAccountHelper(QSettings &settings)
|
||||||
qCInfo(lcAccountManager) << "Account for" << acc->url() << "using auth type" << authType;
|
qCInfo(lcAccountManager) << "Account for" << acc->url() << "using auth type" << authType;
|
||||||
|
|
||||||
acc->_serverVersion = settings.value(QLatin1String(serverVersionC)).toString();
|
acc->_serverVersion = settings.value(QLatin1String(serverVersionC)).toString();
|
||||||
|
acc->_skipE2eeMetadataChecksumValidation = settings.value(QLatin1String(skipE2eeMetadataChecksumValidationC), {}).toBool();
|
||||||
acc->_davUser = settings.value(QLatin1String(davUserC), "").toString();
|
acc->_davUser = settings.value(QLatin1String(davUserC), "").toString();
|
||||||
|
|
||||||
// We want to only restore settings for that auth type and the user value
|
// We want to only restore settings for that auth type and the user value
|
||||||
|
|
|
@ -61,6 +61,7 @@ namespace {
|
||||||
constexpr int pushNotificationsReconnectInterval = 1000 * 60 * 2;
|
constexpr int pushNotificationsReconnectInterval = 1000 * 60 * 2;
|
||||||
constexpr int usernamePrefillServerVersionMinSupportedMajor = 24;
|
constexpr int usernamePrefillServerVersionMinSupportedMajor = 24;
|
||||||
constexpr int checksumRecalculateRequestServerVersionMinSupportedMajor = 24;
|
constexpr int checksumRecalculateRequestServerVersionMinSupportedMajor = 24;
|
||||||
|
constexpr auto isSkipE2eeMetadataChecksumValidationAllowedInClientVersion = MIRALL_VERSION_MAJOR == 3 && MIRALL_VERSION_MINOR == 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
|
@ -677,6 +678,17 @@ QString Account::serverVersion() const
|
||||||
return _serverVersion;
|
return _serverVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Account::shouldSkipE2eeMetadataChecksumValidation() const
|
||||||
|
{
|
||||||
|
return isSkipE2eeMetadataChecksumValidationAllowedInClientVersion && _skipE2eeMetadataChecksumValidation;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Account::resetShouldSkipE2eeMetadataChecksumValidation()
|
||||||
|
{
|
||||||
|
_skipE2eeMetadataChecksumValidation = false;
|
||||||
|
emit wantsAccountSaved(this);
|
||||||
|
}
|
||||||
|
|
||||||
int Account::serverVersionInt() const
|
int Account::serverVersionInt() const
|
||||||
{
|
{
|
||||||
// FIXME: Use Qt 5.5 QVersionNumber
|
// FIXME: Use Qt 5.5 QVersionNumber
|
||||||
|
|
|
@ -233,6 +233,10 @@ public:
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] QString serverVersion() const;
|
[[nodiscard]] QString serverVersion() const;
|
||||||
|
|
||||||
|
// check if the checksum validation of E2EE metadata is allowed to be skipped via config file, this will only work before client 3.9.0
|
||||||
|
[[nodiscard]] bool shouldSkipE2eeMetadataChecksumValidation() const;
|
||||||
|
void resetShouldSkipE2eeMetadataChecksumValidation();
|
||||||
|
|
||||||
/** Server version for easy comparison.
|
/** Server version for easy comparison.
|
||||||
*
|
*
|
||||||
* Example: serverVersionInt() >= makeServerVersion(11, 2, 3)
|
* Example: serverVersionInt() >= makeServerVersion(11, 2, 3)
|
||||||
|
@ -402,6 +406,7 @@ private:
|
||||||
QSslConfiguration _sslConfiguration;
|
QSslConfiguration _sslConfiguration;
|
||||||
Capabilities _capabilities;
|
Capabilities _capabilities;
|
||||||
QString _serverVersion;
|
QString _serverVersion;
|
||||||
|
bool _skipE2eeMetadataChecksumValidation = false;
|
||||||
QScopedPointer<AbstractSslErrorHandler> _sslErrorHandler;
|
QScopedPointer<AbstractSslErrorHandler> _sslErrorHandler;
|
||||||
QSharedPointer<QNetworkAccessManager> _am;
|
QSharedPointer<QNetworkAccessManager> _am;
|
||||||
QScopedPointer<AbstractCredentials> _credentials;
|
QScopedPointer<AbstractCredentials> _credentials;
|
||||||
|
|
|
@ -1643,9 +1643,14 @@ void FolderMetadata::setupExistingMetadata(const QByteArray& metadata)
|
||||||
|
|
||||||
if (!migratedMetadata && !checkMetadataKeyChecksum(metadataKey, metadataKeyChecksum)) {
|
if (!migratedMetadata && !checkMetadataKeyChecksum(metadataKey, metadataKeyChecksum)) {
|
||||||
qCInfo(lcCseMetadata) << "checksum comparison failed" << "server value" << metadataKeyChecksum << "client value" << computeMetadataKeyChecksum(metadataKey);
|
qCInfo(lcCseMetadata) << "checksum comparison failed" << "server value" << metadataKeyChecksum << "client value" << computeMetadataKeyChecksum(metadataKey);
|
||||||
_metadataKey.clear();
|
if (_account->shouldSkipE2eeMetadataChecksumValidation()) {
|
||||||
_files.clear();
|
qCDebug(lcCseMetadata) << "shouldSkipE2eeMetadataChecksumValidation is set. Allowing invalid checksum until next sync.";
|
||||||
return;
|
_encryptedMetadataNeedUpdate = true;
|
||||||
|
} else {
|
||||||
|
_metadataKey.clear();
|
||||||
|
_files.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// decryption finished, create new metadata key to be used for encryption
|
// decryption finished, create new metadata key to be used for encryption
|
||||||
|
@ -1721,13 +1726,6 @@ bool FolderMetadata::checkMetadataKeyChecksum(const QByteArray &metadataKey,
|
||||||
{
|
{
|
||||||
const auto referenceMetadataKeyValue = computeMetadataKeyChecksum(metadataKey);
|
const auto referenceMetadataKeyValue = computeMetadataKeyChecksum(metadataKey);
|
||||||
|
|
||||||
if (referenceMetadataKeyValue != metadataKeyChecksum) {
|
|
||||||
if (recoverMetadataKeyChecksum(metadataKeyChecksum, metadataKey)) {
|
|
||||||
qCInfo(lcCseMetadata) << "Checksum recovery done";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return referenceMetadataKeyValue == metadataKeyChecksum;
|
return referenceMetadataKeyValue == metadataKeyChecksum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1748,31 +1746,6 @@ QByteArray FolderMetadata::computeMetadataKeyChecksum(const QByteArray &metadata
|
||||||
return hashAlgorithm.result().toHex();
|
return hashAlgorithm.result().toHex();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FolderMetadata::recoverMetadataKeyChecksum(const QByteArray &expectedChecksum,
|
|
||||||
const QByteArray &metadataKey) const
|
|
||||||
{
|
|
||||||
const auto sortLambda = [] (const auto &first, const auto &second) {
|
|
||||||
return first.encryptedFilename < second.encryptedFilename;
|
|
||||||
};
|
|
||||||
auto sortedFiles = _files;
|
|
||||||
|
|
||||||
std::sort(sortedFiles.begin(), sortedFiles.end(), sortLambda);
|
|
||||||
|
|
||||||
do {
|
|
||||||
auto hashAlgorithm = QCryptographicHash{QCryptographicHash::Sha256};
|
|
||||||
hashAlgorithm.addData(_account->e2e()->_mnemonic.remove(' ').toUtf8());
|
|
||||||
for (const auto &singleFile : sortedFiles) {
|
|
||||||
hashAlgorithm.addData(singleFile.encryptedFilename.toUtf8());
|
|
||||||
}
|
|
||||||
hashAlgorithm.addData(metadataKey);
|
|
||||||
if (hashAlgorithm.result().toHex() == expectedChecksum) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} while (std::next_permutation(sortedFiles.begin(), sortedFiles.end(), sortLambda));
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FolderMetadata::isMetadataSetup() const
|
bool FolderMetadata::isMetadataSetup() const
|
||||||
{
|
{
|
||||||
return _isMetadataSetup;
|
return _isMetadataSetup;
|
||||||
|
|
|
@ -233,8 +233,6 @@ private:
|
||||||
[[nodiscard]] bool checkMetadataKeyChecksum(const QByteArray &metadataKey, const QByteArray &metadataKeyChecksum) const;
|
[[nodiscard]] bool checkMetadataKeyChecksum(const QByteArray &metadataKey, const QByteArray &metadataKeyChecksum) const;
|
||||||
|
|
||||||
[[nodiscard]] QByteArray computeMetadataKeyChecksum(const QByteArray &metadataKey) const;
|
[[nodiscard]] QByteArray computeMetadataKeyChecksum(const QByteArray &metadataKey) const;
|
||||||
[[nodiscard]] bool recoverMetadataKeyChecksum(const QByteArray &expectedChecksum,
|
|
||||||
const QByteArray &metadataKey) const;
|
|
||||||
|
|
||||||
QByteArray _metadataKey;
|
QByteArray _metadataKey;
|
||||||
|
|
||||||
|
|
|
@ -652,7 +652,11 @@ void OwncloudPropagator::startDirectoryPropagation(const SyncFileItemPtr &item,
|
||||||
_anotherSyncNeeded = true;
|
_anotherSyncNeeded = true;
|
||||||
} else if (item->_isEncryptedMetadataNeedUpdate) {
|
} else if (item->_isEncryptedMetadataNeedUpdate) {
|
||||||
SyncJournalFileRecord record;
|
SyncJournalFileRecord record;
|
||||||
if (_journal->getFileRecord(item->_file, &record) && record._e2eEncryptionStatus == SyncJournalFileRecord::EncryptionStatus::EncryptedMigratedV1_2) {
|
const auto isUnexpectedMetadataFormat = _journal->getFileRecord(item->_file, &record)
|
||||||
|
&& record._e2eEncryptionStatus == SyncJournalFileRecord::EncryptionStatus::EncryptedMigratedV1_2;
|
||||||
|
if (isUnexpectedMetadataFormat && _account->shouldSkipE2eeMetadataChecksumValidation()) {
|
||||||
|
qCDebug(lcPropagator) << "Getting unexpected metadata format, but allowing to continue as shouldSkipE2eeMetadataChecksumValidation is set.";
|
||||||
|
} else if (isUnexpectedMetadataFormat && !_account->shouldSkipE2eeMetadataChecksumValidation()) {
|
||||||
qCDebug(lcPropagator) << "could have upgraded metadata";
|
qCDebug(lcPropagator) << "could have upgraded metadata";
|
||||||
item->_instruction = CSyncEnums::CSYNC_INSTRUCTION_ERROR;
|
item->_instruction = CSyncEnums::CSYNC_INSTRUCTION_ERROR;
|
||||||
item->_errorString = tr("Error with the metadata. Getting unexpected metadata format.");
|
item->_errorString = tr("Error with the metadata. Getting unexpected metadata format.");
|
||||||
|
|
|
@ -956,6 +956,11 @@ void SyncEngine::finalize(bool success)
|
||||||
_syncRunning = false;
|
_syncRunning = false;
|
||||||
emit finished(success);
|
emit finished(success);
|
||||||
|
|
||||||
|
if (_account->shouldSkipE2eeMetadataChecksumValidation()) {
|
||||||
|
qCDebug(lcEngine) << "shouldSkipE2eeMetadataChecksumValidation was set. Sync is finished, so resetting it...";
|
||||||
|
_account->resetShouldSkipE2eeMetadataChecksumValidation();
|
||||||
|
}
|
||||||
|
|
||||||
// Delete the propagator only after emitting the signal.
|
// Delete the propagator only after emitting the signal.
|
||||||
_propagator.clear();
|
_propagator.clear();
|
||||||
_seenConflictFiles.clear();
|
_seenConflictFiles.clear();
|
||||||
|
|
Loading…
Reference in a new issue