From 0da55c8f393564b92687eb44f218bb98cf3ffcfe Mon Sep 17 00:00:00 2001 From: Nick Logozzo Date: Fri, 1 Dec 2023 16:47:17 -0500 Subject: [PATCH] (WIP) SystemCredentials API (#1) * Keyring - Start SystemCredentials API * Keyring - Implement SystemCredentials for Windows * Keyring - dbus --> libsecret * Keyring - Implement SystemCredentials for Linux * Update systemcredentials.cpp * Update systemcredentials.cpp * Update systemcredentials.cpp --- CMakeLists.txt | 5 +- conanfile-linux.txt | 2 +- include/keyring/credential.h | 6 +- include/keyring/systemcredentials.h | 30 ++++++++++ src/keyring/systemcredentials.cpp | 93 +++++++++++++++++++++++++++++ 5 files changed, 130 insertions(+), 6 deletions(-) create mode 100644 include/keyring/systemcredentials.h create mode 100644 src/keyring/systemcredentials.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7784592..def22d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,7 @@ add_library (${PROJECT_NAME} SHARED src/helpers/webhelpers.cpp src/keyring/credential.cpp src/keyring/passwordgenerator.cpp + src/keyring/systemcredentials.cpp src/update/updater.cpp src/appinfo.cpp src/aura.cpp @@ -48,8 +49,8 @@ target_link_libraries(${PROJECT_NAME} PUBLIC sqlcipher::sqlcipher) find_package(SQLiteCpp REQUIRED) target_link_libraries(${PROJECT_NAME} PUBLIC SQLiteCpp) if(LINUX) - find_package(DBus1 REQUIRED) - target_link_libraries(${PROJECT_NAME} PUBLIC dbus-1) + find_package(libsecret REQUIRED CONFIG) + target_link_libraries(${PROJECT_NAME} PUBLIC libsecret::libsecret) endif() #libaura Install diff --git a/conanfile-linux.txt b/conanfile-linux.txt index 08bc905..23a0388 100644 --- a/conanfile-linux.txt +++ b/conanfile-linux.txt @@ -5,7 +5,7 @@ libcurl/8.4.0 maddy/1.3.0 sqlcipher/4.5.1 sqlitecpp/3.3.1 -dbus/1.15.8 +libsecret/0.20.5 [generators] CMakeDeps diff --git a/include/keyring/credential.h b/include/keyring/credential.h index 6cdb9f3..5909534 100644 --- a/include/keyring/credential.h +++ b/include/keyring/credential.h @@ -16,7 +16,7 @@ namespace Nickvision::Aura::Keyring * @brief Constructs a credential. * @param id The id of the credential * @param name The name of the credential - * @param uri The uri of the credential + * @param uri The uri of the credential (can also be used as a comment for the Credential) * @param username The username of the credential * @param password The password of the credential */ @@ -45,12 +45,12 @@ namespace Nickvision::Aura::Keyring */ void setName(const std::string& name); /** - * @brief Gets the uri of the credential + * @brief Gets the uri of the credential (can also be used as a comment for the Credential) * @return The uri of the credential */ const std::string& getUri() const; /** - * @brief Sets the uri of the credential + * @brief Sets the uri of the credential (can also be used as a comment for the Credential) * @param uri The uri of the credential */ void setUri(const std::string& uri); diff --git a/include/keyring/systemcredentials.h b/include/keyring/systemcredentials.h new file mode 100644 index 0000000..560c9bb --- /dev/null +++ b/include/keyring/systemcredentials.h @@ -0,0 +1,30 @@ +#ifndef SYSTEMCREDENTIALS_H +#define SYSTEMCREDENTIALS_H + +#include +#include +#include "credential.h" + +namespace Nickvision::Aura::Keyring::SystemCredentials +{ + /** + * @brief Gets a credential from the system's credential manager. + * @param name The name of the credential + * @return The Credential object, if found + */ + std::optional getCredential(const std::string& name); + /** + * @brief Adds a new credential with a random password to the system's credential manager. + * @param name The name of the credential + * @return The new Credential object, if successful + */ + std::optional addNewCredential(const std::string& name); + /** + * @brief Deletes a Credential from the system's credential manager + * @param credential The Credential to delete + * @return True if successful, else false + */ + bool deleteCredential(const Credential& credential); +} + +#endif //SYSTEMCREDENTIALS_H \ No newline at end of file diff --git a/src/keyring/systemcredentials.cpp b/src/keyring/systemcredentials.cpp new file mode 100644 index 0000000..1af6a5a --- /dev/null +++ b/src/keyring/systemcredentials.cpp @@ -0,0 +1,93 @@ +#include "keyring/systemcredentials.h" +#include +#include "keyring/passwordgenerator.h" +#ifdef _WIN32 +#include +#include +#else +#include +#endif + +namespace Nickvision::Aura::Keyring +{ +#ifndef _WIN32 + static const SecretSchema KEYRING_SCHEMA = { "org.nickvision.aura.keyring", SECRET_SCHEMA_NONE, { { "application", SECRET_SCHEMA_ATTRIBUTE_STRING }, { "NULL", SECRET_SCHEMA_ATTRIBUTE_STRING } } }; +#endif + + std::optional SystemCredentials::getCredential(const std::string& name) + { +#ifdef _WIN32 + CREDENTIALA* cred{ nullptr }; + if (CredReadA(name.c_str(), CRED_TYPE_GENERIC, 0, &cred)) + { + if (cred->CredentialBlob) + { + Credential c{ cred->TargetName, cred->Comment, cred->UserName, LPCSTR(cred->CredentialBlob) }; + CredFree(cred); + return c; + } + } +#else + GError* error{ nullptr }; + char* password = secret_password_lookup_sync(&KEYRING_SCHEMA, nullptr, &error, "application", name.c_str(), NULL); + if (!error && password) + { + Credential c{ name, "", "default", password }; + secret_password_free(password); + return c; + } + g_error_free(error); +#endif + return std::nullopt; + } + + std::optional SystemCredentials::addNewCredential(const std::string& name) + { + PasswordGenerator passGen; + Credential c{ name, "", "NickvisionKeyring", passGen.next() }; +#ifdef _WIN32 + CREDENTIALA* cred{ new CREDENTIALA() }; + cred->AttributeCount = 0; + cred->Attributes = nullptr; + cred->Comment = nullptr; + cred->Type = CRED_TYPE_GENERIC; + cred->Persist = CRED_PERSIST_LOCAL_MACHINE; + cred->TargetName = LPSTR(c.getName().c_str()); + cred->UserName = LPSTR(c.getUsername().c_str()); + cred->CredentialBlobSize = (unsigned long)c.getPassword().size(); + cred->CredentialBlob = LPBYTE(c.getPassword().c_str()); + bool res = CredWriteA(cred, 0); + CredFree(cred); + if (!res) + { + return std::nullopt; + } + return c; +#else + GError* error{ nullptr }; + secret_password_store_sync(&KEYRING_SCHEMA, SECRET_COLLECTION_DEFAULT, c.getName().c_str(), c.getPassword().c_str(), nullptr, &error, "application", c.getName().c_str(), NULL); + if (error) + { + g_error_free(error); + return std::nullopt; + } + return c; +#endif + } + + bool SystemCredentials::deleteCredential(const Credential& credential) + { +#ifdef _WIN32 + return CredDeleteA(credential.getName().c_str(), CRED_TYPE_GENERIC, 0); +#else + GError* error{ nullptr }; + bool res = secret_password_clear_sync(&KEYRING_SCHEMA, nullptr, &error, "application", credential.getName().c_str(), NULL); + if (!error) + { + return res; + } + g_error_free(error); +#endif + return false; + } +} \ No newline at end of file