diff options
author | evan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-24 21:19:05 +0000 |
---|---|---|
committer | evan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-24 21:19:05 +0000 |
commit | 667fbe3cab5b02f2ce11cd20fe0974b3c1a23acf (patch) | |
tree | 111ae1e31526cf2154344a8e5bdd32673e58cf2d /chrome/browser/password_manager | |
parent | 1e0fe282a7f14796d7e17cd8ea40733309088583 (diff) | |
download | chromium_src-667fbe3cab5b02f2ce11cd20fe0974b3c1a23acf.zip chromium_src-667fbe3cab5b02f2ce11cd20fe0974b3c1a23acf.tar.gz chromium_src-667fbe3cab5b02f2ce11cd20fe0974b3c1a23acf.tar.bz2 |
linux: work around gnome keyring memory corruption
Older versions of gnome keyring corrupt memory if you fetch too much
data from it. Despite our best efforts, we couldn't come up with a way
to tell if you're using the broken old version so that we could blacklist
it. Instead, we fetch passwords one-by-one, which is slow but tolerable
since we do it on a background thread anyway.
While I'm here, refactor the code a bit to simplify it.
TEST=password manager doesn't crash when using mdm's sample 100 password db
with --password-store=gnome on hardy
Review URL: http://codereview.chromium.org/2838023
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@50771 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/password_manager')
-rw-r--r-- | chrome/browser/password_manager/native_backend_gnome_x.cc | 276 |
1 files changed, 171 insertions, 105 deletions
diff --git a/chrome/browser/password_manager/native_backend_gnome_x.cc b/chrome/browser/password_manager/native_backend_gnome_x.cc index dbf617b..124ae40b 100644 --- a/chrome/browser/password_manager/native_backend_gnome_x.cc +++ b/chrome/browser/password_manager/native_backend_gnome_x.cc @@ -4,13 +4,12 @@ #include "chrome/browser/password_manager/native_backend_gnome_x.h" -#if defined(DLOPEN_GNOME_KEYRING) -#include <dlfcn.h> -#endif - #include <map> #include <string> +#include <dbus/dbus-glib.h> +#include <dlfcn.h> + #include "base/logging.h" #include "base/string_util.h" #include "base/time.h" @@ -19,6 +18,8 @@ using webkit_glue::PasswordForm; +namespace { + /* Many of the gnome_keyring_* functions use variable arguments, which makes * them difficult if not impossible to wrap in C. Therefore, we want the * actual uses below to either call the functions directly (if we are linking @@ -33,53 +34,43 @@ using webkit_glue::PasswordForm; #if defined(DLOPEN_GNOME_KEYRING) -namespace { - -gboolean (*wrap_gnome_keyring_is_available)(); -GnomeKeyringResult (*wrap_gnome_keyring_store_password_sync)( // NOLINT - const GnomeKeyringPasswordSchema* schema, const gchar* keyring, - const gchar* display_name, const gchar* password, ...); -GnomeKeyringResult (*wrap_gnome_keyring_delete_password_sync)( // NOLINT - const GnomeKeyringPasswordSchema* schema, ...); -GnomeKeyringResult (*wrap_gnome_keyring_find_itemsv_sync)( // NOLINT - GnomeKeyringItemType type, GList** found, ...); -const gchar* (*wrap_gnome_keyring_result_to_message)(GnomeKeyringResult res); -void (*wrap_gnome_keyring_found_list_free)(GList* found_list); - -/* Cause the compiler to complain if the types of the above function pointers - * do not correspond to the types of the actual gnome_keyring_* functions. */ -#define GNOME_KEYRING_VERIFY_TYPE(name) \ - typeof(&gnome_keyring_##name) name = wrap_gnome_keyring_##name; name = name - -inline void VerifyGnomeKeyringTypes() { - GNOME_KEYRING_VERIFY_TYPE(is_available); - GNOME_KEYRING_VERIFY_TYPE(store_password_sync); - GNOME_KEYRING_VERIFY_TYPE(delete_password_sync); - GNOME_KEYRING_VERIFY_TYPE(find_itemsv_sync); - GNOME_KEYRING_VERIFY_TYPE(result_to_message); - GNOME_KEYRING_VERIFY_TYPE(found_list_free); -} -#undef GNOME_KEYRING_VERIFY_TYPE - -/* Make it easy to initialize the function pointers above with a loop below. */ +// Call a given parameter with the name of each func we use from +// gnome keyring. +#define GNOME_KEYRING_FOR_EACH_FUNC(F) \ + F(is_available) \ + F(store_password_sync) \ + F(delete_password_sync) \ + F(find_itemsv_sync) \ + F(result_to_message) \ + F(found_list_free) \ + F(list_item_ids_sync) \ + F(item_get_attributes_sync) \ + F(attribute_list_free) \ + F(item_get_info_sync) \ + F(item_info_get_secret) \ + F(item_info_free) + +#define GNOME_KEYRING_DECLARE_TYPE(name) \ + typeof(&gnome_keyring_##name) wrap_gnome_keyring_##name; +GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_DECLARE_TYPE) +#undef GNOME_KEYRING_DECLARE_TYPE + +// Make it easy to initialize the function pointers above with a loop below. #define GNOME_KEYRING_FUNCTION(name) \ - {#name, reinterpret_cast<void**>(&wrap_##name)} + {"gnome_keyring_"#name, reinterpret_cast<void**>(&wrap_gnome_keyring_##name)}, const struct { const char* name; void** pointer; } gnome_keyring_functions[] = { - GNOME_KEYRING_FUNCTION(gnome_keyring_is_available), - GNOME_KEYRING_FUNCTION(gnome_keyring_store_password_sync), - GNOME_KEYRING_FUNCTION(gnome_keyring_delete_password_sync), - GNOME_KEYRING_FUNCTION(gnome_keyring_find_itemsv_sync), - GNOME_KEYRING_FUNCTION(gnome_keyring_result_to_message), - GNOME_KEYRING_FUNCTION(gnome_keyring_found_list_free), - {NULL, NULL} + GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_FUNCTION) + {NULL, NULL} }; #undef GNOME_KEYRING_FUNCTION -/* Allow application code below to use the normal function names, but actually - * end up using the function pointers above instead. */ +#undef GNOME_KEYRING_FOR_EACH_FUNC + +// Allow application code below to use the normal function names, but actually +// end up using the function pointers above instead. #define gnome_keyring_is_available \ wrap_gnome_keyring_is_available #define gnome_keyring_store_password_sync \ @@ -92,20 +83,18 @@ const struct { wrap_gnome_keyring_result_to_message #define gnome_keyring_found_list_free \ wrap_gnome_keyring_found_list_free - -// Older versions of GNOME Keyring have bugs that prevent them from working -// correctly with this code. (In particular, the non-pageable memory allocator -// is rather busted.) There is no official way to check the version, but newer -// versions provide several symbols that older versions did not. It would be -// best if we could check for a new official API, but the only new symbols are -// "private" symbols. Still, it is probable that they will not change, and it's -// the best we can do for now. (And eventually we won't care about the older -// versions anyway so we can remove this version check some day.) -bool GnomeKeyringVersionOK(void* handle) { - dlerror(); - dlsym(handle, "gnome_keyring_socket_connect_daemon"); - return !dlerror(); -} +#define gnome_keyring_list_item_ids_sync \ + wrap_gnome_keyring_list_item_ids_sync +#define gnome_keyring_item_get_attributes_sync \ + wrap_gnome_keyring_item_get_attributes_sync +#define gnome_keyring_attribute_list_free \ + wrap_gnome_keyring_attribute_list_free +#define gnome_keyring_item_get_info_sync \ + wrap_gnome_keyring_item_get_info_sync +#define gnome_keyring_item_info_get_secret \ + wrap_gnome_keyring_item_info_get_secret +#define gnome_keyring_item_info_free \ + wrap_gnome_keyring_item_info_free /* Load the library and initialize the function pointers. */ bool LoadGnomeKeyring() { @@ -117,13 +106,6 @@ bool LoadGnomeKeyring() { LOG(WARNING) << "Could not load libgnome-keyring.so.0: " << dlerror(); return false; } - if (!GnomeKeyringVersionOK(handle)) { - // GNOME Keyring is too old. Only info, not a warning, as this can happen - // on older systems without the user actually asking for it explicitly. - LOG(INFO) << "libgnome-keyring.so.0 is too old!"; - dlclose(handle); - return false; - } for (size_t i = 0; gnome_keyring_functions[i].name; ++i) { dlerror(); *gnome_keyring_functions[i].pointer = @@ -140,24 +122,73 @@ bool LoadGnomeKeyring() { return true; } -} // namespace +// Older versions of GNOME Keyring have bugs that prevent them from +// working correctly with the find_itemsv API. (In particular, the +// non-pageable memory allocator is rather busted.) There is no +// official way to check the version, nor could we figure out any +// reasonable unofficial way to do it. So we work around it by using +// a much slower API. +#define GNOME_KEYRING_WORKAROUND_MEMORY_CORRUPTION #else // !defined(DLOPEN_GNOME_KEYRING) -namespace { - bool LoadGnomeKeyring() { - // We don't do the hacky version check here. When linking directly, we assume - // that whoever is compiling this code has checked that the version is OK. + // We don't need to do anything here. When linking directly, we + // assume that whoever is compiling this code has checked that the + // version is OK. return true; } -} // namespace - #endif // !defined(DLOPEN_GNOME_KEYRING) #define GNOME_KEYRING_APPLICATION_CHROME "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! Returns NULL if the attributes are for the +// wrong application. +PasswordForm* FormFromAttributes(GnomeKeyringAttributeList* attrs) { + // Read the string and int attributes into the appropriate map. + std::map<std::string, std::string> string_attr_map; + std::map<std::string, uint32_t> uint_attr_map; + for (guint i = 0; i < attrs->len; ++i) { + GnomeKeyringAttribute attr = gnome_keyring_attribute_list_index(attrs, i); + if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING) { + if (std::string(attr.name) == "application" && + std::string(attr.value.string) != GNOME_KEYRING_APPLICATION_CHROME) { + // This is not a password we care about. + return NULL; + } + string_attr_map[attr.name] = attr.value.string; + } else if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32) { + uint_attr_map[attr.name] = attr.value.integer; + } + } + + PasswordForm* form = new PasswordForm(); + form->origin = GURL(string_attr_map["origin_url"]); + form->action = GURL(string_attr_map["action_url"]); + form->username_element = UTF8ToUTF16(string_attr_map["username_element"]); + form->username_value = UTF8ToUTF16(string_attr_map["username_value"]); + form->password_element = UTF8ToUTF16(string_attr_map["password_element"]); + form->submit_element = UTF8ToUTF16(string_attr_map["submit_element"]); + form->signon_realm = string_attr_map["signon_realm"]; + form->ssl_valid = uint_attr_map["ssl_valid"]; + form->preferred = uint_attr_map["preferred"]; + int64 date_created = 0; + bool date_ok = StringToInt64(string_attr_map["date_created"], + &date_created); + DCHECK(date_ok); + DCHECK_NE(date_created, 0); + form->date_created = base::Time::FromTimeT(date_created); + form->blacklisted_by_user = uint_attr_map["blacklisted_by_user"]; + form->scheme = static_cast<PasswordForm::Scheme>(uint_attr_map["scheme"]); + + return form; +} + +} // namespace + // Schema is analagous to the fields in PasswordForm. const GnomeKeyringPasswordSchema NativeBackendGnome::kGnomeSchema = { GNOME_KEYRING_ITEM_GENERIC_SECRET, { @@ -374,8 +405,11 @@ bool NativeBackendGnome::GetBlacklistLogins(PasswordFormList* forms) { bool NativeBackendGnome::GetLoginsList(PasswordFormList* forms, bool autofillable) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB)); - GList* found = NULL; + uint32_t blacklisted_by_user = !autofillable; + +#if !defined(GNOME_KEYRING_WORKAROUND_MEMORY_CORRUPTION) + GList* found = NULL; // Search gnome keyring for matching passwords. GnomeKeyringResult result = gnome_keyring_find_itemsv_sync( GNOME_KEYRING_ITEM_GENERIC_SECRET, @@ -394,12 +428,33 @@ bool NativeBackendGnome::GetLoginsList(PasswordFormList* forms, } ConvertFormList(found, forms); return true; +#else + PasswordFormList all_forms; + if (!GetAllLogins(&all_forms)) + return false; + // Now manually filter the result for the values we care about. + for (size_t i = 0; i < all_forms.size(); ++i) { + if (all_forms[i]->blacklisted_by_user == blacklisted_by_user) + forms->push_back(all_forms[i]); + else + delete all_forms[i]; + } + return true; +#endif } bool NativeBackendGnome::GetAllLogins(PasswordFormList* forms) { + // Older versions of GNOME Keyring have bugs that prevent them from + // working correctly with the find_itemsv API. (In particular, the + // non-pageable memory allocator is rather busted.) There is no + // official way to check the version, nor could we figure out any + // reasonable unofficial way to do it. So we work around it by + // using a much slower API. + +#if !defined(GNOME_KEYRING_WORKAROUND_MEMORY_CORRUPTION) GList* found = NULL; - // We need to search for something, otherwise we get no results - so we search - // for the fixed application string. + // We need to search for something, otherwise we get no results - so + // we search for the fixed application string. GnomeKeyringResult result = gnome_keyring_find_itemsv_sync( GNOME_KEYRING_ITEM_GENERIC_SECRET, &found, @@ -415,6 +470,47 @@ bool NativeBackendGnome::GetAllLogins(PasswordFormList* forms) { } ConvertFormList(found, forms); return true; +#else + GList* ids = NULL; + GnomeKeyringResult result = gnome_keyring_list_item_ids_sync(NULL, &ids); + if (result != GNOME_KEYRING_RESULT_OK) { + LOG(ERROR) << "Keyring itemid list failed: " + << gnome_keyring_result_to_message(result); + return false; + } + + for (GList* i = ids; i; i = i->next) { + int id = GPOINTER_TO_INT(i->data); + GnomeKeyringAttributeList* attrs = NULL; + result = gnome_keyring_item_get_attributes_sync(NULL, id, &attrs); + if (result != GNOME_KEYRING_RESULT_OK) { + LOG(ERROR) << "Keyring get item attributes failed:" + << gnome_keyring_result_to_message(result); + gnome_keyring_attribute_list_free(attrs); + break; + } + + PasswordForm* form = FormFromAttributes(attrs); + gnome_keyring_attribute_list_free(attrs); + + if (form) { + GnomeKeyringItemInfo* info = NULL; + result = gnome_keyring_item_get_info_sync(NULL, id, &info); + if (result != GNOME_KEYRING_RESULT_OK) { + delete form; + break; + } + form->password_value = UTF8ToUTF16( + gnome_keyring_item_info_get_secret(info)); + + gnome_keyring_item_info_free(info); + forms->push_back(form); + } + } + g_list_free(ids); + + return result == GNOME_KEYRING_RESULT_OK; +#endif } void NativeBackendGnome::ConvertFormList(GList* found, @@ -422,40 +518,10 @@ void NativeBackendGnome::ConvertFormList(GList* found, GList* element = g_list_first(found); while (element != NULL) { GnomeKeyringFound* data = static_cast<GnomeKeyringFound*>(element->data); - GnomeKeyringAttributeList* attrs = data->attributes; - // Read the string and int attributes into the appropriate map. - std::map<std::string, std::string> string_attr_map; - std::map<std::string, uint32_t> uint_attr_map; - for (guint i = 0; i < attrs->len; ++i) { - GnomeKeyringAttribute attr = gnome_keyring_attribute_list_index(attrs, i); - if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING) { - string_attr_map[attr.name] = attr.value.string; - } else if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32) { - uint_attr_map[attr.name] = attr.value.integer; - } - } - PasswordForm* form = new PasswordForm(); - form->origin = GURL(string_attr_map["origin_url"]); - form->action = GURL(string_attr_map["action_url"]); - form->username_element = UTF8ToUTF16(string_attr_map["username_element"]); - form->username_value = UTF8ToUTF16(string_attr_map["username_value"]); - form->password_element = UTF8ToUTF16(string_attr_map["password_element"]); + PasswordForm* form = FormFromAttributes(attrs); form->password_value = UTF8ToUTF16(data->secret); - form->submit_element = UTF8ToUTF16(string_attr_map["submit_element"]); - form->signon_realm = string_attr_map["signon_realm"]; - form->ssl_valid = uint_attr_map["ssl_valid"]; - form->preferred = uint_attr_map["preferred"]; - int64 date_created = 0; - bool date_ok = StringToInt64(string_attr_map["date_created"], - &date_created); - DCHECK(date_ok); - DCHECK_NE(date_created, 0); - form->date_created = base::Time::FromTimeT(date_created); - form->blacklisted_by_user = uint_attr_map["blacklisted_by_user"]; - form->scheme = static_cast<PasswordForm::Scheme>(uint_attr_map["scheme"]); - forms->push_back(form); element = g_list_next(element); |