blob: 41e3415c233b57f55addce520f797ae13bc13dc7 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_OS_CRYPT_ASYNC_BROWSER_FREEDESKTOP_SECRET_KEY_PROVIDER_H_
#define COMPONENTS_OS_CRYPT_ASYNC_BROWSER_FREEDESKTOP_SECRET_KEY_PROVIDER_H_
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/files/scoped_file.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted_memory.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/types/expected.h"
#include "build/branding_buildflags.h"
#include "components/dbus/properties/types.h"
#include "components/dbus/utils/check_for_service_and_start.h"
#include "components/dbus/utils/name_has_owner.h"
#include "components/os_crypt/async/browser/key_provider.h"
#include "dbus/bus.h"
#include "dbus/object_path.h"
#include "dbus/object_proxy.h"
namespace os_crypt_async {
// FreedesktopSecretKeyProvider uses the org.freedesktop.secrets interface
// to retrieve a secret from backend (GNOME Keyring, KWallet, KeePassXC),
// which can then be used to encrypt confidential data.
class FreedesktopSecretKeyProvider : public KeyProvider {
public:
enum class InitStatus {
// These values are persisted to logs. Do not renumber or reuse.
kSuccess = 0,
kCreateCollectionFailed = 1,
kCreateItemFailed = 2,
kEmptySecret = 3,
kGetSecretFailed = 4,
kGnomeKeyringDeadlock = 5,
kNoService = 6,
kReadAliasFailed = 7,
kSearchItemsFailed = 8,
kSessionFailure = 9,
kUnlockFailed = 10,
kDisabled = 11,
kKWalletNoService = 12,
kKWalletDisabled = 13,
kKWalletNoNetworkWallet = 14,
kKWalletOpenFailed = 15,
kKWalletNoSecret = 16,
kKWalletFolderCheckFailed = 17,
kKWalletFolderCreationFailed = 18,
kKWalletEntryCheckFailed = 19,
kKWalletReadFailed = 20,
kKWalletWriteFailed = 21,
kMaxValue = kKWalletWriteFailed,
};
// Supplements InitStatus in case of errors.
enum class ErrorDetail {
// These values are persisted to logs. Do not renumber or reuse.
kNone = 0,
kDestructedBeforeComplete = 1,
kEmptyObjectPaths = 2,
kInvalidReplyFormat = 3,
kInvalidSignalFormat = 4,
kInvalidVariantFormat = 5,
kNoResponse = 6,
kPromptDismissed = 7,
kPromptFailedSignalConnection = 8,
kKWalletApiReturnedError = 9,
kKWalletApiReturnedFalse = 10,
kMaxValue = kKWalletApiReturnedFalse,
};
FreedesktopSecretKeyProvider(const std::string& password_store,
bool use_for_encryption,
const std::string& product_name,
scoped_refptr<dbus::Bus> bus);
~FreedesktopSecretKeyProvider() override;
// KeyProvider:
void GetKey(KeyCallback callback) override;
bool UseForEncryption() override;
bool IsCompatibleWithOsCryptSync() override;
private:
FRIEND_TEST_ALL_PREFIXES(FreedesktopSecretKeyProviderTest, BasicHappyPath);
FRIEND_TEST_ALL_PREFIXES(FreedesktopSecretKeyProviderTest,
CreateCollectionAndItemWithUnlockPrompt);
FRIEND_TEST_ALL_PREFIXES(FreedesktopSecretKeyProviderTest, KWallet);
FRIEND_TEST_ALL_PREFIXES(FreedesktopSecretKeyProviderTest,
KWalletCreateFolderAndPassword);
friend class FreedesktopSecretKeyProviderCompatTest;
template <typename T>
class Prompter;
using DbusSecret = DbusStruct</*session=*/DbusObjectPath,
/*parameters=*/DbusByteArray,
/*value=*/DbusByteArray,
/*content_type=*/DbusString>;
static constexpr char kSecretServiceName[] = "org.freedesktop.secrets";
static constexpr char kSecretServicePath[] = "/org/freedesktop/secrets";
static constexpr char kSecretServiceInterface[] =
"org.freedesktop.Secret.Service";
static constexpr char kSecretCollectionInterface[] =
"org.freedesktop.Secret.Collection";
static constexpr char kSecretItemInterface[] = "org.freedesktop.Secret.Item";
static constexpr char kSecretSessionInterface[] =
"org.freedesktop.Secret.Session";
static constexpr char kSecretPromptInterface[] =
"org.freedesktop.Secret.Prompt";
static constexpr char kMethodReadAlias[] = "ReadAlias";
static constexpr char kMethodCreateCollection[] = "CreateCollection";
static constexpr char kMethodGetSecret[] = "GetSecret";
static constexpr char kMethodOpenSession[] = "OpenSession";
static constexpr char kMethodCreateItem[] = "CreateItem";
static constexpr char kMethodUnlock[] = "Unlock";
static constexpr char kMethodClose[] = "Close";
static constexpr char kMethodSearchItems[] = "SearchItems";
static constexpr char kPropertiesInterface[] =
"org.freedesktop.DBus.Properties";
static constexpr char kMethodGet[] = "Get";
static constexpr char kMethodPrompt[] = "Prompt";
static constexpr char kKWalletInterface[] = "org.kde.KWallet";
static constexpr char kKWalletMethodIsEnabled[] = "isEnabled";
static constexpr char kKWalletMethodNetworkWallet[] = "networkWallet";
static constexpr char kKWalletMethodOpen[] = "open";
static constexpr char kKWalletMethodReadPassword[] = "readPassword";
static constexpr char kKWalletMethodClose[] = "close";
static constexpr char kKWalletMethodHasFolder[] = "hasFolder";
static constexpr char kKWalletMethodCreateFolder[] = "createFolder";
static constexpr char kKWalletMethodHasEntry[] = "hasEntry";
static constexpr char kKWalletMethodWritePassword[] = "writePassword";
static constexpr char kDefaultAlias[] = "default";
// These constants are duplicated from the sync backend.
static constexpr char kApplicationAttributeKey[] = "application";
static constexpr char kSchemaAttributeKey[] = "xdg:schema";
static constexpr char kSchemaAttributeValue[] =
"chrome_libsecret_os_crypt_password_v2";
static constexpr char kAlgorithmPlain[] = "plain";
static constexpr char kInputPlain[] = "";
static constexpr char kMimePlain[] = "text/plain";
static constexpr char kSecretCollectionLabelProperty[] =
"org.freedesktop.Secret.Collection.Label";
static constexpr char kSecretItemAttributesProperty[] =
"org.freedesktop.Secret.Item.Attributes";
static constexpr char kSecretItemLabelProperty[] =
"org.freedesktop.Secret.Item.Label";
static constexpr char kDefaultCollectionLabel[] = "Default Keyring";
static constexpr char kLabelProperty[] = "Label";
static constexpr char kKWalletDService[] = "org.kde.kwalletd";
static constexpr char kKWalletDPath[] = "/modules/kwalletd";
static constexpr char kKWalletD5Service[] = "org.kde.kwalletd5";
static constexpr char kKWalletD5Path[] = "/modules/kwalletd5";
static constexpr char kKWalletD6Service[] = "org.kde.kwalletd6";
static constexpr char kKWalletD6Path[] = "/modules/kwalletd6";
static constexpr int kKWalletInvalidHandle = -1;
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
static constexpr char kKWalletFolder[] = "Chrome Keys";
static constexpr char kKeyName[] = "Chrome Safe Storage";
static constexpr char kAppName[] = "chrome";
#else
static constexpr char kKWalletFolder[] = "Chromium Keys";
static constexpr char kKeyName[] = "Chromium Safe Storage";
static constexpr char kAppName[] = "chromium";
#endif
void InitializeFreedesktopSecretService();
void OnServiceStarted(std::optional<bool> service_started);
void OnReadAliasDefault(
base::expected<DbusObjectPath, ErrorDetail> collection_path);
void OnGetCollectionLabelResponse(
base::expected<DbusVariant, ErrorDetail> variant);
void OnCreateCollection(
base::expected<DbusObjectPath, ErrorDetail> create_collection_reply);
void OnUnlock(base::expected<DbusArray<DbusObjectPath>, ErrorDetail>
unlocked_collection);
void OnOpenSession(base::expected<DbusParameters<DbusVariant, DbusObjectPath>,
ErrorDetail> session_reply);
void OnSearchItems(
base::expected<DbusArray<DbusObjectPath>, ErrorDetail> results);
void OnGetSecret(base::expected<DbusSecret, ErrorDetail> secret_reply);
// KWallet password storage
void InitializeKWallet(const char* kwallet_service, const char* kwallet_path);
void OnKWalletServiceStarted(std::optional<bool> has_owner);
void OnKWalletIsEnabled(base::expected<DbusBoolean, ErrorDetail> is_enabled);
void OnKWalletNetworkWallet(
base::expected<DbusString, ErrorDetail> wallet_name);
void OnKWalletOpen(base::expected<DbusInt32, ErrorDetail> handle_reply);
void OnKWalletHasFolder(base::expected<DbusBoolean, ErrorDetail> has_folder);
void OnKWalletCreateFolder(base::expected<DbusBoolean, ErrorDetail> success);
void OnKWalletHasEntry(base::expected<DbusBoolean, ErrorDetail> has_entry);
void OnKWalletReadPassword(
base::expected<DbusString, ErrorDetail> secret_reply);
void GenerateAndWriteKWalletPassword();
void OnKWalletWritePassword(
scoped_refptr<base::RefCountedMemory> generated_secret,
base::expected<DbusInt32, ErrorDetail> return_code);
void UnlockDefaultCollection();
void OpenSession();
void CreateItem(scoped_refptr<base::RefCountedMemory> secret);
void OnCreateItem(scoped_refptr<base::RefCountedMemory> secret,
base::expected<DbusObjectPath, ErrorDetail> created_item);
void DeriveKeyFromSecret(base::span<const uint8_t> secret);
void FinalizeSuccess(Encryptor::Key key);
void FinalizeFailure(InitStatus status, ErrorDetail detail);
void RecordInitStatus(InitStatus status, ErrorDetail detail);
void CloseSession();
raw_ptr<dbus::ObjectProxy> default_collection_proxy_ = nullptr;
raw_ptr<dbus::ObjectProxy> session_proxy_ = nullptr;
bool session_opened_ = false;
// For KWallet password storage
raw_ptr<dbus::ObjectProxy> kwallet_proxy_ = nullptr;
int32_t kwallet_handle_ = kKWalletInvalidHandle;
const std::string password_store_;
const bool use_for_encryption_;
const std::string product_name_;
scoped_refptr<dbus::Bus> bus_;
KeyCallback key_callback_;
std::string secret_for_testing_;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<FreedesktopSecretKeyProvider> weak_ptr_factory_{this};
};
} // namespace os_crypt_async
#endif // COMPONENTS_OS_CRYPT_ASYNC_BROWSER_FREEDESKTOP_SECRET_KEY_PROVIDER_H_