[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [ksecrets] /: Refactoring after the code review
From:       Valentin Rusu <kde () rusu ! info>
Date:       2015-08-12 11:26:27
Message-ID: E1ZPUAt-0007H7-L1 () scm ! kde ! org
[Download RAW message or body]

Git commit a250017bb30db279b5e9a24d3843bbd91005fd61 by Valentin Rusu.
Committed on 12/08/2015 at 11:25.
Pushed by vrusu into branch 'master'.

Refactoring after the code review

Also added and implemented the setup method. More tests should now be added.

M  +5    -2    autotests/api/CMakeLists.txt
M  +2    -2    autotests/api/ksecretsservice-test.cpp
M  +8    -4    autotests/ksecrets_store/ksecrets_store_test.cpp
M  +1    -1    src/api/ksecrets/ksecretsitem.cpp
M  +0    -1    src/runtime/CMakeLists.txt
M  +4    -4    src/runtime/ksecrets-crypt/config.cpp
M  +5    -4    src/runtime/ksecrets_store/CMakeLists.txt
R  +24   -116  src/runtime/ksecrets_store/ksecrets_credentials.c [from: \
src/runtime/ksecrets_store/ksecrets_crypt.c - 053% similarity] R  +7    -7    \
src/runtime/ksecrets_store/ksecrets_credentials.h [from: \
src/runtime/ksecrets_store/ksecrets_crypt.h - 077% similarity] M  +102  -49   \
src/runtime/ksecrets_store/ksecrets_store.cpp M  +50   -18   \
src/runtime/ksecrets_store/ksecrets_store.h M  +19   -8    \
src/runtime/ksecrets_store/ksecrets_store_p.h M  +4    -4    \
src/runtime/pam-ksecrets/CMakeLists.txt M  +9    -3    src/runtime/pam-ksecrets/pam_ksecrets.c

http://commits.kde.org/ksecrets/a250017bb30db279b5e9a24d3843bbd91005fd61

diff --git a/autotests/api/CMakeLists.txt b/autotests/api/CMakeLists.txt
index 071cb72..6dc2343 100644
--- a/autotests/api/CMakeLists.txt
+++ b/autotests/api/CMakeLists.txt
@@ -6,7 +6,10 @@ find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Test)
 
 find_package(LibGcrypt 1.6.0 REQUIRED)
 
-include_directories(${CMAKE_SOURCE_DIR}/src/runtime/ksecrets-crypt)
+include_directories(
+   ${CMAKE_SOURCE_DIR}/src/runtime/ksecrets_store
+   ${CMAKE_BINARY_DIR}/src/runtime/ksecrets_store
+   )
 
 if(NOT Qt5Test_FOUND)
     message(STATUS "Qt5Test not found, autotests will not be built.")
@@ -17,6 +20,6 @@ find_package(KF5 REQUIRED CoreAddons)
 
 ecm_add_test(
     ksecretsservice-test.cpp
-    LINK_LIBRARIES KF5::Secrets Qt5::Test KF5::Secrets KF5::CoreAddons ksecrets_crypt \
${LIBGCRYPT_LIBRARIES} keyutils +    LINK_LIBRARIES KF5::Secrets Qt5::Test KF5::Secrets \
KF5::CoreAddons ksecrets_store ${LIBGCRYPT_LIBRARIES} keyutils  )
 
diff --git a/autotests/api/ksecretsservice-test.cpp b/autotests/api/ksecretsservice-test.cpp
index efa0788..c16c424 100644
--- a/autotests/api/ksecretsservice-test.cpp
+++ b/autotests/api/ksecretsservice-test.cpp
@@ -24,7 +24,7 @@
 #include <ksecretscollection.h>
 #include <ksecretsvalue.h>
 #include <ksecretsitem.h>
-#include <ksecrets-crypt.h>
+#include <ksecrets_credentials.h>
 
 #include <QtCore/QtGlobal>
 #include <QtTest/QTest>
@@ -47,7 +47,7 @@ KSecretServiceTest::KSecretServiceTest(QObject* parent)
 }
 
 KSecrets::CollectionPtr collection;
-extern KSharedConfig::Ptr sharedConfig; // defined in the ksecrets_crypt library
+KSharedConfig::Ptr sharedConfig;
 
 void KSecretServiceTest::initTestCase()
 {
diff --git a/autotests/ksecrets_store/ksecrets_store_test.cpp \
b/autotests/ksecrets_store/ksecrets_store_test.cpp index ba70646..353f10a 100644
--- a/autotests/ksecrets_store/ksecrets_store_test.cpp
+++ b/autotests/ksecrets_store/ksecrets_store_test.cpp
@@ -19,9 +19,9 @@
     Boston, MA 02110-1301, USA.
 */
 
-#include "ksecrets_backend_test.h"
+#include "ksecrets_store_test.h"
 
-#include <ksecrets_backend.h>
+#include <ksecrets_store.h>
 #include <QtTest/QtTest>
 #include <QtCore/QDir>
 
@@ -36,6 +36,9 @@ KSecretServiceStoreTest::KSecretServiceStoreTest(QObject* parent)
 
 void KSecretServiceStoreTest::initTestCase()
 {
+    // create a test file here and performe the real open test below
+    KSecretsStore backend;
+    auto setupfut = backend.setup(test_file_path, "test");
 }
 
 void KSecretServiceStoreTest::cleanupTestCase()
@@ -46,7 +49,8 @@ void KSecretServiceStoreTest::cleanupTestCase()
 void KSecretServiceStoreTest::testOpen()
 {
     KSecretsStore backend;
-    auto openfut = backend.open(test_file_path, true);
+    auto setupfut = backend.setup(test_file_path, "test");
+    auto openfut = backend.open(true);
     auto openres = openfut.get();
-    QVERIFY(openres.status_ == KSecretsStore::OpenStatus::Good);
+    QVERIFY(openres.status_ == KSecretsStore::StoreStatus::Good);
 }
diff --git a/src/api/ksecrets/ksecretsitem.cpp b/src/api/ksecrets/ksecretsitem.cpp
index e8e039f..8eda750 100644
--- a/src/api/ksecrets/ksecretsitem.cpp
+++ b/src/api/ksecrets/ksecretsitem.cpp
@@ -32,7 +32,7 @@ SecretItem::SecretItem()
 }
 
 SecretItem::SecretItem(const SecretItem& that)
