Added the capability to reset the password manager

Also some documentation about the password manager.
This commit is contained in:
eelke 2022-09-05 07:33:08 +02:00
parent f8528143ac
commit 4fa2189b27
17 changed files with 233 additions and 85 deletions

View file

@ -159,6 +159,18 @@ bool PasswordManager::locked() const
return m_cryptoEngine == nullptr;
}
void PasswordManager::resetMasterPassword(QSqlDatabase &db)
{
if (!isPskStoreInitialized(db))
return;
closeDatabase();
QSqlQuery del_algo("DELETE FROM " + m_secretAlgoTableName + " WHERE id=1", db);
del_algo.exec();
QSqlQuery del_hash("DELETE FROM " + m_secretHashTableName + " WHERE id=1", db);
del_hash.exec();
}
std::string PasswordManager::encrypt(const std::string &name, const std::string &passwd)
{
if (m_cryptoEngine) {
@ -297,3 +309,4 @@ KeyStrengthener PasswordManager::createKeyStrengthener()
key_size
);
}

View file

@ -53,6 +53,8 @@ public:
bool openDatabase(QSqlDatabase &db, QString passphrase);
void closeDatabase();
bool locked() const;
void resetMasterPassword(QSqlDatabase &db);
std::string encrypt(const std::string &id, const std::string &passwd);
std::string decrypt(const std::string &id, const std::string_view &encpwd);

View file

@ -0,0 +1,7 @@
==================
Connection Manager
==================
.. toctree::
passwordmanager

View file

@ -0,0 +1,35 @@
================
Password manager
================
The connection manager has a build-in password manager which uses encryption
to savely store passwords. If you want to use it is up to you. Check the Save
password checkbox and the password manager will be used to safely store the
password. The first time this happens you will be asked for a password you wish
to use for the password manager. Make sure to remember this password cause there
is no way to recover it if you loose it.
When you try to open a connection for which the password was saved the program
will prompt you for the password managers password. You can choose how long the
program will keep the password manager unlocked. If you cancel this password
prompt the program will prompt you for the password of the database connection.
------------------------------
Resetting the password manager
------------------------------
Unfortunatly at this time there is no way to change the master password.
Neither is it possible to completely reset the password manager other then
removing the `pglabuser.db` file.
--------
Security
--------
To make it hard to brute force decode the saved passwords the passwords are not
simply encrypted with the master password. Instead a keystrengthening algorithm
is applied to your master password combined with a salt. The exact parameters
choosen for keystrengthening depends on the speed of your computer. This
strengthened key will be used only as a basis for encryption. Each password
saved is associated with a random guid, this 128-bit value is combined with
your strengthened key to create the key to save the password.

View file

@ -7,11 +7,13 @@ pgLab User Manual
====================================================
.. toctree::
:maxdepth: 2
:maxdepth: 3
:caption: Contents:
releasenotes
installation
connectionmanager/index
internals
Indices and tables
==================

View file

@ -8,9 +8,11 @@ Currently only binaries for Windows 64-bit are provided.
Windows
-------
Downloads can be found `here <https://eelkeklein.stackstorage.com/s/E9xkMGQDFjHv5XN3>`_.
Downloads can be found `here
<https://eelkeklein.stackstorage.com/s/E9xkMGQDFjHv5XN3>`_.
Unpack the contents of the 7zip archive to a folder of your choosing for instance
`C:\\Program files\\pgLab`. You can run the pgLab.exe from there. If it is complaining
about missing files this is probably because the required Visual C++ Runtime has
not yet been installed on your machine you can get it from `microsoft <https://aka.ms/vs/17/release/vc_redist.x64.exe>`_.
Unpack the contents of the 7zip archive to a folder of your choosing for
instance `C:\\Program files\\pgLab`. You can run the pgLab.exe from there. If
it is complaining about missing files this is probably because the required
Visual C++ Runtime has not yet been installed on your machine you can get it
from `microsoft <https://aka.ms/vs/17/release/vc_redist.x64.exe>`_.

11
docs/internals.rst Normal file
View file

@ -0,0 +1,11 @@
=========
Internals
=========
---------
User data
---------
All connection configuration information is stored in
`%AppData%\pglab\pglab\pglabuser.db` this is an sqlite database.

View file

