keepassxc

Форк
0
/
DatabaseSettingsWidgetDatabaseKey.cpp 
291 строка · 10.7 Кб
1
/*
2
 *  Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
3
 *
4
 *  This program is free software: you can redistribute it and/or modify
5
 *  it under the terms of the GNU General Public License as published by
6
 *  the Free Software Foundation, either version 2 or (at your option)
7
 *  version 3 of the License.
8
 *
9
 *  This program is distributed in the hope that it will be useful,
10
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 *  GNU General Public License for more details.
13
 *
14
 *  You should have received a copy of the GNU General Public License
15
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
 */
17

18
#include "DatabaseSettingsWidgetDatabaseKey.h"
19

20
#include "core/Config.h"
21
#include "core/Database.h"
22
#include "core/PasswordHealth.h"
23
#include "gui/MessageBox.h"
24
#include "gui/databasekey/KeyFileEditWidget.h"
25
#include "gui/databasekey/PasswordEditWidget.h"
26
#include "gui/databasekey/YubiKeyEditWidget.h"
27
#include "keys/ChallengeResponseKey.h"
28
#include "keys/FileKey.h"
29
#include "keys/PasswordKey.h"
30
#include "quickunlock/QuickUnlockInterface.h"
31

32
#include <QLayout>
33
#include <QPushButton>
34

35
DatabaseSettingsWidgetDatabaseKey::DatabaseSettingsWidgetDatabaseKey(QWidget* parent)
36
    : DatabaseSettingsWidget(parent)
37
    , m_additionalKeyOptionsToggle(new QPushButton(tr("Add additional protection…"), this))
38
    , m_additionalKeyOptions(new QWidget(this))
39
    , m_passwordEditWidget(new PasswordEditWidget(this))
40
    , m_keyFileEditWidget(new KeyFileEditWidget(this))
41
#ifdef WITH_XC_YUBIKEY
42
    , m_yubiKeyEditWidget(new YubiKeyEditWidget(this))
43
#endif
44
{
45
    auto* vbox = new QVBoxLayout(this);
46
    vbox->setSizeConstraint(QLayout::SetMinimumSize);
47
    vbox->setSpacing(20);
48

49
    // primary password option
50
    vbox->addWidget(m_passwordEditWidget);
51

52
    // additional key options
53
    m_additionalKeyOptionsToggle->setObjectName("additionalKeyOptionsToggle");
54
    vbox->addWidget(m_additionalKeyOptionsToggle);
55
    vbox->addWidget(m_additionalKeyOptions);
56
    vbox->setSizeConstraint(QLayout::SetMinimumSize);
57
    m_additionalKeyOptions->setLayout(new QVBoxLayout());
58
    m_additionalKeyOptions->layout()->setMargin(0);
59
    m_additionalKeyOptions->layout()->setSpacing(20);
60
    m_additionalKeyOptions->layout()->addWidget(m_keyFileEditWidget);
61
#ifdef WITH_XC_YUBIKEY
62
    m_additionalKeyOptions->layout()->addWidget(m_yubiKeyEditWidget);
63
#endif
64
    m_additionalKeyOptions->setVisible(false);
65

66
    connect(m_additionalKeyOptionsToggle, SIGNAL(clicked()), SLOT(showAdditionalKeyOptions()));
67

68
    vbox->addStretch();
69
    setLayout(vbox);
70
}
71

72
DatabaseSettingsWidgetDatabaseKey::~DatabaseSettingsWidgetDatabaseKey() = default;
73

74
void DatabaseSettingsWidgetDatabaseKey::load(QSharedPointer<Database> db)
75
{
76
    DatabaseSettingsWidget::load(db);
77

78
    if (!m_db->key() || m_db->key()->keys().isEmpty()) {
79
        // database has no key, we are about to add a new one
80
        m_passwordEditWidget->changeVisiblePage(KeyComponentWidget::Page::Edit);
81
        m_passwordEditWidget->setPasswordVisible(true);
82
    }
83

84
    bool hasAdditionalKeys = false;
85
    for (const auto& key : m_db->key()->keys()) {
86
        if (key->uuid() == PasswordKey::UUID) {
87
            m_passwordEditWidget->setComponentAdded(true);
88
        } else if (key->uuid() == FileKey::UUID) {
89
            m_keyFileEditWidget->setComponentAdded(true);
90
            hasAdditionalKeys = true;
91
        }
92
    }
93

94
#ifdef WITH_XC_YUBIKEY
95
    for (const auto& key : m_db->key()->challengeResponseKeys()) {
96
        if (key->uuid() == ChallengeResponseKey::UUID) {
97
            m_yubiKeyEditWidget->setComponentAdded(true);
98
            hasAdditionalKeys = true;
99
        }
100
    }
101
#endif
102

103
    setAdditionalKeyOptionsVisible(hasAdditionalKeys);
104

105
    connect(m_passwordEditWidget->findChild<QPushButton*>("removeButton"), SIGNAL(clicked()), SLOT(markDirty()));
106
    connect(m_keyFileEditWidget->findChild<QPushButton*>("removeButton"), SIGNAL(clicked()), SLOT(markDirty()));
107
#ifdef WITH_XC_YUBIKEY
108
    connect(m_yubiKeyEditWidget->findChild<QPushButton*>("removeButton"), SIGNAL(clicked()), SLOT(markDirty()));
109
#endif
110
}
111