-    d(that.d)
+    : d(that.d)
 {
 }
 
diff --git a/src/runtime/CMakeLists.txt b/src/runtime/CMakeLists.txt
index d26d1bb..01eb2e5 100644
--- a/src/runtime/CMakeLists.txt
+++ b/src/runtime/CMakeLists.txt
@@ -1,4 +1,3 @@
-add_subdirectory(ksecrets-crypt)
 add_subdirectory(pam-ksecrets)
 add_subdirectory(ksecrets_store)
 # add_subdirectory(ksecrets)
diff --git a/src/runtime/ksecrets-crypt/config.cpp b/src/runtime/ksecrets-crypt/config.cpp
index 771254f..a711c6c 100644
--- a/src/runtime/ksecrets-crypt/config.cpp
+++ b/src/runtime/ksecrets-crypt/config.cpp
@@ -19,7 +19,7 @@
     Boston, MA 02110-1301, USA.
 */
 
-#include <ksecrets_backend.h>
+#include <ksecrets_store.h>
 
 #include <ksharedconfig.h>
 #include <kconfiggroup.h>
@@ -80,9 +80,9 @@ const char* get_keyname_mac()
 bool get_salt(const char* path, char* buf, size_t len)
 {
     bool res= false;
-    KSecretsBackend backend;
-    auto openfut = backend.open(path);
-    if (openfut.get().status_ == KSecretsBackend::OpenStatus::Good) {
+    KSecretsStore store;
+    auto openfut = store.open(path);
+    if (openfut.get().status_ == KSecretsStore::OpenStatus::Good) {
 
     }
     return res;
diff --git a/src/runtime/ksecrets_store/CMakeLists.txt \
b/src/runtime/ksecrets_store/CMakeLists.txt index dcee020..82b5ad1 100644
--- a/src/runtime/ksecrets_store/CMakeLists.txt
+++ b/src/runtime/ksecrets_store/CMakeLists.txt
@@ -9,14 +9,15 @@ ecm_setup_version(${KF5_VERSION} VARIABLE_PREFIX KSECRETS_BACKEND
                   PACKAGE_VERSION_FILE \
"${CMAKE_CURRENT_BINARY_DIR}/KF5SecretsStoreConfigVersion.cmake")  
 set(ksecrets_store_SRC
-   ksecrets_crypt.c
-   ksecrets_store.cpp)
+    ksecrets_credentials.c
+    ksecrets_store.cpp)
 
 add_library(ksecrets_store SHARED ${ksecrets_store_SRC})
 generate_export_header(ksecrets_store BASE_NAME ksecrets_store)
 target_link_libraries(ksecrets_store
-   pthread
-   ${LIBGCRYPT_LIBRARIES})
+    pthread
+    keyutils
+    ${LIBGCRYPT_LIBRARIES})
 target_compile_features(ksecrets_store PRIVATE cxx_range_for)
 set_target_properties(ksecrets_store PROPERTIES PREFIX "")
 
diff --git a/src/runtime/ksecrets_store/ksecrets_crypt.c \
b/src/runtime/ksecrets_store/ksecrets_credentials.c similarity index 53%
rename from src/runtime/ksecrets_store/ksecrets_crypt.c
rename to src/runtime/ksecrets_store/ksecrets_credentials.c
index 959765f..155796a 100644
--- a/src/runtime/ksecrets_store/ksecrets_crypt.c
+++ b/src/runtime/ksecrets_store/ksecrets_credentials.c
@@ -17,7 +17,7 @@
  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  * Boston, MA 02110-1301, USA.
  */
-#include "ksecrets_crypt.h"
+#include "ksecrets_credentials.h"
 
 #include <unistd.h>
 #include <errno.h>
@@ -39,114 +39,37 @@
 #define KSS_LOG_INFO (LOG_AUTH | LOG_INFO)
 #define KSS_LOG_ERR (LOG_AUTH | LOG_ERR)
 
-#define KSECRETS_SALTSIZE 56
-#define KSECRETS_KEYSIZE 256
 #define KSECRETS_ITERATIONS 50000
 
 /* these functions are implemented in config.cpp next to this file */
 extern const char* prepare_secret_file_location(const char*);
 extern const char* get_keyname_encrypting();
 extern const char* get_keyname_mac();
-extern bool get_salt(const char*, char*, size_t);
 
