File indexing completed on 2024-06-16 03:53:03
0001 /* 0002 This file is part of the KDE project 0003 SPDX-FileCopyrightText: 2013 Valentin Rusu <kde@rusu.info> 0004 0005 SPDX-License-Identifier: LGPL-2.0-only 0006 */ 0007 0008 #include "backendpersisthandler.h" 0009 #include "kwalletbackend_debug.h" 0010 0011 #include <KLocalizedString> 0012 #include <KMessageBox> 0013 #include <QCryptographicHash> 0014 #include <QFile> 0015 #include <QIODevice> 0016 #include <QSaveFile> 0017 #include <assert.h> 0018 #ifdef HAVE_GPGMEPP 0019 #include <gpgme++/context.h> 0020 #include <gpgme++/data.h> 0021 #include <gpgme++/decryptionresult.h> 0022 #include <gpgme++/encryptionresult.h> 0023 #include <gpgme++/key.h> 0024 #include <gpgme++/keylistresult.h> 0025 #endif 0026 #include "blowfish.h" 0027 #include "cbc.h" 0028 #include "kwalletbackend.h" 0029 #include "sha1.h" 0030 0031 #ifdef Q_OS_WIN 0032 #include <windows.h> // Must be included before wincrypt.h 0033 0034 #include <wincrypt.h> 0035 #endif 0036 0037 #define KWALLET_CIPHER_BLOWFISH_ECB 0 // this was the old KWALLET_CIPHER_BLOWFISH_CBC 0038 #define KWALLET_CIPHER_3DES_CBC 1 // unsupported 0039 #define KWALLET_CIPHER_GPG 2 0040 #define KWALLET_CIPHER_BLOWFISH_CBC 3 0041 0042 #define KWALLET_HASH_SHA1 0 0043 #define KWALLET_HASH_MD5 1 // unsupported 0044 #define KWALLET_HASH_PBKDF2_SHA512 2 // used when using kwallet with pam or since 4.13 version 0045 0046 namespace KWallet 0047 { 0048 typedef char Digest[16]; 0049 0050 static int getRandomBlock(QByteArray &randBlock) 0051 { 0052 #ifdef Q_OS_WIN // krazy:exclude=cpp 0053 0054 // Use windows crypto API to get randomness on win32 0055 // HACK: this should be done using qca 0056 HCRYPTPROV hProv; 0057 0058 if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { 0059 return -1; // couldn't get random data 0060 } 0061 0062 if (!CryptGenRandom(hProv, static_cast<DWORD>(randBlock.size()), (BYTE *)randBlock.data())) { 0063 return -3; // read error 0064 } 0065 0066 // release the crypto context 0067 CryptReleaseContext(hProv, 0); 0068 0069 return randBlock.size(); 0070 0071 #else 0072 0073 // First try /dev/urandom 0074 if (QFile::exists(QStringLiteral("/dev/urandom"))) { 0075 QFile devrand(QStringLiteral("/dev/urandom")); 0076 if (devrand.open(QIODevice::ReadOnly)) { 0077 int rc = devrand.read(randBlock.data(), randBlock.size()); 0078 0079 if (rc != randBlock.size()) { 0080 return -3; // not enough data read 0081 } 0082 0083 return 0; 0084 } 0085 } 0086 0087 // If that failed, try /dev/random 0088 // FIXME: open in noblocking mode! 0089 if (QFile::exists(QStringLiteral("/dev/random"))) { 0090 QFile devrand(QStringLiteral("/dev/random")); 0091 if (devrand.open(QIODevice::ReadOnly)) { 0092 int rc = 0; 0093 int cnt = 0; 0094 0095 do { 0096 int rc2 = devrand.read(randBlock.data() + rc, randBlock.size()); 0097 0098 if (rc2 < 0) { 0099 return -3; // read error 0100 } 0101 0102 rc += rc2; 0103 cnt++; 0104 if (cnt > randBlock.size()) { 0105 return -4; // reading forever?! 0106 } 0107 } while (rc < randBlock.size()); 0108 0109 return 0; 0110 } 0111 } 0112 0113 // EGD method 0114 QString randFilename = QString::fromLocal8Bit(qgetenv("RANDFILE")); 0115 if (!randFilename.isEmpty()) { 0116 if (QFile::exists(randFilename)) { 0117 QFile devrand(randFilename); 0118 if (devrand.open(QIODevice::ReadOnly)) { 0119 int rc = devrand.read(randBlock.data(), randBlock.size()); 0120 if (rc != randBlock.size()) { 0121 return -3; // not enough data read 0122 } 0123 return 0; 0124 } 0125 } 0126 } 0127 0128 // Couldn't get any random data!! 0129 return -1; 0130 0131 #endif 0132 } 0133 0134 BackendPersistHandler *BackendPersistHandler::getPersistHandler(BackendCipherType cipherType) 0135 { 0136 switch (cipherType) { 0137 case BACKEND_CIPHER_BLOWFISH: 0138 return new BlowfishPersistHandler; 0139 #ifdef HAVE_GPGMEPP 0140 case BACKEND_CIPHER_GPG: 0141 return new GpgPersistHandler; 0142 #endif // HAVE_GPGMEPP 0143 default: 0144 Q_ASSERT(0); 0145 return nullptr; 0146 } 0147 } 0148 0149 BackendPersistHandler *BackendPersistHandler::getPersistHandler(char magicBuf[12]) 0150 { 0151 if ((magicBuf[2] == KWALLET_CIPHER_BLOWFISH_ECB || magicBuf[2] == KWALLET_CIPHER_BLOWFISH_CBC) 0152 && (magicBuf[3] == KWALLET_HASH_SHA1 || magicBuf[3] == KWALLET_HASH_PBKDF2_SHA512)) { 0153 bool useECBforReading = magicBuf[2] == KWALLET_CIPHER_BLOWFISH_ECB; 0154 if (useECBforReading) { 0155 qCDebug(KWALLETBACKEND_LOG) << "this wallet uses ECB encryption. It'll be converted to CBC on next save."; 0156 } 0157 return new BlowfishPersistHandler(useECBforReading); 0158 } 0159 #ifdef HAVE_GPGMEPP 0160 if (magicBuf[2] == KWALLET_CIPHER_GPG && magicBuf[3] == 0) { 0161 return new GpgPersistHandler; 0162 } 0163 #endif // HAVE_GPGMEPP 0164 return nullptr; // unknown cipher or hash 0165 } 0166 0167 int BlowfishPersistHandler::write(Backend *wb, QSaveFile &sf, QByteArray &version, WId) 0168 { 0169 assert(wb->_cipherType == BACKEND_CIPHER_BLOWFISH); 0170 0171 if (_useECBforReading) { 0172 qCDebug(KWALLETBACKEND_LOG) << "This wallet used ECB and is now saved using CBC"; 0173 _useECBforReading = false; 0174 } 0175 0176 version[2] = KWALLET_CIPHER_BLOWFISH_CBC; 0177 if (!wb->_useNewHash) { 0178 version[3] = KWALLET_HASH_SHA1; 0179 } else { 0180 version[3] = KWALLET_HASH_PBKDF2_SHA512; // Since 4.13 we always use PBKDF2_SHA512 0181 } 0182 0183 if (sf.write(version) != 4) { 0184 sf.cancelWriting(); 0185 return -4; // write error 0186 } 0187 0188 // Holds the hashes we write out 0189 QByteArray hashes; 0190 QDataStream hashStream(&hashes, QIODevice::WriteOnly); 0191 QCryptographicHash md5(QCryptographicHash::Md5); 0192 hashStream << static_cast<quint32>(wb->_entries.count()); 0193 0194 // Holds decrypted data prior to encryption 0195 QByteArray decrypted; 0196 0197 // FIXME: we should estimate the amount of data we will write in each 0198 // buffer and resize them approximately in order to avoid extra 0199 // resizes. 0200 0201 // populate decrypted 0202 QDataStream dStream(&decrypted, QIODevice::WriteOnly); 0203 for (Backend::FolderMap::ConstIterator i = wb->_entries.constBegin(); i != wb->_entries.constEnd(); ++i) { 0204 dStream << i.key(); 0205 dStream << static_cast<quint32>(i.value().count()); 0206 0207 md5.reset(); 0208 md5.addData(i.key().toUtf8()); 0209 hashStream.writeRawData(md5.result().constData(), 16); 0210 hashStream << static_cast<quint32>(i.value().count()); 0211 0212 for (Backend::EntryMap::ConstIterator j = i.value().constBegin(); j != i.value().constEnd(); ++j) { 0213 dStream << j.key(); 0214 dStream << static_cast<qint32>(j.value()->type()); 0215 dStream << j.value()->value(); 0216 0217 md5.reset(); 0218 md5.addData(j.key().toUtf8()); 0219 hashStream.writeRawData(md5.result().constData(), 16); 0220 } 0221 } 0222 0223 if (sf.write(hashes) != hashes.size()) { 0224 sf.cancelWriting(); 0225 return -4; // write error 0226 } 0227 0228 // calculate the hash of the file 0229 SHA1 sha; 0230 BlowFish _bf; 0231 CipherBlockChain bf(&_bf); 0232 0233 sha.process(decrypted.data(), decrypted.size()); 0234 0235 // prepend and append the random data 0236 QByteArray wholeFile; 0237 long blksz = bf.blockSize(); 0238 long newsize = decrypted.size() + blksz + // encrypted block 0239 4 + // file size 0240 20; // size of the SHA hash 0241 0242 int delta = (blksz - (newsize % blksz)); 0243 newsize += delta; 0244 wholeFile.resize(newsize); 0245 0246 QByteArray randBlock; 0247 randBlock.resize(blksz + delta); 0248 if (getRandomBlock(randBlock) < 0) { 0249 sha.reset(); 0250 decrypted.fill(0); 0251 sf.cancelWriting(); 0252 return -3; // Fatal error: can't get random 0253 } 0254 0255 for (int i = 0; i < blksz; i++) { 0256 wholeFile[i] = randBlock[i]; 0257 } 0258 0259 for (int i = 0; i < 4; i++) { 0260 wholeFile[(int)(i + blksz)] = (decrypted.size() >> 8 * (3 - i)) & 0xff; 0261 } 0262 0263 for (int i = 0; i < decrypted.size(); i++) { 0264 wholeFile[(int)(i + blksz + 4)] = decrypted[i]; 0265 } 0266 0267 for (int i = 0; i < delta; i++) { 0268 wholeFile[(int)(i + blksz + 4 + decrypted.size())] = randBlock[(int)(i + blksz)]; 0269 } 0270 0271 const char *hash = (const char *)sha.hash(); 0272 for (int i = 0; i < 20; i++) { 0273 wholeFile[(int)(newsize - 20 + i)] = hash[i]; 0274 } 0275 0276 sha.reset(); 0277 decrypted.fill(0); 0278 0279 // encrypt the data 0280 if (!bf.setKey(wb->_passhash.data(), wb->_passhash.size() * 8)) { 0281 wholeFile.fill(0); 0282 sf.cancelWriting(); 0283 return -2; // encrypt error 0284 } 0285 0286 int rc = bf.encrypt(wholeFile.data(), wholeFile.size()); 0287 if (rc < 0) { 0288 wholeFile.fill(0); 0289 sf.cancelWriting(); 0290 return -2; // encrypt error 0291 } 0292 0293 // write the file 0294 auto written = sf.write(wholeFile); 0295 if (written != wholeFile.size()) { 0296 wholeFile.fill(0); 0297 sf.cancelWriting(); 0298 return -4; // write error 0299 } 0300 if (!sf.commit()) { 0301 qCDebug(KWALLETBACKEND_LOG) << "WARNING: wallet sync to disk failed! QSaveFile status was " << sf.errorString(); 0302 wholeFile.fill(0); 0303 return -4; // write error 0304 } 0305 0306 wholeFile.fill(0); 0307 0308 return 0; 0309 } 0310 0311 int BlowfishPersistHandler::read(Backend *wb, QFile &db, WId) 0312 { 0313 wb->_cipherType = BACKEND_CIPHER_BLOWFISH; 0314 wb->_hashes.clear(); 0315 // Read in the hashes 0316 QDataStream hds(&db); 0317 quint32 n; 0318 hds >> n; 0319 if (n > 0xffff) { // sanity check 0320 return -43; 0321 } 0322 0323 for (size_t i = 0; i < n; ++i) { 0324 Digest d; 0325 Digest d2; // judgment day 0326 MD5Digest ba; 0327 QMap<MD5Digest, QList<MD5Digest>>::iterator it; 0328 quint32 fsz; 0329 if (hds.atEnd()) { 0330 return -43; 0331 } 0332 hds.readRawData(d, 16); 0333 hds >> fsz; 0334 ba = MD5Digest(reinterpret_cast<char *>(d)); 0335 it = wb->_hashes.insert(ba, QList<MD5Digest>()); 0336 for (size_t j = 0; j < fsz; ++j) { 0337 hds.readRawData(d2, 16); 0338 ba = MD5Digest(d2); 0339 (*it).append(ba); 0340 } 0341 } 0342 0343 // Read in the rest of the file. 0344 QByteArray encrypted = db.readAll(); 0345 assert(encrypted.size() < db.size()); 0346 0347 BlowFish _bf; 0348 CipherBlockChain bf(&_bf, _useECBforReading); 0349 int blksz = bf.blockSize(); 0350 if ((encrypted.size() % blksz) != 0) { 0351 return -5; // invalid file structure 0352 } 0353 0354 bf.setKey((void *)wb->_passhash.data(), wb->_passhash.size() * 8); 0355 0356 if (!encrypted.data()) { 0357 wb->_passhash.fill(0); 0358 encrypted.fill(0); 0359 return -7; // file structure error 0360 } 0361 0362 int rc = bf.decrypt(encrypted.data(), encrypted.size()); 0363 if (rc < 0) { 0364 wb->_passhash.fill(0); 0365 encrypted.fill(0); 0366 return -6; // decrypt error 0367 } 0368 0369 const char *t = encrypted.data(); 0370 0371 // strip the leading data 0372 t += blksz; // one block of random data 0373 0374 // strip the file size off 0375 long fsize = 0; 0376 0377 fsize |= (long(*t) << 24) & 0xff000000; 0378 t++; 0379 fsize |= (long(*t) << 16) & 0x00ff0000; 0380 t++; 0381 fsize |= (long(*t) << 8) & 0x0000ff00; 0382 t++; 0383 fsize |= long(*t) & 0x000000ff; 0384 t++; 0385 0386 if (fsize < 0 || fsize > long(encrypted.size()) - blksz - 4) { 0387 qCDebug(KWALLETBACKEND_LOG) << "fsize: " << fsize << " encrypted.size(): " << encrypted.size() << " blksz: " << blksz; 0388 encrypted.fill(0); 0389 return -9; // file structure error. 0390 } 0391 0392 // compute the hash ourself 0393 SHA1 sha; 0394 sha.process(t, fsize); 0395 const char *testhash = (const char *)sha.hash(); 0396 0397 // compare hashes 0398 int sz = encrypted.size(); 0399 for (int i = 0; i < 20; i++) { 0400 if (testhash[i] != encrypted[sz - 20 + i]) { 0401 encrypted.fill(0); 0402 sha.reset(); 0403 return -8; // hash error. 0404 } 0405 } 0406 0407 sha.reset(); 0408 0409 // chop off the leading blksz+4 bytes 0410 QByteArray tmpenc(encrypted.data() + blksz + 4, fsize); 0411 encrypted = tmpenc; 0412 tmpenc.fill(0); 0413 0414 // Load the data structures up 0415 QDataStream eStream(encrypted); 0416 0417 while (!eStream.atEnd()) { 0418 QString folder; 0419 quint32 n; 0420 0421 eStream >> folder; 0422 eStream >> n; 0423 0424 // Force initialisation 0425 wb->_entries[folder].clear(); 0426 0427 for (size_t i = 0; i < n; ++i) { 0428 QString key; 0429 KWallet::Wallet::EntryType et = KWallet::Wallet::Unknown; 0430 Entry *e = new Entry; 0431 eStream >> key; 0432 qint32 x = 0; // necessary to read properly 0433 eStream >> x; 0434 et = static_cast<KWallet::Wallet::EntryType>(x); 0435 0436 switch (et) { 0437 case KWallet::Wallet::Password: 0438 case KWallet::Wallet::Stream: 0439 case KWallet::Wallet::Map: 0440 break; 0441 default: // Unknown entry 0442 delete e; 0443 continue; 0444 } 0445 0446 QByteArray a; 0447 eStream >> a; 0448 e->setValue(a); 0449 e->setType(et); 0450 e->setKey(key); 0451 wb->_entries[folder][key] = e; 0452 } 0453 } 0454 0455 wb->_open = true; 0456 encrypted.fill(0); 0457 return 0; 0458 } 0459 0460 #ifdef HAVE_GPGMEPP 0461 GpgME::Error initGpgME() 0462 { 0463 GpgME::Error err; 0464 static bool alreadyInitialized = false; 0465 if (!alreadyInitialized) { 0466 GpgME::initializeLibrary(); 0467 err = GpgME::checkEngine(GpgME::OpenPGP); 0468 if (err) { 0469 qCDebug(KWALLETBACKEND_LOG) << "OpenPGP not supported!"; 0470 } 0471 alreadyInitialized = true; 0472 } 0473 return err; 0474 } 0475 0476 int GpgPersistHandler::write(Backend *wb, QSaveFile &sf, QByteArray &version, WId w) 0477 { 0478 version[2] = KWALLET_CIPHER_GPG; 0479 version[3] = 0; 0480 if (sf.write(version) != 4) { 0481 sf.cancelWriting(); 0482 return -4; // write error 0483 } 0484 0485 GpgME::Error err = initGpgME(); 0486 if (err) { 0487 qCDebug(KWALLETBACKEND_LOG) << "initGpgME returned " << err.code(); 0488 KMessageBox::errorWId(w, 0489 i18n("<qt>Error when attempting to initialize OpenPGP while attempting to save the wallet <b>%1</b>. Error code is <b>%2</b>. " 0490 "Please fix your system configuration, then try again.</qt>", 0491 wb->_name.toHtmlEscaped(), 0492 err.code())); 0493 sf.cancelWriting(); 0494 return -5; 0495 } 0496 0497 std::shared_ptr<GpgME::Context> ctx(GpgME::Context::createForProtocol(GpgME::OpenPGP)); 0498 if (!ctx) { 0499 qCDebug(KWALLETBACKEND_LOG) << "Cannot setup OpenPGP context!"; 0500 KMessageBox::errorWId(w, 0501 i18n("<qt>Error when attempting to initialize OpenPGP while attempting to save the wallet <b>%1</b>. Please fix your system " 0502 "configuration, then try again.</qt>"), 0503 wb->_name.toHtmlEscaped()); 0504 return -6; 0505 } 0506 0507 assert(wb->_cipherType == BACKEND_CIPHER_GPG); 0508 0509 QByteArray hashes; 0510 QDataStream hashStream(&hashes, QIODevice::WriteOnly); 0511 QCryptographicHash md5(QCryptographicHash::Md5); 0512 hashStream << static_cast<quint32>(wb->_entries.count()); 0513 0514 QByteArray values; 0515 QDataStream valueStream(&values, QIODevice::WriteOnly); 0516 Backend::FolderMap::ConstIterator i = wb->_entries.constBegin(); 0517 Backend::FolderMap::ConstIterator ie = wb->_entries.constEnd(); 0518 for (; i != ie; ++i) { 0519 valueStream << i.key(); 0520 valueStream << static_cast<quint32>(i.value().count()); 0521 0522 md5.reset(); 0523 md5.addData(i.key().toUtf8()); 0524 hashStream.writeRawData(md5.result().constData(), 16); 0525 hashStream << static_cast<quint32>(i.value().count()); 0526 0527 Backend::EntryMap::ConstIterator j = i.value().constBegin(); 0528 Backend::EntryMap::ConstIterator je = i.value().constEnd(); 0529 for (; j != je; ++j) { 0530 valueStream << j.key(); 0531 valueStream << static_cast<qint32>(j.value()->type()); 0532 valueStream << j.value()->value(); 0533 0534 md5.reset(); 0535 md5.addData(j.key().toUtf8()); 0536 hashStream.writeRawData(md5.result().constData(), 16); 0537 } 0538 } 0539 0540 QByteArray dataBuffer; 0541 QDataStream dataStream(&dataBuffer, QIODevice::WriteOnly); 0542 QString keyID(wb->_gpgKey.keyID()); 0543 dataStream << keyID; 0544 dataStream << hashes; 0545 dataStream << values; 0546 0547 GpgME::Data decryptedData(dataBuffer.data(), size_t(dataBuffer.size()), false); 0548 GpgME::Data encryptedData; 0549 std::vector<GpgME::Key> keys; 0550 keys.push_back(wb->_gpgKey); 0551 const GpgME::EncryptionResult res = ctx->encrypt(keys, decryptedData, encryptedData, GpgME::Context::None); 0552 if (res.error()) { 0553 const int gpgerr = res.error().code(); 0554 KMessageBox::errorWId(w, 0555 i18n("<qt>Encryption error while attempting to save the wallet <b>%1</b>. Error code is <b>%2 (%3)</b>. Please fix your system " 0556 "configuration, then try again. This error may occur if you are not using a full trust GPG key. Please ensure you have the " 0557 "secret key for the key you are using.</qt>", 0558 wb->_name.toHtmlEscaped(), 0559 gpgerr, 0560 res.error().asString())); 0561 qCDebug(KWALLETBACKEND_LOG) << "GpgME encryption error: " << gpgerr; 0562 sf.cancelWriting(); 0563 return -7; 0564 } 0565 0566 char buffer[4096]; 0567 ssize_t bytes = 0; 0568 encryptedData.seek(0, SEEK_SET); 0569 while ((bytes = encryptedData.read(buffer, sizeof(buffer) / sizeof(buffer[0]))) > 0) { 0570 if (sf.write(buffer, bytes) != bytes) { 0571 KMessageBox::errorWId(w, 0572 i18n("<qt>File handling error while attempting to save the wallet <b>%1</b>. Error was <b>%2</b>. Please fix your system " 0573 "configuration, then try again.</qt>", 0574 wb->_name.toHtmlEscaped(), 0575 sf.errorString())); 0576 sf.cancelWriting(); 0577 return -4; // write error 0578 } 0579 } 0580 0581 if (!sf.commit()) { 0582 qCDebug(KWALLETBACKEND_LOG) << "WARNING: wallet sync to disk failed! QSaveFile status was " << sf.errorString(); 0583 return -4; // write error 0584 } 0585 0586 return 0; 0587 } 0588 0589 int GpgPersistHandler::read(Backend *wb, QFile &sf, WId w) 0590 { 0591 GpgME::Error err = initGpgME(); 0592 if (err) { 0593 KMessageBox::errorWId(w, 0594 i18n("<qt>Error when attempting to initialize OpenPGP while attempting to open the wallet <b>%1</b>. Error code is <b>%2</b>. " 0595 "Please fix your system configuration, then try again.</qt>", 0596 wb->_name.toHtmlEscaped(), 0597 err.code())); 0598 return -1; 0599 } 0600 0601 wb->_cipherType = BACKEND_CIPHER_GPG; 0602 wb->_hashes.clear(); 0603 0604 // the remainder of the file is GPG encrypted. Let's decrypt it 0605 GpgME::Data encryptedData; 0606 char buffer[4096]; 0607 ssize_t bytes = 0; 0608 while ((bytes = sf.read(buffer, sizeof(buffer) / sizeof(buffer[0])))) { 0609 encryptedData.write(buffer, bytes); 0610 } 0611 0612 retry_label: 0613 std::shared_ptr<GpgME::Context> ctx(GpgME::Context::createForProtocol(GpgME::OpenPGP)); 0614 if (nullptr == ctx) { 0615 KMessageBox::errorWId(w, 0616 i18n("<qt>Error when attempting to initialize OpenPGP while attempting to open the wallet <b>%1</b>. Please fix your system " 0617 "configuration, then try again.</qt>", 0618 wb->_name.toHtmlEscaped())); 0619 qCDebug(KWALLETBACKEND_LOG) << "Cannot setup OpenPGP context!"; 0620 return -1; 0621 } 0622 0623 GpgME::Data decryptedData; 0624 encryptedData.seek(0, SEEK_SET); 0625 GpgME::DecryptionResult res = ctx->decrypt(encryptedData, decryptedData); 0626 if (res.error()) { 0627 qCDebug(KWALLETBACKEND_LOG) << "Error decrypting message: " << res.error().asString() << ", code " << res.error().code() << ", source " 0628 << res.error().source(); 0629 KGuiItem btnRetry(i18n("Retry")); 0630 // FIXME the logic here should be a little more elaborate; a dialog box should be used with "retry", "cancel", but also "troubleshoot" with options to 0631 // show card status and to kill scdaemon 0632 int userChoice = 0633 KMessageBox::warningTwoActionsWId(w, 0634 i18n("<qt>Error when attempting to decrypt the wallet <b>%1</b> using GPG. If you're using a SmartCard, " 0635 "please ensure it's inserted then try again.<br><br>GPG error was <b>%2</b></qt>", 0636 wb->_name.toHtmlEscaped(), 0637 res.error().asString()), 0638 i18n("kwalletd GPG backend"), 0639 btnRetry, 0640 KStandardGuiItem::cancel()); 0641 if (userChoice == KMessageBox::PrimaryAction) { 0642 decryptedData.seek(0, SEEK_SET); 0643 goto retry_label; 0644 } 0645 return -1; 0646 } 0647 0648 decryptedData.seek(0, SEEK_SET); 0649 QByteArray dataBuffer; 0650 while ((bytes = decryptedData.read(buffer, sizeof(buffer) / sizeof(buffer[0])))) { 0651 dataBuffer.append(buffer, bytes); 0652 } 0653 0654 // load the wallet from the decrypted data 0655 QDataStream dataStream(dataBuffer); 0656 QString keyID; 0657 QByteArray hashes; 0658 QByteArray values; 0659 dataStream >> keyID; 0660 dataStream >> hashes; 0661 dataStream >> values; 0662 0663 // locate the GPG key having the ID found inside the file. This will be needed later, when writing changes to disk. 0664 QDataStream fileStream(&sf); 0665 fileStream.setDevice(nullptr); 0666 qCDebug(KWALLETBACKEND_LOG) << "This wallet was encrypted using GPG key with ID " << keyID; 0667 0668 ctx->setKeyListMode(GpgME::KeyListMode::Local); 0669 err = ctx->startKeyListing(); 0670 while (!err) { 0671 GpgME::Key k = ctx->nextKey(err); 0672 if (err) { 0673 break; 0674 } 0675 if (keyID == k.keyID()) { 0676 qCDebug(KWALLETBACKEND_LOG) << "The key was found."; 0677 wb->_gpgKey = k; 0678 break; 0679 } 0680 } 0681 ctx->endKeyListing(); 0682 if (wb->_gpgKey.isNull()) { 0683 KMessageBox::errorWId(w, 0684 i18n("<qt>Error when attempting to open the wallet <b>%1</b>. The wallet was encrypted using the GPG Key ID <b>%2</b> but this " 0685 "key was not found on your system.</qt>", 0686 wb->_name.toHtmlEscaped(), 0687 keyID)); 0688 return -1; 0689 } 0690 0691 QDataStream hashStream(hashes); 0692 QDataStream valueStream(values); 0693 0694 quint32 hashCount; 0695 hashStream >> hashCount; 0696 if (hashCount > 0xFFFF) { 0697 return -43; 0698 } 0699 0700 quint32 folderCount = hashCount; 0701 while (hashCount--) { 0702 Digest d; 0703 hashStream.readRawData(d, 16); 0704 0705 quint32 folderSize; 0706 hashStream >> folderSize; 0707 0708 MD5Digest ba = MD5Digest(reinterpret_cast<char *>(d)); 0709 QMap<MD5Digest, QList<MD5Digest>>::iterator it = wb->_hashes.insert(ba, QList<MD5Digest>()); 0710 while (folderSize--) { 0711 Digest d2; 0712 hashStream.readRawData(d2, 16); 0713 ba = MD5Digest(d2); 0714 (*it).append(ba); 0715 } 0716 } 0717 0718 while (folderCount--) { 0719 QString folder; 0720 valueStream >> folder; 0721 0722 quint32 entryCount; 0723 valueStream >> entryCount; 0724 0725 wb->_entries[folder].clear(); 0726 0727 while (entryCount--) { 0728 KWallet::Wallet::EntryType et = KWallet::Wallet::Unknown; 0729 Entry *e = new Entry; 0730 0731 QString key; 0732 valueStream >> key; 0733 0734 qint32 x = 0; // necessary to read properly 0735 valueStream >> x; 0736 et = static_cast<KWallet::Wallet::EntryType>(x); 0737 0738 switch (et) { 0739 case KWallet::Wallet::Password: 0740 case KWallet::Wallet::Stream: 0741 case KWallet::Wallet::Map: 0742 break; 0743 default: // Unknown entry 0744 delete e; 0745 continue; 0746 } 0747 0748 QByteArray a; 0749 valueStream >> a; 0750 e->setValue(a); 0751 e->setType(et); 0752 e->setKey(key); 0753 wb->_entries[folder][key] = e; 0754 } 0755 } 0756 0757 wb->_open = true; 0758 0759 return 0; 0760 } 0761 #endif // HAVE_GPGMEPP 0762 0763 } // namespace