keepassxc

Форк
0
/
DatabaseEdit.cpp 
174 строки · 7.2 Кб
1
/*
2
 *  Copyright (C) 2022 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 "DatabaseEdit.h"
19

20
#include "Utils.h"
21
#include "cli/DatabaseCreate.h"
22
#include "keys/ChallengeResponseKey.h"
23
#include "keys/FileKey.h"
24
#include "keys/PasswordKey.h"
25

26
#include <QCommandLineParser>
27
#include <QFileInfo>
28

29
const QCommandLineOption DatabaseEdit::UnsetPasswordOption =
30
    QCommandLineOption(QStringList() << "unset-password", QObject::tr("Unset the password for the database."));
31
const QCommandLineOption DatabaseEdit::UnsetKeyFileOption =
32
    QCommandLineOption(QStringList() << "unset-key-file", QObject::tr("Unset the key file for the database."));
33

34
DatabaseEdit::DatabaseEdit()
35
{
36
    name = QString("db-edit");
37
    description = QObject::tr("Edit a database.");
38
    options.append(DatabaseCreate::SetKeyFileOption);
39
    options.append(DatabaseCreate::SetPasswordOption);
40
    options.append(DatabaseEdit::UnsetKeyFileOption);
41
    options.append(DatabaseEdit::UnsetPasswordOption);
42
}
43

44
int DatabaseEdit::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser> parser)
45
{
46
    auto& out = Utils::STDOUT;
47
    auto& err = Utils::STDERR;
48

49
    const QStringList args = parser->positionalArguments();
50
    bool databaseWasChanged = false;
51

52
    if (parser->isSet(DatabaseCreate::SetPasswordOption) && parser->isSet(DatabaseEdit::UnsetPasswordOption)) {
53
        err << QObject::tr("Cannot use %1 and %2 at the same time.")
54
                   .arg(DatabaseCreate::SetPasswordOption.names().at(0))
55
                   .arg(DatabaseEdit::UnsetPasswordOption.names().at(0))
56
            << endl;
57
        return EXIT_FAILURE;
58
    }
59

60
    if (parser->isSet(DatabaseCreate::SetKeyFileOption) && parser->isSet(DatabaseEdit::UnsetKeyFileOption)) {
61
        err << QObject::tr("Cannot use %1 and %2 at the same time.")
62
                   .arg(DatabaseCreate::SetKeyFileOption.names().at(0))
63
                   .arg(DatabaseEdit::UnsetKeyFileOption.names().at(0))
64
            << endl;
65
        return EXIT_FAILURE;
66
    }
67

68
    bool hasKeyChange =
69
        (parser->isSet(DatabaseCreate::SetPasswordOption) || parser->isSet(DatabaseCreate::SetKeyFileOption)
70
         || parser->isSet(DatabaseEdit::UnsetPasswordOption) || parser->isSet(DatabaseEdit::UnsetKeyFileOption));
71

72
    if (hasKeyChange) {
73
        auto newDatabaseKey = getNewDatabaseKey(database,
74
                                                parser->isSet(DatabaseCreate::SetPasswordOption),
75
                                                parser->isSet(DatabaseEdit::UnsetPasswordOption),
76
                                                parser->value(DatabaseCreate::SetKeyFileOption),
77
                                                parser->isSet(DatabaseEdit::UnsetKeyFileOption));
78
        if (newDatabaseKey.isNull()) {
79
            err << QObject::tr("Could not change the database key.") << endl;
80
            return EXIT_FAILURE;
81
        }
82
        database->setKey(newDatabaseKey);
83
        databaseWasChanged = true;
84
    }
85

86
    if (!databaseWasChanged) {
87
        out << QObject::tr("Database was not modified.") << endl;
88
        return EXIT_SUCCESS;
89
    }
90

91
    QString errorMessage;
92
    if (!database->save(Database::Atomic, {}, &errorMessage)) {
93
        err << QObject::tr("Writing the database failed: %1").arg(errorMessage) << endl;
94
        return EXIT_FAILURE;
95
    }
96

97
    out << QObject::tr("Successfully edited the database.") << endl;
98
    return EXIT_SUCCESS;
99
}
100

101
QSharedPointer<CompositeKey> DatabaseEdit::getNewDatabaseKey(QSharedPointer<Database> database,
102
                                                             bool updatePassword,
103
                                                             bool removePassword,
104
                                                             QString newFileKeyPath,
105
                                                             bool removeKeyFile)
106
{
107
    auto& err = Utils::STDERR;
108
    auto newDatabaseKey = QSharedPointer<CompositeKey>::create();
109
    bool updateKeyFile = !newFileKeyPath.isEmpty();
110

111
    auto currentPasswordKey = database->key()->getKey(PasswordKey::UUID);
112
    auto currentFileKey = database->key()->getKey(FileKey::UUID);
113
    auto currentChallengeResponseKey = database->key()->getChallengeResponseKey(ChallengeResponseKey::UUID);
114

115
    if (removePassword && currentPasswordKey.isNull()) {
116
        err << QObject::tr("Cannot remove password: The database does not have a password.") << endl;
117
        return {};
118
    }
119

120
    if (removeKeyFile && currentFileKey.isNull()) {
121
        err << QObject::tr("Cannot remove file key: The database does not have a file key.") << endl;
122
        return {};
123
    }
124

125
    if (updatePassword) {
126
        QSharedPointer<PasswordKey> newPasswordKey = Utils::getConfirmedPassword();
127
        if (newPasswordKey.isNull()) {
128
            err << QObject::tr("Failed to set database password.") << endl;
129
            return {};
130
        }
131
        newDatabaseKey->addKey(newPasswordKey);
132
    } else if (!removePassword && !currentPasswordKey.isNull()) {
133
        newDatabaseKey->addKey(currentPasswordKey);
134
    }
135

136
    if (updateKeyFile) {
137
        QSharedPointer<FileKey> newFileKey = QSharedPointer<FileKey>::create();
138
        QString errorMessage;
139
        if (!Utils::loadFileKey(newFileKeyPath, newFileKey)) {
140
            err << QObject::tr("Loading the new key file failed: %1").arg(errorMessage) << endl;
141
            return {};
142
        }
143
        newDatabaseKey->addKey(newFileKey);
144
    } else if (!removeKeyFile && !currentFileKey.isNull()) {
145
        newDatabaseKey->addKey(currentFileKey);
146
    }
147

148
    // This is a sanity check to make sure that this function is not used if
149
    // new key types are introduced. Otherwise, those key types would be
150
    // silently removed from the database.
151
    for (const QSharedPointer<Key>& key : database->key()->keys()) {
152
        if (key->uuid() != PasswordKey::UUID && key->uuid() != FileKey::UUID) {
153
            err << QObject::tr("Found unexpected Key type %1").arg(key->uuid().toString()) << endl;
154
            return {};
155
        }
156
    }
157
    for (const QSharedPointer<ChallengeResponseKey>& key : database->key()->challengeResponseKeys()) {
158
        if (key->uuid() != ChallengeResponseKey::UUID) {
159
            err << QObject::tr("Found unexpected Key type %1").arg(key->uuid().toString()) << endl;
160
            return {};
161
        }
162
    }
163

164
    if (!currentChallengeResponseKey.isNull()) {
165
        newDatabaseKey->addChallengeResponseKey(currentChallengeResponseKey);
166
    }
167

168
    if (newDatabaseKey->keys().isEmpty() && newDatabaseKey->challengeResponseKeys().isEmpty()) {
169
        err << QObject::tr("Cannot remove all the keys from a database.") << endl;
170
        return {};
171
    }
172

173
    return newDatabaseKey;
174
}
175

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

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

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

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