-bool kss_init_gcry()
+#define false 0
+#define true 1
+
+int kss_init_gcry()
 {
-    syslog(KSS_LOG_DEBUG, "setting-up grypt library");
+    syslog(KSS_LOG_DEBUG, "ksecrets: setting-up grypt library");
     if (!gcry_check_version(GCRYPT_REQUIRED_VERSION)) {
-        syslog(KSS_LOG_ERR, "kwalletd: libcrypt version is too old");
-        return false;
+        syslog(KSS_LOG_ERR, "ksecrets_store: libcrypt version is too old");
+        return 0;
     }
 
     gcry_error_t gcryerr;
     gcryerr = gcry_control(GCRYCTL_INIT_SECMEM, 32768, 0);
     if (gcryerr != 0) {
-        syslog(KSS_LOG_ERR, "ksecrets: cannot get secure memory: %d", gcryerr);
-        return false;
+        syslog(KSS_LOG_ERR, "ksecrets_store: cannot get secure memory: %d", gcryerr);
+        return 0;
     }
 
     gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
     syslog(KSS_LOG_DEBUG, "gcrypt library now set-up");
-    return true;
+    return 1;
 }
 
-/**
- * This function reads the crypting salt from the main ksecrets data file
- * If the file is not present, then it is created. This case should happen
- * when user opens the session for the first time. That is possible because
- * ksecrets file format is SALT-IV-CIPHERTEXT-MAC. So in our case we'll create
- * file containing the SALT. It'll be extended by the ksecrets daemon upon
- * first usage.
- *
- * NOTE This function has code from pam-kwallet
- */
-bool kss_get_salt(const char* username, char** salt)
-{
-    if (0 == username) {
-        syslog(KSS_LOG_ERR, "no username given, salt could not been retrieved");
-        return false;
-    }
-
-    struct passwd* user_info;
-    user_info = getpwnam(username);
-    if (!user_info) {
-        syslog(KSS_LOG_ERR, "pam_kwallet: Couldn't get user %s info", username);
-        return false;
-    }
-
-    struct stat info;
-    if (stat(user_info->pw_dir, &info) != 0) {
-        syslog(KSS_LOG_ERR, "pam_kwallet: Cannot stat user directory");
-        return false;
-    }
-
-    const char* path = prepare_secret_file_location(user_info->pw_dir);
-    if (NULL == path) {
-        syslog(KSS_LOG_ERR, "pam_kwallet: Cannot prepare secret file location");
-        return false;
-    }
-
-    *salt = NULL;
-    if (stat(path, &info) != 0 || info.st_size == 0) {
-        unlink(path); /* in case the file already exists and it has size of 0 */
-
-        *salt = gcry_random_bytes(KSECRETS_SALTSIZE, GCRY_STRONG_RANDOM);
-        FILE* fd = fopen(path, "w");
-
-        /* If the file can't be created */
-        if (fd == NULL) {
-            syslog(KSS_LOG_ERR, "Couldn't open file: %s because: %d (%m)", path, errno);
-            return false;
-        }
-
-        fwrite(*salt, KSECRETS_SALTSIZE, 1, fd);
-        fclose(fd);
-
-        if (chown(path, user_info->pw_uid, user_info->pw_gid) == -1) {
-            syslog(KSS_LOG_ERR, "Couldn't change ownership of the created salt file");
-            return false;
-        }
-        syslog(KSS_LOG_INFO, "ksecrets: created secrets file path : %s", path);
-    }
-    else {
-        FILE* fd = fopen(path, "r");
-        if (fd == NULL) {
-            syslog(KSS_LOG_ERR, "Couldn't open file: %s because: %d (%m)", path, errno);
-            return 1;
-        }
-        *salt = (char*)malloc(sizeof(char) * KSECRETS_SALTSIZE);
-        memset(*salt, '\0', KSECRETS_SALTSIZE);
-        fread(*salt, KSECRETS_SALTSIZE, 1, fd);
-        fclose(fd);
-        syslog(KSS_LOG_INFO, "ksecrets: read salt from secrets file : %s", path);
-    }
-    if (salt == NULL) {
-        syslog(KSS_LOG_ERR, "ksecrets: Couldn't create or read the salt file");
-        return false;
-    }
-    return true;
-}
-
-bool kss_derive_keys(const char* user_name, const char* password, char* encryption_key, char* \
mac_key) +int kss_derive_keys(const char* salt, const char* password, char* encryption_key, \
char* mac_key, size_t keySize)  {
     gpg_error_t gcryerr;
 
@@ -156,34 +79,26 @@ bool kss_derive_keys(const char* user_name, const char* password, char* \
encrypti  return false;
     }
 
-    if (!kss_init_gcry())
-        return false;
-
-    char* salt;
-    salt = 0;
-    if (!kss_get_salt(user_name, &salt))
-        return false;
-
     /* generate both encryption and MAC key in one go */
-    char keys[2 * KSECRETS_KEYSIZE];
-    gcryerr = gcry_kdf_derive(password, strlen(password), GCRY_KDF_ITERSALTED_S2K, \
GCRY_MD_SHA512, salt, 8, KSECRETS_ITERATIONS, 2 * KSECRETS_KEYSIZE, keys); +    char keys[2 * \
keySize]; +    gcryerr = gcry_kdf_derive(password, strlen(password), GCRY_KDF_ITERSALTED_S2K, \
GCRY_MD_SHA512, salt, 8, KSECRETS_ITERATIONS, 2 * keySize, keys);  if (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, KSECRETS_KEYSIZE);
-    memcpy(mac_key, keys + KSECRETS_KEYSIZE, KSECRETS_KEYSIZE);
+    memcpy(encryption_key, keys, keySize);
+    memcpy(mac_key, keys + keySize, keySize);
     syslog(KSS_LOG_INFO, "successuflly generated ksecrets keys from user password.");
 
     return true;
 }
 
