diff options
author | mdm@chromium.org <mdm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-14 22:50:28 +0000 |
---|---|---|
committer | mdm@chromium.org <mdm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-14 22:50:28 +0000 |
commit | 9b96ccd84e5adb4f85babaef98b7259932a84788 (patch) | |
tree | fd131844c748d74c52f8f30725ff01168c226761 /chrome | |
parent | 8a297f0391d76378ab63b6070bef33ef319c0e22 (diff) | |
download | chromium_src-9b96ccd84e5adb4f85babaef98b7259932a84788.zip chromium_src-9b96ccd84e5adb4f85babaef98b7259932a84788.tar.gz chromium_src-9b96ccd84e5adb4f85babaef98b7259932a84788.tar.bz2 |
Linux: make externally-stored passwords (e.g. GNOME Keyring) profile-specific.
This is accomplished by associating a randomly generated id with each profile,
and storing the profile id with the passwords. The ids are chosen such that they
are obviously not unique ids (there are more users than ids), yet within one
machine they should be unique with high probability.
Although profiles have names, it turns out that using these names as the
identifiers is not preferable for two reasons. First, the names are actually the
account email addresses, and not user-provided strings. The default profile, if
not using sync, has the empty string for its name. This means that we still have
to worry about migration in this case, and can't cleanly assume that existing
passwords "belong" to the default profile, because we can't always tell which
profile is the default. Second, the sync code seems to be rather non-robust and
fails frequently when passwords change underneath it. Using the profile name
would mean that the same account synced within different user data dirs would
share passwords, which the sync code won't really like. (Of course, this is the
current situation as well, with different user data dirs.)
Speaking of migration: this change leaves the original, shared passwords alone,
and they will be migrated (copied, really) into each profile the first time it
is used. After a while, we can add code to delete these shared passwords so they
don't persist forever without being visible in the UI. Eventually, we can remove
the migration and deletion code.
BUG=77022
Review URL: http://codereview.chromium.org/7212031
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@92615 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/password_manager/native_backend_gnome_x.cc | 191 | ||||
-rw-r--r-- | chrome/browser/password_manager/native_backend_gnome_x.h | 27 | ||||
-rw-r--r-- | chrome/browser/password_manager/native_backend_kwallet_x.cc | 149 | ||||
-rw-r--r-- | chrome/browser/password_manager/native_backend_kwallet_x.h | 45 | ||||
-rw-r--r-- | chrome/browser/password_manager/password_store_x.cc | 37 | ||||
-rw-r--r-- | chrome/browser/password_manager/password_store_x.h | 12 | ||||
-rw-r--r-- | chrome/browser/profiles/profile.cc | 17 | ||||
-rw-r--r-- | chrome/browser/profiles/profile.h | 13 | ||||
-rw-r--r-- | chrome/browser/profiles/profile_impl.cc | 34 | ||||
-rw-r--r-- | chrome/browser/profiles/profile_impl.h | 4 | ||||
-rw-r--r-- | chrome/common/pref_names.cc | 10 | ||||
-rw-r--r-- | chrome/common/pref_names.h | 5 |
12 files changed, 424 insertions, 120 deletions
diff --git a/chrome/browser/password_manager/native_backend_gnome_x.cc b/chrome/browser/password_manager/native_backend_gnome_x.cc index 6141a9e..80c9c80 100644 --- a/chrome/browser/password_manager/native_backend_gnome_x.cc +++ b/chrome/browser/password_manager/native_backend_gnome_x.cc @@ -13,7 +13,9 @@ #include "base/logging.h" #include "base/string_number_conversions.h" +#include "base/string_piece.h" #include "base/string_util.h" +#include "base/stringprintf.h" #include "base/time.h" #include "base/utf_string_conversions.h" #include "base/synchronization/waitable_event.h" @@ -44,12 +46,7 @@ namespace { F(store_password) \ F(delete_password) \ F(find_itemsv) \ - F(result_to_message) \ - F(list_keyring_names) \ - F(list_item_ids) \ - F(item_get_attributes) \ - F(item_get_info) \ - F(item_info_get_secret) + F(result_to_message) // Define the actual function pointers that we'll use in application code. #define GNOME_KEYRING_DEFINE_WRAPPER(name) \ @@ -83,16 +80,6 @@ const struct { wrap_gnome_keyring_find_itemsv #define gnome_keyring_result_to_message \ wrap_gnome_keyring_result_to_message -#define gnome_keyring_list_keyring_names \ - wrap_gnome_keyring_list_keyring_names -#define gnome_keyring_list_item_ids \ - wrap_gnome_keyring_list_item_ids -#define gnome_keyring_item_get_attributes \ - wrap_gnome_keyring_item_get_attributes -#define gnome_keyring_item_get_info \ - wrap_gnome_keyring_item_get_info -#define gnome_keyring_item_info_get_secret \ - wrap_gnome_keyring_item_info_get_secret /* Load the library and initialize the function pointers. */ bool LoadGnomeKeyring() { @@ -123,14 +110,13 @@ bool LoadGnomeKeyring() { #else // !defined(DLOPEN_GNOME_KEYRING) bool LoadGnomeKeyring() { - // We don't need to do anything here. When linking directly, we also assume - // that whoever is compiling this code has checked that the version is OK. + // We don't need to do anything here. return true; } #endif // !defined(DLOPEN_GNOME_KEYRING) -#define GNOME_KEYRING_APPLICATION_CHROME "chrome" +const char kGnomeKeyringAppString[] = "chrome"; // Convert the attributes of a given keyring entry into a new PasswordForm. // Note: does *not* get the actual password, as that is not a key attribute! @@ -147,7 +133,8 @@ PasswordForm* FormFromAttributes(GnomeKeyringAttributeList* attrs) { uint_attr_map[attr.name] = attr.value.integer; } // Check to make sure this is a password we care about. - if (string_attr_map["application"] != GNOME_KEYRING_APPLICATION_CHROME) + const std::string& app_value = string_attr_map["application"]; + if (!base::StringPiece(app_value).starts_with(kGnomeKeyringAppString)) return NULL; PasswordForm* form = new PasswordForm(); @@ -241,13 +228,14 @@ class GKRMethod { GKRMethod() : event_(false, false), result_(GNOME_KEYRING_RESULT_CANCELLED) {} // Action methods. These call gnome_keyring_* functions. Call from UI thread. - void AddLogin(const PasswordForm& form); - void AddLoginSearch(const PasswordForm& form); - void UpdateLoginSearch(const PasswordForm& form); - void RemoveLogin(const PasswordForm& form); - void GetLogins(const PasswordForm& form); - void GetLoginsList(uint32_t blacklisted_by_user); - void GetAllLogins(); + // See GetProfileSpecificAppString() for more information on the app string. + void AddLogin(const PasswordForm& form, const char* app_string); + void AddLoginSearch(const PasswordForm& form, const char* app_string); + void UpdateLoginSearch(const PasswordForm& form, const char* app_string); + void RemoveLogin(const PasswordForm& form, const char* app_string); + void GetLogins(const PasswordForm& form, const char* app_string); + void GetLoginsList(uint32_t blacklisted_by_user, const char* app_string); + void GetAllLogins(const char* app_string); // Use after AddLogin, RemoveLogin. GnomeKeyringResult WaitResult(); @@ -268,7 +256,7 @@ class GKRMethod { NativeBackendGnome::PasswordFormList forms_; }; -void GKRMethod::AddLogin(const PasswordForm& form) { +void GKRMethod::AddLogin(const PasswordForm& form, const char* app_string) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); time_t date_created = form.date_created.ToTimeT(); // If we are asked to save a password with 0 date, use the current time. @@ -295,11 +283,12 @@ void GKRMethod::AddLogin(const PasswordForm& form) { "date_created", base::Int64ToString(date_created).c_str(), "blacklisted_by_user", form.blacklisted_by_user, "scheme", form.scheme, - "application", GNOME_KEYRING_APPLICATION_CHROME, + "application", app_string, NULL); } -void GKRMethod::AddLoginSearch(const PasswordForm& form) { +void GKRMethod::AddLoginSearch(const PasswordForm& form, + const char* app_string) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // Search GNOME Keyring for matching passwords to update. gnome_keyring_find_itemsv( @@ -320,11 +309,12 @@ void GKRMethod::AddLoginSearch(const PasswordForm& form) { "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, form.signon_realm.c_str(), "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, - GNOME_KEYRING_APPLICATION_CHROME, + app_string, NULL); } -void GKRMethod::UpdateLoginSearch(const PasswordForm& form) { +void GKRMethod::UpdateLoginSearch(const PasswordForm& form, + const char* app_string) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // Search GNOME Keyring for matching passwords to update. gnome_keyring_find_itemsv( @@ -343,11 +333,11 @@ void GKRMethod::UpdateLoginSearch(const PasswordForm& form) { "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, form.signon_realm.c_str(), "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, - GNOME_KEYRING_APPLICATION_CHROME, + app_string, NULL); } -void GKRMethod::RemoveLogin(const PasswordForm& form) { +void GKRMethod::RemoveLogin(const PasswordForm& form, const char* app_string) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // We find forms using the same fields as LoginDatabase::RemoveLogin(). gnome_keyring_delete_password( @@ -365,7 +355,7 @@ void GKRMethod::RemoveLogin(const PasswordForm& form) { NULL); } -void GKRMethod::GetLogins(const PasswordForm& form) { +void GKRMethod::GetLogins(const PasswordForm& form, const char* app_string) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // Search GNOME Keyring for matching passwords. gnome_keyring_find_itemsv( @@ -376,11 +366,12 @@ void GKRMethod::GetLogins(const PasswordForm& form) { "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, form.signon_realm.c_str(), "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, - GNOME_KEYRING_APPLICATION_CHROME, + app_string, NULL); } -void GKRMethod::GetLoginsList(uint32_t blacklisted_by_user) { +void GKRMethod::GetLoginsList(uint32_t blacklisted_by_user, + const char* app_string) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // Search GNOME Keyring for matching passwords. gnome_keyring_find_itemsv( @@ -391,11 +382,11 @@ void GKRMethod::GetLoginsList(uint32_t blacklisted_by_user) { "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32, blacklisted_by_user, "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, - GNOME_KEYRING_APPLICATION_CHROME, + app_string, NULL); } -void GKRMethod::GetAllLogins() { +void GKRMethod::GetAllLogins(const char* app_string) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // We need to search for something, otherwise we get no results - so // we search for the fixed application string. @@ -405,7 +396,7 @@ void GKRMethod::GetAllLogins() { this, // data NULL, // destroy_data "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, - GNOME_KEYRING_APPLICATION_CHROME, + app_string, NULL); } @@ -450,7 +441,16 @@ struct RunnableMethodTraits<GKRMethod> { void ReleaseCallee(GKRMethod*) {} }; -NativeBackendGnome::NativeBackendGnome() { +NativeBackendGnome::NativeBackendGnome(LocalProfileId id, PrefService* prefs) + : profile_id_(id), prefs_(prefs) { + if (PasswordStoreX::PasswordsUseLocalProfileId(prefs)) { + app_string_ = GetProfileSpecificAppString(); + // We already did the migration previously. Don't try again. + migrate_tried_ = true; + } else { + app_string_ = kGnomeKeyringAppString; + migrate_tried_ = false; + } } NativeBackendGnome::~NativeBackendGnome() { @@ -466,13 +466,16 @@ bool NativeBackendGnome::RawAddLogin(const PasswordForm& form) { BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, NewRunnableMethod(&method, &GKRMethod::AddLogin, - form)); + form, app_string_.c_str())); GnomeKeyringResult result = method.WaitResult(); if (result != GNOME_KEYRING_RESULT_OK) { LOG(ERROR) << "Keyring save failed: " << gnome_keyring_result_to_message(result); return false; } + // Successful write. Try migration if necessary. + if (!migrate_tried_) + MigrateToProfileSpecificLogins(); return true; } @@ -485,9 +488,9 @@ bool NativeBackendGnome::AddLogin(const PasswordForm& form) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); GKRMethod method; BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - NewRunnableMethod(&method, - &GKRMethod::AddLoginSearch, - form)); + NewRunnableMethod(&method, + &GKRMethod::AddLoginSearch, + form, app_string_.c_str())); PasswordFormList forms; GnomeKeyringResult result = method.WaitResult(&forms); if (result != GNOME_KEYRING_RESULT_OK && @@ -498,9 +501,15 @@ bool NativeBackendGnome::AddLogin(const PasswordForm& form) { } if (forms.size() > 0) { if (forms.size() > 1) { - LOG(WARNING) << "Adding login when there are " << forms.size() << - " matching logins already! Will replace only the first."; + LOG(WARNING) << "Adding login when there are " << forms.size() + << " matching logins already! Will replace only the first."; } + + // We try migration before updating the existing logins, since otherwise + // we'd do it after making some but not all of the changes below. + if (forms.size() > 0 && !migrate_tried_) + MigrateToProfileSpecificLogins(); + RemoveLogin(*forms[0]); for (size_t i = 0; i < forms.size(); ++i) delete forms[i]; @@ -521,7 +530,7 @@ bool NativeBackendGnome::UpdateLogin(const PasswordForm& form) { BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, NewRunnableMethod(&method, &GKRMethod::UpdateLoginSearch, - form)); + form, app_string_.c_str())); PasswordFormList forms; GnomeKeyringResult result = method.WaitResult(&forms); if (result != GNOME_KEYRING_RESULT_OK) { @@ -529,6 +538,12 @@ bool NativeBackendGnome::UpdateLogin(const PasswordForm& form) { << gnome_keyring_result_to_message(result); return false; } + + // We try migration before updating the existing logins, since otherwise + // we'd do it after making some but not all of the changes below. + if (forms.size() > 0 && !migrate_tried_) + MigrateToProfileSpecificLogins(); + bool ok = true; for (size_t i = 0; i < forms.size(); ++i) { if (forms[i]->action != form.action || @@ -561,13 +576,18 @@ bool NativeBackendGnome::RemoveLogin(const PasswordForm& form) { BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, NewRunnableMethod(&method, &GKRMethod::RemoveLogin, - form)); + form, app_string_.c_str())); GnomeKeyringResult result = method.WaitResult(); if (result != GNOME_KEYRING_RESULT_OK) { LOG(ERROR) << "Keyring delete failed: " << gnome_keyring_result_to_message(result); return false; } + // Successful write. Try migration if necessary. Note that presumably if we've + // been asked to delete a login, it's because we returned it previously; thus, + // this will probably never happen since we'd have already tried migration. + if (!migrate_tried_) + MigrateToProfileSpecificLogins(); return true; } @@ -581,6 +601,7 @@ bool NativeBackendGnome::RemoveLoginsCreatedBetween( PasswordFormList forms; if (!GetAllLogins(&forms)) return false; + // No need to try migration here: GetAllLogins() does it. for (size_t i = 0; i < forms.size(); ++i) { if (delete_begin <= forms[i]->date_created && @@ -597,10 +618,10 @@ bool NativeBackendGnome::GetLogins(const PasswordForm& form, PasswordFormList* forms) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); GKRMethod method; - BrowserThread::PostTask( - BrowserThread::UI, - FROM_HERE, - NewRunnableMethod(&method, &GKRMethod::GetLogins, form)); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + NewRunnableMethod(&method, + &GKRMethod::GetLogins, + form, app_string_.c_str())); GnomeKeyringResult result = method.WaitResult(forms); if (result == GNOME_KEYRING_RESULT_NO_MATCH) return true; @@ -609,6 +630,9 @@ bool NativeBackendGnome::GetLogins(const PasswordForm& form, << gnome_keyring_result_to_message(result); return false; } + // Successful read of actual data. Try migration if necessary. + if (!migrate_tried_) + MigrateToProfileSpecificLogins(); return true; } @@ -621,6 +645,7 @@ bool NativeBackendGnome::GetLoginsCreatedBetween(const base::Time& get_begin, PasswordFormList all_forms; if (!GetAllLogins(&all_forms)) return false; + // No need to try migration here: GetAllLogins() does it. forms->reserve(forms->size() + all_forms.size()); for (size_t i = 0; i < all_forms.size(); ++i) { @@ -653,7 +678,8 @@ bool NativeBackendGnome::GetLoginsList(PasswordFormList* forms, BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, NewRunnableMethod(&method, &GKRMethod::GetLoginsList, - blacklisted_by_user)); + blacklisted_by_user, + app_string_.c_str())); GnomeKeyringResult result = method.WaitResult(forms); if (result == GNOME_KEYRING_RESULT_NO_MATCH) return true; @@ -662,6 +688,9 @@ bool NativeBackendGnome::GetLoginsList(PasswordFormList* forms, << gnome_keyring_result_to_message(result); return false; } + // Successful read of actual data. Try migration if necessary. + if (!migrate_tried_) + MigrateToProfileSpecificLogins(); return true; } @@ -669,7 +698,8 @@ bool NativeBackendGnome::GetAllLogins(PasswordFormList* forms) { GKRMethod method; BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, NewRunnableMethod(&method, - &GKRMethod::GetAllLogins)); + &GKRMethod::GetAllLogins, + app_string_.c_str())); GnomeKeyringResult result = method.WaitResult(forms); if (result == GNOME_KEYRING_RESULT_NO_MATCH) return true; @@ -678,5 +708,56 @@ bool NativeBackendGnome::GetAllLogins(PasswordFormList* forms) { << gnome_keyring_result_to_message(result); return false; } + // Successful read of actual data. Try migration if necessary. + if (!migrate_tried_) + MigrateToProfileSpecificLogins(); return true; } + +std::string NativeBackendGnome::GetProfileSpecificAppString() const { + // Originally, the application string was always just "chrome" and used only + // so that we had *something* to search for since GNOME Keyring won't search + // for nothing. Now we use it to distinguish passwords for different profiles. + return StringPrintf("%s-%d", kGnomeKeyringAppString, profile_id_); +} + +void NativeBackendGnome::MigrateToProfileSpecificLogins() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); + + DCHECK(!migrate_tried_); + DCHECK_EQ(app_string_, kGnomeKeyringAppString); + + // Record the fact that we've attempted migration already right away, so that + // we don't get recursive calls back to MigrateToProfileSpecificLogins(). + migrate_tried_ = true; + + // First get all the logins, using the old app string. + PasswordFormList forms; + if (!GetAllLogins(&forms)) + return; + + // Now switch to a profile-specific app string. + app_string_ = GetProfileSpecificAppString(); + + // Try to add all the logins with the new app string. + bool ok = true; + for (size_t i = 0; i < forms.size(); ++i) { + if (!RawAddLogin(*forms[i])) + ok = false; + delete forms[i]; + } + + if (ok) { + // All good! Keep the new app string and set a persistent pref. + // NOTE: We explicitly don't delete the old passwords yet. They are + // potentially shared with other profiles and other user data dirs! + // Each other profile must be able to migrate the shared data as well, + // so we must leave it alone. After a few releases, we'll add code to + // delete them, and eventually remove this migration code. + // TODO(mdm): follow through with the plan above. + PasswordStoreX::SetPasswordsUseLocalProfileId(prefs_); + } else { + // We failed to migrate for some reason. Use the old app string. + app_string_ = kGnomeKeyringAppString; + } +} diff --git a/chrome/browser/password_manager/native_backend_gnome_x.h b/chrome/browser/password_manager/native_backend_gnome_x.h index 3eebcfa..1740c42 100644 --- a/chrome/browser/password_manager/native_backend_gnome_x.h +++ b/chrome/browser/password_manager/native_backend_gnome_x.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,9 +6,14 @@ #define CHROME_BROWSER_PASSWORD_MANAGER_NATIVE_BACKEND_GNOME_X_H_ #pragma once +#include <string> + #include "base/basictypes.h" #include "base/time.h" #include "chrome/browser/password_manager/password_store_x.h" +#include "chrome/browser/profiles/profile.h" + +class PrefService; namespace webkit_glue { struct PasswordForm; @@ -17,7 +22,7 @@ struct PasswordForm; // NativeBackend implementation using GNOME Keyring. class NativeBackendGnome : public PasswordStoreX::NativeBackend { public: - NativeBackendGnome(); + NativeBackendGnome(LocalProfileId id, PrefService* prefs); virtual ~NativeBackendGnome(); @@ -47,6 +52,24 @@ class NativeBackendGnome : public PasswordStoreX::NativeBackend { // Helper for GetLoginsCreatedBetween(). bool GetAllLogins(PasswordFormList* forms); + // Generates a profile-specific app string based on profile_id_. + std::string GetProfileSpecificAppString() const; + + // Migrates non-profile-specific logins to be profile-specific. + void MigrateToProfileSpecificLogins(); + + // The local profile id, used to generate the app string. + const LocalProfileId profile_id_; + + // The pref service to use for persistent migration settings. + PrefService* prefs_; + + // The app string, possibly based on the local profile id. + std::string app_string_; + + // True once MigrateToProfileSpecificLogins() has been attempted. + bool migrate_tried_; + DISALLOW_COPY_AND_ASSIGN(NativeBackendGnome); }; diff --git a/chrome/browser/password_manager/native_backend_kwallet_x.cc b/chrome/browser/password_manager/native_backend_kwallet_x.cc index 2417a07..9e7c172 100644 --- a/chrome/browser/password_manager/native_backend_kwallet_x.cc +++ b/chrome/browser/password_manager/native_backend_kwallet_x.cc @@ -11,27 +11,37 @@ #include "base/stl_util-inl.h" #include "base/string_util.h" #include "content/browser/browser_thread.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 these strings, but then changing your locale would cause +// We could localize this string, but then changing your locale would cause // you to lose access to all your stored passwords. Maybe best not to do that. -const char* NativeBackendKWallet::kAppId = "Chrome"; -const char* NativeBackendKWallet::kKWalletFolder = "Chrome Form Data"; - -const char* NativeBackendKWallet::kKWalletServiceName = "org.kde.kwalletd"; -const char* NativeBackendKWallet::kKWalletPath = "/modules/kwalletd"; -const char* NativeBackendKWallet::kKWalletInterface = "org.kde.KWallet"; -const char* NativeBackendKWallet::kKLauncherServiceName = "org.kde.klauncher"; -const char* NativeBackendKWallet::kKLauncherPath = "/KLauncher"; -const char* NativeBackendKWallet::kKLauncherInterface = "org.kde.KLauncher"; - -NativeBackendKWallet::NativeBackendKWallet() - : error_(NULL), - connection_(NULL), - proxy_(NULL) { +const char NativeBackendKWallet::kKWalletFolder[] = "Chrome Form Data"; + +const char NativeBackendKWallet::kKWalletServiceName[] = "org.kde.kwalletd"; +const char NativeBackendKWallet::kKWalletPath[] = "/modules/kwalletd"; +const char NativeBackendKWallet::kKWalletInterface[] = "org.kde.KWallet"; +const char NativeBackendKWallet::kKLauncherServiceName[] = "org.kde.klauncher"; +const char NativeBackendKWallet::kKLauncherPath[] = "/KLauncher"; +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(); + // We already did the migration previously. Don't try again. + migrate_tried_ = true; + } else { + folder_name_ = kKWalletFolder; + migrate_tried_ = false; + } } NativeBackendKWallet::~NativeBackendKWallet() { @@ -202,8 +212,8 @@ bool NativeBackendKWallet::RemoveLoginsCreatedBetween( char** realm_list = NULL; dbus_g_proxy_call(proxy_, "entryList", &error_, G_TYPE_INT, wallet_handle, // handle - G_TYPE_STRING, kKWalletFolder, // folder - G_TYPE_STRING, kAppId, // appid + 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); @@ -215,9 +225,9 @@ bool NativeBackendKWallet::RemoveLoginsCreatedBetween( GArray* byte_array = NULL; dbus_g_proxy_call(proxy_, "readEntry", &error_, G_TYPE_INT, wallet_handle, // handle - G_TYPE_STRING, kKWalletFolder, // folder + G_TYPE_STRING, folder_name_.c_str(), // folder G_TYPE_STRING, *realm, // key - G_TYPE_STRING, kAppId, // appid + G_TYPE_STRING, app_name_.c_str(), // appid G_TYPE_INVALID, DBUS_TYPE_G_UCHAR_ARRAY, &byte_array, G_TYPE_INVALID); @@ -290,9 +300,9 @@ bool NativeBackendKWallet::GetLoginsList(PasswordFormList* forms, gboolean has_entry = false; dbus_g_proxy_call(proxy_, "hasEntry", &error_, G_TYPE_INT, wallet_handle, // handle - G_TYPE_STRING, kKWalletFolder, // folder + G_TYPE_STRING, folder_name_.c_str(), // folder G_TYPE_STRING, signon_realm.c_str(), // key - G_TYPE_STRING, kAppId, // appid + G_TYPE_STRING, app_name_.c_str(), // appid G_TYPE_INVALID, G_TYPE_BOOLEAN, &has_entry, G_TYPE_INVALID); @@ -307,9 +317,9 @@ bool NativeBackendKWallet::GetLoginsList(PasswordFormList* forms, GArray* byte_array = NULL; dbus_g_proxy_call(proxy_, "readEntry", &error_, G_TYPE_INT, wallet_handle, // handle - G_TYPE_STRING, kKWalletFolder, // folder + G_TYPE_STRING, folder_name_.c_str(), // folder G_TYPE_STRING, signon_realm.c_str(), // key - G_TYPE_STRING, kAppId, // appid + G_TYPE_STRING, app_name_.c_str(), // appid G_TYPE_INVALID, DBUS_TYPE_G_UCHAR_ARRAY, &byte_array, G_TYPE_INVALID); @@ -379,8 +389,8 @@ bool NativeBackendKWallet::GetAllLogins(PasswordFormList* forms, char** realm_list = NULL; dbus_g_proxy_call(proxy_, "entryList", &error_, G_TYPE_INT, wallet_handle, // handle - G_TYPE_STRING, kKWalletFolder, // folder - G_TYPE_STRING, kAppId, // appid + 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); @@ -391,9 +401,9 @@ bool NativeBackendKWallet::GetAllLogins(PasswordFormList* forms, GArray* byte_array = NULL; dbus_g_proxy_call(proxy_, "readEntry", &error_, G_TYPE_INT, wallet_handle, // handle - G_TYPE_STRING, kKWalletFolder, // folder + G_TYPE_STRING, folder_name_.c_str(), // folder G_TYPE_STRING, *realm, // key - G_TYPE_STRING, kAppId, // appid + G_TYPE_STRING, app_name_.c_str(), // appid G_TYPE_INVALID, DBUS_TYPE_G_UCHAR_ARRAY, &byte_array, G_TYPE_INVALID); @@ -419,9 +429,9 @@ bool NativeBackendKWallet::SetLoginsList(const PasswordFormList& forms, int ret = 0; dbus_g_proxy_call(proxy_, "removeEntry", &error_, G_TYPE_INT, wallet_handle, // handle - G_TYPE_STRING, kKWalletFolder, // folder + G_TYPE_STRING, folder_name_.c_str(), // folder G_TYPE_STRING, signon_realm.c_str(), // key - G_TYPE_STRING, kAppId, // appid + G_TYPE_STRING, app_name_.c_str(), // appid G_TYPE_INVALID, G_TYPE_INT, &ret, G_TYPE_INVALID); @@ -444,10 +454,10 @@ bool NativeBackendKWallet::SetLoginsList(const PasswordFormList& forms, int ret = 0; dbus_g_proxy_call(proxy_, "writeEntry", &error_, G_TYPE_INT, wallet_handle, // handle - G_TYPE_STRING, kKWalletFolder, // folder + 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, kAppId, // appid + G_TYPE_STRING, app_name_.c_str(), // appid G_TYPE_INVALID, G_TYPE_INT, &ret, G_TYPE_INVALID); @@ -592,11 +602,12 @@ bool NativeBackendKWallet::CheckError() { 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, kAppId, // appid + 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); @@ -606,9 +617,9 @@ int NativeBackendKWallet::WalletHandle() { // Check if our folder exists. gboolean has_folder = false; dbus_g_proxy_call(proxy_, "hasFolder", &error_, - G_TYPE_INT, handle, // handle - G_TYPE_STRING, kKWalletFolder, // folder - G_TYPE_STRING, kAppId, // appid + 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); @@ -619,9 +630,9 @@ int NativeBackendKWallet::WalletHandle() { if (!has_folder) { gboolean success = false; dbus_g_proxy_call(proxy_, "createFolder", &error_, - G_TYPE_INT, handle, // handle - G_TYPE_STRING, kKWalletFolder, // folder - G_TYPE_STRING, kAppId, // appid + 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); @@ -629,5 +640,63 @@ int NativeBackendKWallet::WalletHandle() { return kInvalidKWalletHandle; } + // Successful initialization. Try migration if necessary. + if (!migrate_tried_) + MigrateToProfileSpecificLogins(); + return handle; } + +std::string NativeBackendKWallet::GetProfileSpecificFolderName() const { + // Originally, the folder name was always just "Chrome Form Data". + // Now we use it to distinguish passwords for different profiles. + return StringPrintf("%s (%d)", kKWalletFolder, profile_id_); +} + +void NativeBackendKWallet::MigrateToProfileSpecificLogins() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); + + DCHECK(!migrate_tried_); + DCHECK_EQ(folder_name_, kKWalletFolder); + + // Record the fact that we've attempted migration already right away, so that + // we don't get recursive calls back to MigrateToProfileSpecificLogins(). + migrate_tried_ = true; + + // First get all the logins, using the old folder name. + int wallet_handle = WalletHandle(); + if (wallet_handle == kInvalidKWalletHandle) + return; + PasswordFormList forms; + if (!GetAllLogins(&forms, wallet_handle)) + return; + + // Now switch to a profile-specific folder name. + folder_name_ = GetProfileSpecificFolderName(); + + // Try to add all the logins with the new folder name. + // This could be done more efficiently by grouping by signon realm and using + // SetLoginsList(), but we do this for simplicity since it is only done once. + // Note, however, that we do need another call to WalletHandle() to create + // this folder if necessary. + bool ok = true; + for (size_t i = 0; i < forms.size(); ++i) { + if (!AddLogin(*forms[i])) + ok = false; + delete forms[i]; + } + + if (ok) { + // All good! Keep the new app string and set a persistent pref. + // NOTE: We explicitly don't delete the old passwords yet. They are + // potentially shared with other profiles and other user data dirs! + // Each other profile must be able to migrate the shared data as well, + // so we must leave it alone. After a few releases, we'll add code to + // delete them, and eventually remove this migration code. + // TODO(mdm): follow through with the plan above. + PasswordStoreX::SetPasswordsUseLocalProfileId(prefs_); + } else { + // We failed to migrate for some reason. Use the old folder name. + folder_name_ = kKWalletFolder; + } +} diff --git a/chrome/browser/password_manager/native_backend_kwallet_x.h b/chrome/browser/password_manager/native_backend_kwallet_x.h index b12d8ed..7481f28 100644 --- a/chrome/browser/password_manager/native_backend_kwallet_x.h +++ b/chrome/browser/password_manager/native_backend_kwallet_x.h @@ -14,14 +14,19 @@ #include "base/basictypes.h" #include "base/time.h" #include "chrome/browser/password_manager/password_store_x.h" -#include "webkit/glue/password_form.h" +#include "chrome/browser/profiles/profile.h" class Pickle; +class PrefService; + +namespace webkit_glue { +struct PasswordForm; +} // NativeBackend implementation using KWallet. class NativeBackendKWallet : public PasswordStoreX::NativeBackend { public: - NativeBackendKWallet(); + NativeBackendKWallet(LocalProfileId id, PrefService* prefs); virtual ~NativeBackendKWallet(); @@ -109,22 +114,38 @@ class NativeBackendKWallet : public PasswordStoreX::NativeBackend { // read old pickles. (Note: do not eat old pickles past the expiration date.) static const int kPickleVersion = 0; - // Name of the application - will appear in kwallet's dialogs. - static const char* kAppId; // Name of the folder to store passwords in. - static const char* kKWalletFolder; + static const char kKWalletFolder[]; // DBus stuff. - static const char* kKWalletServiceName; - static const char* kKWalletPath; - static const char* kKWalletInterface; - static const char* kKLauncherServiceName; - static const char* kKLauncherPath; - static const char* kKLauncherInterface; + static const char kKWalletServiceName[]; + static const char kKWalletPath[]; + static const char kKWalletInterface[]; + static const char kKLauncherServiceName[]; + static const char kKLauncherPath[]; + static const char kKLauncherInterface[]; // Invalid handle returned by WalletHandle(). static const int kInvalidKWalletHandle = -1; + // Generates a profile-specific folder name based on profile_id_. + std::string GetProfileSpecificFolderName() const; + + // Migrates non-profile-specific logins to be profile-specific. + void MigrateToProfileSpecificLogins(); + + // The local profile id, used to generate the folder name. + const LocalProfileId profile_id_; + + // The pref service to use for persistent migration settings. + PrefService* prefs_; + + // The KWallet folder name, possibly based on the local profile id. + std::string folder_name_; + + // 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_; @@ -135,6 +156,8 @@ class NativeBackendKWallet : public PasswordStoreX::NativeBackend { // The name of the wallet we've opened. Set during Init(). std::string wallet_name_; + // The application name (e.g. "Chromium"), shown in KWallet auth dialogs. + const std::string app_name_; DISALLOW_COPY_AND_ASSIGN(NativeBackendKWallet); }; diff --git a/chrome/browser/password_manager/password_store_x.cc b/chrome/browser/password_manager/password_store_x.cc index 9ffd31c..87a9e25 100644 --- a/chrome/browser/password_manager/password_store_x.cc +++ b/chrome/browser/password_manager/password_store_x.cc @@ -11,6 +11,8 @@ #include "base/logging.h" #include "base/stl_util-inl.h" #include "chrome/browser/password_manager/password_store_change.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/common/pref_names.h" #include "content/browser/browser_thread.h" #include "chrome/common/chrome_notification_types.h" #include "content/common/notification_service.h" @@ -262,3 +264,38 @@ ssize_t PasswordStoreX::MigrateLogins() { STLDeleteElements(&forms); return result; } + +// static +void PasswordStoreX::RegisterUserPrefs(PrefService* prefs) { + // Normally we should be on the UI thread here, but in tests we might not. + prefs->RegisterBooleanPref(prefs::kPasswordsUseLocalProfileId, + false, // default: passwords don't use local ids + PrefService::UNSYNCABLE_PREF); +} + +// static +bool PasswordStoreX::PasswordsUseLocalProfileId(PrefService* prefs) { + // Normally we should be on the UI thread here, but in tests we might not. + return prefs->GetBoolean(prefs::kPasswordsUseLocalProfileId); +} + +namespace { +// This function is a hack to do something not entirely thread safe: the pref +// service comes from the UI thread, but it's not ref counted. We keep a pointer +// to it on the DB thread, and need to invoke a method on the UI thread. This +// function does that for us without requiring ref counting the pref service. +// TODO(mdm): Fix this if it becomes a problem. Given that this function will +// be called once ever per profile, it probably will not cause a problem... +void UISetPasswordsUseLocalProfileId(PrefService* prefs) { + prefs->SetBoolean(prefs::kPasswordsUseLocalProfileId, true); +} +} // anonymous namespace + +// static +void PasswordStoreX::SetPasswordsUseLocalProfileId(PrefService* prefs) { + // This method should work on any thread, but we expect the DB thread. + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + NewRunnableFunction(UISetPasswordsUseLocalProfileId, + prefs)); +} diff --git a/chrome/browser/password_manager/password_store_x.h b/chrome/browser/password_manager/password_store_x.h index ee9ad30..100e09e 100644 --- a/chrome/browser/password_manager/password_store_x.h +++ b/chrome/browser/password_manager/password_store_x.h @@ -13,6 +13,7 @@ #include "chrome/browser/password_manager/password_store_default.h" class LoginDatabase; +class PrefService; class Profile; class WebDataService; @@ -56,6 +57,17 @@ class PasswordStoreX : public PasswordStoreDefault { WebDataService* web_data_service, NativeBackend* backend); + // Registers the pref setting used for the methods below. + static void RegisterUserPrefs(PrefService* prefs); + + // Returns true if passwords have been tagged with the local profile id. + static bool PasswordsUseLocalProfileId(PrefService* prefs); + + // Sets the persistent bit indicating that passwords have been tagged with the + // local profile id. This cannot be unset; passwords get migrated only once. + // The caller promises that |prefs| will not be deleted any time soon. + static void SetPasswordsUseLocalProfileId(PrefService* prefs); + private: friend class PasswordStoreXTest; diff --git a/chrome/browser/profiles/profile.cc b/chrome/browser/profiles/profile.cc index f9d095e..763bf86 100644 --- a/chrome/browser/profiles/profile.cc +++ b/chrome/browser/profiles/profile.cc @@ -60,16 +60,7 @@ #include "chrome/browser/ui/gtk/gtk_theme_service.h" #endif -#if defined(OS_WIN) -#include "chrome/browser/password_manager/password_store_win.h" -#elif defined(OS_MACOSX) -#include "chrome/browser/keychain_mac.h" -#include "chrome/browser/password_manager/password_store_mac.h" -#elif defined(OS_POSIX) && !defined(OS_CHROMEOS) -#include "chrome/browser/password_manager/native_backend_gnome_x.h" -#include "chrome/browser/password_manager/native_backend_kwallet_x.h" -#include "chrome/browser/password_manager/password_store_x.h" -#elif defined(OS_CHROMEOS) +#if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/preferences.h" #endif @@ -104,6 +95,12 @@ Profile::Profile() // static const char* Profile::kProfileKey = "__PROFILE__"; +#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS) && defined(OS_POSIX) +// static +const LocalProfileId Profile::kInvalidLocalProfileId = + static_cast<LocalProfileId>(0); +#endif + // static void Profile::RegisterUserPrefs(PrefService* prefs) { prefs->RegisterBooleanPref(prefs::kSearchSuggestEnabled, diff --git a/chrome/browser/profiles/profile.h b/chrome/browser/profiles/profile.h index 45423d1..af40235 100644 --- a/chrome/browser/profiles/profile.h +++ b/chrome/browser/profiles/profile.h @@ -102,6 +102,14 @@ namespace net { class URLRequestContextGetter; } +#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS) && defined(OS_POSIX) +// Local profile ids are used to associate resources stored outside the profile +// directory, like saved passwords in GNOME Keyring / KWallet, with a profile. +// With high probability, they are unique on the local machine. They are almost +// certainly not unique globally, by design. Do not send them over the network. +typedef int LocalProfileId; +#endif + class Profile { public: // Profile services are accessed with the following parameter. This parameter @@ -138,6 +146,11 @@ class Profile { // Key used to bind profile to the widget with which it is associated. static const char* kProfileKey; +#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS) && defined(OS_POSIX) + // Value that represents no local profile id. + static const LocalProfileId kInvalidLocalProfileId; +#endif + Profile(); virtual ~Profile() {} diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc index 0cbc653..8f333f3 100644 --- a/chrome/browser/profiles/profile_impl.cc +++ b/chrome/browser/profiles/profile_impl.cc @@ -270,6 +270,14 @@ void ProfileImpl::RegisterUserPrefs(PrefService* prefs) { prefs->RegisterBooleanPref(prefs::kClearSiteDataOnExit, false, PrefService::SYNCABLE_PREF); +#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS) && defined(OS_POSIX) + prefs->RegisterIntegerPref(prefs::kLocalProfileId, + kInvalidLocalProfileId, + PrefService::UNSYNCABLE_PREF); + // Notice that the preprocessor conditions above are exactly those that will + // result in using PasswordStoreX in CreatePasswordStore() below. + PasswordStoreX::RegisterUserPrefs(prefs); +#endif } ProfileImpl::ProfileImpl(const FilePath& path, @@ -1072,6 +1080,28 @@ PasswordStore* ProfileImpl::GetPasswordStore(ServiceAccessType sat) { return password_store_.get(); } +#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS) && defined(OS_POSIX) +LocalProfileId ProfileImpl::GetLocalProfileId() { + PrefService* prefs = GetPrefs(); + LocalProfileId id = prefs->GetInteger(prefs::kLocalProfileId); + if (id == kInvalidLocalProfileId) { + // Note that there are many more users than this. Thus, by design, this is + // not a unique id. However, it is large enough that it is very unlikely + // that it would be repeated twice on a single machine. It is still possible + // for that to occur though, so the potential results of it actually + // happening should be considered when using this value. + static const LocalProfileId kLocalProfileIdMask = + static_cast<LocalProfileId>((1 << 24) - 1); + do { + id = rand() & kLocalProfileIdMask; + // TODO(mdm): scan other profiles to make sure they are not using this id? + } while (id == kInvalidLocalProfileId); + prefs->SetInteger(prefs::kLocalProfileId, id); + } + return id; +} +#endif // !defined(OS_MACOSX) && !defined(OS_CHROMEOS) && defined(OS_POSIX) + void ProfileImpl::CreatePasswordStore() { DCHECK(!created_password_store_ && password_store_.get() == NULL); created_password_store_ = true; @@ -1121,7 +1151,7 @@ void ProfileImpl::CreatePasswordStore() { if (desktop_env == base::nix::DESKTOP_ENVIRONMENT_KDE4) { // KDE3 didn't use DBus, which our KWallet store uses. VLOG(1) << "Trying KWallet for password storage."; - backend.reset(new NativeBackendKWallet()); + backend.reset(new NativeBackendKWallet(GetLocalProfileId(), GetPrefs())); if (backend->Init()) VLOG(1) << "Using KWallet for password storage."; else @@ -1130,7 +1160,7 @@ void ProfileImpl::CreatePasswordStore() { desktop_env == base::nix::DESKTOP_ENVIRONMENT_XFCE) { #if defined(USE_GNOME_KEYRING) VLOG(1) << "Trying GNOME keyring for password storage."; - backend.reset(new NativeBackendGnome()); + backend.reset(new NativeBackendGnome(GetLocalProfileId(), GetPrefs())); if (backend->Init()) VLOG(1) << "Using GNOME keyring for password storage."; else diff --git a/chrome/browser/profiles/profile_impl.h b/chrome/browser/profiles/profile_impl.h index 8c4da34..7c405fc 100644 --- a/chrome/browser/profiles/profile_impl.h +++ b/chrome/browser/profiles/profile_impl.h @@ -160,6 +160,10 @@ class ProfileImpl : public Profile, void CreateWebDataService(); FilePath GetPrefFilePath(); +#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS) && defined(OS_POSIX) + LocalProfileId GetLocalProfileId(); +#endif + void CreatePasswordStore(); void StopCreateSessionServiceTimer(); diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index a7be7a3..25d54d8 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc @@ -713,6 +713,16 @@ const char kEnableHyperlinkAuditing[] = "enable_a_ping"; // Whether to enable sending referrers. const char kEnableReferrers[] = "enable_referrers"; +#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS) && defined(OS_POSIX) +// The local profile id for this profile. +const char kLocalProfileId[] = "profile.local_profile_id"; + +// Whether passwords in external services (e.g. GNOME Keyring) have been tagged +// with the local profile id yet. (Used for migrating to tagged passwords.) +const char kPasswordsUseLocalProfileId[] = + "profile.passwords_use_local_profile_id"; +#endif + // *************** LOCAL STATE *************** // These are attached to the machine/installation diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index 99f272c..7458130 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h @@ -245,6 +245,11 @@ extern const char kDisable3DAPIs[]; extern const char kEnableHyperlinkAuditing[]; extern const char kEnableReferrers[]; +#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS) && defined(OS_POSIX) +extern const char kLocalProfileId[]; +extern const char kPasswordsUseLocalProfileId[]; +#endif + // Local state prefs. Please add Profile prefs above instead. extern const char kCertRevocationCheckingEnabled[]; extern const char kSSL3Enabled[]; |