From 5e099f522e9fa91d40f1f7a5f36b939d12c5b5ad Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Mon, 3 Apr 2023 17:27:55 +0300 Subject: [PATCH] added private key export without password to client config --- client/core/defs.h | 2 +- client/core/errorstrings.cpp | 1 + client/core/servercontroller.cpp | 12 ++- client/core/servercontroller.h | 2 +- client/core/sshclient.cpp | 73 ++++++++++--------- client/core/sshclient.h | 4 +- client/resources.qrc | 2 +- client/ui/pages_logic/StartPageLogic.cpp | 30 +++++--- ...hInputField.qml => PopupWithTextField.qml} | 0 client/ui/qml/main.qml | 6 +- 10 files changed, 76 insertions(+), 56 deletions(-) rename client/ui/qml/Controls/{PopupWithInputField.qml => PopupWithTextField.qml} (100%) diff --git a/client/core/defs.h b/client/core/defs.h index 9017fbd3a..257de62f8 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -13,7 +13,6 @@ struct ServerCredentials QString hostName; QString userName; QString password; - QString decryptedPrivateKey; int port = 22; bool isValid() const { return !hostName.isEmpty() && !userName.isEmpty() && !password.isEmpty() && port > 0; } @@ -36,6 +35,7 @@ enum ErrorCode // Ssh connection errors SshRequsetDeniedError, SshInterruptedError, SshInternalError, + SshPrivateKeyError, // Ssh sftp errors SshSftpEofError, SshSftpNoSuchFileError, SshSftpPermissionDeniedError, diff --git a/client/core/errorstrings.cpp b/client/core/errorstrings.cpp index 871eb61b3..8108ff53c 100644 --- a/client/core/errorstrings.cpp +++ b/client/core/errorstrings.cpp @@ -21,6 +21,7 @@ QString errorString(ErrorCode code){ case(SshRequsetDeniedError): return QObject::tr("Ssh request was denied"); case(SshInterruptedError): return QObject::tr("Ssh request was interrupted"); case(SshInternalError): return QObject::tr("Ssh internal error"); + case(SshPrivateKeyError): return QObject::tr("Invalid private key or invalid passphrase entered"); // Libssh sftp errors case(SshSftpEofError): return QObject::tr("Sftp error: End-of-file encountered"); diff --git a/client/core/servercontroller.cpp b/client/core/servercontroller.cpp index eb9305a27..5087e92aa 100644 --- a/client/core/servercontroller.cpp +++ b/client/core/servercontroller.cpp @@ -46,7 +46,7 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr const std::function &cbReadStdOut, const std::function &cbReadStdErr) { - auto error = m_sshClient.connectToHost(credentials, m_passphraseCallback); + auto error = m_sshClient.connectToHost(credentials); if (error != ErrorCode::NoError) { return error; } @@ -221,7 +221,7 @@ ErrorCode ServerController::checkOpenVpnServer(DockerContainer container, const ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath, libssh::SftpOverwriteMode overwriteMode) { - auto error = m_sshClient.connectToHost(credentials, m_passphraseCallback); + auto error = m_sshClient.connectToHost(credentials); if (error != ErrorCode::NoError) { return error; } @@ -757,5 +757,11 @@ ErrorCode ServerController::getAlreadyInstalledContainers(const ServerCredential void ServerController::setPassphraseCallback(const std::function &callback) { - m_passphraseCallback = callback; + m_sshClient.setPassphraseCallback(callback); +} + +ErrorCode ServerController::getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey) +{ + auto error = m_sshClient.getDecryptedPrivateKey(credentials, decryptedPrivateKey); + return error; } diff --git a/client/core/servercontroller.h b/client/core/servercontroller.h index 6b1a4793c..0547cbf06 100644 --- a/client/core/servercontroller.h +++ b/client/core/servercontroller.h @@ -74,6 +74,7 @@ public: ErrorCode getAlreadyInstalledContainers(const ServerCredentials &credentials, QMap &installedContainers); void setPassphraseCallback(const std::function &callback); + ErrorCode getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey); private: ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container); ErrorCode prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject()); @@ -88,7 +89,6 @@ private: bool m_cancelInstallation = false; libssh::Client m_sshClient; - std::function m_passphraseCallback; signals: void serverIsBusy(const bool isBusy); }; diff --git a/client/core/sshclient.cpp b/client/core/sshclient.cpp index 1523257e0..db3ca9f54 100644 --- a/client/core/sshclient.cpp +++ b/client/core/sshclient.cpp @@ -25,15 +25,8 @@ namespace libssh { return 0; } - ErrorCode Client::connectToHost(const ServerCredentials &credentials, const std::function &passphraseCallback) + ErrorCode Client::connectToHost(const ServerCredentials &credentials) { -// if (is_ssh_initialized()) { -// qDebug() << "Failed to initialize ssh"; -// return ErrorCode::InternalError; -// } - - m_passphraseCallback = passphraseCallback; - if (m_session == nullptr) { m_session = ssh_new(); @@ -64,39 +57,20 @@ namespace libssh { int authResult = SSH_ERROR; if (credentials.password.contains("BEGIN") && credentials.password.contains("PRIVATE KEY")) { ssh_key privateKey; - authResult = ssh_pki_import_privkey_base64(credentials.password.toStdString().c_str(), nullptr, callback, nullptr, &privateKey); - if (authResult != SSH_OK) { - qDebug() << ssh_get_error(m_session); - return fromLibsshErrorCode(ssh_get_error_code(m_session)); - } - ssh_key publicKey; - authResult = ssh_pki_export_privkey_to_pubkey(privateKey, &publicKey); - if (authResult != SSH_OK) { - qDebug() << ssh_get_error(m_session); - return fromLibsshErrorCode(ssh_get_error_code(m_session)); - } - authResult = ssh_userauth_try_publickey(m_session, authUsername.c_str(), publicKey); - if (authResult != SSH_OK) { - qDebug() << ssh_get_error(m_session); - return fromLibsshErrorCode(ssh_get_error_code(m_session)); + authResult = ssh_pki_import_privkey_base64(credentials.password.toStdString().c_str(), nullptr, callback, nullptr, &privateKey); + if (authResult == SSH_OK) { + authResult = ssh_pki_export_privkey_to_pubkey(privateKey, &publicKey); } - authResult = ssh_userauth_publickey(m_session, authUsername.c_str(), privateKey); - if (authResult != SSH_OK) { - qDebug() << ssh_get_error(m_session); - return fromLibsshErrorCode(ssh_get_error_code(m_session)); + if (authResult == SSH_OK) { + authResult = ssh_userauth_try_publickey(m_session, authUsername.c_str(), publicKey); } - char* key = new char[65535]; - authResult = ssh_pki_export_privkey_base64(privateKey, nullptr, nullptr, nullptr, &key); - if (authResult != SSH_OK) { - qDebug() << ssh_get_error(m_session); - return fromLibsshErrorCode(ssh_get_error_code(m_session)); + if (authResult == SSH_OK) { + authResult = ssh_userauth_publickey(m_session, authUsername.c_str(), privateKey); } -// credentials.decryptedPrivateKey(key); - ssh_key_free(publicKey); ssh_key_free(privateKey); } else { @@ -363,4 +337,35 @@ namespace libssh { default: return ErrorCode::SshSftpFailureError; } } + + ErrorCode Client::getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey) + { + int authResult = SSH_ERROR; + ErrorCode errorCode = ErrorCode::NoError; + + ssh_key privateKey; + authResult = ssh_pki_import_privkey_base64(credentials.password.toStdString().c_str(), nullptr, callback, nullptr, &privateKey); + if (authResult == SSH_OK) { + char* key = new char[65535]; + + authResult = ssh_pki_export_privkey_base64(privateKey, nullptr, nullptr, nullptr, &key); + decryptedPrivateKey = key; + delete[] key; + + if (authResult != SSH_OK) { + qDebug() << "failed to export private key"; + errorCode = ErrorCode::InternalError; + } + } else { + errorCode = ErrorCode::SshPrivateKeyError; + } + + ssh_key_free(privateKey); + return errorCode; + } + + void Client::setPassphraseCallback(const std::function &callback) + { + m_passphraseCallback = callback; + } } diff --git a/client/core/sshclient.h b/client/core/sshclient.h index 057c72fe3..25f181b61 100644 --- a/client/core/sshclient.h +++ b/client/core/sshclient.h @@ -26,7 +26,7 @@ namespace libssh { Client(QObject *parent = nullptr); ~Client(); - ErrorCode connectToHost(const ServerCredentials &credentials, const std::function &passphraseCallback); + ErrorCode connectToHost(const ServerCredentials &credentials); void disconnectFromHost(); ErrorCode executeCommand(const QString &data, const std::function &cbReadStdOut, @@ -36,6 +36,8 @@ namespace libssh { const std::string& localPath, const std::string& remotePath, const std::string& fileDesc); + ErrorCode getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey); + void setPassphraseCallback(const std::function &callback); private: ErrorCode closeChannel(); ErrorCode closeSftpSession(); diff --git a/client/resources.qrc b/client/resources.qrc index ccc6ad688..3aa23c64d 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -165,6 +165,6 @@ ui/qml/Controls/PopupWithQuestion.qml ui/qml/Pages/PageAdvancedServerSettings.qml ui/qml/Controls/PopupWarning.qml - ui/qml/Controls/PopupWithInputField.qml + ui/qml/Controls/PopupWithTextField.qml diff --git a/client/ui/pages_logic/StartPageLogic.cpp b/client/ui/pages_logic/StartPageLogic.cpp index 8c78667d7..3fdfb29ba 100644 --- a/client/ui/pages_logic/StartPageLogic.cpp +++ b/client/ui/pages_logic/StartPageLogic.cpp @@ -6,6 +6,7 @@ #include "configurators/vpn_configurator.h" #include "../uilogic.h" #include "utilities.h" +#include "core/servercontroller.h" #include #include @@ -94,8 +95,7 @@ void StartPageLogic::onPushButtonConnect() set_labelWaitInfoText(tr("Please fill in all fields")); return; } - } - else { + } else { if (lineEditIpText().isEmpty() || lineEditLoginText().isEmpty() || lineEditPasswordText().isEmpty() ) { @@ -111,7 +111,7 @@ void StartPageLogic::onPushButtonConnect() serverCredentials.hostName = serverCredentials.hostName.split(":").at(0); } serverCredentials.userName = lineEditLoginText(); - if (pushButtonConnectKeyChecked()){ + if (pushButtonConnectKeyChecked()) { QString key = textEditSshKeyText(); if (key.startsWith("ssh-rsa")) { emit uiLogic()->showPublicKeyWarning(); @@ -123,28 +123,34 @@ void StartPageLogic::onPushButtonConnect() } serverCredentials.password = key; - } - else { + } else { serverCredentials.password = lineEditPasswordText(); } set_pushButtonConnectEnabled(false); set_pushButtonConnectText(tr("Connecting...")); - ErrorCode e = ErrorCode::NoError; + ErrorCode errorCode = ErrorCode::NoError; #ifdef Q_DEBUG //QString output = m_serverController->checkSshConnection(serverCredentials, &e); #else QString output; #endif - bool ok = true; - if (e) { - set_labelWaitInfoVisible(true); - set_labelWaitInfoText(errorString(e)); - ok = false; + if (pushButtonConnectKeyChecked()) { + QString decryptedPrivateKey; + errorCode = uiLogic()->m_serverController->getDecryptedPrivateKey(serverCredentials, decryptedPrivateKey); + if (errorCode == ErrorCode::NoError) { + serverCredentials.password = decryptedPrivateKey; + } } - else { + + bool ok = true; + if (errorCode) { + set_labelWaitInfoVisible(true); + set_labelWaitInfoText(errorString(errorCode)); + ok = false; + } else { if (output.contains("Please login as the user")) { output.replace("\n", ""); set_labelWaitInfoVisible(true); diff --git a/client/ui/qml/Controls/PopupWithInputField.qml b/client/ui/qml/Controls/PopupWithTextField.qml similarity index 100% rename from client/ui/qml/Controls/PopupWithInputField.qml rename to client/ui/qml/Controls/PopupWithTextField.qml diff --git a/client/ui/qml/main.qml b/client/ui/qml/main.qml index 8a23d7a21..dfa33e5a0 100644 --- a/client/ui/qml/main.qml +++ b/client/ui/qml/main.qml @@ -235,7 +235,7 @@ Window { popupWarning.open() } function onShowPassphraseRequestMessage() { - popupWithInputField.open() + popupWithTextField.open() } } @@ -358,8 +358,8 @@ Window { PopupWarning { id: popupWarning } - PopupWithInputField { - id: popupWithInputField + PopupWithTextField { + id: popupWithTextField placeholderText: "Enter private key passphrase" yesFunc: function() { editingFinished()