From ff7edebd98e6b092f20bae5d889a8815fe52a51c Mon Sep 17 00:00:00 2001 From: Matthieu Gallien Date: Tue, 6 Jun 2023 12:33:34 +0200 Subject: [PATCH] addd a cmake option to enable single account desktop client Signed-off-by: Matthieu Gallien --- NEXTCLOUD.cmake | 2 + config.h.in | 2 + src/gui/accountsettings.cpp | 9 +++ src/gui/accountsettings.h | 1 + src/gui/systray.cpp | 39 ++++++++++ src/gui/systray.h | 6 +- src/libsync/CMakeLists.txt | 2 + src/libsync/account.cpp | 6 +- src/libsync/account.h | 6 +- src/libsync/clientsideencryption.cpp | 26 ++++++- src/libsync/clientsideencryption.h | 9 +++ src/libsync/clientsidetokenselector.cpp | 95 +++++++++++++++++++++++++ src/libsync/clientsidetokenselector.h | 54 ++++++++++++++ 13 files changed, 247 insertions(+), 10 deletions(-) create mode 100644 src/libsync/clientsidetokenselector.cpp create mode 100644 src/libsync/clientsidetokenselector.h diff --git a/NEXTCLOUD.cmake b/NEXTCLOUD.cmake index 40d0f2ff403ca..42a6747bac1f2 100644 --- a/NEXTCLOUD.cmake +++ b/NEXTCLOUD.cmake @@ -80,3 +80,5 @@ endif() if (APPLE) option( BUILD_FILE_PROVIDER_MODULE "Build the macOS virtual files File Provider module" OFF ) endif() + +set (CLIENTSIDEENCRYPTION_ENFORCE_USB_TOKEN true) diff --git a/config.h.in b/config.h.in index ca9907c52f294..9d0a9aef590dc 100644 --- a/config.h.in +++ b/config.h.in @@ -61,4 +61,6 @@ #cmakedefine CFAPI_SHELL_EXTENSIONS_LIB_NAME "@CFAPI_SHELL_EXTENSIONS_LIB_NAME@" +#cmakedefine CLIENTSIDEENCRYPTION_ENFORCE_USB_TOKEN @CLIENTSIDEENCRYPTION_ENFORCE_USB_TOKEN@ + #endif diff --git a/src/gui/accountsettings.cpp b/src/gui/accountsettings.cpp index 4f4523a4231df..59617141596e1 100644 --- a/src/gui/accountsettings.cpp +++ b/src/gui/accountsettings.cpp @@ -283,6 +283,7 @@ void AccountSettings::slotE2eEncryptionMnemonicReady() void AccountSettings::slotE2eEncryptionGenerateKeys() { connect(_accountState->account()->e2e(), &ClientSideEncryption::initializationFinished, this, &AccountSettings::slotE2eEncryptionInitializationFinished); + connect(_accountState->account()->e2e(), &ClientSideEncryption::displayTokenInitDialog, this, &AccountSettings::slotDisplayTokenInitDialog); _accountState->account()->setE2eEncryptionKeysGenerationAllowed(true); _accountState->account()->setAskUserForMnemonic(true); _accountState->account()->e2e()->initialize(_accountState->account()); @@ -291,6 +292,7 @@ void AccountSettings::slotE2eEncryptionGenerateKeys() void AccountSettings::slotE2eEncryptionInitializationFinished(bool isNewMnemonicGenerated) { disconnect(_accountState->account()->e2e(), &ClientSideEncryption::initializationFinished, this, &AccountSettings::slotE2eEncryptionInitializationFinished); + disconnect(_accountState->account()->e2e(), &ClientSideEncryption::displayTokenInitDialog, this, &AccountSettings::slotDisplayTokenInitDialog); if (_accountState->account()->e2e()->isInitialized()) { removeActionFromEncryptionMessage(e2EeUiActionEnableEncryptionId); slotE2eEncryptionMnemonicReady(); @@ -301,6 +303,13 @@ void AccountSettings::slotE2eEncryptionInitializationFinished(bool isNewMnemonic _accountState->account()->setAskUserForMnemonic(false); } +void AccountSettings::slotDisplayTokenInitDialog() +{ + disconnect(_accountState->account()->e2e(), &ClientSideEncryption::initializationFinished, this, &AccountSettings::slotE2eEncryptionInitializationFinished); + disconnect(_accountState->account()->e2e(), &ClientSideEncryption::displayTokenInitDialog, this, &AccountSettings::slotDisplayTokenInitDialog); + Systray::instance()->createTokenInitDialog(_accountState->account()->e2e()->discoveredTokens()); +} + void AccountSettings::slotEncryptFolderFinished(int status) { qCInfo(lcAccountSettings) << "Current folder encryption status code:" << status; diff --git a/src/gui/accountsettings.h b/src/gui/accountsettings.h index 1ddbc097d413e..3ec4551dc1221 100644 --- a/src/gui/accountsettings.h +++ b/src/gui/accountsettings.h @@ -106,6 +106,7 @@ protected slots: void slotE2eEncryptionMnemonicReady(); void slotE2eEncryptionGenerateKeys(); void slotE2eEncryptionInitializationFinished(bool isNewMnemonicGenerated); + void slotDisplayTokenInitDialog(); void slotEncryptFolderFinished(int status); void slotSelectiveSyncChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, diff --git a/src/gui/systray.cpp b/src/gui/systray.cpp index 936b999557c64..0043c9a2599e0 100644 --- a/src/gui/systray.cpp +++ b/src/gui/systray.cpp @@ -412,6 +412,45 @@ void Systray::createFileActivityDialog(const QString &localPath) Q_EMIT showFileDetailsPage(localPath, FileDetailsPage::Activity); } +void Systray::createTokenInitDialog(const QVariantList &tokensInfo) +{ + if(_tokenInitDialog) { + destroyDialog(_tokenInitDialog); + _tokenInitDialog = nullptr; + } + + qCDebug(lcSystray) << "Opening new token init dialog with " << tokensInfo.size() << "possible tokens"; + + if (!_trayEngine) { + qCWarning(lcSystray) << "Could not open token init dialog as no tray engine was available"; + return; + } + + const QVariantMap initialProperties{ + {"tokensInfo", tokensInfo}, + }; + + QQmlComponent tokenInitDialogComponent(_trayEngine, QStringLiteral("qrc:/qml/src/gui/filedetails/FileDetailsWindow.qml")); + + if (!tokenInitDialogComponent.isError()) { + const auto createdDialog = tokenInitDialogComponent.createWithInitialProperties(initialProperties); + const auto dialog = qobject_cast(createdDialog); + + if(!dialog) { + qCWarning(lcSystray) << "Token init dialog window resulted in creation of object that was not a window!"; + return; + } + + _tokenInitDialog = dialog; + + _tokenInitDialog->show(); + _tokenInitDialog->raise(); + _tokenInitDialog->requestActivate(); + } else { + qCWarning(lcSystray) << tokenInitDialogComponent.errorString(); + } +} + void Systray::presentShareViewInTray(const QString &localPath) { const auto folder = FolderMan::instance()->folderForPath(localPath); diff --git a/src/gui/systray.h b/src/gui/systray.h index 370bebc447923..f41e616ea7e82 100644 --- a/src/gui/systray.h +++ b/src/gui/systray.h @@ -15,11 +15,11 @@ #ifndef SYSTRAY_H #define SYSTRAY_H -#include - #include "accountmanager.h" #include "tray/usermodel.h" +#include +#include #include class QScreen; @@ -145,6 +145,7 @@ public slots: void createShareDialog(const QString &localPath); void createFileActivityDialog(const QString &localPath); + void createTokenInitDialog(const QVariantList &tokensInfo); void presentShareViewInTray(const QString &localPath); @@ -185,6 +186,7 @@ private slots: QSet _callsAlreadyNotified; QPointer _editFileLocallyLoadingDialog; QVector _fileDetailDialogs; + QQuickWindow* _tokenInitDialog = nullptr; }; } // namespace OCC diff --git a/src/libsync/CMakeLists.txt b/src/libsync/CMakeLists.txt index b74916c96338d..04e92e7117ea2 100644 --- a/src/libsync/CMakeLists.txt +++ b/src/libsync/CMakeLists.txt @@ -105,6 +105,8 @@ set(libsync_SRCS clientsideencryption.cpp clientsideencryptionjobs.h clientsideencryptionjobs.cpp + clientsidetokenselector.h + clientsidetokenselector.cpp datetimeprovider.h datetimeprovider.cpp ocsuserstatusconnector.h diff --git a/src/libsync/account.cpp b/src/libsync/account.cpp index 6b2aa2b7903b7..685dfc2a6cc8f 100644 --- a/src/libsync/account.cpp +++ b/src/libsync/account.cpp @@ -33,6 +33,8 @@ #include "clientsideencryption.h" #include "ocsuserstatusconnector.h" +#include "config.h" + #include #include #include @@ -1023,9 +1025,9 @@ bool Account::askUserForMnemonic() const return _e2eAskUserForMnemonic; } -bool Account::useHardwareTokenEncryption() const +bool Account::enforceUseHardwareTokenEncryption() const { - return !encryptionHardwareTokenDriverPath().isEmpty(); + return CLIENTSIDEENCRYPTION_ENFORCE_USB_TOKEN; } QString Account::encryptionHardwareTokenDriverPath() const diff --git a/src/libsync/account.h b/src/libsync/account.h index 47c1c0135adf9..6f67715b92e44 100644 --- a/src/libsync/account.h +++ b/src/libsync/account.h @@ -88,7 +88,7 @@ class OWNCLOUDSYNC_EXPORT Account : public QObject Q_PROPERTY(QUrl url MEMBER _url) Q_PROPERTY(bool e2eEncryptionKeysGenerationAllowed MEMBER _e2eEncryptionKeysGenerationAllowed) Q_PROPERTY(bool askUserForMnemonic READ askUserForMnemonic WRITE setAskUserForMnemonic NOTIFY askUserForMnemonicChanged) - Q_PROPERTY(bool useHardwareTokenEncryption READ useHardwareTokenEncryption NOTIFY useHardwareTokenEncryptionChanged) + Q_PROPERTY(bool enforceUseHardwareTokenEncryption READ enforceUseHardwareTokenEncryption NOTIFY enforceUseHardwareTokenEncryptionChanged) Q_PROPERTY(QString encryptionHardwareTokenDriverPath READ encryptionHardwareTokenDriverPath NOTIFY encryptionHardwareTokenDriverPathChanged) public: @@ -328,7 +328,7 @@ class OWNCLOUDSYNC_EXPORT Account : public QObject [[nodiscard]] bool askUserForMnemonic() const; - [[nodiscard]] bool useHardwareTokenEncryption() const; + [[nodiscard]] bool enforceUseHardwareTokenEncryption() const; [[nodiscard]] QString encryptionHardwareTokenDriverPath() const; @@ -360,7 +360,7 @@ public slots: void accountChangedDisplayName(); void prettyNameChanged(); void askUserForMnemonicChanged(); - void useHardwareTokenEncryptionChanged(); + void enforceUseHardwareTokenEncryptionChanged(); void encryptionHardwareTokenDriverPathChanged(); /// Used in RemoteWipe diff --git a/src/libsync/clientsideencryption.cpp b/src/libsync/clientsideencryption.cpp index 254268c3afeb0..ea110ff7bbbac 100644 --- a/src/libsync/clientsideencryption.cpp +++ b/src/libsync/clientsideencryption.cpp @@ -1012,13 +1012,22 @@ std::optional decryptStringAsymmetricWithToken(ENGINE *sslEngine, } -ClientSideEncryption::ClientSideEncryption() = default; +ClientSideEncryption::ClientSideEncryption() +{ + connect(&_usbTokenInformation, &ClientSideTokenSelector::discoveredTokensChanged, + this, &ClientSideEncryption::displayTokenInitDialog); +} bool ClientSideEncryption::isInitialized() const { return !getMnemonic().isEmpty(); } +QVariantList ClientSideEncryption::discoveredTokens() const +{ + return _usbTokenInformation.discoveredTokens(); +} + const QSslKey &ClientSideEncryption::getPublicKey() const { return _publicKey; @@ -1080,8 +1089,19 @@ void ClientSideEncryption::initialize(const AccountPtr &account) return; } - if (account->useHardwareTokenEncryption()) { - initializeHardwareTokenEncryption(account); + if (account->enforceUseHardwareTokenEncryption()) { + if (_usbTokenInformation.isSetup()) { + initializeHardwareTokenEncryption(account); + } else if (account->e2eEncryptionKeysGenerationAllowed() && account->askUserForMnemonic()) { + _usbTokenInformation.searchForToken(); + if (_usbTokenInformation.isSetup()) { + initializeHardwareTokenEncryption(account); + } else { + emit initializationFinished(); + } + } else { + emit initializationFinished(); + } } else { fetchCertificateFromKeyChain(account); } diff --git a/src/libsync/clientsideencryption.h b/src/libsync/clientsideencryption.h index 7dbc2b0636657..8cd1ae97c0989 100644 --- a/src/libsync/clientsideencryption.h +++ b/src/libsync/clientsideencryption.h @@ -16,7 +16,9 @@ #define CLIENTSIDEENCRYPTION_H #include "accountfwd.h" + #include "networkjobs.h" +#include "clientsidetokenselector.h" #include #include @@ -139,6 +141,10 @@ class OWNCLOUDSYNC_EXPORT ClientSideEncryption : public QObject { [[nodiscard]] bool isInitialized() const; + [[nodiscard]] bool tokenIsSetup() const; + + [[nodiscard]] QVariantList discoveredTokens() const; + [[nodiscard]] const QSslKey& getPublicKey() const; void setPublicKey(const QSslKey &publicKey); @@ -166,6 +172,7 @@ class OWNCLOUDSYNC_EXPORT ClientSideEncryption : public QObject { void certificateDeleted(); void mnemonicDeleted(); void publicKeyDeleted(); + void displayTokenInitDialog(); public slots: void initialize(const OCC::AccountPtr &account); @@ -248,6 +255,8 @@ private slots: QString _mnemonic; bool _newMnemonicGenerated = false; + ClientSideTokenSelector _usbTokenInformation; + PKCS11_KEY* _tokenPublicKey = nullptr; PKCS11_KEY* _tokenPrivateKey = nullptr; }; diff --git a/src/libsync/clientsidetokenselector.cpp b/src/libsync/clientsidetokenselector.cpp new file mode 100644 index 0000000000000..b1eb08c2f217e --- /dev/null +++ b/src/libsync/clientsidetokenselector.cpp @@ -0,0 +1,95 @@ +/* + * Copyright © 2023, Matthieu Gallien + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#define OPENSSL_SUPPRESS_DEPRECATED + +#include "clientsidetokenselector.h" + +#include + +#include + +namespace OCC +{ + +Q_LOGGING_CATEGORY(lcCseSelector, "nextcloud.sync.clientsideencryption.selector", QtInfoMsg) + +ClientSideTokenSelector::ClientSideTokenSelector(QObject *parent) + : QObject{parent} +{ + +} + +bool ClientSideTokenSelector::isSetup() const +{ + return false; +} + +QVariantList ClientSideTokenSelector::discoveredTokens() const +{ + return _discoveredTokens; +} + +void failedToInitialize(std::nullptr_t) +{ +} + +void ClientSideTokenSelector::searchForToken() +{ + auto account = nullptr; + auto ctx = PKCS11_CTX_new(); + + auto rc = PKCS11_CTX_load(ctx, "opensc-pkcs11.so"); + if (rc) { + qCWarning(lcCseSelector()) << "loading pkcs11 engine failed:" << ERR_reason_error_string(ERR_get_error()); + + failedToInitialize(account); + return; + } + + auto tokensCount = 0u; + PKCS11_SLOT *tokenSlots = nullptr; + /* get information on all slots */ + if (PKCS11_enumerate_slots(ctx, &tokenSlots, &tokensCount) < 0) { + qCWarning(lcCseSelector()) << "no slots available" << ERR_reason_error_string(ERR_get_error()); + + failedToInitialize(account); + return; + } + + _discoveredTokens.clear(); + auto currentSlot = static_cast(nullptr); + for(auto i = 0u; i < tokensCount; ++i) { + currentSlot = PKCS11_find_next_token(ctx, tokenSlots, tokensCount, currentSlot); + if (currentSlot == nullptr || currentSlot->token == nullptr) { + qCWarning(lcCseSelector()) << "no token available" << ERR_reason_error_string(ERR_get_error()); + + failedToInitialize(account); + return; + } + qCInfo(lcCseSelector()) << "Slot manufacturer......:" << currentSlot->manufacturer; + qCInfo(lcCseSelector()) << "Slot description.......:" << currentSlot->description; + qCInfo(lcCseSelector()) << "Slot token label.......:" << currentSlot->token->label; + qCInfo(lcCseSelector()) << "Slot token manufacturer:" << currentSlot->token->manufacturer; + qCInfo(lcCseSelector()) << "Slot token model.......:" << currentSlot->token->model; + qCInfo(lcCseSelector()) << "Slot token serialnr....:" << currentSlot->token->serialnr; + + _discoveredTokens.push_back(QVariantMap{{QStringLiteral("manufacturer"), QString::fromLatin1(currentSlot->manufacturer)}, + {QStringLiteral("description"), QString::fromLatin1(currentSlot->description)}, + {QStringLiteral("label"), QString::fromLatin1(currentSlot->token->label)},}); + } + emit discoveredTokensChanged(); +} + +} diff --git a/src/libsync/clientsidetokenselector.h b/src/libsync/clientsidetokenselector.h new file mode 100644 index 0000000000000..1cd02b612ee0f --- /dev/null +++ b/src/libsync/clientsidetokenselector.h @@ -0,0 +1,54 @@ +/* + * Copyright © 2023, Matthieu Gallien + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef CLIENTSIDETOKENSELECTOR_H +#define CLIENTSIDETOKENSELECTOR_H + +#include + +namespace OCC +{ + +class ClientSideTokenSelector : public QObject +{ + Q_OBJECT + + Q_PROPERTY(bool isSetup READ isSetup NOTIFY isSetupChanged) + + Q_PROPERTY(QVariantList discoveredTokens READ discoveredTokens NOTIFY discoveredTokensChanged) +public: + explicit ClientSideTokenSelector(QObject *parent = nullptr); + + [[nodiscard]] bool isSetup() const; + + [[nodiscard]] QVariantList discoveredTokens() const; + +public slots: + + void searchForToken(); + +signals: + + void isSetupChanged(); + + void discoveredTokensChanged(); + +private: + + QVariantList _discoveredTokens; +}; + +} + +#endif // CLIENTSIDETOKENSELECTOR_H