-bool kss_store_keys(const char* encryption_key, const char* mac_key)
+int kss_store_keys(const char* encryption_key, const char* mac_key, size_t keySize)
 {
     key_serial_t ks;
     const char* key_name = get_keyname_encrypting();
-    ks = add_key("user", key_name, encryption_key, KSECRETS_KEYSIZE, \
KEY_SPEC_SESSION_KEYRING); +    ks = add_key("user", key_name, encryption_key, keySize, \
KEY_SPEC_SESSION_KEYRING);  if (-1 == ks) {
         syslog(KSS_LOG_ERR, "ksecrets: cannot store encryption key in kernel keyring: errno=%d \
(%m)", errno);  return false;
@@ -191,7 +106,7 @@ bool kss_store_keys(const char* encryption_key, const char* mac_key)
     syslog(KSS_LOG_DEBUG, "ksecrets: encrpyting key now in kernel keyring with id %d and desc \
%s", ks, key_name);  
     key_name = get_keyname_mac();
-    ks = add_key("user", key_name, mac_key, KSECRETS_KEYSIZE, KEY_SPEC_SESSION_KEYRING);
+    ks = add_key("user", key_name, mac_key, keySize, KEY_SPEC_SESSION_KEYRING);
     if (-1 == ks) {
         syslog(KSS_LOG_ERR, "ksecrets: cannot store mac key in kernel keyring: errno=%d (%m)", \
errno);  return false;
@@ -200,7 +115,7 @@ bool kss_store_keys(const char* encryption_key, const char* mac_key)
     return true;
 }
 
-bool kss_keys_already_there()
+int kss_keys_already_there()
 {
     key_serial_t key;
     key = request_key("user", get_keyname_encrypting(), 0, KEY_SPEC_SESSION_KEYRING);
@@ -212,24 +127,17 @@ bool kss_keys_already_there()
     return true;
 }
 
-bool kss_set_credentials(const char* user_name, const char* password)
+int kss_set_credentials(const char* user_name, const char* password)
 {
     syslog(KSS_LOG_DEBUG, "kss_set_credentials for %s", user_name);
     if (kss_keys_already_there())
         return true;
 
-    char encryption_key[KSECRETS_KEYSIZE];
-    char mac_key[KSECRETS_KEYSIZE];
-    if (!kss_derive_keys(user_name, password, encryption_key, mac_key))
-        return false;
-
-    if (!kss_store_keys(encryption_key, mac_key))
-        return false;
 
     return true;
 }
 
-bool kss_delete_credentials()
+int kss_delete_credentials()
 {
     syslog(KSS_LOG_INFO, "kss_delete_credentials");
     key_serial_t key;
@@ -257,14 +165,14 @@ bool kss_delete_credentials()
     return true;
 }
 
-bool kss_can_change_password()
+int kss_can_change_password()
 {
     /* nothing to do for the moment */
     syslog(KSS_LOG_INFO, "kss_can_change_password");
     return true;
 }
 
-bool kss_change_password(const char* new_password)
+int kss_change_password(const char* new_password)
 {
     syslog(LOG_INFO, "kss_change_password");
     return true;
diff --git a/src/runtime/ksecrets_store/ksecrets_crypt.h \
b/src/runtime/ksecrets_store/ksecrets_credentials.h similarity index 77%
rename from src/runtime/ksecrets_store/ksecrets_crypt.h
rename to src/runtime/ksecrets_store/ksecrets_credentials.h
index 8e743d8..0242a24 100644
--- a/src/runtime/ksecrets_store/ksecrets_crypt.h
+++ b/src/runtime/ksecrets_store/ksecrets_credentials.h
@@ -18,22 +18,22 @@
     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     Boston, MA 02110-1301, USA.
 */
-#ifndef KSECRETSCRYPT_H
-#define KSECRETSCRYPT_H
+#ifndef KSECRETS_CREDENTIALS_H
+#define KSECRETS_CREDENTIALS_H
 
-#include <stdbool.h>
+#include <ksecrets_store_export.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-bool kss_set_credentials(const char* user_name, const char* password);
+int kss_set_credentials(const char* user_name, const char* password);
 
-bool kss_delete_credentials();
+int kss_delete_credentials();
 
-bool kss_can_change_password();
+int kss_can_change_password();
 
-bool kss_change_password(const char* newPassword);
+int kss_change_password(const char* newPassword);
 
 #ifdef __cplusplus
 }
diff --git a/src/runtime/ksecrets_store/ksecrets_store.cpp \
b/src/runtime/ksecrets_store/ksecrets_store.cpp index 775f896..df43001 100644
--- a/src/runtime/ksecrets_store/ksecrets_store.cpp
+++ b/src/runtime/ksecrets_store/ksecrets_store.cpp
@@ -24,16 +24,34 @@
 
 #include <future>
 #include <thread>
+#include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/file.h>
 #include <unistd.h>
 #include <stdio.h>
+#include <syslog.h>
 #define GCRPYT_NO_DEPRECATED
 #include <gcrypt.h>
 
+#define KSECRETS_SALTSIZE 56
+#define KSECRETS_KEYSIZE 256
+#define KSS_LOG_ERR (LOG_AUTH | LOG_ERR)
+
+const char* keyNameEncrypting = nullptr;
+const char* keyNameMac = nullptr;
+
+extern "C" {
+bool kss_init_gcry();
+bool kss_derive_keys(const char* salt, const char* password, char* encryption_key, char* \
mac_key, size_t); +bool kss_store_keys(const char* encryption_key, const char* mac_key, size_t \
keySize); +const char* get_keyname_encrypting() { return keyNameEncrypting; }
+const char* get_keyname_mac() { return keyNameMac; }
+}
+
 KSecretsStorePrivate::KSecretsStorePrivate(KSecretsStore* b)
     : b_(b)
 {
-    openStatus_.status_ = KSecretsStore::OpenStatus::NotYetOpened;
+    status_ = KSecretsStore::StoreStatus::JustCreated;
 }
 
 KSecretsStore::KSecretsStore()
@@ -43,52 +61,66 @@ KSecretsStore::KSecretsStore()
 
 KSecretsStore::~KSecretsStore() = default;
 
-std::future<KSecretsStore::OpenResult> KSecretsStore::open(std::string&& path, bool readonly \
/* =true */) noexcept +std::future<KSecretsStore::SetupResult> KSecretsStore::setup(const char* \
path, const char* password, const char* keyNameEcrypting, const char* keyNameMac)  {
     // sanity checks
-    if (path.empty()) {
-        return std::async(std::launch::deferred, []() { return OpenResult{ \
OpenStatus::NoPathGiven, 0 }; }); +    if (d->status_ != StoreStatus::JustCreated) {
+        return std::async(std::launch::deferred, []() { return SetupResult{ \
StoreStatus::IncorrectState, -1 }; }); +    }
+    if (path == nullptr || strlen(path) == 0) {
+        return std::async(std::launch::deferred, []() { return SetupResult{ \
StoreStatus::NoPathGiven, 0 }; });  }
 
     bool shouldCreateFile = false;
     struct stat buf;
-    if (stat(path.c_str(), &buf) != 0) {
+    if (stat(path, &buf) != 0) {
         auto err = errno;
         if (ENOENT == err) {
             shouldCreateFile = true;
         }
         else {
-            return std::async(std::launch::deferred, [err]() { return OpenResult{ \
OpenStatus::SystemError, errno }; }); +            return std::async(std::launch::deferred, \
[err]() { return SetupResult{ StoreStatus::SystemError, errno }; });  }
     }
     else {
         if (buf.st_size == 0) {
-            unlink(path.c_str());
+            unlink(path);
             shouldCreateFile = true; // recreate if empty file was found
         }
     }
 
-    // now we can proceed
+    ::keyNameEncrypting = keyNameEcrypting;
+    ::keyNameMac = keyNameMac;
+
     auto localThis = this;
-    if (!readonly) {
-        return std::async(
-            std::launch::async, [localThis, path, shouldCreateFile]() { return \
localThis->d->createFileIfNeededThenDo(path, shouldCreateFile, \
                std::bind(&KSecretsStorePrivate::lock_open, localThis->d.get(), path)); });
-    }
-    else {
-        return std::async(
-            std::launch::deferred, [localThis, path, shouldCreateFile]() { return \
localThis->d->createFileIfNeededThenDo(path, shouldCreateFile, \
                std::bind(&KSecretsStorePrivate::open, localThis->d.get(), path)); });
-    }
+    std::string filePath = path;
+    std::string pwd = password;
+    return std::async(std::launch::async, [localThis, filePath, shouldCreateFile, pwd]() { \
return localThis->d->setup(filePath, shouldCreateFile, pwd); });  }
 
-KSecretsStore::OpenResult KSecretsStorePrivate::createFileIfNeededThenDo(const std::string& \
path, bool shouldCreateFile, open_action action) +KSecretsStore::SetupResult \
KSecretsStorePrivate::setup(const std::string& path, bool shouldCreateFile, const std::string& \
password)  {
     if (shouldCreateFile) {
-        auto createRes = createFile(path);
-        if (createRes != 0) {
-            return setOpenStatus({ KSecretsStore::OpenStatus::SystemError, createRes });
+        auto createres = createFile(path);
+        if (createres != 0) {
+            return setStoreStatus(KSecretsStore::SetupResult({ \
KSecretsStore::StoreStatus::SystemError, createres }));  }
     }
-    return action(path);
+    if (!kss_init_gcry()) {
+        return setStoreStatus(KSecretsStore::SetupResult({ \
KSecretsStore::StoreStatus::CannotInitGcrypt, -1 })); +    }
+    // 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];
+    if (!kss_derive_keys(salt(), password.c_str(), encryption_key, mac_key, KSECRETS_KEYSIZE)) \
{ +        return setStoreStatus(KSecretsStore::SetupResult({ \
KSecretsStore::StoreStatus::CannotDeriveKeys, -1 })); +    }
+
+    if (!kss_store_keys(encryption_key, mac_key, KSECRETS_KEYSIZE)) {
+        return setStoreStatus(KSecretsStore::SetupResult({ \
KSecretsStore::StoreStatus::CannotStoreKeys, -1 })); +    }
+    secretsFile_.filePath_ = path;
+    return setStoreStatus(KSecretsStore::SetupResult({ KSecretsStore::StoreStatus::SetupDone, \
0 }));  }
 
 char fileMagic[] = { 'k', 's', 'e', 'c', 'r', 'e', 't', 's' };
@@ -114,42 +146,63 @@ int KSecretsStorePrivate::createFile(const std::string& path)
     return res;
 }
 
-const char* KSecretsStore::salt() const { return d->salt(); }
-
-const char* KSecretsStorePrivate::salt() const { return fileHead_.salt; }
-
-KSecretsStore::OpenResult KSecretsStorePrivate::lock_open(const std::string& path)
+std::future<KSecretsStore::OpenResult> KSecretsStore::open(bool readonly /* =true */) noexcept
 {
-    file_ = fopen(path.c_str(), "w+");
-    if (file_ == nullptr) {
-        return { KSecretsStore::OpenStatus::SystemError, errno };
+    // check the state first
+    if (d->status_ == StoreStatus::JustCreated) {
+        return std::async(std::launch::deferred, []() { return OpenResult{ \
StoreStatus::SetupShouldBeCalledFirst, -1 }; }); +    }
+    if (d->status_ != StoreStatus::SetupDone) {
+        // open could not be called two times or perhaps the setup call left this store in an \
invalid state because of some error +        return std::async(std::launch::deferred, []() { \
return OpenResult{ StoreStatus::IncorrectState, -1 }; }); +    }
+    // now we can proceed
+    auto localThis = this;
+    if (!readonly) {
+        return std::async([localThis]() { return localThis->d->open(true); });
+    }
+    else {
+        return std::async([localThis]() { return localThis->d->open(false); });
     }
-    // TODO perform the lock here
-    return open(path);
 }
 
-KSecretsStore::OpenResult KSecretsStorePrivate::setOpenStatus(KSecretsStore::OpenResult \
                openStatus)
-{
-    openStatus_ = openStatus;
-    return openStatus;
-}
+const char* KSecretsStore::salt() const { return d->salt(); }
 
-KSecretsStore::OpenResult KSecretsStorePrivate::open(const std::string& path)
+const char* KSecretsStorePrivate::salt() const { return fileHead_.salt; }
+
+KSecretsStore::OpenResult KSecretsStorePrivate::open(bool lockFile)
 {
-    if (file_ == nullptr) {
-        file_ = fopen(path.c_str(), "r");
+    using OpenResult = KSecretsStore::OpenResult;
+    secretsFile_.file_ = ::open(secretsFile_.filePath_.c_str(), O_DSYNC | O_NOATIME | \
O_NOFOLLOW); +    if (secretsFile_.file_ == -1) {
+        return setStoreStatus(OpenResult({ KSecretsStore::StoreStatus::CannotOpenFile, errno \
}));  }
-    if (file_ == nullptr) {
-        return setOpenStatus({ KSecretsStore::OpenStatus::SystemError, errno });
+    if (lockFile) {
+        if (flock(secretsFile_.file_, LOCK_EX) == -1) {
+            return KSecretsStore::OpenResult{ KSecretsStore::StoreStatus::CannotLockFile, \
errno }; +        }
+        secretsFile_.locked_ = true;
     }
-    if (fread(&fileHead_, sizeof(fileHead_), 1, file_) != sizeof(fileHead_)) {
-        return setOpenStatus({ KSecretsStore::OpenStatus::SystemError, ferror(file_) });
+    auto r = read(secretsFile_.file_, &fileHead_, sizeof(fileHead_));
+    if (r == -1) {
+        return setStoreStatus(OpenResult({ KSecretsStore::StoreStatus::CannotReadFile, errno \
}));  }
-    if (memcmp(fileHead_.magic, fileMagic, fileMagicLen) != 0) {
-        return setOpenStatus({ KSecretsStore::OpenStatus::InvalidFile, 0 });
+    if ((size_t)r < sizeof(fileHead_) || memcmp(fileHead_.magic, fileMagic, fileMagicLen) != \
0) { +        return setStoreStatus(OpenResult({ KSecretsStore::StoreStatus::InvalidFile, -1 \
}));  }
     // decrypting will occur upon collection request
-    return setOpenStatus({ KSecretsStore::OpenStatus::Good, 0 });
+    return setStoreStatus(OpenResult({ KSecretsStore::StoreStatus::Good, 0 }));
+}
+
+KSecretsStorePrivate::SecretsFile::~SecretsFile(){
+    if (file_ != -1) {
+        auto r = close(file_);
+        if (r == -1) {
+            syslog(KSS_LOG_ERR, "ksecrets: system return erro upon secrets file close: %d \
(%m)", errno); +            syslog(KSS_LOG_ERR, "ksecrets: the secrets file might now be corrup \
because of the previous error"); +        }
+        file_ = -1;
+    }
 }
 
 KSecretsStore::CollectionNames KSecretsStore::dirCollections() const noexcept
@@ -158,13 +211,13 @@ KSecretsStore::CollectionNames KSecretsStore::dirCollections() const \
noexcept  return CollectionNames();
 }
 
-KSecretsStore::CollectionPtr KSecretsStore::createCollection(std::string&&) noexcept
+KSecretsStore::CollectionPtr KSecretsStore::createCollection(const char*) noexcept
 {
     // TODO
     return CollectionPtr();
 }
 
-KSecretsStore::CollectionPtr KSecretsStore::readCollection(std::string&&) const noexcept
+KSecretsStore::CollectionPtr KSecretsStore::readCollection(const char*) const noexcept
 {
     // TODO
     return CollectionPtr();
@@ -176,7 +229,7 @@ bool KSecretsStore::deleteCollection(CollectionPtr) noexcept
     return false;
 }
 
-bool KSecretsStore::deleteCollection(std::string&&) noexcept
+bool KSecretsStore::deleteCollection(const char*) noexcept
 {
     // TODO
     return false;
diff --git a/src/runtime/ksecrets_store/ksecrets_store.h \
b/src/runtime/ksecrets_store/ksecrets_store.h index b7d0440..bb4f394 100644
--- a/src/runtime/ksecrets_store/ksecrets_store.h
+++ b/src/runtime/ksecrets_store/ksecrets_store.h
@@ -18,10 +18,10 @@
     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     Boston, MA 02110-1301, USA.
 */
-#ifndef KSECRETS_BACKEND_H
-#define KSECRETS_BACKEND_H
+#ifndef KSECRETS_STORE_H
+#define KSECRETS_STORE_H
 
-#include <ksecrets_backend_export.h>
+#include <ksecrets_store_export.h>
 
 #include <memory>
 #include <ctime>
@@ -45,7 +45,7 @@ class KSecretsStorePrivate;
  *   with each API call.
  * That guarantees client applications that edits are always synced to disk/storage.
  *
- * The API calls are organized in classes, following the structure of data in the backend.
+ * The API calls are organized in classes, following the structure of data in the store.
  * Applications will first work with a Collection, the search or insert Items into it.
  * The Item class holds, sure enough, the secret value but also let applications associate
  * the secret value with metadata, such as the label or other custom properties.
@@ -69,7 +69,7 @@ class KSecretsStorePrivate;
  *       By providing a class, one could use local variables and the class
  *       would be destroyed, releasing the file, upon block exit.
  */
-class KSECRETS_BACKEND_EXPORT KSecretsStore {
+class KSECRETS_STORE_EXPORT KSecretsStore {
     class ItemPrivate;
     class CollectionPrivate;
 
@@ -144,6 +144,11 @@ public:
         ItemList searchItems(const char*, AttributesMap&&) noexcept;
 
         /**
+         * Creates an item in the collection in one go. This is more efficient than
+         * initializing en empty than setting the label, eventually the parameters and
+         * the value. The returned item may still be modified, keep in mind that each
+         * method call will trigger an store file update.
+         *
          * @return ItemPtr which can be empty if creating the item was not
          * possible. So please check it via it's operator bool() before using
          * it.
@@ -171,7 +176,7 @@ public:
     /*
      * Default constructor.
      *
-     * This constructor only initializes the backend class. You should call
+     * This constructor only initializes the store class. You should call
      * the open() method right after the initialization and before any other
      * methods of this API.
      *
@@ -181,28 +186,56 @@ public:
     KSecretsStore(const KSecretsStore&) = delete;
     virtual ~KSecretsStore();
 
-    enum class OpenStatus {
-        NotYetOpened,
+    enum class StoreStatus {
         Good,
+        JustCreated,
+        SetupShouldBeCalledFirst,
+        IncorrectState,
+        CannotInitGcrypt,
+        CannotDeriveKeys,
+        CannotStoreKeys,
+        SetupDone,
+        SetupError,
         NoPathGiven,
         InvalidFile, // the file format was not recognized. Is this a ksecrets file?
-        FileLocked,
+        CannotOpenFile,
+        CannotLockFile,
+        CannotReadFile,
         SystemError
     };
+
+    struct SetupResult {
+        StoreStatus status_;
+        int errno_;
+        operator bool() const { return status_ == StoreStatus::SetupDone; }
+    };
+
+    /*
+     * Before opening, the store must be setup, that is, it must know the
+     * file path and the user's password. This method is typically called
+     * from the pam module but it can also be called from another program
+     * after prompting user for the password, for example.
+     *
+     * This call creates the file if it's not found.
+     *
+     * @return SetupResult whose operator bool could be used to check the error condition
+     */
+    std::future<SetupResult> setup(const char* path, const char* password, const char* \
keyNameEcrypting = "ksecrets:encrypting", const char* keyNameMac = "ksecrets:mac"); +
     struct OpenResult {
-        OpenStatus status_;
+        StoreStatus status_;
         int errno_;
+        operator bool() const { return status_ == StoreStatus::Good; }
     };
 
     /**
-     * Open the designated file. Creates file if not found, regardless
-     * of the readOnly parameter value.
-     *
-     * @parameter path full path to the backend file to handle
+     * @parameter path full path to the store file to handle
      * @parameter readOnly set it to true if you only intend reading to speed
-     * things-up
+     * things-up and avoid placing a lock on the file
+     *
+     * @return OpenResult whose operator bool could be used to check the error condition
      */
-    std::future<OpenResult> open(const char* path, bool readOnly = true) noexcept;
+    std::future<OpenResult> open(bool readOnly = true) noexcept;
 
     constexpr static auto SALT_SIZE = 56;
     /**
@@ -214,9 +247,8 @@ public:
     CollectionNames dirCollections() const noexcept;
     /*
      * @return CollectionPtr which can empty if the call did not succeed.
-     *Please check that with operator bool().
+     * Please check that with operator bool().
      * If it fails, have you already called open()?
-     *
      */
     CollectionPtr createCollection(const char*) noexcept;
     /*
diff --git a/src/runtime/ksecrets_store/ksecrets_store_p.h \
b/src/runtime/ksecrets_store/ksecrets_store_p.h index 25f2de0..4ec75c8 100644
--- a/src/runtime/ksecrets_store/ksecrets_store_p.h
+++ b/src/runtime/ksecrets_store/ksecrets_store_p.h
@@ -58,10 +58,9 @@ public:
     KSecretsStorePrivate() = delete;
     explicit KSecretsStorePrivate(KSecretsStore*);
 
-    KSecretsStore::OpenResult lock_open(const std::string&);
-    KSecretsStore::OpenResult open(const std::string&);
+    KSecretsStore::SetupResult setup(const std::string& path, bool, const std::string&);
+    KSecretsStore::OpenResult open(bool lock);
     using open_action = std::function<KSecretsStore::OpenResult(const std::string&)>;
-    KSecretsStore::OpenResult createFileIfNeededThenDo(const std::string&, bool, open_action);
     int createFile(const std::string&);
     const char* salt() const;
 
@@ -72,13 +71,25 @@ public:
         char iv[IV_SIZE];
     };
 
-    KSecretsStore::OpenResult setOpenStatus(KSecretsStore::OpenResult);
-    bool isOpen() const noexcept { return KSecretsStore::OpenStatus::Good == \
                openStatus_.status_; }
-
+    template <typename S> S setStoreStatus(S s)
+    {
+        status_ = s.status_;
+        return s;
+    }
+    bool isOpen() const noexcept { return KSecretsStore::StoreStatus::Good == status_; }
+
+    struct SecretsFile {
+        SecretsFile()
+            : file_(-1), locked_(false) {};
+        ~SecretsFile();
+        std::string filePath_;
+        int file_;
+        bool locked_;
+    };
     KSecretsStore* b_;
-    FILE* file_;
+    SecretsFile secretsFile_;
     FileHeadStruct fileHead_;
-    KSecretsStore::OpenResult openStatus_;
+    KSecretsStore::StoreStatus status_;
 };
 
 #endif
diff --git a/src/runtime/pam-ksecrets/CMakeLists.txt b/src/runtime/pam-ksecrets/CMakeLists.txt
index 9804532..8bdcdf4 100644
--- a/src/runtime/pam-ksecrets/CMakeLists.txt
+++ b/src/runtime/pam-ksecrets/CMakeLists.txt
@@ -11,8 +11,8 @@ INCLUDE_DIRECTORIES(
   ${PAM_INCLUDE_DIR}
   ${CMAKE_BINARY_DIR}
   ${CMAKE_CURRENT_BINARY_DIR}
-  ${CMAKE_CURRENT_SOURCE_DIR}/../ksecrets_backend
-  ${CMAKE_CURRENT_BINARY_DIR}/../ksecrets_backend
+  ${CMAKE_CURRENT_SOURCE_DIR}/../ksecrets_store
+  ${CMAKE_CURRENT_BINARY_DIR}/../ksecrets_store
 )
 
 set(pam_ksecret_SRC
@@ -21,10 +21,10 @@ set(pam_ksecret_SRC
 add_library(pam_ksecrets STATIC ${pam_ksecret_SRC})
 set_target_properties(pam_ksecrets PROPERTIES PREFIX "")
 target_link_libraries(pam_ksecrets
-  ksecrets_backend
+  ksecrets_store
   ${LIBGCRYPT_LIBRARIES}
   ${PAM_LIBRARIES}
-  keyutils)
+  )
 
 install(TARGETS pam_ksecrets DESTINATION /${CMAKE_INSTALL_LIBDIR}/security)
 
diff --git a/src/runtime/pam-ksecrets/pam_ksecrets.c b/src/runtime/pam-ksecrets/pam_ksecrets.c
index 144e22c..eaf8f21 100644
--- a/src/runtime/pam-ksecrets/pam_ksecrets.c
+++ b/src/runtime/pam-ksecrets/pam_ksecrets.c
@@ -3,17 +3,23 @@
 #define PAM_SM_SESSION
 #define PAM_SM_PASSWORD
 
+#include <ksecrets_credentials.h>
+
 #include <syslog.h>
 #include <security/pam_ext.h>
 #include <security/pam_appl.h>
 #include <security/pam_modules.h>
 
-#include "ksecrets-crypt.h"
-
 #define UNUSED(x) (void)(x)
 
 const char* password;
 
+/* these extern functions are implemented in ksecrets_store_bridge.cpp */
+extern int kss_set_credentials(const char*, const char*);
+extern int kss_delete_credentials();
+extern int kss_can_change_password();
+extern int kss_change_password(const char*);
+
 PAM_EXTERN int pam_sm_authenticate(
     pam_handle_t* pamh, int flags, int argc, const char** argv)
 {
@@ -93,7 +99,7 @@ PAM_EXTERN int pam_sm_chauthtok(
 
   if (flags & PAM_PRELIM_CHECK) {
     pam_syslog(pamh, LOG_INFO, "pam_sm_chauthtok preliminary check");
-    if (kss_can_change_password(pamh)) {
+    if (kss_can_change_password()) {
       return PAM_SUCCESS;
     }
     else {


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic