Git commit 58c491477506a1fe6adbd04412da565dba0f6268 by Valentin Rusu. Committed on 15/08/2015 at 22:02. Pushed by vrusu into branch 'master'. Starting to implement createCollection M +1 -0 src/runtime/ksecrets_store/CMakeLists.txt M +153 -24 src/runtime/ksecrets_store/ksecrets_crypt.cpp M +52 -18 src/runtime/ksecrets_store/ksecrets_data.cpp M +24 -9 src/runtime/ksecrets_store/ksecrets_data.h M +43 -2 src/runtime/ksecrets_store/ksecrets_file.cpp M +14 -1 src/runtime/ksecrets_store/ksecrets_file.h M +9 -3 src/runtime/ksecrets_store/ksecrets_store.cpp M +1 -0 src/runtime/ksecrets_store/ksecrets_store_p.h http://commits.kde.org/ksecrets/58c491477506a1fe6adbd04412da565dba0f6268 diff --git a/src/runtime/ksecrets_store/CMakeLists.txt b/src/runtime/ksecre= ts_store/CMakeLists.txt index 5805ce7..1236bf0 100644 --- a/src/runtime/ksecrets_store/CMakeLists.txt +++ b/src/runtime/ksecrets_store/CMakeLists.txt @@ -9,6 +9,7 @@ ecm_setup_version(${KF5_VERSION} VARIABLE_PREFIX KSECRETS_B= ACKEND PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5Sec= retsStoreConfigVersion.cmake") = set(ksecrets_store_SRC + ksecrets_data.cpp ksecrets_file.cpp ksecrets_crypt.cpp ksecrets_credentials.cpp diff --git a/src/runtime/ksecrets_store/ksecrets_crypt.cpp b/src/runtime/ks= ecrets_store/ksecrets_crypt.cpp index 0af3e1f..a5a32f8 100644 --- a/src/runtime/ksecrets_store/ksecrets_crypt.cpp +++ b/src/runtime/ksecrets_store/ksecrets_crypt.cpp @@ -20,6 +20,7 @@ */ = #include "defines.h" +#include "ksecrets_crypt.h" = #include #include @@ -51,7 +52,8 @@ int kss_init_gcry() gcry_error_t gcryerr; gcryerr =3D gcry_control(GCRYCTL_INIT_SECMEM, 32768, 0); if (gcryerr !=3D 0) { - syslog(KSS_LOG_ERR, "ksecrets_store: cannot get secure memory: %d"= , gcryerr); + syslog(KSS_LOG_ERR, "ksecrets_store: cannot get secure memory: %d", + gcryerr); return 0; } = @@ -60,59 +62,79 @@ int kss_init_gcry() return 1; } = -int kss_derive_keys(const char* salt, const char* password, char* encrypti= on_key, char* mac_key, size_t keySize) +int kss_derive_keys(const char* salt, const char* password, + char* encryption_key, char* mac_key, size_t keySize) { gpg_error_t gcryerr; = syslog(KSS_LOG_INFO, "kss_set_credentials: attempting keys generation"= ); if (0 =3D=3D password) { - syslog(KSS_LOG_INFO, "NULL password given. ksecrets will not be av= ailable."); + syslog(KSS_LOG_INFO, + "NULL password given. ksecrets will not be available."); return FALSE; } = /* generate both encryption and MAC key in one go */ char keys[2 * keySize]; - gcryerr =3D gcry_kdf_derive(password, strlen(password), GCRY_KDF_ITERS= ALTED_S2K, GCRY_MD_SHA512, salt, 8, KSECRETS_ITERATIONS, 2 * keySize, keys); + gcryerr + =3D gcry_kdf_derive(password, strlen(password), GCRY_KDF_ITERSALTE= D_S2K, + GCRY_MD_SHA512, salt, 8, KSECRETS_ITERATIONS, 2 * keySize, key= s); if (gcryerr) { - syslog(KSS_LOG_ERR, "key derivation failed: code 0x%0x: %s/%s", gc= ryerr, gcry_strsource(gcryerr), gcry_strerror(gcryerr)); + syslog(KSS_LOG_ERR, "key derivation failed: code 0x%0x: %s/%s", + gcryerr, gcry_strsource(gcryerr), gcry_strerror(gcryerr)); return FALSE; } = memcpy(encryption_key, keys, keySize); memcpy(mac_key, keys + keySize, keySize); - syslog(KSS_LOG_INFO, "successuflly generated ksecrets keys from user p= assword."); + syslog(KSS_LOG_INFO, + "successuflly generated ksecrets keys from user password."); = return TRUE; } = -int kss_store_keys(const char* encryption_key, const char* mac_key, size_t= keySize) +int kss_store_keys( + const char* encryption_key, const char* mac_key, size_t keySize) { key_serial_t ks; const char* key_name =3D get_keyname_encrypting(); - ks =3D add_key("user", key_name, encryption_key, keySize, KEY_SPEC_SES= SION_KEYRING); + ks =3D add_key( + "user", key_name, encryption_key, keySize, KEY_SPEC_SESSION_KEYRIN= G); if (-1 =3D=3D ks) { - syslog(KSS_LOG_ERR, "ksecrets: cannot store encryption key in kern= el keyring: errno=3D%d (%m)", errno); + syslog(KSS_LOG_ERR, "ksecrets: cannot store encryption key in kern= el " + "keyring: errno=3D%d (%m)", + errno); return FALSE; } - syslog(KSS_LOG_DEBUG, "ksecrets: encrpyting key now in kernel keyring = with id %d and desc %s", ks, key_name); + syslog(KSS_LOG_DEBUG, "ksecrets: encrpyting key now in kernel keyring " + "with id %d and desc %s", + ks, key_name); = key_name =3D get_keyname_mac(); - ks =3D add_key("user", key_name, mac_key, keySize, KEY_SPEC_SESSION_KE= YRING); + ks =3D add_key( + "user", key_name, mac_key, keySize, KEY_SPEC_SESSION_KEYRING); if (-1 =3D=3D ks) { - syslog(KSS_LOG_ERR, "ksecrets: cannot store mac key in kernel keyr= ing: errno=3D%d (%m)", errno); + syslog(KSS_LOG_ERR, + "ksecrets: cannot store mac key in kernel keyring: errno=3D%d = (%m)", + errno); return FALSE; } - syslog(KSS_LOG_DEBUG, "ksecrets: mac key now in kernel keyring with id= %d and desc %s", ks, key_name); + syslog(KSS_LOG_DEBUG, + "ksecrets: mac key now in kernel keyring with id %d and desc %s", = ks, + key_name); return TRUE; } = -int kss_set_credentials(const std::string &password, const char *salt) +int kss_set_credentials(const std::string& password, const char* salt) { - // FIXME this should be adjusted on platforms where kernel keyring is = not available and store the keys elsewhere + // FIXME this should be adjusted on platforms where kernel keyring is = not + // available and store the keys elsewhere char encryption_key[KSECRETS_KEYSIZE]; char mac_key[KSECRETS_KEYSIZE]; - auto res =3D kss_derive_keys(salt, password.c_str(), encryption_key, m= ac_key, KSECRETS_KEYSIZE); - if (res) return res; + auto res =3D kss_derive_keys( + salt, password.c_str(), encryption_key, mac_key, KSECRETS_KEYSIZE); + if (res) + return res; = return kss_store_keys(encryption_key, mac_key, KSECRETS_KEYSIZE); } @@ -120,26 +142,32 @@ int kss_set_credentials(const std::string &password, = const char *salt) int kss_keys_already_there() { key_serial_t key; - key =3D request_key("user", get_keyname_encrypting(), 0, KEY_SPEC_SESS= ION_KEYRING); + key =3D request_key( + "user", get_keyname_encrypting(), 0, KEY_SPEC_SESSION_KEYRING); if (-1 =3D=3D key) { - syslog(KSS_LOG_DEBUG, "request_key failed with errno %d (%m), so a= ssuming ksecrets not yet loaded", errno); + syslog(KSS_LOG_DEBUG, "request_key failed with errno %d (%m), so " + "assuming ksecrets not yet loaded", + errno); return FALSE; } syslog(KSS_LOG_DEBUG, "ksecrets: keys already in keyring"); return TRUE; } = -long kss_read_key(const char *keyName, char* buffer, size_t bufferSize) +long kss_read_key(const char* keyName, char* buffer, size_t bufferSize) { key_serial_t key; key =3D request_key("user", keyName, 0, KEY_SPEC_SESSION_KEYRING); if (-1 =3D=3D key) { - syslog(KSS_LOG_DEBUG, "request_key failed with errno %d (%m) when = reading MAC key %s", errno, keyName); + syslog(KSS_LOG_DEBUG, + "request_key failed with errno %d (%m) when reading MAC key %s= ", + errno, keyName); return -1; } auto bytes =3D keyctl_read(key, buffer, bufferSize); if (bytes =3D=3D -1) { - syslog(KSS_LOG_ERR, "error reading key %s contents from the keyrin= g", keyName); + syslog(KSS_LOG_ERR, "error reading key %s contents from the keyrin= g", + keyName); return -1; } if ((size_t)bytes > bufferSize) { @@ -148,13 +176,114 @@ long kss_read_key(const char *keyName, char* buffer,= size_t bufferSize) return 0; // key contents correctly transffered into the buffer } = -long kss_read_mac_key(char *buffer, size_t bufferSize) +long kss_read_mac_key(char* buffer, size_t bufferSize) { return kss_read_key(get_keyname_mac(), buffer, bufferSize); } = -long kss_read_encrypting_key(char *buffer, size_t bufferSize) +long kss_read_encrypting_key(char* buffer, size_t bufferSize) { return kss_read_key(get_keyname_encrypting(), buffer, bufferSize); } = +#define ERRNO(cryres) gcry_err_code_to_errno(gcry_err_code(cryres)) + +long kss_cipher_setup(gcry_cipher_hd_t* hd, const void* iv, size_t liv) +{ + // FIXME perhaps all this initialization stuff could only be done once, + // when password is setup + auto cryres + =3D gcry_cipher_open(hd, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CB= C, 0); + if (cryres) { + syslog(KSS_LOG_ERR, "ksecrets: gcry_cipher_open returned error %d", + cryres); + return ERRNO(cryres); + } + cryres =3D gcry_cipher_setiv(*hd, iv, liv); + if (cryres) { + syslog(KSS_LOG_ERR, "ksecrets: gcry_cipher_setif returned error %d= ", + cryres); + return ERRNO(cryres); + } + char encryptingKey[KSECRETS_KEYSIZE]; + auto keyres =3D kss_read_encrypting_key( + encryptingKey, sizeof(encryptingKey) / sizeof(encryptingKey[0])); + if (!keyres) { + syslog( + KSS_LOG_ERR, "ksecrets: encrypting key not found in the keyrin= g"); + return keyres; + } + cryres =3D gcry_cipher_setkey( + *hd, encryptingKey, sizeof(encryptingKey) / sizeof(encryptingKey[0= ])); + if (cryres) { + syslog( + KSS_LOG_ERR, "ksecrets: gcry_cipher_setkey returned %d", cryre= s); + return ERRNO(cryres); + } + return 0; +} + +long kss_encrypt_buffer(unsigned char* out, size_t lout, const void* iv, + size_t liv, const unsigned char* in, size_t lin) +{ + gcry_cipher_hd_t hd; + auto cryres =3D kss_cipher_setup(&hd, iv, liv); + if (cryres) + return cryres; // error already logged + cryres =3D gcry_cipher_encrypt(hd, out, lout, in, lin); + if (cryres) { + syslog(KSS_LOG_ERR, "ksecrets: gcry_cipher_encrypt returned %ld", + cryres); + return ERRNO(cryres); + } + return 0; +} + +long kss_decrypt_buffer(unsigned char* out, size_t lout, const void* iv, + size_t liv, const unsigned char* in, size_t lin) +{ + gcry_cipher_hd_t hd; + auto cryres =3D kss_cipher_setup(&hd, iv, liv); + if (cryres) + return cryres; // error already logged + cryres =3D gcry_cipher_decrypt(hd, out, lout, in, lin); + if (cryres) { + syslog(KSS_LOG_ERR, "ksecrets: gcry_cipher_decrypt returned %ld", + cryres); + return ERRNO(cryres); + } + return 0; +} + +bool CryptBuffer::resize(size_t rlen) +{ + if (rlen <=3D len_) { + return false; // if less bytes, we cannot copy; if same, why resiz= e? + } + CryptBuffer oldBuffer(std::move(*this)); + len_ =3D (rlen / cipherBlockLen_ + 1) * cipherBlockLen_; + data_ =3D new unsigned char[len_]; + if (data_ && oldBuffer.len_) { + memmove(data_, oldBuffer.data_, oldBuffer.len_); + return true; + } + return false; +} + +void CryptBuffer::empty() +{ + delete[] data_, data_ =3D nullptr; + len_ =3D 0; +} + +bool CryptBuffer::allocate(size_t rlen) +{ + empty(); + data_ =3D new unsigned char[rlen]; + if (data_ =3D=3D nullptr) { + return false; + } + len_ =3D rlen; + return true; +} +// vim: tw=3D220:ts=3D4 diff --git a/src/runtime/ksecrets_store/ksecrets_data.cpp b/src/runtime/kse= crets_store/ksecrets_data.cpp index 4998fcd..61a8f64 100644 --- a/src/runtime/ksecrets_store/ksecrets_data.cpp +++ b/src/runtime/ksecrets_store/ksecrets_data.cpp @@ -23,32 +23,66 @@ = #include = +long kss_encrypt_buffer(unsigned char* out, size_t lout, const void* iv, + size_t liv, const unsigned char* in, size_t lin); +long kss_decrypt_buffer(unsigned char* out, size_t lout, const void* iv, + size_t liv, const unsigned char* in, size_t lin); +char* kss_alloc_crypt_buffer(size_t rlen); + SecretsEntity::SecretsEntity() - : size_(0) - , state_(Empty) - , encrypted_(nullptr) - , unencrypted_(nullptr) + : state_(State::Empty) { } -SecretsEntity::~SecretsEntity() + +SecretsEntity::~SecretsEntity() {} + +// TODO refactor this when encrypting plugins will be put in place +// the KSecretsFile should place the IV in the plugin structure instead of +// this class +const char* iv =3D nullptr; +size_t liv =3D KSecretsFile::IV_SIZE; + +bool SecretsEntity::read(KSecretsFile& file) { - if (encrypted_) { - ::free(encrypted_); - encrypted_ =3D nullptr; + if (iv =3D=3D nullptr) { + iv =3D file.iv(); } - if (unencrypted_) { - ::free(unencrypted_); - unencrypted_ =3D nullptr; + + size_t s; + if (!file.read(s)) { + return false; } -} -bool SecretsEntity::read(KSecretsFile& file) -{ - if (!file.read(size_)) { + if (!encrypted_.allocate(s)) { return false; } = - encrypted_ =3D ::malloc(res); - if (encrypted_ =3D=3D nullptr) { + return file.read(encrypted_.data_, + encrypted_.len_); // beware not to specify encrypted.size_ here +} + +bool SecretsEntity::decrypt() +{ + if (isEmpty()) return false; - } + if (unencrypted_.len_ > 0) + return true; // already decrpyted + if (encrypted_.len_ =3D=3D 0) + return false; // what to decrypt? + unencrypted_.allocate(encrypted_.len_); + auto dres =3D kss_decrypt_buffer(unencrypted_.data_, unencrypted_.len_= , iv, + liv, encrypted_.data_, encrypted_.len_); + return dres =3D=3D 0; +} + +bool SecretsEntity::encrypt() +{ + // TODO + return false; +} + +bool SecretsEntity::write(KSecretsFile &) const +{ + // TODO + return false; } +// vim: tw=3D220:ts=3D4 diff --git a/src/runtime/ksecrets_store/ksecrets_data.h b/src/runtime/ksecr= ets_store/ksecrets_data.h index e5b8964..dca51f7 100644 --- a/src/runtime/ksecrets_store/ksecrets_data.h +++ b/src/runtime/ksecrets_store/ksecrets_data.h @@ -21,33 +21,48 @@ #ifndef KSECRETS_DATA_H #define KSECRETS_DATA_H = +#include "ksecrets_crypt.h" + #include #include = class KSecretsFile; = +/** + * @brief Elementary secret element + * + * TODO this class uses routines from ksecrets_crypt.cpp file to handle + * encrypting and decrypting of the files. It would be better to define so= me + * plugin architecture, allowing users specify different encryption method= s. + */ struct SecretsEntity { SecretsEntity(); SecretsEntity(const SecretsEntity&) =3D delete; SecretsEntity(SecretsEntity&&) =3D delete; virtual ~SecretsEntity(); = - enum class State: std::uint8_t { + enum class State : std::uint8_t { Empty =3D 0, Encrypted =3D 0x01, Decrypted =3D 0x02 }; = - size_t size_; - State state_; - char* encrypted_; - char* unencrypted_; + bool isEmpty() const noexcept { return state_ =3D=3D State::Empty; } + bool isDecrypted() const noexcept + { + return (static_cast(state_) + & static_cast(State::Decrypted)) !=3D 0; + } = - bool decrypt() noexcept; - bool encrypt() noexcept; + virtual bool decrypt() noexcept; + virtual bool encrypt() noexcept; = - bool read(KSecretsFile&) noexcept; - bool write(KSecretsFile&) const noexcept; + virtual bool read(KSecretsFile&) noexcept; + virtual bool write(KSecretsFile&) const noexcept; + + State state_; + CryptBuffer encrypted_; + CryptBuffer unencrypted_; }; = struct SecretsCollection : public SecretsEntity { diff --git a/src/runtime/ksecrets_store/ksecrets_file.cpp b/src/runtime/kse= crets_store/ksecrets_file.cpp index 7d91de3..c3dc275 100644 --- a/src/runtime/ksecrets_store/ksecrets_file.cpp +++ b/src/runtime/ksecrets_store/ksecrets_file.cpp @@ -37,6 +37,7 @@ KSecretsFile::KSecretsFile() : file_(-1) , locked_(false) , empty_(true) + , eof_(false) { memset(&fileHead_, 0, sizeof(fileHead_)); } @@ -105,6 +106,7 @@ bool KSecretsFile::readHeader() if (eoftest =3D=3D 0) { // we are at EOF already, so file is empty empty_ =3D true; + eof_ =3D true; } else { if (-1 =3D=3D lseek(file_, -bytes, SEEK_CUR)) { @@ -139,8 +141,47 @@ int KSecretsFile::checkMAC() const return -1; } = -KSecretsFile::DirCollectionResult KSecretsFile::dirCollections() { - return DirCollectionResult(); +bool KSecretsFile::read(size_t& s) { return read(&s, sizeof(s)); } + +bool KSecretsFile::read(void* buf, size_t len) +{ + if (eof_) return false; + auto rres =3D ::read(file_, buf, len); + if (rres < 0) + return setFailState(errno); + if (static_cast(rres) < len) + return setEOF(); // are we @ EOF? + return true; +} + +KSecretsFile::DirCollectionResult KSecretsFile::dirCollections() +{ + DirCollectionResult res; + res.first =3D false; + res.second =3D nullptr; + + if (empty_) { + return res; + } + + readDirectory(); + decryptEntity(directory_); + + if (directory_.isDecrypted()) { + res.first =3D true; + res.second =3D &directory_; + } + + return res; } = +bool KSecretsFile::readDirectory() +{ + if (empty_) + return false; // file is empty, don't event attempt read + return directory_.read(*this); +} + +bool KSecretsFile::decryptEntity(SecretsEntity& entity) { return entity.de= crypt(); } + // vim: tw=3D220:ts=3D4 diff --git a/src/runtime/ksecrets_store/ksecrets_file.h b/src/runtime/ksecr= ets_store/ksecrets_file.h index a929cca..5ed8c79 100644 --- a/src/runtime/ksecrets_store/ksecrets_file.h +++ b/src/runtime/ksecrets_store/ksecrets_file.h @@ -26,6 +26,9 @@ #include #include = +/** + * @brief This is the secrets file format handling class + */ class KSecretsFile { public: KSecretsFile(); @@ -46,6 +49,7 @@ public: bool readHeader() noexcept; bool checkMagic() noexcept; const char* salt() const noexcept { return fileHead_.salt_; } + const char* iv() const noexcept { return fileHead_.iv_; } int checkMAC() const noexcept; bool read(void* buf, size_t count); bool read(size_t&); @@ -54,11 +58,18 @@ public: DirCollectionResult dirCollections() noexcept; = private: - bool setFailState(int err, bool retval =3D false) + bool setFailState(int err, bool retval =3D false) noexcept { errno_ =3D err; return retval; // wo do like this so this function could end other= methods with an elegant return setFailState(errno); } + bool setEOF() noexcept { + eof_ =3D true; + return false; // this work the same as setFailState + } + bool readDirectory() noexcept; + bool decryptEntity(SecretsEntity&) noexcept; + using Entities =3D std::list; = std::string filePath_; @@ -69,7 +80,9 @@ private: bool empty_; char fileMAC_[64]; Entities entities_; + CollectionDirectory directory_; int errno_; + bool eof_; }; = #endif diff --git a/src/runtime/ksecrets_store/ksecrets_store.cpp b/src/runtime/ks= ecrets_store/ksecrets_store.cpp index 9e3967b..8e4bf00 100644 --- a/src/runtime/ksecrets_store/ksecrets_store.cpp +++ b/src/runtime/ksecrets_store/ksecrets_store.cpp @@ -165,10 +165,16 @@ KSecretsStore::DirCollectionsResult KSecretsStorePriv= ate::dirCollections() return res; } = -KSecretsStore::CreateCollectionResult KSecretsStore::createCollection(cons= t char*) noexcept +KSecretsStore::CreateCollectionResult KSecretsStore::createCollection(cons= t char* collName) noexcept { - // TODO - return CreateCollectionResult(); + return d->createCollection(collName); +} + +KSecretsStore::CreateCollectionResult KSecretsStorePrivate::createCollecti= on(const std::string &collName) noexcept +{ + KSecretsStore::CreateCollectionResult res; + + return res; } = KSecretsStore::ReadCollectionResult KSecretsStore::readCollection(const ch= ar*) const noexcept diff --git a/src/runtime/ksecrets_store/ksecrets_store_p.h b/src/runtime/ks= ecrets_store/ksecrets_store_p.h index 8dee77c..c8f8aaa 100644 --- a/src/runtime/ksecrets_store/ksecrets_store_p.h +++ b/src/runtime/ksecrets_store/ksecrets_store_p.h @@ -64,6 +64,7 @@ public: KSecretsStore::SetupResult open(bool) noexcept; int createFile(const std::string&) noexcept; const char* salt() const noexcept; + KSecretsStore::CreateCollectionResult createCollection(const std::stri= ng&) noexcept; KSecretsStore::DirCollectionsResult dirCollections() noexcept; = template S setStoreStatus(S s) noexcept