summaryrefslogtreecommitdiffstats
path: root/kwallet/backend/kwalletbackend.cc
diff options
context:
space:
mode:
Diffstat (limited to 'kwallet/backend/kwalletbackend.cc')
-rw-r--r--kwallet/backend/kwalletbackend.cc838
1 files changed, 0 insertions, 838 deletions
diff --git a/kwallet/backend/kwalletbackend.cc b/kwallet/backend/kwalletbackend.cc
deleted file mode 100644
index cfc296a61..000000000
--- a/kwallet/backend/kwalletbackend.cc
+++ /dev/null
@@ -1,838 +0,0 @@
-/* This file is part of the KDE project
- *
- * Copyright (C) 2001-2004 George Staikos <[email protected]>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB. If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#include "kwalletbackend.h"
-
-#include <stdlib.h>
-
-#include <kdebug.h>
-#include <kglobal.h>
-#include <klocale.h>
-#include <kmdcodec.h>
-#include <ksavefile.h>
-#include <kstandarddirs.h>
-
-#include <tqfile.h>
-#include <tqfileinfo.h>
-#include <tqregexp.h>
-
-#include "blowfish.h"
-#include "sha1.h"
-#include "cbc.h"
-
-#include <assert.h>
-
-#define KWALLET_VERSION_MAJOR 0
-#define KWALLET_VERSION_MINOR 0
-
-#define KWALLET_CIPHER_BLOWFISH_CBC 0
-#define KWALLET_CIPHER_3DES_CBC 1 // unsupported
-
-#define KWALLET_HASH_SHA1 0
-#define KWALLET_HASH_MD5 1 // unsupported
-
-
-using namespace KWallet;
-
-#define KWMAGIC "KWALLET\n\r\0\r\n"
-#define KWMAGIC_LEN 12
-
-static void initKWalletDir()
-{
- TDEGlobal::dirs()->addResourceType("kwallet", "share/apps/kwallet");
-}
-
-Backend::Backend(const TQString& name, bool isPath) : _name(name), _ref(0) {
- initKWalletDir();
- if (isPath) {
- _path = name;
- } else {
- _path = TDEGlobal::dirs()->saveLocation("kwallet") + "/" + _name + ".kwl";
- }
-
- _open = false;
-}
-
-
-Backend::~Backend() {
- if (_open) {
- close();
- }
-}
-
-
-int Backend::close() {
- for (FolderMap::ConstIterator i = _entries.begin(); i != _entries.end(); ++i) {
- for (EntryMap::ConstIterator j = i.data().begin(); j != i.data().end(); ++j) {
- delete j.data();
- }
- }
- _entries.clear();
-
-return 0;
-}
-
-
-static int getRandomBlock(TQByteArray& randBlock) {
- // First try /dev/urandom
- if (TQFile::exists("/dev/urandom")) {
- TQFile devrand("/dev/urandom");
- if (devrand.open(IO_ReadOnly)) {
- unsigned int rc = devrand.readBlock(randBlock.data(), randBlock.size());
-
- if (rc != randBlock.size()) {
- return -3; // not enough data read
- }
-
- return 0;
- }
- }
-
- // If that failed, try /dev/random
- // FIXME: open in noblocking mode!
- if (TQFile::exists("/dev/random")) {
- TQFile devrand("/dev/random");
- if (devrand.open(IO_ReadOnly)) {
- unsigned int rc = 0;
- unsigned int cnt = 0;
-
- do {
- int rc2 = devrand.readBlock(randBlock.data() + rc, randBlock.size());
-
- if (rc2 < 0) {
- return -3; // read error
- }
-
- rc += rc2;
- cnt++;
- if (cnt > randBlock.size()) {
- return -4; // reading forever?!
- }
- } while(rc < randBlock.size());
-
- return 0;
- }
- }
-
- // EGD method
- char *randFilename;
- if ((randFilename = getenv("RANDFILE"))) {
- if (TQFile::exists(randFilename)) {
- TQFile devrand(randFilename);
- if (devrand.open(IO_ReadOnly)) {
- unsigned int rc = devrand.readBlock(randBlock.data(), randBlock.size());
- if (rc != randBlock.size()) {
- return -3; // not enough data read
- }
- return 0;
- }
- }
- }
-
- // Couldn't get any random data!!
-
- return -1;
-}
-
-
-// this should be SHA-512 for release probably
-static int password2hash(const TQByteArray& password, TQByteArray& hash) {
- SHA1 sha;
- int shasz = sha.size() / 8;
-
- assert(shasz >= 20);
-
- TQByteArray block1(shasz);
-
- sha.process(password.data(), QMIN(password.size(), 16));
-
- // To make brute force take longer
- for (int i = 0; i < 2000; i++) {
- memcpy(block1.data(), sha.hash(), shasz);
- sha.reset();
- sha.process(block1.data(), shasz);
- }
-
- sha.reset();
-
- if (password.size() > 16) {
- sha.process(password.data() + 16, QMIN(password.size() - 16, 16));
- TQByteArray block2(shasz);
- // To make brute force take longer
- for (int i = 0; i < 2000; i++) {
- memcpy(block2.data(), sha.hash(), shasz);
- sha.reset();
- sha.process(block2.data(), shasz);
- }
-
- sha.reset();
-
- if (password.size() > 32) {
- sha.process(password.data() + 32, QMIN(password.size() - 32, 16));
-
- TQByteArray block3(shasz);
- // To make brute force take longer
- for (int i = 0; i < 2000; i++) {
- memcpy(block3.data(), sha.hash(), shasz);
- sha.reset();
- sha.process(block3.data(), shasz);
- }
-
- sha.reset();
-
- if (password.size() > 48) {
- sha.process(password.data() + 48, password.size() - 48);
-
- TQByteArray block4(shasz);
- // To make brute force take longer
- for (int i = 0; i < 2000; i++) {
- memcpy(block4.data(), sha.hash(), shasz);
- sha.reset();
- sha.process(block4.data(), shasz);
- }
-
- sha.reset();
- // split 14/14/14/14
- hash.resize(56);
- memcpy(hash.data(), block1.data(), 14);
- memcpy(hash.data() + 14, block2.data(), 14);
- memcpy(hash.data() + 28, block3.data(), 14);
- memcpy(hash.data() + 42, block4.data(), 14);
- block4.fill(0);
- } else {
- // split 20/20/16
- hash.resize(56);
- memcpy(hash.data(), block1.data(), 20);
- memcpy(hash.data() + 20, block2.data(), 20);
- memcpy(hash.data() + 40, block3.data(), 16);
- }
- block3.fill(0);
- } else {
- // split 20/20
- hash.resize(40);
- memcpy(hash.data(), block1.data(), 20);
- memcpy(hash.data() + 20, block2.data(), 20);
- }
- block2.fill(0);
- } else {
- // entirely block1
- hash.resize(20);
- memcpy(hash.data(), block1.data(), 20);
- }
-
- block1.fill(0);
-
- return 0;
-}
-
-
-bool Backend::exists(const TQString& wallet) {
- initKWalletDir();
- TQString path = TDEGlobal::dirs()->saveLocation("kwallet") + "/" + wallet + ".kwl";
- // Note: 60 bytes is presently the minimum size of a wallet file.
- // Anything smaller is junk.
-return TQFile::exists(path) && TQFileInfo(path).size() >= 60;
-}
-
-
-TQString Backend::openRCToString(int rc) {
- switch (rc) {
- case -255:
- return i18n("Already open.");
- case -2:
- return i18n("Error opening file.");
- case -3:
- return i18n("Not a wallet file.");
- case -4:
- return i18n("Unsupported file format revision.");
- case -42:
- return i18n("Unknown encryption scheme.");
- case -43:
- return i18n("Corrupt file?");
- case -8:
- return i18n("Error validating wallet integrity. Possibly corrupted.");
- case -5:
- case -7:
- case -9:
- return i18n("Read error - possibly incorrect password.");
- case -6:
- return i18n("Decryption error.");
- default:
- return TQString::null;
- }
-}
-
-
-int Backend::open(const TQByteArray& password) {
-
- if (_open) {
- return -255; // already open
- }
-
- TQByteArray passhash;
-
- // No wallet existed. Let's create it.
- // Note: 60 bytes is presently the minimum size of a wallet file.
- // Anything smaller is junk and should be deleted.
- if (!TQFile::exists(_path) || TQFileInfo(_path).size() < 60) {
- TQFile newfile(_path);
- if (!newfile.open(IO_ReadWrite)) {
- return -2; // error opening file
- }
- newfile.close();
- _open = true;
- sync(password);
- return 1; // new file opened, but OK
- }
-
- TQFile db(_path);
-
- if (!db.open(IO_ReadOnly)) {
- return -2; // error opening file
- }
-
- char magicBuf[KWMAGIC_LEN];
- db.readBlock(magicBuf, KWMAGIC_LEN);
- if (memcmp(magicBuf, KWMAGIC, KWMAGIC_LEN) != 0) {
- return -3; // bad magic
- }
-
- db.readBlock(magicBuf, 4);
-
- // First byte is major version, second byte is minor version
- if (magicBuf[0] != KWALLET_VERSION_MAJOR) {
- return -4; // unknown version
- }
-
- if (magicBuf[1] != KWALLET_VERSION_MINOR) {
- return -4; // unknown version
- }
-
- if (magicBuf[2] != KWALLET_CIPHER_BLOWFISH_CBC) {
- return -42; // unknown cipher
- }
-
- if (magicBuf[3] != KWALLET_HASH_SHA1) {
- return -42; // unknown hash
- }
-
- _hashes.clear();
- // Read in the hashes
- TQDataStream hds(&db);
- TQ_UINT32 n;
- hds >> n;
- if (n > 0xffff) { // sanity check
- return -43;
- }
-
- for (size_t i = 0; i < n; ++i) {
- KMD5::Digest d, d2; // judgment day
- MD5Digest ba;
- TQMap<MD5Digest,TQValueList<MD5Digest> >::iterator it;
- TQ_UINT32 fsz;
- if (hds.atEnd()) return -43;
- hds.readRawBytes(reinterpret_cast<char *>(d), 16);
- hds >> fsz;
- ba.duplicate(reinterpret_cast<char *>(d), 16);
- it = _hashes.insert(ba, TQValueList<MD5Digest>());
- for (size_t j = 0; j < fsz; ++j) {
- hds.readRawBytes(reinterpret_cast<char *>(d2), 16);
- ba.duplicate(reinterpret_cast<char *>(d2), 16);
- (*it).append(ba);
- }
- }
-
- // Read in the rest of the file.
- TQByteArray encrypted = db.readAll();
- assert(encrypted.size() < db.size());
-
- BlowFish _bf;
- CipherBlockChain bf(&_bf);
- int blksz = bf.blockSize();
- if ((encrypted.size() % blksz) != 0) {
- return -5; // invalid file structure
- }
-
- // Decrypt the encrypted data
- passhash.resize(bf.keyLen()/8);
- password2hash(password, passhash);
-
- bf.setKey((void *)passhash.data(), passhash.size()*8);
-
- if (!encrypted.data()) {
- passhash.fill(0);
- encrypted.fill(0);
- return -7; // file structure error
- }
-
- int rc = bf.decrypt(encrypted.data(), encrypted.size());
- if (rc < 0) {
- passhash.fill(0);
- encrypted.fill(0);
- return -6; // decrypt error
- }
-
- passhash.fill(0); // passhash is UNUSABLE NOW
-
- const char *t = encrypted.data();
-
- // strip the leading data
- t += blksz; // one block of random data
-
- // strip the file size off
- long fsize = 0;
-
- fsize |= (long(*t) << 24) & 0xff000000;
- t++;
- fsize |= (long(*t) << 16) & 0x00ff0000;
- t++;
- fsize |= (long(*t) << 8) & 0x0000ff00;
- t++;
- fsize |= long(*t) & 0x000000ff;
- t++;
-
- if (fsize < 0 || fsize > long(encrypted.size()) - blksz - 4) {
- //kdDebug() << "fsize: " << fsize << " encrypted.size(): " << encrypted.size() << " blksz: " << blksz << endl;
- encrypted.fill(0);
- return -9; // file structure error.
- }
-
- // compute the hash ourself
- SHA1 sha;
- sha.process(t, fsize);
- const char *testhash = (const char *)sha.hash();
-
- // compare hashes
- int sz = encrypted.size();
- for (int i = 0; i < 20; i++) {
- if (testhash[i] != static_cast<const char>(encrypted.at(sz - 20 + i))) {
- encrypted.fill(0);
- sha.reset();
- return -8; // hash error.
- }
- }
-
- sha.reset();
-
- // chop off the leading blksz+4 bytes
- TQByteArray tmpenc;
- tmpenc.duplicate(encrypted.data()+blksz+4, fsize);
- encrypted.fill(0);
- encrypted.duplicate(tmpenc.data(), tmpenc.size());
- tmpenc.fill(0);
-
- // Load the data structures up
- TQDataStream eStream(encrypted, IO_ReadOnly);
-
- while (!eStream.atEnd()) {
- TQString folder;
- TQ_UINT32 n;
-
- eStream >> folder;
- eStream >> n;
-
- // Force initialisation
- _entries[folder].clear();
-
- for (size_t i = 0; i < n; i++) {
- TQString key;
- KWallet::Wallet::EntryType et = KWallet::Wallet::Unknown;
- Entry *e = new Entry;
- eStream >> key;
- TQ_INT32 x = 0; // necessary to read properly
- eStream >> x;
- et = static_cast<KWallet::Wallet::EntryType>(x);
-
- switch (et) {
- case KWallet::Wallet::Password:
- case KWallet::Wallet::Stream:
- case KWallet::Wallet::Map:
- break;
- default: // Unknown entry
- delete e;
- continue;
- }
-
- TQByteArray a;
- eStream >> a;
- e->setValue(a);
- e->setType(et);
- e->setKey(key);
- _entries[folder][key] = e;
- }
- }
-
- _open = true;
- return 0;
-}
-
-
-int Backend::sync(const TQByteArray& password) {
- if (!_open) {
- return -255; // not open yet
- }
-
- KSaveFile sf(_path, 0600);
- TQFile *qf = sf.file();
-
- if (!qf) {
- sf.abort();
- return -1; // error opening file
- }
-
- qf->writeBlock(KWMAGIC, KWMAGIC_LEN);
-
- // Write the version number
- TQByteArray version(4);
- version[0] = KWALLET_VERSION_MAJOR;
- version[1] = KWALLET_VERSION_MINOR;
- version[2] = KWALLET_CIPHER_BLOWFISH_CBC;
- version[3] = KWALLET_HASH_SHA1;
- qf->writeBlock(version, 4);
-
- // Holds the hashes we write out
- TQByteArray hashes;
- TQDataStream hashStream(hashes, IO_WriteOnly);
- KMD5 md5;
- hashStream << static_cast<TQ_UINT32>(_entries.count());
-
- // Holds decrypted data prior to encryption
- TQByteArray decrypted;
-
- // FIXME: we should estimate the amount of data we will write in each
- // buffer and resize them approximately in order to avoid extra
- // resizes.
-
- // populate decrypted
- TQDataStream dStream(decrypted, IO_WriteOnly);
- for (FolderMap::ConstIterator i = _entries.begin(); i != _entries.end(); ++i) {
- dStream << i.key();
- dStream << static_cast<TQ_UINT32>(i.data().count());
-
- md5.reset();
- md5.update(i.key().utf8());
- hashStream.writeRawBytes(reinterpret_cast<const char*>(&(md5.rawDigest()[0])), 16);
- hashStream << static_cast<TQ_UINT32>(i.data().count());
-
- for (EntryMap::ConstIterator j = i.data().begin(); j != i.data().end(); ++j) {
- dStream << j.key();
- dStream << static_cast<TQ_INT32>(j.data()->type());
- dStream << j.data()->value();
-
- md5.reset();
- md5.update(j.key().utf8());
- hashStream.writeRawBytes(reinterpret_cast<const char*>(&(md5.rawDigest()[0])), 16);
- }
- }
-
- qf->writeBlock(hashes, hashes.size());
-
- // calculate the hash of the file
- SHA1 sha;
- BlowFish _bf;
- CipherBlockChain bf(&_bf);
-
- sha.process(decrypted.data(), decrypted.size());
-
- // prepend and append the random data
- TQByteArray wholeFile;
- long blksz = bf.blockSize();
- long newsize = decrypted.size() +
- blksz + // encrypted block
- 4 + // file size
- 20; // size of the SHA hash
-
- int delta = (blksz - (newsize % blksz));
- newsize += delta;
- wholeFile.resize(newsize);
-
- TQByteArray randBlock;
- randBlock.resize(blksz+delta);
- if (getRandomBlock(randBlock) < 0) {
- sha.reset();
- decrypted.fill(0);
- sf.abort();
- return -3; // Fatal error: can't get random
- }
-
- for (int i = 0; i < blksz; i++) {
- wholeFile[i] = randBlock[i];
- }
-
- for (int i = 0; i < 4; i++) {
- wholeFile[(int)(i+blksz)] = (decrypted.size() >> 8*(3-i))&0xff;
- }
-
- for (unsigned int i = 0; i < decrypted.size(); i++) {
- wholeFile[(int)(i+blksz+4)] = decrypted[i];
- }
-
- for (int i = 0; i < delta; i++) {
- wholeFile[(int)(i+blksz+4+decrypted.size())] = randBlock[(int)(i+blksz)];
- }
-
- const char *hash = (const char *)sha.hash();
- for (int i = 0; i < 20; i++) {
- wholeFile[(int)(newsize - 20 + i)] = hash[i];
- }
-
- sha.reset();
- decrypted.fill(0);
-
- // hash the passphrase
- TQByteArray passhash;
- password2hash(password, passhash);
-
- // encrypt the data
- if (!bf.setKey(passhash.data(), passhash.size() * 8)) {
- passhash.fill(0);
- wholeFile.fill(0);
- sf.abort();
- return -2;
- }
-
- int rc = bf.encrypt(wholeFile.data(), wholeFile.size());
- if (rc < 0) {
- passhash.fill(0);
- wholeFile.fill(0);
- sf.abort();
- return -2; // encrypt error
- }
-
- passhash.fill(0); // passhash is UNUSABLE NOW
-
- // write the file
- qf->writeBlock(wholeFile, wholeFile.size());
- if (!sf.close()) {
- wholeFile.fill(0);
- sf.abort();
- return -4; // write error
- }
-
- wholeFile.fill(0);
-
-return 0;
-}
-
-
-int Backend::close(const TQByteArray& password) {
- int rc = sync(password);
- _open = false;
- if (rc != 0) {
- return rc;
- }
- return close();
-}
-
-
-const TQString& Backend::walletName() const {
- return _name;
-}
-
-
-bool Backend::isOpen() const {
- return _open;
-}
-
-
-TQStringList Backend::folderList() const {
- return _entries.keys();
-}
-
-
-TQStringList Backend::entryList() const {
- return _entries[_folder].keys();
-}
-
-
-Entry *Backend::readEntry(const TQString& key) {
-Entry *rc = 0L;
-
- if (_open && hasEntry(key)) {
- rc = _entries[_folder][key];
- }
-
-return rc;
-}
-
-
-TQPtrList<Entry> Backend::readEntryList(const TQString& key) {
- TQPtrList<Entry> rc;
-
- if (!_open) {
- return rc;
- }
-
- TQRegExp re(key, true, true);
-
- const EntryMap& map = _entries[_folder];
- for (EntryMap::ConstIterator i = map.begin(); i != map.end(); ++i) {
- if (re.exactMatch(i.key())) {
- rc.append(i.data());
- }
- }
- return rc;
-}
-
-
-bool Backend::createFolder(const TQString& f) {
- if (_entries.contains(f)) {
- return false;
- }
-
- _entries.insert(f, EntryMap());
-
- KMD5 folderMd5;
- folderMd5.update(f.utf8());
- _hashes.insert(MD5Digest(folderMd5.rawDigest()), TQValueList<MD5Digest>());
-
-return true;
-}
-
-
-int Backend::renameEntry(const TQString& oldName, const TQString& newName) {
-EntryMap& emap = _entries[_folder];
-EntryMap::Iterator oi = emap.find(oldName);
-EntryMap::Iterator ni = emap.find(newName);
-
- if (oi != emap.end() && ni == emap.end()) {
- Entry *e = oi.data();
- emap.remove(oi);
- emap[newName] = e;
-
- KMD5 folderMd5;
- folderMd5.update(_folder.utf8());
-
- HashMap::iterator i = _hashes.find(MD5Digest(folderMd5.rawDigest()));
- if (i != _hashes.end()) {
- KMD5 oldMd5, newMd5;
- oldMd5.update(oldName.utf8());
- newMd5.update(newName.utf8());
- i.data().remove(MD5Digest(oldMd5.rawDigest()));
- i.data().append(MD5Digest(newMd5.rawDigest()));
- }
- return 0;
- }
-
-return -1;
-}
-
-
-void Backend::writeEntry(Entry *e) {
- if (!_open)
- return;
-
- if (!hasEntry(e->key())) {
- _entries[_folder][e->key()] = new Entry;
- }
- _entries[_folder][e->key()]->copy(e);
-
- KMD5 folderMd5;
- folderMd5.update(_folder.utf8());
-
- HashMap::iterator i = _hashes.find(MD5Digest(folderMd5.rawDigest()));
- if (i != _hashes.end()) {
- KMD5 md5;
- md5.update(e->key().utf8());
- i.data().append(MD5Digest(md5.rawDigest()));
- }
-}
-
-
-bool Backend::hasEntry(const TQString& key) const {
- return _entries.contains(_folder) && _entries[_folder].contains(key);
-}
-
-
-bool Backend::removeEntry(const TQString& key) {
- if (!_open) {
- return false;
- }
-
- FolderMap::Iterator fi = _entries.find(_folder);
- EntryMap::Iterator ei = fi.data().find(key);
-
- if (fi != _entries.end() && ei != fi.data().end()) {
- delete ei.data();
- fi.data().remove(ei);
- KMD5 folderMd5;
- folderMd5.update(_folder.utf8());
-
- HashMap::iterator i = _hashes.find(MD5Digest(folderMd5.rawDigest()));
- if (i != _hashes.end()) {
- KMD5 md5;
- md5.update(key.utf8());
- i.data().remove(MD5Digest(md5.rawDigest()));
- }
- return true;
- }
-
-return false;
-}
-
-
-bool Backend::removeFolder(const TQString& f) {
- if (!_open) {
- return false;
- }
-
- FolderMap::Iterator fi = _entries.find(f);
-
- if (fi != _entries.end()) {
- if (_folder == f) {
- _folder = TQString::null;
- }
-
- for (EntryMap::Iterator ei = fi.data().begin(); ei != fi.data().end(); ++ei) {
- delete ei.data();
- }
-
- _entries.remove(fi);
-
- KMD5 folderMd5;
- folderMd5.update(f.utf8());
- _hashes.erase(MD5Digest(folderMd5.rawDigest()));
- return true;
- }
-
-return false;
-}
-
-
-bool Backend::folderDoesNotExist(const TQString& folder) const {
- KMD5 md5;
- md5.update(folder.utf8());
- return !_hashes.contains(MD5Digest(md5.rawDigest()));
-}
-
-
-bool Backend::entryDoesNotExist(const TQString& folder, const TQString& entry) const {
- KMD5 md5;
- md5.update(folder.utf8());
- HashMap::const_iterator i = _hashes.find(MD5Digest(md5.rawDigest()));
- if (i != _hashes.end()) {
- md5.reset();
- md5.update(entry.utf8());
- return !i.data().contains(MD5Digest(md5.rawDigest()));
- }
- return true;
-}
-
-