diff options
author | mdm@chromium.org <mdm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-15 05:37:08 +0000 |
---|---|---|
committer | mdm@chromium.org <mdm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-15 05:37:08 +0000 |
commit | 30385d7f8476aca2caa466b0a5db25a15150e1d1 (patch) | |
tree | bddc2028d8fbeeb88f071b939c7169e6d2cf4562 /chrome/browser/password_manager | |
parent | e5bcca22490f37ef14599ad290a610809822b6a9 (diff) | |
download | chromium_src-30385d7f8476aca2caa466b0a5db25a15150e1d1.zip chromium_src-30385d7f8476aca2caa466b0a5db25a15150e1d1.tar.gz chromium_src-30385d7f8476aca2caa466b0a5db25a15150e1d1.tar.bz2 |
Linux: use our spiffy new DBus client library for KWallet, instead of dbus-glib.
This will also allow us to add unit tests for our KWallet integration.
BUG=90036
Review URL: http://codereview.chromium.org/7835021
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@101251 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/password_manager')
-rw-r--r-- | chrome/browser/password_manager/DEPS | 3 | ||||
-rw-r--r-- | chrome/browser/password_manager/native_backend_kwallet_x.cc | 670 | ||||
-rw-r--r-- | chrome/browser/password_manager/native_backend_kwallet_x.h | 40 |
3 files changed, 459 insertions, 254 deletions
diff --git a/chrome/browser/password_manager/DEPS b/chrome/browser/password_manager/DEPS new file mode 100644 index 0000000..d6abdda --- /dev/null +++ b/chrome/browser/password_manager/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+dbus", +] diff --git a/chrome/browser/password_manager/native_backend_kwallet_x.cc b/chrome/browser/password_manager/native_backend_kwallet_x.cc index ffbd55b..41d1edd 100644 --- a/chrome/browser/password_manager/native_backend_kwallet_x.cc +++ b/chrome/browser/password_manager/native_backend_kwallet_x.cc @@ -4,18 +4,20 @@ #include "chrome/browser/password_manager/native_backend_kwallet_x.h" -#include <sstream> +#include <vector> #include "base/logging.h" #include "base/pickle.h" #include "base/stl_util.h" #include "base/stringprintf.h" +#include "base/synchronization/waitable_event.h" #include "content/browser/browser_thread.h" +#include "dbus/bus.h" +#include "dbus/message.h" +#include "dbus/object_proxy.h" #include "grit/chromium_strings.h" #include "ui/base/l10n/l10n_util.h" -using std::string; -using std::vector; using webkit_glue::PasswordForm; // We could localize this string, but then changing your locale would cause @@ -32,7 +34,6 @@ const char NativeBackendKWallet::kKLauncherInterface[] = "org.kde.KLauncher"; NativeBackendKWallet::NativeBackendKWallet(LocalProfileId id, PrefService* prefs) : profile_id_(id), prefs_(prefs), - error_(NULL), connection_(NULL), proxy_(NULL), app_name_(l10n_util::GetStringUTF8(IDS_PRODUCT_NAME)) { if (PasswordStoreX::PasswordsUseLocalProfileId(prefs)) { folder_name_ = GetProfileSpecificFolderName(); @@ -45,86 +46,156 @@ NativeBackendKWallet::NativeBackendKWallet(LocalProfileId id, } NativeBackendKWallet::~NativeBackendKWallet() { - if (proxy_) - g_object_unref(proxy_); + // This destructor is called on the thread that is destroying the Profile + // containing the PasswordStore that owns this NativeBackend. Generally that + // won't be the DB thread; it will be the UI thread. So we post a message to + // shut it down on the DB thread, and it will be destructed afterward when the + // scoped_refptr<dbus::Bus> goes out of scope. The NativeBackend will be + // destroyed before that occurs, but that's OK. + if (session_bus_.get()) { + BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, + NewRunnableMethod( + session_bus_.get(), + &dbus::Bus::ShutdownAndBlock)); + } } bool NativeBackendKWallet::Init() { - // Get a connection to the session bus. - connection_ = dbus_g_bus_get(DBUS_BUS_SESSION, &error_); - if (CheckError()) - return false; + // We must synchronously do a few DBus calls to figure out if initialization + // succeeds, but later, we'll want to do most work on the DB thread. So we + // have to do the initialization on the DB thread here too, and wait for it. + scoped_refptr<dbus::Bus> optional_bus; // Will construct its own. + bool success = false; + base::WaitableEvent event(false, false); + BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, + NewRunnableMethod( + this, &NativeBackendKWallet::InitWithBus, + optional_bus, &event, &success)); + event.Wait(); + return success; +} - if (!InitWallet()) { - // kwalletd may not be running. Try to start it and try again. - if (!StartKWalletd() || !InitWallet()) - return false; +// NativeBackendKWallet isn't reference counted, but the one place we post a +// message to it (in InitWithBus below) waits for the task to run. So we can +// disable needing reference counting safely here. +template<> +struct RunnableMethodTraits<NativeBackendKWallet> { + void RetainCallee(NativeBackendKWallet*) {} + void ReleaseCallee(NativeBackendKWallet*) {} +}; + +void NativeBackendKWallet::InitWithBus(scoped_refptr<dbus::Bus> optional_bus, + base::WaitableEvent* event, + bool* success) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); + DCHECK(!session_bus_.get()); + if (optional_bus.get()) { + // The optional_bus parameter is given when this method is called in tests. + session_bus_ = optional_bus; + } else { + // Get a (real) connection to the session bus. + dbus::Bus::Options options; + options.bus_type = dbus::Bus::SESSION; + options.connection_type = dbus::Bus::PRIVATE; + session_bus_ = new dbus::Bus(options); } - - return true; + kwallet_proxy_ = + session_bus_->GetObjectProxy(kKWalletServiceName, kKWalletPath); + // kwalletd may not be running. If it fails to initialize, try to start it + // and then try to initialize it again. (Note the short-circuit evaluation.) + *success = (InitWallet() || (StartKWalletd() && InitWallet())); + if (event) + event->Signal(); } bool NativeBackendKWallet::StartKWalletd() { - // Sadly kwalletd doesn't use DBUS activation, so we have to make a call to + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); + // Sadly kwalletd doesn't use DBus activation, so we have to make a call to // klauncher to start it. - DBusGProxy* klauncher_proxy = - dbus_g_proxy_new_for_name(connection_, kKLauncherServiceName, - kKLauncherPath, kKLauncherInterface); - - char* empty_string_list = NULL; - int ret = 1; - char* error = NULL; - dbus_g_proxy_call(klauncher_proxy, "start_service_by_desktop_name", &error_, - G_TYPE_STRING, "kwalletd", // serviceName - G_TYPE_STRV, &empty_string_list, // urls - G_TYPE_STRV, &empty_string_list, // envs - G_TYPE_STRING, "", // startup_id - G_TYPE_BOOLEAN, (gboolean) false, // blind - G_TYPE_INVALID, - G_TYPE_INT, &ret, // result - G_TYPE_STRING, NULL, // dubsName - G_TYPE_STRING, &error, // error - G_TYPE_INT, NULL, // pid - G_TYPE_INVALID); - - if (error && *error) { - LOG(ERROR) << "Error launching kwalletd: " << error; - ret = 1; // Make sure we return false after freeing. - } - - g_free(error); - g_object_unref(klauncher_proxy); - - if (CheckError() || ret != 0) + dbus::ObjectProxy* klauncher = + session_bus_->GetObjectProxy(kKLauncherServiceName, kKLauncherPath); + + dbus::MethodCall method_call(kKLauncherInterface, + "start_service_by_desktop_name"); + dbus::MessageWriter builder(&method_call); + dbus::MessageWriter empty(&method_call); + builder.AppendString("kwalletd"); // serviceName + builder.OpenArray("s", &empty); // urls + builder.CloseContainer(&empty); + builder.OpenArray("s", &empty); // envs + builder.CloseContainer(&empty); + builder.AppendString(""); // startup_id + builder.AppendBool(false); // blind + scoped_ptr<dbus::Response> response( + klauncher->CallMethodAndBlock( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); + if (!response.get()) { + LOG(ERROR) << "Error contacting klauncher to start kwalletd"; + return false; + } + dbus::MessageReader reader(response.get()); + int32_t ret = -1; + std::string dbus_name; + std::string error; + int32_t pid = -1; + if (!reader.PopInt32(&ret) || !reader.PopString(&dbus_name) || + !reader.PopString(&error) || !reader.PopInt32(&pid)) { + LOG(ERROR) << "Error reading response from klauncher to start kwalletd: " + << response->ToString(); + return false; + } + if (!error.empty() || ret) { + LOG(ERROR) << "Error launching kwalletd: error '" << error << "' " + << " (code " << ret << ")"; return false; + } + return true; } bool NativeBackendKWallet::InitWallet() { - // Make a proxy to KWallet. - proxy_ = dbus_g_proxy_new_for_name(connection_, kKWalletServiceName, - kKWalletPath, kKWalletInterface); - - // Check KWallet is enabled. - gboolean is_enabled = false; - dbus_g_proxy_call(proxy_, "isEnabled", &error_, - G_TYPE_INVALID, - G_TYPE_BOOLEAN, &is_enabled, - G_TYPE_INVALID); - if (CheckError() || !is_enabled) - return false; - - // Get the wallet name. - char* wallet_name = NULL; - dbus_g_proxy_call(proxy_, "networkWallet", &error_, - G_TYPE_INVALID, - G_TYPE_STRING, &wallet_name, - G_TYPE_INVALID); - if (CheckError() || !wallet_name) - return false; + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); + { + // Check that KWallet is enabled. + dbus::MethodCall method_call(kKWalletInterface, "isEnabled"); + scoped_ptr<dbus::Response> response( + kwallet_proxy_->CallMethodAndBlock( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); + if (!response.get()) { + LOG(ERROR) << "Error contacting kwalletd (isEnabled)"; + return false; + } + dbus::MessageReader reader(response.get()); + bool enabled = false; + if (!reader.PopBool(&enabled)) { + LOG(ERROR) << "Error reading response from kwalletd (isEnabled): " + << response->ToString(); + return false; + } + // Not enabled? Don't use KWallet. But also don't warn here. + if (!enabled) { + VLOG(1) << "kwalletd reports that KWallet is not enabled."; + return false; + } + } - wallet_name_.assign(wallet_name); - g_free(wallet_name); + { + // Get the wallet name. + dbus::MethodCall method_call(kKWalletInterface, "networkWallet"); + scoped_ptr<dbus::Response> response( + kwallet_proxy_->CallMethodAndBlock( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); + if (!response.get()) { + LOG(ERROR) << "Error contacting kwalletd (networkWallet)"; + return false; + } + dbus::MessageReader reader(response.get()); + if (!reader.PopString(&wallet_name_)) { + LOG(ERROR) << "Error reading response from kwalletd (networkWallet): " + << response->ToString(); + return false; + } + } return true; } @@ -209,39 +280,69 @@ bool NativeBackendKWallet::RemoveLoginsCreatedBetween( return false; // We could probably also use readEntryList here. - char** realm_list = NULL; - dbus_g_proxy_call(proxy_, "entryList", &error_, - G_TYPE_INT, wallet_handle, // handle - G_TYPE_STRING, folder_name_.c_str(), // folder - G_TYPE_STRING, app_name_.c_str(), // appid - G_TYPE_INVALID, - G_TYPE_STRV, &realm_list, - G_TYPE_INVALID); - if (CheckError()) - return false; + std::vector<std::string> realm_list; + { + dbus::MethodCall method_call(kKWalletInterface, "entryList"); + dbus::MessageWriter builder(&method_call); + builder.AppendInt32(wallet_handle); // handle + builder.AppendString(folder_name_); // folder + builder.AppendString(app_name_); // appid + scoped_ptr<dbus::Response> response( + kwallet_proxy_->CallMethodAndBlock( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); + if (!response.get()) { + LOG(ERROR) << "Error contacting kwalletd (entryList)"; + return false; + } + dbus::MessageReader reader(response.get()); + dbus::MessageReader array(response.get()); + if (!reader.PopArray(&array)) { + LOG(ERROR) << "Error reading response from kwalletd (entryList): " + << response->ToString(); + return false; + } + while (array.HasMoreData()) { + std::string realm; + if (!array.PopString(&realm)) { + LOG(ERROR) << "Error reading response from kwalletd (entryList): " + << response->ToString(); + return false; + } + realm_list.push_back(realm); + } + } bool ok = true; - for (char** realm = realm_list; *realm; ++realm) { - GArray* byte_array = NULL; - dbus_g_proxy_call(proxy_, "readEntry", &error_, - G_TYPE_INT, wallet_handle, // handle - G_TYPE_STRING, folder_name_.c_str(), // folder - G_TYPE_STRING, *realm, // key - G_TYPE_STRING, app_name_.c_str(), // appid - G_TYPE_INVALID, - DBUS_TYPE_G_UCHAR_ARRAY, &byte_array, - G_TYPE_INVALID); - - if (CheckError() || !byte_array || - !CheckSerializedValue(byte_array, *realm)) { + for (size_t i = 0; i < realm_list.size(); ++i) { + const std::string& signon_realm = realm_list[i]; + dbus::MethodCall method_call(kKWalletInterface, "readEntry"); + dbus::MessageWriter builder(&method_call); + builder.AppendInt32(wallet_handle); // handle + builder.AppendString(folder_name_); // folder + builder.AppendString(signon_realm); // key + builder.AppendString(app_name_); // appid + scoped_ptr<dbus::Response> response( + kwallet_proxy_->CallMethodAndBlock( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); + if (!response.get()) { + LOG(ERROR) << "Error contacting kwalletd (readEntry)"; + continue; + } + dbus::MessageReader reader(response.get()); + uint8_t* bytes = NULL; + size_t length = 0; + if (!reader.PopArrayOfBytes(&bytes, &length)) { + LOG(ERROR) << "Error reading response from kwalletd (readEntry): " + << response->ToString(); continue; } + if (!bytes || !CheckSerializedValue(bytes, length, signon_realm)) + continue; - string signon_realm(*realm); - Pickle pickle(byte_array->data, byte_array->len); + // Can't we all just agree on whether bytes are signed or not? Please? + Pickle pickle(reinterpret_cast<const char*>(bytes), length); PasswordFormList all_forms; DeserializeValue(signon_realm, pickle, &all_forms); - g_array_free(byte_array, true); PasswordFormList kept_forms; kept_forms.reserve(all_forms.size()); @@ -258,7 +359,6 @@ bool NativeBackendKWallet::RemoveLoginsCreatedBetween( ok = false; STLDeleteElements(&kept_forms); } - g_strfreev(realm_list); return ok; } @@ -294,50 +394,73 @@ bool NativeBackendKWallet::GetBlacklistLogins(PasswordFormList* forms) { } bool NativeBackendKWallet::GetLoginsList(PasswordFormList* forms, - const string& signon_realm, + const std::string& signon_realm, int wallet_handle) { // Is there an entry in the wallet? - gboolean has_entry = false; - dbus_g_proxy_call(proxy_, "hasEntry", &error_, - G_TYPE_INT, wallet_handle, // handle - G_TYPE_STRING, folder_name_.c_str(), // folder - G_TYPE_STRING, signon_realm.c_str(), // key - G_TYPE_STRING, app_name_.c_str(), // appid - G_TYPE_INVALID, - G_TYPE_BOOLEAN, &has_entry, - G_TYPE_INVALID); - - if (CheckError()) - return false; - if (!has_entry) { - // This is not an error. There just isn't a matching entry. - return true; - } - - GArray* byte_array = NULL; - dbus_g_proxy_call(proxy_, "readEntry", &error_, - G_TYPE_INT, wallet_handle, // handle - G_TYPE_STRING, folder_name_.c_str(), // folder - G_TYPE_STRING, signon_realm.c_str(), // key - G_TYPE_STRING, app_name_.c_str(), // appid - G_TYPE_INVALID, - DBUS_TYPE_G_UCHAR_ARRAY, &byte_array, - G_TYPE_INVALID); - - if (CheckError() || !byte_array) - return false; - if (!CheckSerializedValue(byte_array, signon_realm.c_str())) { - // This is weird, but we choose not to call it an error. There's an invalid - // entry somehow, but by pretending it just doesn't exist, we make it easier - // to repair without having to delete it using kwalletmanager (that is, by - // just saving a new password within this realm to overwrite it). - g_array_free(byte_array, true); - return true; + { + dbus::MethodCall method_call(kKWalletInterface, "hasEntry"); + dbus::MessageWriter builder(&method_call); + builder.AppendInt32(wallet_handle); // handle + builder.AppendString(folder_name_); // folder + builder.AppendString(signon_realm); // key + builder.AppendString(app_name_); // appid + scoped_ptr<dbus::Response> response( + kwallet_proxy_->CallMethodAndBlock( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); + if (!response.get()) { + LOG(ERROR) << "Error contacting kwalletd (hasEntry)"; + return false; + } + dbus::MessageReader reader(response.get()); + bool has_entry = false; + if (!reader.PopBool(&has_entry)) { + LOG(ERROR) << "Error reading response from kwalletd (hasEntry): " + << response->ToString(); + return false; + } + if (!has_entry) { + // This is not an error. There just isn't a matching entry. + return true; + } } - Pickle pickle(byte_array->data, byte_array->len); - DeserializeValue(signon_realm, pickle, forms); - g_array_free(byte_array, true); + { + dbus::MethodCall method_call(kKWalletInterface, "readEntry"); + dbus::MessageWriter builder(&method_call); + builder.AppendInt32(wallet_handle); // handle + builder.AppendString(folder_name_); // folder + builder.AppendString(signon_realm); // key + builder.AppendString(app_name_); // appid + scoped_ptr<dbus::Response> response( + kwallet_proxy_->CallMethodAndBlock( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); + if (!response.get()) { + LOG(ERROR) << "Error contacting kwalletd (readEntry)"; + return false; + } + dbus::MessageReader reader(response.get()); + uint8_t* bytes = NULL; + size_t length = 0; + if (!reader.PopArrayOfBytes(&bytes, &length)) { + LOG(ERROR) << "Error reading response from kwalletd (readEntry): " + << response->ToString(); + return false; + } + if (!bytes) + return false; + if (!CheckSerializedValue(bytes, length, signon_realm)) { + // This is weird, but we choose not to call it an error. There is an + // invalid entry somehow, but by just ignoring it, we make it easier to + // repair without having to delete it using kwalletmanager (that is, by + // just saving a new password within this realm to overwrite it). + return true; + } + + // Can't we all just agree on whether bytes are signed or not? Please? + Pickle pickle(reinterpret_cast<const char*>(bytes), length); + PasswordFormList all_forms; + DeserializeValue(signon_realm, pickle, forms); + } return true; } @@ -386,57 +509,97 @@ bool NativeBackendKWallet::GetLoginsList(PasswordFormList* forms, bool NativeBackendKWallet::GetAllLogins(PasswordFormList* forms, int wallet_handle) { // We could probably also use readEntryList here. - char** realm_list = NULL; - dbus_g_proxy_call(proxy_, "entryList", &error_, - G_TYPE_INT, wallet_handle, // handle - G_TYPE_STRING, folder_name_.c_str(), // folder - G_TYPE_STRING, app_name_.c_str(), // appid - G_TYPE_INVALID, - G_TYPE_STRV, &realm_list, - G_TYPE_INVALID); - if (CheckError()) - return false; + std::vector<std::string> realm_list; + { + dbus::MethodCall method_call(kKWalletInterface, "entryList"); + dbus::MessageWriter builder(&method_call); + builder.AppendInt32(wallet_handle); // handle + builder.AppendString(folder_name_); // folder + builder.AppendString(app_name_); // appid + scoped_ptr<dbus::Response> response( + kwallet_proxy_->CallMethodAndBlock( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); + if (!response.get()) { + LOG(ERROR) << "Error contacting kwalletd (entryList)"; + return false; + } + dbus::MessageReader reader(response.get()); + dbus::MessageReader array(response.get()); + if (!reader.PopArray(&array)) { + LOG(ERROR) << "Error reading response from kwalletd (entryList): " + << response->ToString(); + return false; + } + while (array.HasMoreData()) { + std::string realm; + if (!array.PopString(&realm)) { + LOG(ERROR) << "Error reading response from kwalletd (entryList): " + << response->ToString(); + return false; + } + realm_list.push_back(realm); + } + } - for (char** realm = realm_list; *realm; ++realm) { - GArray* byte_array = NULL; - dbus_g_proxy_call(proxy_, "readEntry", &error_, - G_TYPE_INT, wallet_handle, // handle - G_TYPE_STRING, folder_name_.c_str(), // folder - G_TYPE_STRING, *realm, // key - G_TYPE_STRING, app_name_.c_str(), // appid - G_TYPE_INVALID, - DBUS_TYPE_G_UCHAR_ARRAY, &byte_array, - G_TYPE_INVALID); - - if (CheckError() || !byte_array || - !CheckSerializedValue(byte_array, *realm)) { + for (size_t i = 0; i < realm_list.size(); ++i) { + const std::string& signon_realm = realm_list[i]; + dbus::MethodCall method_call(kKWalletInterface, "readEntry"); + dbus::MessageWriter builder(&method_call); + builder.AppendInt32(wallet_handle); // handle + builder.AppendString(folder_name_); // folder + builder.AppendString(signon_realm); // key + builder.AppendString(app_name_); // appid + scoped_ptr<dbus::Response> response( + kwallet_proxy_->CallMethodAndBlock( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); + if (!response.get()) { + LOG(ERROR) << "Error contacting kwalletd (readEntry)"; continue; } + dbus::MessageReader reader(response.get()); + uint8_t* bytes = NULL; + size_t length = 0; + if (!reader.PopArrayOfBytes(&bytes, &length)) { + LOG(ERROR) << "Error reading response from kwalletd (readEntry): " + << response->ToString(); + continue; + } + if (!bytes || !CheckSerializedValue(bytes, length, signon_realm)) + continue; - Pickle pickle(byte_array->data, byte_array->len); - DeserializeValue(*realm, pickle, forms); - g_array_free(byte_array, true); + // Can't we all just agree on whether bytes are signed or not? Please? + Pickle pickle(reinterpret_cast<const char*>(bytes), length); + PasswordFormList all_forms; + DeserializeValue(signon_realm, pickle, forms); } - g_strfreev(realm_list); return true; } bool NativeBackendKWallet::SetLoginsList(const PasswordFormList& forms, - const string& signon_realm, + const std::string& signon_realm, int wallet_handle) { if (forms.empty()) { // No items left? Remove the entry from the wallet. + dbus::MethodCall method_call(kKWalletInterface, "removeEntry"); + dbus::MessageWriter builder(&method_call); + builder.AppendInt32(wallet_handle); // handle + builder.AppendString(folder_name_); // folder + builder.AppendString(signon_realm); // key + builder.AppendString(app_name_); // appid + scoped_ptr<dbus::Response> response( + kwallet_proxy_->CallMethodAndBlock( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); + if (!response.get()) { + LOG(ERROR) << "Error contacting kwalletd (removeEntry)"; + return kInvalidKWalletHandle; + } + dbus::MessageReader reader(response.get()); int ret = 0; - dbus_g_proxy_call(proxy_, "removeEntry", &error_, - G_TYPE_INT, wallet_handle, // handle - G_TYPE_STRING, folder_name_.c_str(), // folder - G_TYPE_STRING, signon_realm.c_str(), // key - G_TYPE_STRING, app_name_.c_str(), // appid - G_TYPE_INVALID, - G_TYPE_INT, &ret, - G_TYPE_INVALID); - if (CheckError()) + if (!reader.PopInt32(&ret)) { + LOG(ERROR) << "Error reading response from kwalletd (removeEntry): " + << response->ToString(); return false; + } if (ret != 0) LOG(ERROR) << "Bad return code " << ret << " from KWallet removeEntry"; return ret == 0; @@ -445,26 +608,28 @@ bool NativeBackendKWallet::SetLoginsList(const PasswordFormList& forms, Pickle value; SerializeValue(forms, &value); - // Convert the pickled bytes to a GByteArray. - GArray* byte_array = g_array_sized_new(false, false, sizeof(char), - value.size()); - g_array_append_vals(byte_array, value.data(), value.size()); - - // Make the call. + dbus::MethodCall method_call(kKWalletInterface, "writeEntry"); + dbus::MessageWriter builder(&method_call); + builder.AppendInt32(wallet_handle); // handle + builder.AppendString(folder_name_); // folder + builder.AppendString(signon_realm); // key + builder.AppendArrayOfBytes(static_cast<const uint8_t*>(value.data()), + value.size()); // value + builder.AppendString(app_name_); // appid + scoped_ptr<dbus::Response> response( + kwallet_proxy_->CallMethodAndBlock( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); + if (!response.get()) { + LOG(ERROR) << "Error contacting kwalletd (writeEntry)"; + return kInvalidKWalletHandle; + } + dbus::MessageReader reader(response.get()); int ret = 0; - dbus_g_proxy_call(proxy_, "writeEntry", &error_, - G_TYPE_INT, wallet_handle, // handle - G_TYPE_STRING, folder_name_.c_str(), // folder - G_TYPE_STRING, signon_realm.c_str(), // key - DBUS_TYPE_G_UCHAR_ARRAY, byte_array, // value - G_TYPE_STRING, app_name_.c_str(), // appid - G_TYPE_INVALID, - G_TYPE_INT, &ret, - G_TYPE_INVALID); - g_array_free(byte_array, true); - - if (CheckError()) + if (!reader.PopInt32(&ret)) { + LOG(ERROR) << "Error reading response from kwalletd (writeEntry): " + << response->ToString(); return false; + } if (ret != 0) LOG(ERROR) << "Bad return code " << ret << " from KWallet writeEntry"; return ret == 0; @@ -505,19 +670,20 @@ void NativeBackendKWallet::SerializeValue(const PasswordFormList& forms, } } -bool NativeBackendKWallet::CheckSerializedValue(const GArray* byte_array, - const char* realm) { +bool NativeBackendKWallet::CheckSerializedValue(const uint8_t* byte_array, + size_t length, + const std::string& realm) { const Pickle::Header* header = - reinterpret_cast<const Pickle::Header*>(byte_array->data); - if (byte_array->len < sizeof(*header) || - header->payload_size > byte_array->len - sizeof(*header)) { + reinterpret_cast<const Pickle::Header*>(byte_array); + if (length < sizeof(*header) || + header->payload_size > length - sizeof(*header)) { LOG(WARNING) << "Invalid KWallet entry detected (realm: " << realm << ")"; return false; } return true; } -void NativeBackendKWallet::DeserializeValue(const string& signon_realm, +void NativeBackendKWallet::DeserializeValue(const std::string& signon_realm, const Pickle& pickle, PasswordFormList* forms) { void* iter = NULL; @@ -579,7 +745,7 @@ void NativeBackendKWallet::DeserializeValue(const string& signon_realm, bool NativeBackendKWallet::ReadGURL(const Pickle& pickle, void** iter, GURL* url) { - string url_string; + std::string url_string; if (!pickle.ReadString(iter, &url_string)) { LOG(ERROR) << "Failed to deserialize URL"; *url = GURL(); @@ -589,55 +755,85 @@ bool NativeBackendKWallet::ReadGURL(const Pickle& pickle, void** iter, return true; } -bool NativeBackendKWallet::CheckError() { - if (error_) { - LOG(ERROR) << "Failed to complete KWallet call: " << error_->message; - g_error_free(error_); - error_ = NULL; - return true; - } - return false; -} - int NativeBackendKWallet::WalletHandle() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); + // Open the wallet. // TODO(mdm): Are we leaking these handles? Find out. - int handle = kInvalidKWalletHandle; - dbus_g_proxy_call(proxy_, "open", &error_, - G_TYPE_STRING, wallet_name_.c_str(), // wallet - G_TYPE_INT64, 0LL, // wid - G_TYPE_STRING, app_name_.c_str(), // appid - G_TYPE_INVALID, - G_TYPE_INT, &handle, - G_TYPE_INVALID); - if (CheckError() || handle == kInvalidKWalletHandle) - return kInvalidKWalletHandle; + int32_t handle = kInvalidKWalletHandle; + { + dbus::MethodCall method_call(kKWalletInterface, "open"); + dbus::MessageWriter builder(&method_call); + builder.AppendString(wallet_name_); // wallet + builder.AppendInt64(0); // wid + builder.AppendString(app_name_); // appid + scoped_ptr<dbus::Response> response( + kwallet_proxy_->CallMethodAndBlock( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); + if (!response.get()) { + LOG(ERROR) << "Error contacting kwalletd (open)"; + return kInvalidKWalletHandle; + } + dbus::MessageReader reader(response.get()); + if (!reader.PopInt32(&handle)) { + LOG(ERROR) << "Error reading response from kwalletd (open): " + << response->ToString(); + return kInvalidKWalletHandle; + } + if (handle == kInvalidKWalletHandle) { + LOG(ERROR) << "Error obtaining KWallet handle"; + return kInvalidKWalletHandle; + } + } // Check if our folder exists. - gboolean has_folder = false; - dbus_g_proxy_call(proxy_, "hasFolder", &error_, - G_TYPE_INT, handle, // handle - G_TYPE_STRING, folder_name_.c_str(), // folder - G_TYPE_STRING, app_name_.c_str(), // appid - G_TYPE_INVALID, - G_TYPE_BOOLEAN, &has_folder, - G_TYPE_INVALID); - if (CheckError()) - return kInvalidKWalletHandle; + bool has_folder = false; + { + dbus::MethodCall method_call(kKWalletInterface, "hasFolder"); + dbus::MessageWriter builder(&method_call); + builder.AppendInt32(handle); // handle + builder.AppendString(folder_name_); // folder + builder.AppendString(app_name_); // appid + scoped_ptr<dbus::Response> response( + kwallet_proxy_->CallMethodAndBlock( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); + if (!response.get()) { + LOG(ERROR) << "Error contacting kwalletd (hasFolder)"; + return kInvalidKWalletHandle; + } + dbus::MessageReader reader(response.get()); + if (!reader.PopBool(&has_folder)) { + LOG(ERROR) << "Error reading response from kwalletd (hasFolder): " + << response->ToString(); + return kInvalidKWalletHandle; + } + } // Create it if it didn't. if (!has_folder) { - gboolean success = false; - dbus_g_proxy_call(proxy_, "createFolder", &error_, - G_TYPE_INT, handle, // handle - G_TYPE_STRING, folder_name_.c_str(), // folder - G_TYPE_STRING, app_name_.c_str(), // appid - G_TYPE_INVALID, - G_TYPE_BOOLEAN, &success, - G_TYPE_INVALID); - if (CheckError() || !success) + dbus::MethodCall method_call(kKWalletInterface, "createFolder"); + dbus::MessageWriter builder(&method_call); + builder.AppendInt32(handle); // handle + builder.AppendString(folder_name_); // folder + builder.AppendString(app_name_); // appid + scoped_ptr<dbus::Response> response( + kwallet_proxy_->CallMethodAndBlock( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); + if (!response.get()) { + LOG(ERROR) << "Error contacting kwalletd (createFolder)"; + return kInvalidKWalletHandle; + } + dbus::MessageReader reader(response.get()); + bool success = false; + if (!reader.PopBool(&success)) { + LOG(ERROR) << "Error reading response from kwalletd (createFolder): " + << response->ToString(); return kInvalidKWalletHandle; + } + if (!success) { + LOG(ERROR) << "Error creating KWallet folder"; + return kInvalidKWalletHandle; + } } // Successful initialization. Try migration if necessary. diff --git a/chrome/browser/password_manager/native_backend_kwallet_x.h b/chrome/browser/password_manager/native_backend_kwallet_x.h index 173cabc..82470e3 100644 --- a/chrome/browser/password_manager/native_backend_kwallet_x.h +++ b/chrome/browser/password_manager/native_backend_kwallet_x.h @@ -6,12 +6,10 @@ #define CHROME_BROWSER_PASSWORD_MANAGER_NATIVE_BACKEND_KWALLET_X_H_ #pragma once -#include <dbus/dbus-glib.h> -#include <glib.h> - #include <string> #include "base/basictypes.h" +#include "base/memory/ref_counted.h" #include "base/time.h" #include "chrome/browser/password_manager/password_store_x.h" #include "chrome/browser/profiles/profile.h" @@ -23,6 +21,15 @@ namespace webkit_glue { struct PasswordForm; } +namespace base { +class WaitableEvent; +} + +namespace dbus { +class Bus; +class ObjectProxy; +} + // NativeBackend implementation using KWallet. class NativeBackendKWallet : public PasswordStoreX::NativeBackend { public: @@ -46,6 +53,12 @@ class NativeBackendKWallet : public PasswordStoreX::NativeBackend { virtual bool GetAutofillableLogins(PasswordFormList* forms) OVERRIDE; virtual bool GetBlacklistLogins(PasswordFormList* forms) OVERRIDE; + protected: + // Internally used by Init(), but also for testing to provide a mock bus. + void InitWithBus(scoped_refptr<dbus::Bus> optional_bus, + base::WaitableEvent* event, + bool* success); + private: // Initialization. bool StartKWalletd(); @@ -77,11 +90,6 @@ class NativeBackendKWallet : public PasswordStoreX::NativeBackend { const std::string& signon_realm, int wallet_handle); - // Checks if the last DBus call returned an error. If it did, logs the error - // message, frees it and returns true. - // This must be called after every DBus call. - bool CheckError(); - // Opens the wallet and ensures that the "Chrome Form Data" folder exists. // Returns kInvalidWalletHandle on error. int WalletHandle(); @@ -99,7 +107,8 @@ class NativeBackendKWallet : public PasswordStoreX::NativeBackend { // Checks a serialized list of PasswordForms for sanity. Returns true if OK. // Note that |realm| is only used for generating a useful warning message. - static bool CheckSerializedValue(const GArray* byte_array, const char* realm); + static bool CheckSerializedValue(const uint8_t* byte_array, size_t length, + const std::string& realm); // Deserializes a list of PasswordForms from the wallet. static void DeserializeValue(const std::string& signon_realm, @@ -117,7 +126,7 @@ class NativeBackendKWallet : public PasswordStoreX::NativeBackend { // Name of the folder to store passwords in. static const char kKWalletFolder[]; - // DBus stuff. + // DBus service, path, and interface names for klauncher and kwalletd. static const char kKWalletServiceName[]; static const char kKWalletPath[]; static const char kKWalletInterface[]; @@ -146,13 +155,10 @@ class NativeBackendKWallet : public PasswordStoreX::NativeBackend { // True once MigrateToProfileSpecificLogins() has been attempted. bool migrate_tried_; - // Error from the last DBus call. NULL when there's no error. Freed and - // cleared by CheckError(). - GError* error_; - // Connection to the DBus session bus. - DBusGConnection* connection_; - // Proxy to the kwallet DBus service. - DBusGProxy* proxy_; + // DBus handle for communication with klauncher and kwalletd. + scoped_refptr<dbus::Bus> session_bus_; + // Object proxy for kwalletd. We do not own this. + dbus::ObjectProxy* kwallet_proxy_; // The name of the wallet we've opened. Set during Init(). std::string wallet_name_; |