112
void DatabaseSettingsWidgetDatabaseKey::initialize()
113
{
114
    bool blocked = blockSignals(true);
115
    m_passwordEditWidget->setComponentAdded(false);
116
    m_keyFileEditWidget->setComponentAdded(false);
117
#ifdef WITH_XC_YUBIKEY
118
    m_yubiKeyEditWidget->setComponentAdded(false);
119
#endif
120
    blockSignals(blocked);
121
}
122

123
void DatabaseSettingsWidgetDatabaseKey::uninitialize()
124
{
125
}
126

127
bool DatabaseSettingsWidgetDatabaseKey::save()
128
{
129
    m_isDirty |= (m_passwordEditWidget->visiblePage() == KeyComponentWidget::Page::Edit);
130
    m_isDirty |= (m_keyFileEditWidget->visiblePage() == KeyComponentWidget::Page::Edit);
131
#ifdef WITH_XC_YUBIKEY
132
    m_isDirty |= (m_yubiKeyEditWidget->visiblePage() == KeyComponentWidget::Page::Edit);
133
#endif
134

135
    if (m_db->key() && !m_db->key()->keys().isEmpty() && !m_isDirty) {
136
        // key unchanged
137
        return true;
138
    }
139

140
    auto newKey = QSharedPointer<CompositeKey>::create();
141

142
    QSharedPointer<Key> oldPasswordKey;
143
    QSharedPointer<Key> oldFileKey;
144
    QSharedPointer<ChallengeResponseKey> oldChallengeResponse;
145

146
    for (const auto& key : m_db->key()->keys()) {
147
        if (key->uuid() == PasswordKey::UUID) {
148
            oldPasswordKey = key;
149
        } else if (key->uuid() == FileKey::UUID) {
150
            oldFileKey = key;
151
        }
152
    }
153

154
    for (const auto& key : m_db->key()->challengeResponseKeys()) {
155
        if (key->uuid() == ChallengeResponseKey::UUID) {
156
            oldChallengeResponse = key;
157
        }
158
    }
159

160
    // Show warning if database password has not been set
161
    if (m_passwordEditWidget->visiblePage() == KeyComponentWidget::Page::AddNew || m_passwordEditWidget->isEmpty()) {
162
        QScopedPointer<QMessageBox> msgBox(new QMessageBox(this));
163
        msgBox->setIcon(QMessageBox::Warning);
164
        msgBox->setWindowTitle(tr("No password set"));
165
        msgBox->setText(tr("WARNING! You have not set a password. Using a database without "
166
                           "a password is strongly discouraged!\n\n"
167
                           "Are you sure you want to continue without a password?"));
168
        auto btn = msgBox->addButton(tr("Continue without password"), QMessageBox::ButtonRole::AcceptRole);
169
        msgBox->addButton(QMessageBox::Cancel);
170
        msgBox->setDefaultButton(QMessageBox::Cancel);
171
        msgBox->layout()->setSizeConstraint(QLayout::SetMinimumSize);
172
        msgBox->exec();
173
        if (msgBox->clickedButton() != btn) {
174
            return false;
175
        }
176
    } else if (!addToCompositeKey(m_passwordEditWidget, newKey, oldPasswordKey)) {
177
        return false;
178
    }
179

180
    // Show warning if database password is weak
181
    if (!m_passwordEditWidget->isEmpty()
182
        && m_passwordEditWidget->getPasswordQuality() < PasswordHealth::Quality::Good) {
183
        auto dialogResult = MessageBox::warning(this,
184
                                                tr("Weak password"),
185
                                                tr("This is a weak password! For better protection of your secrets, "
186
                                                   "you should choose a stronger password."),
187
                                                MessageBox::ContinueWithWeakPass | MessageBox::Cancel,
188
                                                MessageBox::Cancel);
189

190
        if (dialogResult == MessageBox::Cancel) {
191
            return false;
192
        }
193
    }
194

195
    // If enforced in the config file, deny users from continuing with a weak password
196
    auto minQuality =
197
        static_cast<PasswordHealth::Quality>(config()->get(Config::Security_DatabasePasswordMinimumQuality).toInt());
198
    if (!m_passwordEditWidget->isEmpty() && m_passwordEditWidget->getPasswordQuality() < minQuality) {
199
        MessageBox::critical(this,
200
                             tr("Weak password"),
201
                             tr("You must enter a stronger password to protect your database."),
202
                             MessageBox::Ok,
203
                             MessageBox::Ok);
204
        return false;
205
    }
206

207
    if (!addToCompositeKey(m_keyFileEditWidget, newKey, oldFileKey)) {
208
        return false;
209
    }
210

211
#ifdef WITH_XC_YUBIKEY
212
    if (!addToCompositeKey(m_yubiKeyEditWidget, newKey, oldChallengeResponse)) {
213
        return false;
214
    }
215
#endif
216

217
    if (newKey->keys().isEmpty() && newKey->challengeResponseKeys().isEmpty()) {
218
        MessageBox::critical(this,
219
                             tr("No encryption key added"),
220
                             tr("You must add at least one encryption key to secure your database!"),
221
                             MessageBox::Ok,
222
                             MessageBox::Ok);
223
        return false;
224
    }
225

226
    m_db->setKey(newKey, true, false, false);
227

228
    getQuickUnlock()->reset(m_db->publicUuid());
229

230
    emit editFinished(true);
231
    if (m_isDirty) {
232
        m_db->markAsModified();
233
    }
234

235
    return true;
236
}
237

