1 /*
2 * Copyright (C) 2018 Rafael Ostertag
3 *
4 * This file is part of YAPET.
5 *
6 * YAPET is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
9 * version.
10 *
11 * YAPET is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14 * details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * YAPET. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Additional permission under GNU GPL version 3 section 7
20 *
21 * If you modify this program, or any covered work, by linking or combining it
22 * with the OpenSSL project's OpenSSL library (or a modified version of that
23 * library), containing parts covered by the terms of the OpenSSL or SSLeay
24 * licenses, Rafael Ostertag grants you additional permission to convey the
25 * resulting work. Corresponding Source for a non-source form of such a
26 * combination shall include the source code for the parts of OpenSSL used as
27 * well as that of the covered work.
28 */
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #include <algorithm>
35
36 #include "cryptoerror.hh"
37 #include "file.h"
38 #include "fileutils.hh"
39 #include "intl.h"
40 #include "logger.hh"
41 #include "yapeterror.hh"
42
43 using namespace YAPET;
44 using namespace yapet;
45
readHeader()46 Header10 File::readHeader() {
47 auto encryptedSerializedHeader{_yapetFile->readHeader()};
48 auto serializedHeader{_crypto->decrypt(encryptedSerializedHeader)};
49 return Header10{serializedHeader};
50 }
51
initializeEmptyFile()52 void File::initializeEmptyFile() {
53 Header10 header{time(0)};
54
55 _yapetFile->writeIdentifier();
56 _yapetFile->writeUnencryptedMetaData(
57 _crypto->getKey()->keyingParameters().serialize());
58
59 auto encryptedSerializedHeader = _crypto->encrypt(header.serialize());
60
61 _yapetFile->writeHeader(encryptedSerializedHeader);
62 }
63
validateExistingFile()64 void File::validateExistingFile() {
65 auto encryptedSerializedHeader = _yapetFile->readHeader();
66 yapet::SecureArray serializedHeader;
67 try {
68 serializedHeader = _crypto->decrypt(encryptedSerializedHeader);
69 } catch (EncryptionError& e) {
70 LOG_MESSAGE(std::string{__func__} + ": invalid password");
71 // most likely caused by invalid password
72 throw InvalidPasswordError{_("Invalid password")};
73 }
74
75 Header10 header;
76 try {
77 header = Header10{serializedHeader};
78 } catch (ControlStringMismatch& e) {
79 LOG_MESSAGE(std::string{__func__} + ": invalid password");
80 }
81
82 // If we read an invalid header version, intToHeaderVersion() will throw an
83 // exception
84 intToHeaderVersion(header.version());
85 }
86
notModifiedOrThrow()87 void File::notModifiedOrThrow() {
88 auto currentFileModificationTime{
89 yapet::getModificationTime(_yapetFile->filename())};
90 if (currentFileModificationTime != _fileModificationTime) {
91 throw RetryableError{_("File has been externally modified")};
92 }
93 }
94
File(std::shared_ptr<yapet::AbstractCryptoFactory> abstractCryptoFactory,const std::string & filename,bool create,bool secure)95 File::File(std::shared_ptr<yapet::AbstractCryptoFactory> abstractCryptoFactory,
96 const std::string& filename, bool create, bool secure)
97 : _fileModificationTime{0},
98 _abstractCryptoFactory{abstractCryptoFactory},
99 _yapetFile{abstractCryptoFactory->file(filename, create, secure)},
100 _crypto{abstractCryptoFactory->crypto()} {
101 _yapetFile->open();
102 if (getFileSize(filename) == 0) {
103 initializeEmptyFile();
104 } else {
105 validateExistingFile();
106 }
107
108 _fileModificationTime = getModificationTime(_yapetFile->filename());
109 }
110
~File()111 File::~File() {}
112
save(const std::list<PasswordListItem> & records,bool forcewrite)113 void File::save(const std::list<PasswordListItem>& records, bool forcewrite) {
114 if (!forcewrite) {
115 notModifiedOrThrow();
116 }
117
118 std::list<SecureArray> encryptedPasswordRecords{};
119 std::for_each(records.begin(), records.end(),
120 [&encryptedPasswordRecords](const PasswordListItem& i) {
121 encryptedPasswordRecords.push_back(i.encryptedRecord());
122 });
123
124 _yapetFile->writePasswordRecords(encryptedPasswordRecords);
125 _fileModificationTime = yapet::getModificationTime(_yapetFile->filename());
126 LOG_MESSAGE("Save yapet file");
127 }
128
read()129 std::list<PasswordListItem> File::read() {
130 auto encryptedPasswordRecords{_yapetFile->readPasswordRecords()};
131
132 std::list<SecureArray>::iterator it = encryptedPasswordRecords.begin();
133 std::list<PasswordListItem> result;
134 while (it != encryptedPasswordRecords.end()) {
135 auto decryptedSerializedPasswordRecord{_crypto->decrypt(*it)};
136 PasswordRecord passwordRecord{decryptedSerializedPasswordRecord};
137
138 result.push_back(PasswordListItem{
139 reinterpret_cast<const char*>(passwordRecord.name()), *it});
140 it++;
141 }
142
143 LOG_MESSAGE("Read yapet file");
144 return result;
145 }
146
setNewKey(const std::shared_ptr<yapet::AbstractCryptoFactory> & newCryptoFactory,bool forcewrite)147 void File::setNewKey(
148 const std::shared_ptr<yapet::AbstractCryptoFactory>& newCryptoFactory,
149 bool forcewrite) {
150 if (!forcewrite) {
151 notModifiedOrThrow();
152 }
153
154 bool isSecure = _yapetFile->isSecure();
155 std::string filename{_yapetFile->filename()};
156
157 // close and destroy old file
158 LOG_MESSAGE("File::setNewKey(): close currently open file");
159 _yapetFile.reset();
160
161 LOG_MESSAGE("File::setNewKey(): rename file");
162 std::string backupfilename(filename + ".bak");
163 yapet::renameFile(filename, backupfilename);
164
165 LOG_MESSAGE("File::setNewKey(): open renamed file");
166 std::unique_ptr<yapet::YapetFile> oldFile{
167 _abstractCryptoFactory->file(backupfilename, false, false)};
168 oldFile->open();
169
170 LOG_MESSAGE("File::setNewKey(): swap crypto factories");
171 auto cryptoFactory{newCryptoFactory};
172 auto otherCrypto{cryptoFactory->crypto()};
173 _abstractCryptoFactory.swap(cryptoFactory);
174
175 _crypto.swap(otherCrypto);
176
177 LOG_MESSAGE("File::setNewKey(): create new file");
178 _yapetFile = _abstractCryptoFactory->file(filename, true, isSecure);
179 _yapetFile->open();
180
181 LOG_MESSAGE("File::setNewKey(): initialize new file");
182 initializeEmptyFile();
183 std::list<yapet::SecureArray> newlyEncryptedRecords{};
184 LOG_MESSAGE("File::setNewKey(): read password records from renamed file");
185 for (auto encryptedSerializedRecord : oldFile->readPasswordRecords()) {
186 auto serializedRecord{otherCrypto->decrypt(encryptedSerializedRecord)};
187 auto newlyEncryptedSerializedRecord{_crypto->encrypt(serializedRecord)};
188 newlyEncryptedRecords.push_back(newlyEncryptedSerializedRecord);
189 }
190 LOG_MESSAGE("File::setNewKey(): write password records to new file");
191 _yapetFile->writePasswordRecords(newlyEncryptedRecords);
192 _fileModificationTime = yapet::getModificationTime(_yapetFile->filename());
193 }
194
getMasterPWSet()195 int64_t File::getMasterPWSet() {
196 auto header{readHeader()};
197 return header.passwordSetTime();
198 }
199
200 /**
201 * New since version 0.6.
202 *
203 * Return the file version.
204 */
getFileVersion()205 SecureArray File::getFileVersion() { return _yapetFile->readIdentifier(); }
206
getHeaderVersion()207 HEADER_VERSION File::getHeaderVersion() {
208 auto encryptedSerializedHeader{_yapetFile->readHeader()};
209 auto serializedHeader{_crypto->decrypt(encryptedSerializedHeader)};
210
211 Header10 header{serializedHeader};
212 return yapet::intToHeaderVersion(header.version());
213 }
214