Finally encrypt subdirectories during sync
authorKevin Ottens <kevin.ottens@nextcloud.com>
Wed, 17 Jun 2020 17:19:30 +0000 (19:19 +0200)
committerKevin Ottens <kevin.ottens@nextcloud.com>
Tue, 30 Jun 2020 09:29:08 +0000 (11:29 +0200)
We catch when a directory is inside a known encrypted folder and in such
a case we now do the following:
 1) we encrypt the folder meta data (its name) properly and create it
    under that mangled name on the server side
 2) we mark the new folder itself as encrypted

Signed-off-by: Kevin Ottens <kevin.ottens@nextcloud.com>
src/libsync/propagateremotemkdir.cpp
src/libsync/propagateremotemkdir.h

index 4e5ae7ab4c241a066fe9c833edc933c45b28dc78..107cc2831d7c6168d64ad2afec80fb9d53a22c3d 100644 (file)
 #include "owncloudpropagator_p.h"
 #include "account.h"
 #include "common/syncjournalfilerecord.h"
+#include "propagateuploadencrypted.h"
 #include "propagateremotedelete.h"
 #include "common/asserts.h"
+#include "encryptfolderjob.h"
 
 #include <QFile>
 #include <QLoggingCategory>
@@ -36,13 +38,15 @@ void PropagateRemoteMkdir::start()
     propagator()->_activeJobList.append(this);
 
     if (!_deleteExisting) {
-        return slotStartMkcolJob();
+        slotMkdir();
+        return;
     }
 
     _job = new DeleteJob(propagator()->account(),
         propagator()->_remoteFolder + _item->_file,
         this);
-    connect(_job, SIGNAL(finishedSignal()), SLOT(slotStartMkcolJob()));
+    connect(static_cast<DeleteJob*>(_job.data()), &DeleteJob::finishedSignal,
+            this, &PropagateRemoteMkdir::slotMkdir);
     _job->start();
 }
 
@@ -60,6 +64,28 @@ void PropagateRemoteMkdir::slotStartMkcolJob()
     _job->start();
 }
 
+void PropagateRemoteMkdir::slotStartEncryptedMkcolJob(const QString &path, const QString &filename, quint64 size)
+{
+    Q_UNUSED(path)
+    Q_UNUSED(size)
+
+    if (propagator()->_abortRequested.fetchAndAddRelaxed(0))
+        return;
+
+    qDebug() << filename;
+    qCDebug(lcPropagateRemoteMkdir) << filename;
+
+    auto job = new MkColJob(propagator()->account(),
+                            propagator()->_remoteFolder + filename,
+                            this);
+    connect(job, qOverload<QNetworkReply::NetworkError>(&MkColJob::finished),
+            _uploadEncryptedHelper, &PropagateUploadEncrypted::unlockFolder);
+    connect(job, qOverload<QNetworkReply::NetworkError>(&MkColJob::finished),
+            this, &PropagateRemoteMkdir::slotMkcolJobFinished);
+    _job = job;
+    _job->start();
+}
+
 void PropagateRemoteMkdir::abort(PropagatorJob::AbortType abortType)
 {
     if (_job && _job->reply())
@@ -75,6 +101,36 @@ void PropagateRemoteMkdir::setDeleteExisting(bool enabled)
     _deleteExisting = enabled;
 }
 
+void PropagateRemoteMkdir::slotMkdir()
+{
+    const auto parentPath = [=]() {
+        const auto result = propagator()->_remoteFolder;
+        if (result.startsWith('/')) {
+            return result.mid(1);
+        } else {
+            return result;
+        }
+    }();
+    const auto path = parentPath + _item->_file;
+    const auto account = propagator()->account();
+
+    if (!account->capabilities().clientSideEncryptionAvailable() ||
+        !account->e2e()->isAnyParentFolderEncrypted(path)) {
+        slotStartMkcolJob();
+        return;
+    }
+
+    // We should be encrypted as well since our parent is
+    _uploadEncryptedHelper = new PropagateUploadEncrypted(propagator(), _item);
+    connect(_uploadEncryptedHelper, &PropagateUploadEncrypted::folderNotEncrypted,
+      this, &PropagateRemoteMkdir::slotStartMkcolJob);
+    connect(_uploadEncryptedHelper, &PropagateUploadEncrypted::finalized,
+      this, &PropagateRemoteMkdir::slotStartEncryptedMkcolJob);
+    connect(_uploadEncryptedHelper, &PropagateUploadEncrypted::error,
+      []{ qCDebug(lcPropagateRemoteMkdir) << "Error setting up encryption."; });
+    _uploadEncryptedHelper->start();
+}
+
 void PropagateRemoteMkdir::slotMkcolJobFinished()
 {
     propagator()->_activeJobList.removeOne(this);
@@ -121,6 +177,22 @@ void PropagateRemoteMkdir::slotMkcolJobFinished()
         _job = propfindJob;
         return;
     }
+
+    if (!_uploadEncryptedHelper) {
+        success();
+    } else {
+        // We still need to mark that folder encrypted
+        propagator()->_activeJobList.append(this);
+        auto job = new OCC::EncryptFolderJob(propagator()->account(), _job->path(), _item->_fileId, this);
+        connect(job, &OCC::EncryptFolderJob::finished, this, &PropagateRemoteMkdir::slotEncryptFolderFinished);
+        job->start();
+    }
+}
+
+void PropagateRemoteMkdir::slotEncryptFolderFinished()
+{
+    qCDebug(lcPropagateRemoteMkdir) << "Success making the new folder encrypted";
+    propagator()->_activeJobList.removeOne(this);
     success();
 }
 
index b850e6f2a0dd2a84856738ddf3c9facd2adc4833..469d65bd9d7da04499319114a052b53fd0fa67c3 100644 (file)
@@ -18,6 +18,8 @@
 
 namespace OCC {
 
+class PropagateUploadEncrypted;
+
 /**
  * @brief The PropagateRemoteMkdir class
  * @ingroup libsync
@@ -27,11 +29,13 @@ class PropagateRemoteMkdir : public PropagateItemJob
     Q_OBJECT
     QPointer<AbstractNetworkJob> _job;
     bool _deleteExisting;
+    PropagateUploadEncrypted *_uploadEncryptedHelper;
     friend class PropagateDirectory; // So it can access the _item;
 public:
     PropagateRemoteMkdir(OwncloudPropagator *propagator, const SyncFileItemPtr &item)
         : PropagateItemJob(propagator, item)
         , _deleteExisting(false)
+        , _uploadEncryptedHelper(nullptr)
     {
     }
     void start() override;
@@ -49,8 +53,11 @@ public:
     void setDeleteExisting(bool enabled);
 
 private slots:
+    void slotMkdir();
     void slotStartMkcolJob();
+    void slotStartEncryptedMkcolJob(const QString &path, const QString &filename, quint64 size);
     void slotMkcolJobFinished();
+    void slotEncryptFolderFinished();
     void propfindResult(const QVariantMap &);
     void propfindError();
     void success();