Falkon Develop
Cross-platform Qt-based web browser
databaseencryptedpasswordbackend.cpp
Go to the documentation of this file.
1/* ============================================================
2* Falkon - Qt web browser
3* Copyright (C) 2013-2014 S. Razi Alavizadeh <s.r.alavizadeh@gmail.com>
4* Copyright (C) 2013-2018 David Rosca <nowrep@gmail.com>
5*
6* This program is free software: you can redistribute it and/or modify
7* it under the terms of the GNU General Public License as published by
8* the Free Software Foundation, either version 3 of the License, or
9* (at your option) any later version.
10*
11* This program is distributed in the hope that it will be useful,
12* but WITHOUT ANY WARRANTY; without even the implied warranty of
13* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14* GNU General Public License for more details.
15*
16* You should have received a copy of the GNU General Public License
17* along with this program. If not, see <http://www.gnu.org/licenses/>.
18* ============================================================ */
20#include "mainapplication.h"
21#include "autofill.h"
22#include "aesinterface.h"
23#include "browserwindow.h"
24#include "ui_masterpassworddialog.h"
25#include "sqldatabase.h"
26
27#include <QVector>
28#include <QMessageBox>
29
30#define INTERNAL_SERVER_ID QLatin1String("falkon.internal")
31
34 , m_stateOfMasterPassword(UnKnownState)
35 , m_askPasswordDialogVisible(false)
36 , m_askMasterPassword(false)
37{
38 QSqlDatabase db = SqlDatabase::instance()->database();
39 if (!db.tables().contains(QLatin1String("autofill_encrypted"))) {
40 db.exec(QSL("CREATE TABLE autofill_encrypted (data_encrypted TEXT, id INTEGER PRIMARY KEY,"
41 "password_encrypted TEXT, server TEXT, username_encrypted TEXT, last_used NUMERIC)"));
42 db.exec(QSL("CREATE INDEX autofillEncryptedServer ON autofill_encrypted(server ASC)"));
43 }
44}
45
47= default;
48
50{
51 if (!m_askMasterPassword) {
53 }
54
55 QSqlQuery query(SqlDatabase::instance()->database());
56 query.prepare(QSL("SELECT username_encrypted FROM autofill_encrypted WHERE server=? ORDER BY last_used DESC"));
57 query.addBindValue(PasswordManager::createHost(url));
58 query.exec();
59
60 QStringList list;
61 while (query.next()) {
62 list.append(QSL("Encrypted %1").arg(list.size() + 1));
63 }
64 return list;
65}
66
67QVector<PasswordEntry> DatabaseEncryptedPasswordBackend::getEntries(const QUrl &url)
68{
69 QVector<PasswordEntry> list;
70
71 AesInterface aesDecryptor;
72
73 const QString host = PasswordManager::createHost(url);
74
75 QSqlQuery query(SqlDatabase::instance()->database());
76 query.prepare(QSL("SELECT id, username_encrypted, password_encrypted, data_encrypted FROM autofill_encrypted "
77 "WHERE server=? ORDER BY last_used DESC"));
78 query.addBindValue(host);
79 query.exec();
80
81 if (query.next() && hasPermission()) {
82 do {
83 PasswordEntry data;
84 data.id = query.value(0);
85 data.host = host;
86 data.username = query.value(1).toString();
87 data.password = query.value(2).toString();
88 data.data = query.value(3).toByteArray();
89
90 if (decryptPasswordEntry(data, &aesDecryptor)) {
91 list.append(data);
92 }
93 }
94 while (query.next());
95 }
96
97 return list;
98}
99
101{
102 QVector<PasswordEntry> list;
103
104 AesInterface aesDecryptor;
105
106 QSqlQuery query(SqlDatabase::instance()->database());
107 query.prepare(QSL("SELECT id, server, username_encrypted, password_encrypted, data_encrypted FROM autofill_encrypted"));
108 query.exec();
109
110 if (query.next() && hasPermission()) {
111 do {
112 PasswordEntry data;
113 data.id = query.value(0);
114 data.host = query.value(1).toString();
115 if (data.host == INTERNAL_SERVER_ID) {
116 continue;
117 }
118 data.username = query.value(2).toString();
119 data.password = query.value(3).toString();
120 data.data = query.value(4).toByteArray();
121
122 if (decryptPasswordEntry(data, &aesDecryptor)) {
123 list.append(data);
124 }
125 }
126 while (query.next());
127 }
128
129 return list;
130}
131
133{
134 if (active == isActive()) {
135 return;
136 }
137
139
140 if (active) {
142 if (!isMasterPasswordSetted()) {
143 // master-password is not set this backend needs master-password
145 }
146 }
147 else {
148 // maybe ask from user for decrypting data
149
150 // remove password from memory
151 m_masterPassword.clear();
153 }
154}
155
157{
158 // Data is empty only for HTTP/FTP authorization
159 if (entry.data.isEmpty()) {
160 // Multiple-usernames for HTTP/FTP authorization not supported
161 QSqlQuery query(SqlDatabase::instance()->database());
162 query.prepare(QSL("SELECT username_encrypted FROM autofill_encrypted WHERE server=?"));
163 query.addBindValue(entry.host);
164 query.exec();
165
166 if (query.next()) {
167 return;
168 }
169 }
170
171 PasswordEntry encryptedEntry = entry;
172 AesInterface aesEncryptor;
173
174 if (hasPermission() && encryptPasswordEntry(encryptedEntry, &aesEncryptor)) {
175 QSqlQuery query(SqlDatabase::instance()->database());
176 query.prepare(QSL("INSERT INTO autofill_encrypted (server, data_encrypted, username_encrypted, password_encrypted, last_used) "
177 "VALUES (?,?,?,?,strftime('%s', 'now'))"));
178 query.bindValue(0, encryptedEntry.host);
179 query.bindValue(1, encryptedEntry.data);
180 query.bindValue(2, encryptedEntry.username);
181 query.bindValue(3, encryptedEntry.password);
182 query.exec();
183 }
184}
185
187{
188 AesInterface aesEncryptor;
189 PasswordEntry encryptedEntry = entry;
190
191 if (hasPermission() && encryptPasswordEntry(encryptedEntry, &aesEncryptor)) {
192 QSqlQuery query(SqlDatabase::instance()->database());
193
194 // Data is empty only for HTTP/FTP authorization
195 if (entry.data.isEmpty()) {
196 query.prepare(QSL("UPDATE autofill_encrypted SET username_encrypted=?, password_encrypted=? WHERE server=?"));
197 query.bindValue(0, encryptedEntry.username);
198 query.bindValue(1, encryptedEntry.password);
199 query.bindValue(2, encryptedEntry.host);
200 }
201 else {
202 query.prepare(QSL("UPDATE autofill_encrypted SET data_encrypted=?, username_encrypted=?, password_encrypted=? WHERE id=?"));
203 query.addBindValue(encryptedEntry.data);
204 query.addBindValue(encryptedEntry.username);
205 query.addBindValue(encryptedEntry.password);
206 query.addBindValue(encryptedEntry.id);
207 }
208
209 return query.exec();
210 }
211
212 return false;
213}
214
216{
217 QSqlQuery query(SqlDatabase::instance()->database());
218 query.prepare(QSL("UPDATE autofill_encrypted SET last_used=strftime('%s', 'now') WHERE id=?"));
219 query.addBindValue(entry.id);
220 query.exec();
221}
222
224{
225 if (!hasPermission()) {
226 return;
227 }
228
229 QSqlQuery query(SqlDatabase::instance()->database());
230 query.prepare(QSL("DELETE FROM autofill_encrypted WHERE id=?"));
231 query.addBindValue(entry.id);
232 query.exec();
233
234 m_stateOfMasterPassword = UnKnownState;
235 if (someDataFromDatabase().isEmpty()) {
236 updateSampleData(m_masterPassword);
237 }
238}
239
241{
242 if (!hasPermission()) {
243 return;
244 }
245
246 QSqlQuery query(SqlDatabase::instance()->database());
247 query.prepare(QSL("DELETE FROM autofill_encrypted"));
248 query.exec();
249
250 m_stateOfMasterPassword = PasswordIsSetted;
251
252 updateSampleData(m_masterPassword);
253}
254
256{
257 return AutoFill::tr("Database (encrypted)");
258}
259
261{
262 return true;
263}
264
266{
267 auto* masterPasswordDialog = new MasterPasswordDialog(this, parent);
268 masterPasswordDialog->showSettingPage();
269}
270
272{
273 if (m_stateOfMasterPassword == UnKnownState) {
274 m_stateOfMasterPassword = someDataFromDatabase().isEmpty() ? PasswordIsNotSetted : PasswordIsSetted;
275 }
276
277 return m_stateOfMasterPassword == PasswordIsSetted;
278}
279
281{
282 return m_masterPassword;
283}
284
286{
287 if (!m_askMasterPassword) {
288 return true;
289 }
290
291 if (m_askPasswordDialogVisible) {
292 return false;
293 }
294
295 m_askPasswordDialogVisible = true;
296
297 auto* dialog = new AskMasterPassword(this);
298
299 bool authorized = dialog->exec() == QDialog::Accepted;
300
301 m_askPasswordDialogVisible = false;
302 return authorized;
303}
304
306{
307 if (password.isEmpty()) {
308 return false;
309 }
310
311 if (m_masterPassword == password) {
312 return true;
313 }
314 else if (!m_masterPassword.isEmpty()) {
315 return false;
316 }
317 else {
318 // m_masterPassword is empty we need to check entered password with
319 // decoding some data by it and then save it to m_masterPassword
320 AesInterface aes;
321 aes.decrypt(someDataFromDatabase(), password);
322 if (aes.isOk()) {
323 m_masterPassword = password;
324 return true;
325 }
326 }
327
328 return false;
329}
330
332{
333 entry.username = QString::fromUtf8(aesInterface->decrypt(entry.username.toUtf8(), m_masterPassword));
334 entry.password = QString::fromUtf8(aesInterface->decrypt(entry.password.toUtf8(), m_masterPassword));
335 entry.data = aesInterface->decrypt(entry.data, m_masterPassword);
336
337 return aesInterface->isOk();
338}
339
341{
342 entry.username = QString::fromUtf8(aesInterface->encrypt(entry.username.toUtf8(), m_masterPassword));
343 entry.password = QString::fromUtf8(aesInterface->encrypt(entry.password.toUtf8(), m_masterPassword));
344 entry.data = aesInterface->encrypt(entry.data, m_masterPassword);
345
346 return aesInterface->isOk();
347}
348
350{
351 auto* masterPasswordDialog = new MasterPasswordDialog(this, mApp->getWindow());
352 masterPasswordDialog->showSetMasterPasswordPage();
353 masterPasswordDialog->delayedExec();
354}
355
357{
358 if (m_masterPassword == newPassword) {
359 return;
360 }
361
362 if (newPassword.isEmpty()) {
364 return;
365 }
366
367 encryptDataBaseTableOnFly(m_masterPassword, newPassword);
368
369 m_masterPassword = newPassword;
370 updateSampleData(m_masterPassword);
371}
372
374{
375 if (!m_masterPassword.isEmpty()) {
376 encryptDataBaseTableOnFly(m_masterPassword, QByteArray());
377
378 m_masterPassword.clear();
379 updateSampleData(QByteArray());
380 }
381}
382
384{
385 m_askMasterPassword = ask;
386}
387
388void DatabaseEncryptedPasswordBackend::encryptDataBaseTableOnFly(const QByteArray &decryptorPassword, const QByteArray &encryptorPassword)
389{
390 if (encryptorPassword == decryptorPassword) {
391 return;
392 }
393
394 QSqlQuery query(SqlDatabase::instance()->database());
395 query.prepare(QSL("SELECT id, data_encrypted, password_encrypted, username_encrypted, server FROM autofill_encrypted"));
396 query.exec();
397
398 AesInterface encryptor;
399 AesInterface decryptor;
400
401 while (query.next()) {
402 QString server = query.value(4).toString();
403 if (server == INTERNAL_SERVER_ID) {
404 continue;
405 }
406
407 int id = query.value(0).toInt();
408 QByteArray data = query.value(1).toString().toUtf8();
409 QByteArray password = query.value(2).toString().toUtf8();
410 QByteArray username = query.value(3).toString().toUtf8();
411
412 if (!decryptorPassword.isEmpty()) {
413 data = decryptor.decrypt(data, decryptorPassword);
414 password = decryptor.decrypt(password, decryptorPassword);
415 username = decryptor.decrypt(username, decryptorPassword);
416 }
417
418 if (!encryptorPassword.isEmpty()) {
419 data = encryptor.encrypt(data, encryptorPassword);
420 password = encryptor.encrypt(password, encryptorPassword);
421 username = encryptor.encrypt(username, encryptorPassword);
422 }
423
424 QSqlQuery updateQuery(SqlDatabase::instance()->database());
425 updateQuery.prepare(QSL("UPDATE autofill_encrypted SET data_encrypted = ?, password_encrypted = ?, username_encrypted = ? WHERE id = ?"));
426 updateQuery.addBindValue(data);
427 updateQuery.addBindValue(password);
428 updateQuery.addBindValue(username);
429 updateQuery.addBindValue(id);
430 query.exec();
431 }
432}
433
434QByteArray DatabaseEncryptedPasswordBackend::someDataFromDatabase()
435{
436 if (m_stateOfMasterPassword != UnKnownState && !m_someDataStoredOnDataBase.isEmpty()) {
437 return m_someDataStoredOnDataBase;
438 }
439
440 QSqlQuery query(SqlDatabase::instance()->database());
441 query.prepare(QSL("SELECT password_encrypted, data_encrypted, username_encrypted FROM autofill_encrypted"));
442 query.exec();
443
444 QByteArray someData;
445 if (query.next()) {
446 int i = 0;
447 while (someData.isEmpty()) {
448 if (i > 2) {
449 if (query.next()) {
450 i = 0;
451 continue;
452 }
453 else {
454 break;
455 }
456 }
457 someData = query.value(i).toByteArray();
458 ++i;
459 }
460 }
461
462 m_someDataStoredOnDataBase = someData;
463 return m_someDataStoredOnDataBase;
464}
465
467{
468 QSqlQuery query(SqlDatabase::instance()->database());
469 query.prepare(QSL("SELECT id FROM autofill_encrypted WHERE server = ?"));
470 query.addBindValue(INTERNAL_SERVER_ID);
471 query.exec();
472
473 if (!password.isEmpty()) {
474 AesInterface aes;
475 m_someDataStoredOnDataBase = aes.encrypt(AesInterface::createRandomData(16), password);
476
477 if (query.next()) {
478 query.prepare(QSL("UPDATE autofill_encrypted SET password_encrypted = ? WHERE server=?"));
479 }
480 else {
481 query.prepare(QSL("INSERT INTO autofill_encrypted (password_encrypted, server) VALUES (?,?)"));
482 }
483
484 query.addBindValue(QString::fromUtf8(m_someDataStoredOnDataBase));
485 query.addBindValue(INTERNAL_SERVER_ID);
486 query.exec();
487
488 m_stateOfMasterPassword = PasswordIsSetted;
489 }
490 else if (query.next()) {
491 query.prepare(QSL("DELETE FROM autofill_encrypted WHERE server = ?"));
492 query.addBindValue(INTERNAL_SERVER_ID);
493 query.exec();
494
495 m_stateOfMasterPassword = PasswordIsNotSetted;
496 m_someDataStoredOnDataBase.clear();
497 return;
498 }
499}
500
501
502/******************************
503 * MasterPasswordDialog class *
504 ******************************/
505
506#include <QTimer>
507
509 : QDialog(parent, Qt::WindowStaysOnTopHint | Qt::MSWindowsFixedSizeDialogHint)
510 , ui(new Ui::MasterPasswordDialog)
511 , m_backend(backend)
512{
513 setAttribute(Qt::WA_DeleteOnClose, true);
514 ui->setupUi(this);
515
516 ui->currentPassword->setVisible(m_backend->isMasterPasswordSetted());
517 ui->labelCurrentPassword->setVisible(m_backend->isMasterPasswordSetted());
518
519 connect(ui->setMasterPassword, &QAbstractButton::clicked, this, &MasterPasswordDialog::showSetMasterPasswordPage);
520 connect(ui->clearMasterPassword, &QAbstractButton::clicked, this, &MasterPasswordDialog::clearMasterPasswordAndConvert);
521 connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
522 connect(ui->buttonBoxMasterPassword, SIGNAL(rejected()), this, SLOT(reject()));
523 connect(ui->buttonBoxMasterPassword, SIGNAL(accepted()), this, SLOT(accept()));
524}
525
527{
528 delete ui;
529}
530
532{
533 QTimer::singleShot(0, this, &QDialog::exec);
534}
535
537{
538 if (ui->stackedWidget->currentIndex() != 1) {
539 QDialog::accept();
540 return;
541 }
542
543 QByteArray currentPassField = AesInterface::passwordToHash(ui->currentPassword->text());
544
545 if (m_backend->isMasterPasswordSetted() && !m_backend->isPasswordVerified(currentPassField)) {
546 QMessageBox::information(this, tr("Warning!"), tr("You entered a wrong password!"));
547 return;
548 }
549
550 if (ui->newPassword->text() != ui->confirmPassword->text()) {
551 QMessageBox::information(this, tr("Warning!"), tr("New/Confirm password fields do not match!"));
552 return;
553 }
554
555 if (ui->newPassword->text().isEmpty()) {
556 if (!m_backend->isMasterPasswordSetted()) {
557 return;
558 }
560 }
561 else {
562 // for security reason we don't save master-password as plain in memory
563 QByteArray newPassField = AesInterface::passwordToHash(ui->newPassword->text());
564
565 if (m_backend->masterPassword() != newPassField) {
566 m_backend->tryToChangeMasterPassword(newPassField);
567 }
568 }
569 QDialog::accept();
570}
571
573{
574 QDialog::reject();
575
576 if (m_backend->isActive() && !m_backend->isMasterPasswordSetted()) {
577 // master password not set
578 QMessageBox::information(this, AutoFill::tr("Warning!"),
579 AutoFill::tr("This backend needs a master password to be set! "
580 "Falkon just switches to its default backend"));
581 // active default backend
582 mApp->autoFill()->passwordManager()->switchBackend(QSL("database"));
583 return;
584 }
585}
586
588{
589 ui->stackedWidget->setCurrentIndex(0);
590 delayedExec();
591}
592
594{
595 ui->stackedWidget->setCurrentIndex(1);
596}
597
599{
600 if (QMessageBox::information(this, tr("Warning!"), tr("Are you sure you want to clear master password and decrypt data?"), QMessageBox::Yes | QMessageBox::No)
601 == QMessageBox::No) {
602 reject();
603 return;
604 }
605
606 if (forcedAskPass) {
607 m_backend->setAskMasterPasswordState(true);
608 }
609
610 if (m_backend->hasPermission()) {
611 const QVector<PasswordEntry> list = m_backend->getAllEntries();
612 PasswordBackend* databaseBackend = mApp->autoFill()->passwordManager()->availableBackends().value(QSL("database"));
613 if (!databaseBackend) {
614 return;
615 }
616
617 const QVector<PasswordEntry> databaseList = databaseBackend->getAllEntries();
618 bool allDataMoved = true;
619 for (const PasswordEntry &entry : list) {
620 bool sameEntry = false;
621 for (const PasswordEntry &dbEntry : databaseList) {
622 sameEntry = samePasswordEntry(dbEntry, entry);
623 if (sameEntry) {
624 allDataMoved = false;
625 break;
626 }
627 }
628
629 if (!sameEntry) {
630 databaseBackend->addEntry(entry);
631 m_backend->removeEntry(entry);
632 }
633 }
634
635 if (allDataMoved) {
636 m_backend->removeAll();
637 m_backend->removeMasterPassword();
638 m_backend->setAskMasterPasswordState(false);
639
640 mApp->autoFill()->passwordManager()->switchBackend(QSL("database"));
641 }
642 else {
643 QMessageBox::information(this, tr("Warning!"), tr("Some data has not been decrypted. The master password was not cleared!"));
644 mApp->autoFill()->passwordManager()->switchBackend(QSL("database"));
645 }
646 }
647 reject();
648}
649
651{
652 // Multiple-usernames for HTTP/FTP authorization not supported
653 if ((entry1.data.isEmpty() || entry2.data.isEmpty()) && entry1.host == entry2.host) {
654 return true;
655 }
656
657 if (entry1.host != entry2.host || entry1.username != entry2.username) {
658 return false;
659 }
660 return true;
661}
662
663
665 : QDialog(parent, Qt::WindowStaysOnTopHint | Qt::MSWindowsFixedSizeDialogHint)
666 , m_backend(backend)
667{
668 setWindowModality(Qt::ApplicationModal);
669 setWindowTitle(AutoFill::tr("Enter Master Password"));
670
671 auto* verticalLayout = new QVBoxLayout(this);
672 auto* label = new QLabel(this);
673 label->setText(AutoFill::tr("Permission is required, please enter Master Password:"));
674 m_lineEdit = new QLineEdit(this);
675 m_lineEdit->setEchoMode(QLineEdit::Password);
676 m_buttonBox = new QDialogButtonBox(this);
677 m_buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
678 m_labelWarning = new QLabel(this);
679 m_labelWarning->setText(AutoFill::tr("Entered password is wrong!"));
680 QPalette pal = m_labelWarning->palette();
681 pal.setBrush(QPalette::WindowText, Qt::red);
682 m_labelWarning->setPalette(pal);
683 m_labelWarning->hide();
684
685 verticalLayout->addWidget(label);
686 verticalLayout->addWidget(m_lineEdit);
687 verticalLayout->addWidget(m_labelWarning);
688 verticalLayout->addWidget(m_buttonBox);
689 setLayout(verticalLayout);
690
691 connect(m_lineEdit, &QLineEdit::returnPressed, this, &AskMasterPassword::verifyPassword);
692 connect(m_buttonBox, &QDialogButtonBox::accepted, this, &AskMasterPassword::verifyPassword);
693 connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
694
695 setAttribute(Qt::WA_DeleteOnClose);
696}
697
698void AskMasterPassword::verifyPassword()
699{
700 QByteArray enteredPassword = AesInterface::passwordToHash(m_lineEdit->text());
701 if (!m_backend->isPasswordVerified(enteredPassword)) {
702 m_backend->setAskMasterPasswordState(true);
703 m_labelWarning->show();
704 m_lineEdit->clear();
705 m_lineEdit->setFocus();
706 }
707 else {
708 m_backend->setAskMasterPasswordState(false);
709 //TODO: start timer for reset ask state to true
710
711 accept();
712 }
713}
bool isOk() const
static QByteArray createRandomData(int length)
QByteArray decrypt(const QByteArray &cipherData, const QByteArray &password)
QByteArray encrypt(const QByteArray &plainData, const QByteArray &password)
static QByteArray passwordToHash(const QString &masterPassword)
AskMasterPassword(DatabaseEncryptedPasswordBackend *backend, QWidget *parent=nullptr)
bool decryptPasswordEntry(PasswordEntry &entry, AesInterface *aesInterface)
bool isPasswordVerified(const QByteArray &password)
void updateSampleData(const QByteArray &password)
bool encryptPasswordEntry(PasswordEntry &entry, AesInterface *aesInterface)
bool updateEntry(const PasswordEntry &entry) override
void encryptDataBaseTableOnFly(const QByteArray &decryptorPassword, const QByteArray &encryptorPassword)
QVector< PasswordEntry > getAllEntries() override
void updateLastUsed(PasswordEntry &entry) override
QVector< PasswordEntry > getEntries(const QUrl &url) override
void addEntry(const PasswordEntry &entry) override
void removeEntry(const PasswordEntry &entry) override
void tryToChangeMasterPassword(const QByteArray &newPassword)
QStringList getUsernames(const QUrl &url) override
bool samePasswordEntry(const PasswordEntry &entry1, const PasswordEntry &entry2)
MasterPasswordDialog(DatabaseEncryptedPasswordBackend *backend, QWidget *parent=nullptr)
void clearMasterPasswordAndConvert(bool forcedAskPass=true)
virtual void addEntry(const PasswordEntry &entry)=0
virtual QVector< PasswordEntry > getAllEntries()=0
virtual QStringList getUsernames(const QUrl &url)
bool isActive() const
virtual void setActive(bool active)
static QString createHost(const QUrl &url)
static SqlDatabase * instance()
QSqlDatabase database()
#define INTERNAL_SERVER_ID
#define mApp
i
Definition: i18n.py:23
#define QSL(x)
Definition: qzcommon.h:40
QString password
QString host
QByteArray data
QString username
QVariant id