E2EE. Fix freeze on metadata checksum validation.

Signed-off-by: alex-z <blackslayer4@gmail.com>
This commit is contained in:
alex-z 2023-05-05 16:15:09 +02:00
parent c1bfd8b133
commit 90e3a37a20
7 changed files with 42 additions and 38 deletions

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -1643,10 +1643,15 @@ 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);
if (_account->shouldSkipE2eeMetadataChecksumValidation()) {
qCDebug(lcCseMetadata) << "shouldSkipE2eeMetadataChecksumValidation is set. Allowing invalid checksum until next sync.";
_encryptedMetadataNeedUpdate = true;
} else {
_metadataKey.clear(); _metadataKey.clear();
_files.clear(); _files.clear();
return; return;
} }
}
// decryption finished, create new metadata key to be used for encryption // decryption finished, create new metadata key to be used for encryption
_metadataKey = EncryptionHelper::generateRandom(metadataKeySize); _metadataKey = EncryptionHelper::generateRandom(metadataKeySize);
@ -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;

View file

@ -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;

View file

@ -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.");

View file

@ -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();