nextcloud-desktop/src/libsync/propagateuploadencrypted.cpp
Matthieu Gallien 2a52ec33b0 add support to use PKCS#11 harware token to store certifice for e2ee
Close #5685

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2025-02-07 09:12:29 +01:00

190 lines
7.8 KiB
C++

#include "propagateuploadencrypted.h"
#include "clientsideencryptionjobs.h"
#include "networkjobs.h"
#include "clientsideencryption.h"
#include "foldermetadata.h"
#include "encryptedfoldermetadatahandler.h"
#include "filesystem.h"
#include "account.h"
#include <QFileInfo>
#include <QDir>
#include <QUrl>
#include <QFile>
#include <QTemporaryFile>
#include <QLoggingCategory>
#include <QMimeDatabase>
namespace OCC {
Q_LOGGING_CATEGORY(lcPropagateUploadEncrypted, "nextcloud.sync.propagator.upload.encrypted", QtInfoMsg)
PropagateUploadEncrypted::PropagateUploadEncrypted(OwncloudPropagator *propagator, const QString &remoteParentPath, SyncFileItemPtr item, QObject *parent)
: QObject(parent)
, _propagator(propagator)
, _remoteParentPath(Utility::noLeadingSlashPath(remoteParentPath))
, _item(item)
{
const auto rootPath = Utility::trailingSlashPath(Utility::noLeadingSlashPath(_propagator->remotePath()));
_remoteParentAbsolutePath = Utility::noTrailingSlashPath(rootPath + _remoteParentPath);
}
void PropagateUploadEncrypted::start()
{
/* If the file is in a encrypted folder, which we know, we wouldn't be here otherwise,
* we need to do the long road:
* find the ID of the folder.
* lock the folder using it's id.
* download the metadata
* update the metadata
* upload the file
* upload the metadata
* unlock the folder.
*/
// Encrypt File!
SyncJournalFileRecord rec;
if (!_propagator->_journal->getRootE2eFolderRecord(Utility::fullRemotePathToRemoteSyncRootRelative(_remoteParentAbsolutePath, _propagator->remotePath()),
&rec)
|| !rec.isValid()) {
emit error();
return;
}
_encryptedFolderMetadataHandler.reset(new EncryptedFolderMetadataHandler(_propagator->account(),
_remoteParentAbsolutePath,
_propagator->remotePath(),
_propagator->_journal,
rec.path()));
connect(_encryptedFolderMetadataHandler.data(), &EncryptedFolderMetadataHandler::fetchFinished,
this, &PropagateUploadEncrypted::slotFetchMetadataJobFinished);
_encryptedFolderMetadataHandler->fetchMetadata(EncryptedFolderMetadataHandler::FetchMode::AllowEmptyMetadata);
}
void PropagateUploadEncrypted::unlockFolder()
{
connect(_encryptedFolderMetadataHandler.data(), &EncryptedFolderMetadataHandler::folderUnlocked, this, &PropagateUploadEncrypted::folderUnlocked);
_encryptedFolderMetadataHandler->unlockFolder();
}
bool PropagateUploadEncrypted::isUnlockRunning() const
{
return _encryptedFolderMetadataHandler->isUnlockRunning();
}
bool PropagateUploadEncrypted::isFolderLocked() const
{
return _encryptedFolderMetadataHandler->isFolderLocked();
}
const QByteArray PropagateUploadEncrypted::folderToken() const
{
return _encryptedFolderMetadataHandler ? _encryptedFolderMetadataHandler->folderToken() : QByteArray{};
}
void PropagateUploadEncrypted::slotFetchMetadataJobFinished(int statusCode, const QString &message)
{
qCDebug(lcPropagateUploadEncrypted) << "Metadata Received, Preparing it for the new file." << message;
if (statusCode != 200) {
emit error();
return;
}
if (!_encryptedFolderMetadataHandler->folderMetadata() || !_encryptedFolderMetadataHandler->folderMetadata()->isValid()) {
qCDebug(lcPropagateUploadEncrypted()) << "There was an error encrypting the file, aborting upload. Invalid metadata.";
emit error();
return;
}
const auto metadata = _encryptedFolderMetadataHandler->folderMetadata();
QFileInfo info(_propagator->fullLocalPath(_item->_file));
const QString fileName = info.fileName();
// Find existing metadata for this file
bool found = false;
FolderMetadata::EncryptedFile encryptedFile;
const QVector<FolderMetadata::EncryptedFile> files = metadata->files();
for (const FolderMetadata::EncryptedFile &file : files) {
if (file.originalFilename == fileName) {
encryptedFile = file;
found = true;
}
}
// New encrypted file so set it all up!
if (!found) {
encryptedFile.encryptionKey = EncryptionHelper::generateRandom(16);
encryptedFile.encryptedFilename = EncryptionHelper::generateRandomFilename();
encryptedFile.originalFilename = fileName;
QMimeDatabase mdb;
encryptedFile.mimetype = mdb.mimeTypeForFile(info).name().toLocal8Bit();
// Other clients expect "httpd/unix-directory" instead of "inode/directory"
// Doesn't matter much for us since we don't do much about that mimetype anyway
if (encryptedFile.mimetype == QByteArrayLiteral("inode/directory")) {
encryptedFile.mimetype = QByteArrayLiteral("httpd/unix-directory");
}
}
encryptedFile.initializationVector = EncryptionHelper::generateRandom(16);
_item->_encryptedFileName = Utility::trailingSlashPath(_remoteParentPath) + encryptedFile.encryptedFilename;
_item->_e2eEncryptionStatusRemote = metadata->existingMetadataEncryptionStatus();
_item->_e2eEncryptionServerCapability =
EncryptionStatusEnums::fromEndToEndEncryptionApiVersion(_propagator->account()->capabilities().clientSideEncryptionVersion());
qCDebug(lcPropagateUploadEncrypted) << "Creating the encrypted file.";
if (info.isDir()) {
_completeFileName = encryptedFile.encryptedFilename;
} else {
QFile input(info.absoluteFilePath());
QFile output(QDir::tempPath() + QDir::separator() + encryptedFile.encryptedFilename);
QByteArray tag;
bool encryptionResult = EncryptionHelper::fileEncryption(encryptedFile.encryptionKey, encryptedFile.initializationVector, &input, &output, tag);
if (!encryptionResult) {
qCDebug(lcPropagateUploadEncrypted()) << "There was an error encrypting the file, aborting upload.";
emit error();
return;
}
encryptedFile.authenticationTag = tag;
_completeFileName = output.fileName();
}
qCDebug(lcPropagateUploadEncrypted) << "Creating the metadata for the encrypted file.";
metadata->addEncryptedFile(encryptedFile);
qCDebug(lcPropagateUploadEncrypted) << "Metadata created, sending to the server.";
connect(_encryptedFolderMetadataHandler.data(), &EncryptedFolderMetadataHandler::uploadFinished, this, &PropagateUploadEncrypted::slotUploadMetadataFinished);
_encryptedFolderMetadataHandler->uploadMetadata(EncryptedFolderMetadataHandler::UploadMode::KeepLock);
}
void PropagateUploadEncrypted::slotUploadMetadataFinished(int statusCode, const QString &message)
{
if (statusCode != 200) {
qCDebug(lcPropagateUploadEncrypted) << "Update metadata error for folder" << _encryptedFolderMetadataHandler->folderId() << "with error" << message;
qCDebug(lcPropagateUploadEncrypted()) << "Unlocking the folder.";
emit error();
return;
}
qCDebug(lcPropagateUploadEncrypted) << "Uploading of the metadata success, Encrypting the file";
QFileInfo outputInfo(_completeFileName);
qCDebug(lcPropagateUploadEncrypted) << "Encrypted Info:" << outputInfo.path() << outputInfo.fileName() << outputInfo.size();
qCDebug(lcPropagateUploadEncrypted) << "Finalizing the upload part, now the actuall uploader will take over";
emit finalized(Utility::trailingSlashPath(outputInfo.path()) + outputInfo.fileName(),
Utility::trailingSlashPath(_remoteParentPath) + outputInfo.fileName(),
FileSystem::getSize(_completeFileName));
}
} // namespace OCC