238
void DatabaseSettingsWidgetDatabaseKey::discard()
239
{
240
    emit editFinished(false);
241
}
242

243
void DatabaseSettingsWidgetDatabaseKey::showAdditionalKeyOptions()
244
{
245
    setAdditionalKeyOptionsVisible(true);
246
}
247

248
void DatabaseSettingsWidgetDatabaseKey::setAdditionalKeyOptionsVisible(bool show)
249
{
250
    m_additionalKeyOptionsToggle->setVisible(!show);
251
    m_additionalKeyOptions->setVisible(show);
252
}
253

254
bool DatabaseSettingsWidgetDatabaseKey::addToCompositeKey(KeyComponentWidget* widget,
255
                                                          QSharedPointer<CompositeKey>& newKey,
256
                                                          QSharedPointer<Key>& oldKey)
257
{
258
    if (widget->visiblePage() == KeyComponentWidget::Edit) {
259
        QString error = tr("Unknown error");
260
        if (!widget->validate(error) || !widget->addToCompositeKey(newKey)) {
261
            MessageBox::critical(this, tr("Failed to change database credentials"), error, MessageBox::Ok);
262
            return false;
263
        }
264
    } else if (widget->visiblePage() == KeyComponentWidget::LeaveOrRemove) {
265
        Q_ASSERT(oldKey);
266
        newKey->addKey(oldKey);
267
    }
268
    return true;
269
}
270

271
bool DatabaseSettingsWidgetDatabaseKey::addToCompositeKey(KeyComponentWidget* widget,
272
                                                          QSharedPointer<CompositeKey>& newKey,
273
                                                          QSharedPointer<ChallengeResponseKey>& oldKey)
274
{
275
    if (widget->visiblePage() == KeyComponentWidget::Edit) {
276
        QString error = tr("Unknown error");
277
        if (!widget->validate(error) || !widget->addToCompositeKey(newKey)) {
278
            MessageBox::critical(this, tr("Failed to change database credentials"), error, MessageBox::Ok);
279
            return false;
280
        }
281
    } else if (widget->visiblePage() == KeyComponentWidget::LeaveOrRemove) {
282
        Q_ASSERT(oldKey);
283
        newKey->addChallengeResponseKey(oldKey);
284
    }
285
    return true;
286
}
287

288
void DatabaseSettingsWidgetDatabaseKey::markDirty()
289
{
290
    m_isDirty = true;
291
}
292

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.