@ -6,7 +6,9 @@
#include "DatabaseWindow.h"
#include "BackupDialog.h"
#include "PasswordPromptDialog.h"
#include "ScopeGuard.h"
#include "ConnectionConfigurationWidget.h"
#include <QSqlQuery>
#include <QInputDialog>
#include <QMessageBox>
#include <QTimer>
@ -157,11 +159,13 @@ std::shared_ptr<PasswordManager> ConnectionController::passwordManager()
bool ConnectionController::retrieveConnectionPassword(ConnectionConfig &cc)
{
auto enc_pwd = cc.encodedPassword();
if (!enc_pwd.isEmpty()) {
if (!enc_pwd.isEmpty())
{
std::string pw;
bool result = retrieveFromPasswordManager(getPskId(cc.uuid()),
std::string_view(enc_pwd.data(), enc_pwd.size()) , pw);
if (result) {
if (result)
{
cc.setPassword(QString::fromUtf8(pw.data(), pw.size()));
return true;
}
@ -174,12 +178,13 @@ bool ConnectionController::retrieveConnectionPassword(ConnectionConfig &cc)
dlg->setDescription(QString(tr("Please provide password for connection %1")).arg(str));
int exec_result = dlg->exec();
if (exec_result == QDialog::Accepted) {
if (exec_result == QDialog::Accepted)
{
auto password = dlg->password();
cc.setPassword(password);
if (dlg->saveChecked()) {
if (dlg->saveChecked())
encryptPassword(cc);
}
return true;
}
return false;
@ -224,29 +229,48 @@ bool ConnectionController::decodeConnectionPassword(QUuid id, QByteArray encoded
return res;
}
void ConnectionController::resetPasswordManager()
{
auto&& user_cfg_db = m_masterController->userConfigDatabase();
user_cfg_db.transaction();
try
{
m_passwordManager->resetMasterPassword(user_cfg_db);
m_connectionTreeModel->clearAllPasswords();
user_cfg_db.commit();
}
catch (...)
{
user_cfg_db.rollback();
throw;
}
}
bool ConnectionController::UnlockPasswordManagerIfNeeded()
{
auto&& user_cfg_db = m_masterController->userConfigDatabase();
if (m_passwordManager->initialized(user_cfg_db)) {
if (m_passwordManager->initialized(user_cfg_db))
{
if (!m_passwordManager->locked())
return true;
while (true) {
// ask user for passphrase
while (true)
{
PassphraseResult pp_result = PassphrasePrompt();
if (!pp_result.success)
break; // leave this retry loop
break;
// user gave OK, if succeeds return true otherwise loop a prompt for password again.
if (m_passwordManager->openDatabase(user_cfg_db, pp_result.passphrase)) {
if (m_passwordManager->openDatabase(user_cfg_db, pp_result.passphrase))
{
setRelockTimer(pp_result.rememberForMinutes);
return true;
}
}
}
else {
else
InitializePasswordManager();
}
return false;
}

View file

@ -12,6 +12,7 @@ class ConnectionTreeModel;
class ConnectionManagerWindow;
class PasswordManager;
class QTimer;
class QSqlDatabase;
class PassphraseResult {
@ -58,6 +59,7 @@ public:
bool UnlockPasswordManagerIfNeeded();
bool decodeConnectionPassword(QUuid id, QByteArray encoded, QString &out_password);
void resetPasswordManager();
private:
MasterController *m_masterController;
ConnectionList *m_connectionList = nullptr;
@ -71,7 +73,6 @@ private:
std::shared_ptr<PasswordManager> m_passwordManager;
bool retrieveFromPasswordManager(const std::string &password_id, const std::string_view &enc_password, std::string &password);
// bool updatePasswordManager(const std::string &password_id, const std::string &password, std::string &enc_password);
/// Expects the plaintext password to be the password that needs encoding.
bool encryptPassword(ConnectionConfig &cc);

View file

@ -354,6 +354,16 @@ void ConnectionTreeModel::save(const ConnectionConfig &cc)
saveToDb(cc);
}
void ConnectionTreeModel::clearAllPasswords()
{
for (auto group : m_groups)
for (auto cc : group->connections())
{
cc->setEncodedPassword({});
saveToDb(*cc);
}
}
std::tuple<int, int> ConnectionTreeModel::findConfig(const QUuid uuid) const
{
int group_idx = -1, connection_idx = -1;

View file

@ -59,6 +59,7 @@ public:
/** Save changed config, group is not allowed to change
*/
void save(const ConnectionConfig &cc);
void clearAllPasswords();
/// Create a new group in the DB and place in the tree
std::variant<int, QSqlError> addGroup(const QString &group_name);
std::optional<QSqlError> removeGroup(int row);

View file

@ -115,3 +115,15 @@ void ConnectionManagerWindow::on_actionManual_triggered()
QDesktopServices::openUrl(QString("https://eelke.gitlab.io/pgLab/#pglab-user-manual"));
}
void ConnectionManagerWindow::on_actionReset_password_manager_triggered()
{
auto warning_response = QMessageBox::warning(this, "pgLab", tr(
"Warning you are about to reset the password manager master passwords "
"all stored passwords will be lost! Are you shure you wish to continue?"),
QMessageBox::Yes | QMessageBox::No);
if (warning_response == QMessageBox::Yes)
m_connectionController->resetPasswordManager();
}

View file

@ -43,6 +43,8 @@ private slots:
void on_actionManual_triggered();
void on_actionReset_password_manager_triggered();
private:
Ui::ConnectionManagerWindow *ui;
MasterController *m_masterController;

View file

@ -33,11 +33,18 @@
<property name="title">
<string>Fi&amp;le</string>
</property>
<addaction name="actionAbout"/>
<addaction name="actionManual"/>
<addaction name="actionReset_password_manager"/>
<addaction name="actionQuit_application"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>Help</string>
</property>
<addaction name="actionManual"/>
<addaction name="actionAbout"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuHelp"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QToolBar" name="toolBar">
@ -181,6 +188,11 @@ QToolButton {
<string>Manual</string>
</property>
</action>
<action name="actionReset_password_manager">
<property name="text">
<string>Reset password manager</string>
</property>
</action>
</widget>
<resources>
<include location="resources.qrc"/>

View file

@ -45,6 +45,7 @@ public:
using Connections = QVector<std::shared_ptr<ConnectionConfig>>;
const Connections& connections() const { return m_connections; }
Connections& connections() { return m_connections; }
void erase(int idx, int count = 1);
/// Adds cc to the group and returns the index within the group.

View file

@ -1,2 +1,10 @@
---
sections:
# The prelude section is implicitly included.
- [features, New Features]
- [other, Changes]
- [issues, Known Issues]
- [upgrade, Upgrade Notes]
- [security, Security Issues]
- [fixes, Bug Fixes]
encoding: utf8

View file

@ -0,0 +1,5 @@
---
features:
- |
Added menu option to reset the password managers master password.
This will ofcourse also clear all saved passwords.