| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/password_manager/native_backend_gnome_x.h" | 5 #include "chrome/browser/password_manager/native_backend_gnome_x.h" |
| 6 | 6 |
| 7 #include <dlfcn.h> | |
| 8 #include <gnome-keyring.h> | 7 #include <gnome-keyring.h> |
| 9 | 8 |
| 10 #include <map> | |
| 11 #include <string> | 9 #include <string> |
| 12 #include <vector> | |
| 13 | 10 |
| 14 #include "base/logging.h" | 11 #include "base/logging.h" |
| 15 #include "base/string_number_conversions.h" | 12 #include "base/string_number_conversions.h" |
| 16 #include "base/string_piece.h" | |
| 17 #include "base/string_util.h" | |
| 18 #include "base/stringprintf.h" | 13 #include "base/stringprintf.h" |
| 19 #include "base/synchronization/waitable_event.h" | 14 #include "chrome/browser/password_manager/keyring_proxy/keyring_proxy_client.h" |
| 20 #include "base/time.h" | |
| 21 #include "base/utf_string_conversions.h" | |
| 22 #include "content/public/browser/browser_thread.h" | 15 #include "content/public/browser/browser_thread.h" |
| 23 | 16 |
| 24 using content::BrowserThread; | 17 using content::BrowserThread; |
| 18 using keyring_proxy::KeyringProxyClient; |
| 25 using webkit_glue::PasswordForm; | 19 using webkit_glue::PasswordForm; |
| 26 | 20 |
| 27 #define GNOME_KEYRING_DEFINE_POINTER(name) \ | 21 const char NativeBackendGnome::kGnomeKeyringAppString[] = "chrome"; |
| 28 typeof(&::gnome_keyring_##name) GnomeKeyringLoader::gnome_keyring_##name; | |
| 29 GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_DEFINE_POINTER) | |
| 30 #undef GNOME_KEYRING_DEFINE_POINTER | |
| 31 | |
| 32 bool GnomeKeyringLoader::keyring_loaded = false; | |
| 33 | |
| 34 #if defined(DLOPEN_GNOME_KEYRING) | |
| 35 | |
| 36 #define GNOME_KEYRING_FUNCTION_INFO(name) \ | |
| 37 {"gnome_keyring_"#name, reinterpret_cast<void**>(&gnome_keyring_##name)}, | |
| 38 const GnomeKeyringLoader::FunctionInfo GnomeKeyringLoader::functions[] = { | |
| 39 GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_FUNCTION_INFO) | |
| 40 {NULL, NULL} | |
| 41 }; | |
| 42 #undef GNOME_KEYRING_FUNCTION_INFO | |
| 43 | |
| 44 /* Load the library and initialize the function pointers. */ | |
| 45 bool GnomeKeyringLoader::LoadGnomeKeyring() { | |
| 46 if (keyring_loaded) | |
| 47 return true; | |
| 48 | |
| 49 void* handle = dlopen("libgnome-keyring.so.0", RTLD_NOW | RTLD_GLOBAL); | |
| 50 if (!handle) { | |
| 51 // We wanted to use GNOME Keyring, but we couldn't load it. Warn, because | |
| 52 // either the user asked for this, or we autodetected it incorrectly. (Or | |
| 53 // the system has broken libraries, which is also good to warn about.) | |
| 54 LOG(WARNING) << "Could not load libgnome-keyring.so.0: " << dlerror(); | |
| 55 return false; | |
| 56 } | |
| 57 | |
| 58 for (size_t i = 0; functions[i].name; ++i) { | |
| 59 dlerror(); | |
| 60 *functions[i].pointer = dlsym(handle, functions[i].name); | |
| 61 const char* error = dlerror(); | |
| 62 if (error) { | |
| 63 LOG(ERROR) << "Unable to load symbol " | |
| 64 << functions[i].name << ": " << error; | |
| 65 dlclose(handle); | |
| 66 return false; | |
| 67 } | |
| 68 } | |
| 69 | |
| 70 keyring_loaded = true; | |
| 71 // We leak the library handle. That's OK: this function is called only once. | |
| 72 return true; | |
| 73 } | |
| 74 | |
| 75 #else // defined(DLOPEN_GNOME_KEYRING) | |
| 76 | |
| 77 bool GnomeKeyringLoader::LoadGnomeKeyring() { | |
| 78 if (keyring_loaded) | |
| 79 return true; | |
| 80 #define GNOME_KEYRING_ASSIGN_POINTER(name) \ | |
| 81 gnome_keyring_##name = &::gnome_keyring_##name; | |
| 82 GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_ASSIGN_POINTER) | |
| 83 #undef GNOME_KEYRING_ASSIGN_POINTER | |
| 84 keyring_loaded = true; | |
| 85 return true; | |
| 86 } | |
| 87 | |
| 88 #endif // defined(DLOPEN_GNOME_KEYRING) | |
| 89 | |
| 90 namespace { | |
| 91 | |
| 92 const char kGnomeKeyringAppString[] = "chrome"; | |
| 93 | |
| 94 // Convert the attributes of a given keyring entry into a new PasswordForm. | |
| 95 // Note: does *not* get the actual password, as that is not a key attribute! | |
| 96 // Returns NULL if the attributes are for the wrong application. | |
| 97 PasswordForm* FormFromAttributes(GnomeKeyringAttributeList* attrs) { | |
| 98 // Read the string and int attributes into the appropriate map. | |
| 99 std::map<std::string, std::string> string_attr_map; | |
| 100 std::map<std::string, uint32_t> uint_attr_map; | |
| 101 for (guint i = 0; i < attrs->len; ++i) { | |
| 102 GnomeKeyringAttribute attr = gnome_keyring_attribute_list_index(attrs, i); | |
| 103 if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING) | |
| 104 string_attr_map[attr.name] = attr.value.string; | |
| 105 else if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32) | |
| 106 uint_attr_map[attr.name] = attr.value.integer; | |
| 107 } | |
| 108 // Check to make sure this is a password we care about. | |
| 109 const std::string& app_value = string_attr_map["application"]; | |
| 110 if (!base::StringPiece(app_value).starts_with(kGnomeKeyringAppString)) | |
| 111 return NULL; | |
| 112 | |
| 113 PasswordForm* form = new PasswordForm(); | |
| 114 form->origin = GURL(string_attr_map["origin_url"]); | |
| 115 form->action = GURL(string_attr_map["action_url"]); | |
| 116 form->username_element = UTF8ToUTF16(string_attr_map["username_element"]); | |
| 117 form->username_value = UTF8ToUTF16(string_attr_map["username_value"]); | |
| 118 form->password_element = UTF8ToUTF16(string_attr_map["password_element"]); | |
| 119 form->submit_element = UTF8ToUTF16(string_attr_map["submit_element"]); | |
| 120 form->signon_realm = string_attr_map["signon_realm"]; | |
| 121 form->ssl_valid = uint_attr_map["ssl_valid"]; | |
| 122 form->preferred = uint_attr_map["preferred"]; | |
| 123 int64 date_created = 0; | |
| 124 bool date_ok = base::StringToInt64(string_attr_map["date_created"], | |
| 125 &date_created); | |
| 126 DCHECK(date_ok); | |
| 127 form->date_created = base::Time::FromTimeT(date_created); | |
| 128 form->blacklisted_by_user = uint_attr_map["blacklisted_by_user"]; | |
| 129 form->scheme = static_cast<PasswordForm::Scheme>(uint_attr_map["scheme"]); | |
| 130 | |
| 131 return form; | |
| 132 } | |
| 133 | |
| 134 // Parse all the results from the given GList into a PasswordFormList, and free | |
| 135 // the GList. PasswordForms are allocated on the heap, and should be deleted by | |
| 136 // the consumer. | |
| 137 void ConvertFormList(GList* found, | |
| 138 NativeBackendGnome::PasswordFormList* forms) { | |
| 139 GList* element = g_list_first(found); | |
| 140 while (element != NULL) { | |
| 141 GnomeKeyringFound* data = static_cast<GnomeKeyringFound*>(element->data); | |
| 142 GnomeKeyringAttributeList* attrs = data->attributes; | |
| 143 | |
| 144 PasswordForm* form = FormFromAttributes(attrs); | |
| 145 if (form) { | |
| 146 if (data->secret) { | |
| 147 form->password_value = UTF8ToUTF16(data->secret); | |
| 148 } else { | |
| 149 LOG(WARNING) << "Unable to access password from list element!"; | |
| 150 } | |
| 151 forms->push_back(form); | |
| 152 } else { | |
| 153 LOG(WARNING) << "Could not initialize PasswordForm from attributes!"; | |
| 154 } | |
| 155 | |
| 156 element = g_list_next(element); | |
| 157 } | |
| 158 } | |
| 159 | |
| 160 // Schema is analagous to the fields in PasswordForm. | |
| 161 const GnomeKeyringPasswordSchema kGnomeSchema = { | |
| 162 GNOME_KEYRING_ITEM_GENERIC_SECRET, { | |
| 163 { "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, | |
| 164 { "action_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, | |
| 165 { "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, | |
| 166 { "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, | |
| 167 { "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, | |
| 168 { "submit_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, | |
| 169 { "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, | |
| 170 { "ssl_valid", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 }, | |
| 171 { "preferred", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 }, | |
| 172 { "date_created", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, | |
| 173 { "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 }, | |
| 174 { "scheme", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 }, | |
| 175 // This field is always "chrome" so that we can search for it. | |
| 176 { "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, | |
| 177 { NULL } | |
| 178 } | |
| 179 }; | |
| 180 | 22 |
| 181 // Sadly, PasswordStore goes to great lengths to switch from the originally | 23 // Sadly, PasswordStore goes to great lengths to switch from the originally |
| 182 // calling thread to the DB thread, and to provide an asynchronous API to | 24 // calling thread to the DB thread, and to provide an asynchronous API to |
| 183 // callers while using a synchronous (virtual) API provided by subclasses like | 25 // callers while using a synchronous (virtual) API provided by subclasses like |
| 184 // PasswordStoreX -- but GNOME Keyring really wants to be on the GLib main | 26 // PasswordStoreX -- but GNOME Keyring really wants to be on the GLib main |
| 185 // thread, which is the UI thread to us. So we end up having to switch threads | 27 // thread, which is the UI thread to us. We used to send a message to the UI |
| 186 // again, possibly back to the very same thread (in case the UI thread is the | 28 // thread and then wait for it, but that could cause deadlocks with password |
| 187 // caller, e.g. in the password management UI), and *block* the DB thread | 29 // sync which blocks the UI thread (!) for some operations. To avoid this, we |
| 188 // waiting for a response from the UI thread to provide the synchronous API | 30 // start a small proxy process with its own GLib main thread and talk to that. |
| 189 // PasswordStore expects of us. (It will then in turn switch back to the | 31 // We end up having to use the file thread for this, because the DB thread is |
| 190 // original caller to send the asynchronous reply to the original request.) | 32 // not a MessageLoopForIO and we need one of those to watch file descriptors. |
| 191 | |
| 192 // This class represents a call to a GNOME Keyring method. A RunnableMethod | |
| 193 // should be posted to the UI thread to call one of its action methods, and then | |
| 194 // a WaitResult() method should be called to wait for the result. Each instance | |
| 195 // supports only one outstanding method at a time, though multiple instances may | |
| 196 // be used in parallel. | |
| 197 class GKRMethod : public GnomeKeyringLoader { | |
| 198 public: | |
| 199 typedef NativeBackendGnome::PasswordFormList PasswordFormList; | |
| 200 | |
| 201 GKRMethod() : event_(false, false), result_(GNOME_KEYRING_RESULT_CANCELLED) {} | |
| 202 | |
| 203 // Action methods. These call gnome_keyring_* functions. Call from UI thread. | |
| 204 // See GetProfileSpecificAppString() for more information on the app string. | |
| 205 void AddLogin(const PasswordForm& form, const char* app_string); | |
| 206 void AddLoginSearch(const PasswordForm& form, const char* app_string); | |
| 207 void UpdateLoginSearch(const PasswordForm& form, const char* app_string); | |
| 208 void RemoveLogin(const PasswordForm& form, const char* app_string); | |
| 209 void GetLogins(const PasswordForm& form, const char* app_string); | |
| 210 void GetLoginsList(uint32_t blacklisted_by_user, const char* app_string); | |
| 211 void GetAllLogins(const char* app_string); | |
| 212 | |
| 213 // Use after AddLogin, RemoveLogin. | |
| 214 GnomeKeyringResult WaitResult(); | |
| 215 | |
| 216 // Use after AddLoginSearch, UpdateLoginSearch, GetLogins, GetLoginsList, | |
| 217 // GetAllLogins. | |
| 218 GnomeKeyringResult WaitResult(PasswordFormList* forms); | |
| 219 | |
| 220 private: | |
| 221 // All these callbacks are called on UI thread. | |
| 222 static void OnOperationDone(GnomeKeyringResult result, gpointer data); | |
| 223 | |
| 224 static void OnOperationGetList(GnomeKeyringResult result, GList* list, | |
| 225 gpointer data); | |
| 226 | |
| 227 base::WaitableEvent event_; | |
| 228 GnomeKeyringResult result_; | |
| 229 NativeBackendGnome::PasswordFormList forms_; | |
| 230 }; | |
| 231 | |
| 232 void GKRMethod::AddLogin(const PasswordForm& form, const char* app_string) { | |
| 233 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 234 time_t date_created = form.date_created.ToTimeT(); | |
| 235 // If we are asked to save a password with 0 date, use the current time. | |
| 236 // We don't want to actually save passwords as though on January 1, 1970. | |
| 237 if (!date_created) | |
| 238 date_created = time(NULL); | |
| 239 gnome_keyring_store_password( | |
| 240 &kGnomeSchema, | |
| 241 NULL, // Default keyring. | |
| 242 form.origin.spec().c_str(), // Display name. | |
| 243 UTF16ToUTF8(form.password_value).c_str(), | |
| 244 OnOperationDone, | |
| 245 this, // data | |
| 246 NULL, // destroy_data | |
| 247 "origin_url", form.origin.spec().c_str(), | |
| 248 "action_url", form.action.spec().c_str(), | |
| 249 "username_element", UTF16ToUTF8(form.username_element).c_str(), | |
| 250 "username_value", UTF16ToUTF8(form.username_value).c_str(), | |
| 251 "password_element", UTF16ToUTF8(form.password_element).c_str(), | |
| 252 "submit_element", UTF16ToUTF8(form.submit_element).c_str(), | |
| 253 "signon_realm", form.signon_realm.c_str(), | |
| 254 "ssl_valid", form.ssl_valid, | |
| 255 "preferred", form.preferred, | |
| 256 "date_created", base::Int64ToString(date_created).c_str(), | |
| 257 "blacklisted_by_user", form.blacklisted_by_user, | |
| 258 "scheme", form.scheme, | |
| 259 "application", app_string, | |
| 260 NULL); | |
| 261 } | |
| 262 | |
| 263 void GKRMethod::AddLoginSearch(const PasswordForm& form, | |
| 264 const char* app_string) { | |
| 265 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 266 // Search GNOME Keyring for matching passwords to update. | |
| 267 gnome_keyring_find_itemsv( | |
| 268 GNOME_KEYRING_ITEM_GENERIC_SECRET, | |
| 269 OnOperationGetList, | |
| 270 this, // data | |
| 271 NULL, // destroy_data | |
| 272 "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, | |
| 273 form.origin.spec().c_str(), | |
| 274 "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, | |
| 275 UTF16ToUTF8(form.username_element).c_str(), | |
| 276 "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, | |
| 277 UTF16ToUTF8(form.username_value).c_str(), | |
| 278 "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, | |
| 279 UTF16ToUTF8(form.password_element).c_str(), | |
| 280 "submit_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, | |
| 281 UTF16ToUTF8(form.submit_element).c_str(), | |
| 282 "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, | |
| 283 form.signon_realm.c_str(), | |
| 284 "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, | |
| 285 app_string, | |
| 286 NULL); | |
| 287 } | |
| 288 | |
| 289 void GKRMethod::UpdateLoginSearch(const PasswordForm& form, | |
| 290 const char* app_string) { | |
| 291 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 292 // Search GNOME Keyring for matching passwords to update. | |
| 293 gnome_keyring_find_itemsv( | |
| 294 GNOME_KEYRING_ITEM_GENERIC_SECRET, | |
| 295 OnOperationGetList, | |
| 296 this, // data | |
| 297 NULL, // destroy_data | |
| 298 "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, | |
| 299 form.origin.spec().c_str(), | |
| 300 "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, | |
| 301 UTF16ToUTF8(form.username_element).c_str(), | |
| 302 "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, | |
| 303 UTF16ToUTF8(form.username_value).c_str(), | |
| 304 "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, | |
| 305 UTF16ToUTF8(form.password_element).c_str(), | |
| 306 "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, | |
| 307 form.signon_realm.c_str(), | |
| 308 "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, | |
| 309 app_string, | |
| 310 NULL); | |
| 311 } | |
| 312 | |
| 313 void GKRMethod::RemoveLogin(const PasswordForm& form, const char* app_string) { | |
| 314 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 315 // We find forms using the same fields as LoginDatabase::RemoveLogin(). | |
| 316 gnome_keyring_delete_password( | |
| 317 &kGnomeSchema, | |
| 318 OnOperationDone, | |
| 319 this, // data | |
| 320 NULL, // destroy_data | |
| 321 "origin_url", form.origin.spec().c_str(), | |
| 322 "action_url", form.action.spec().c_str(), | |
| 323 "username_element", UTF16ToUTF8(form.username_element).c_str(), | |
| 324 "username_value", UTF16ToUTF8(form.username_value).c_str(), | |
| 325 "password_element", UTF16ToUTF8(form.password_element).c_str(), | |
| 326 "submit_element", UTF16ToUTF8(form.submit_element).c_str(), | |
| 327 "signon_realm", form.signon_realm.c_str(), | |
| 328 "application", app_string, | |
| 329 NULL); | |
| 330 } | |
| 331 | |
| 332 void GKRMethod::GetLogins(const PasswordForm& form, const char* app_string) { | |
| 333 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 334 // Search GNOME Keyring for matching passwords. | |
| 335 gnome_keyring_find_itemsv( | |
| 336 GNOME_KEYRING_ITEM_GENERIC_SECRET, | |
| 337 OnOperationGetList, | |
| 338 this, // data | |
| 339 NULL, // destroy_data | |
| 340 "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, | |
| 341 form.signon_realm.c_str(), | |
| 342 "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, | |
| 343 app_string, | |
| 344 NULL); | |
| 345 } | |
| 346 | |
| 347 void GKRMethod::GetLoginsList(uint32_t blacklisted_by_user, | |
| 348 const char* app_string) { | |
| 349 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 350 // Search GNOME Keyring for matching passwords. | |
| 351 gnome_keyring_find_itemsv( | |
| 352 GNOME_KEYRING_ITEM_GENERIC_SECRET, | |
| 353 OnOperationGetList, | |
| 354 this, // data | |
| 355 NULL, // destroy_data | |
| 356 "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32, | |
| 357 blacklisted_by_user, | |
| 358 "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, | |
| 359 app_string, | |
| 360 NULL); | |
| 361 } | |
| 362 | |
| 363 void GKRMethod::GetAllLogins(const char* app_string) { | |
| 364 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 365 // We need to search for something, otherwise we get no results - so | |
| 366 // we search for the fixed application string. | |
| 367 gnome_keyring_find_itemsv( | |
| 368 GNOME_KEYRING_ITEM_GENERIC_SECRET, | |
| 369 OnOperationGetList, | |
| 370 this, // data | |
| 371 NULL, // destroy_data | |
| 372 "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, | |
| 373 app_string, | |
| 374 NULL); | |
| 375 } | |
| 376 | |
| 377 GnomeKeyringResult GKRMethod::WaitResult() { | |
| 378 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | |
| 379 event_.Wait(); | |
| 380 return result_; | |
| 381 } | |
| 382 | |
| 383 GnomeKeyringResult GKRMethod::WaitResult(PasswordFormList* forms) { | |
| 384 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | |
| 385 event_.Wait(); | |
| 386 if (forms->empty()) { | |
| 387 // Normal case. Avoid extra allocation by swapping. | |
| 388 forms->swap(forms_); | |
| 389 } else { | |
| 390 // Rare case. Append forms_ to *forms. | |
| 391 forms->insert(forms->end(), forms_.begin(), forms_.end()); | |
| 392 forms_.clear(); | |
| 393 } | |
| 394 return result_; | |
| 395 } | |
| 396 | |
| 397 // static | |
| 398 void GKRMethod::OnOperationDone(GnomeKeyringResult result, gpointer data) { | |
| 399 GKRMethod* method = static_cast<GKRMethod*>(data); | |
| 400 method->result_ = result; | |
| 401 method->event_.Signal(); | |
| 402 } | |
| 403 | |
| 404 // static | |
| 405 void GKRMethod::OnOperationGetList(GnomeKeyringResult result, GList* list, | |
| 406 gpointer data) { | |
| 407 GKRMethod* method = static_cast<GKRMethod*>(data); | |
| 408 method->result_ = result; | |
| 409 method->forms_.clear(); | |
| 410 // |list| will be freed after this callback returns, so convert it now. | |
| 411 ConvertFormList(list, &method->forms_); | |
| 412 method->event_.Signal(); | |
| 413 } | |
| 414 | |
| 415 } // namespace | |
| 416 | 33 |
| 417 NativeBackendGnome::NativeBackendGnome(LocalProfileId id, PrefService* prefs) | 34 NativeBackendGnome::NativeBackendGnome(LocalProfileId id, PrefService* prefs) |
| 418 : profile_id_(id), prefs_(prefs) { | 35 : profile_id_(id), prefs_(prefs) { |
| 419 if (PasswordStoreX::PasswordsUseLocalProfileId(prefs)) { | 36 if (PasswordStoreX::PasswordsUseLocalProfileId(prefs)) { |
| 420 app_string_ = GetProfileSpecificAppString(); | 37 app_string_ = GetProfileSpecificAppString(); |
| 421 // We already did the migration previously. Don't try again. | 38 // We already did the migration previously. Don't try again. |
| 422 migrate_tried_ = true; | 39 migrate_tried_ = true; |
| 423 } else { | 40 } else { |
| 424 app_string_ = kGnomeKeyringAppString; | 41 app_string_ = kGnomeKeyringAppString; |
| 425 migrate_tried_ = false; | 42 migrate_tried_ = false; |
| 426 } | 43 } |
| 427 } | 44 } |
| 428 | 45 |
| 429 NativeBackendGnome::~NativeBackendGnome() { | 46 NativeBackendGnome::~NativeBackendGnome() { |
| 430 } | 47 } |
| 431 | 48 |
| 432 bool NativeBackendGnome::Init() { | 49 bool NativeBackendGnome::Init() { |
| 433 return LoadGnomeKeyring() && gnome_keyring_is_available(); | 50 // We don't need to determine with absolute certainty that we can use GNOME |
| 51 // Keyring here. PasswordStoreX is conservative and will back off if the first |
| 52 // attempt to use it fails. Since that will conveniently occur on the DB |
| 53 // thread, where we need to finish initialization by watching the IPC file |
| 54 // descriptor, we merely start the proxy here and claim success if that works. |
| 55 DCHECK(!proxy_client_.get()); |
| 56 scoped_ptr<KeyringProxyClient> client(new KeyringProxyClient); |
| 57 if (!client->Connect()) |
| 58 return false; |
| 59 proxy_client_.reset(client.release()); |
| 60 return true; |
| 61 } |
| 62 |
| 63 void NativeBackendGnome::InitForTesting(KeyringProxyClient* client) { |
| 64 DCHECK(!proxy_client_.get()); |
| 65 proxy_client_.reset(client); |
| 434 } | 66 } |
| 435 | 67 |
| 436 bool NativeBackendGnome::RawAddLogin(const PasswordForm& form) { | 68 bool NativeBackendGnome::RawAddLogin(const PasswordForm& form) { |
| 437 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | 69 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| 438 GKRMethod method; | 70 KeyringProxyClient::RequestContext context; |
| 439 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 71 proxy_client_->AddLogin(form, app_string_, &context); |
| 440 base::Bind(&GKRMethod::AddLogin, | 72 context.event.Wait(); |
| 441 base::Unretained(&method), | 73 if (context.result_code != GNOME_KEYRING_RESULT_OK) { |
| 442 form, app_string_.c_str())); | 74 LOG(ERROR) << "Keyring save failed: code " << context.result_code; |
| 443 GnomeKeyringResult result = method.WaitResult(); | |
| 444 if (result != GNOME_KEYRING_RESULT_OK) { | |
| 445 LOG(ERROR) << "Keyring save failed: " | |
| 446 << gnome_keyring_result_to_message(result); | |
| 447 return false; | 75 return false; |
| 448 } | 76 } |
| 449 // Successful write. Try migration if necessary. | 77 // Successful write. Try migration if necessary. |
| 450 if (!migrate_tried_) | 78 if (!migrate_tried_) |
| 451 MigrateToProfileSpecificLogins(); | 79 MigrateToProfileSpecificLogins(); |
| 452 return true; | 80 return true; |
| 453 } | 81 } |
| 454 | 82 |
| 455 bool NativeBackendGnome::AddLogin(const PasswordForm& form) { | 83 bool NativeBackendGnome::AddLogin(const PasswordForm& form) { |
| 456 // Based on LoginDatabase::AddLogin(), we search for an existing match based | 84 // Based on LoginDatabase::AddLogin(), we search for an existing match based |
| 457 // on origin_url, username_element, username_value, password_element, submit | 85 // on origin_url, username_element, username_value, password_element, submit |
| 458 // element, and signon_realm first, remove that, and then add the new entry. | 86 // element, and signon_realm first, remove that, and then add the new entry. |
| 459 // We'd add the new one first, and then delete the original, but then the | 87 // We'd add the new one first, and then delete the original, but then the |
| 460 // delete might actually delete the newly-added entry! | 88 // delete might actually delete the newly-added entry! |
| 461 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | 89 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| 462 GKRMethod method; | |
| 463 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 464 base::Bind(&GKRMethod::AddLoginSearch, | |
| 465 base::Unretained(&method), | |
| 466 form, app_string_.c_str())); | |
| 467 PasswordFormList forms; | 90 PasswordFormList forms; |
| 468 GnomeKeyringResult result = method.WaitResult(&forms); | 91 KeyringProxyClient::RequestContext context(&forms); |
| 469 if (result != GNOME_KEYRING_RESULT_OK && | 92 proxy_client_->AddLoginSearch(form, app_string_, &context); |
| 470 result != GNOME_KEYRING_RESULT_NO_MATCH) { | 93 context.event.Wait(); |
| 471 LOG(ERROR) << "Keyring find failed: " | 94 if (context.result_code != GNOME_KEYRING_RESULT_OK && |
| 472 << gnome_keyring_result_to_message(result); | 95 context.result_code != GNOME_KEYRING_RESULT_NO_MATCH) { |
| 96 LOG(ERROR) << "Keyring find failed: code " << context.result_code; |
| 473 return false; | 97 return false; |
| 474 } | 98 } |
| 475 if (forms.size() > 0) { | 99 if (forms.size() > 0) { |
| 476 if (forms.size() > 1) { | 100 if (forms.size() > 1) { |
| 477 LOG(WARNING) << "Adding login when there are " << forms.size() | 101 LOG(WARNING) << "Adding login when there are " << forms.size() |
| 478 << " matching logins already! Will replace only the first."; | 102 << " matching logins already! Will replace only the first."; |
| 479 } | 103 } |
| 480 | 104 |
| 481 // We try migration before updating the existing logins, since otherwise | 105 // We try migration before updating the existing logins, since otherwise |
| 482 // we'd do it after making some but not all of the changes below. | 106 // we'd do it after making some but not all of the changes below. |
| 483 if (forms.size() > 0 && !migrate_tried_) | 107 if (forms.size() > 0 && !migrate_tried_) |
| 484 MigrateToProfileSpecificLogins(); | 108 MigrateToProfileSpecificLogins(); |
| 485 | 109 |
| 486 RemoveLogin(*forms[0]); | 110 RemoveLogin(*forms[0]); |
| 487 for (size_t i = 0; i < forms.size(); ++i) | 111 for (size_t i = 0; i < forms.size(); ++i) |
| 488 delete forms[i]; | 112 delete forms[i]; |
| 489 } | 113 } |
| 490 return RawAddLogin(form); | 114 return RawAddLogin(form); |
| 491 } | 115 } |
| 492 | 116 |
| 493 bool NativeBackendGnome::UpdateLogin(const PasswordForm& form) { | 117 bool NativeBackendGnome::UpdateLogin(const PasswordForm& form) { |
| 494 // Based on LoginDatabase::UpdateLogin(), we search for forms to update by | 118 // Based on LoginDatabase::UpdateLogin(), we search for forms to update by |
| 495 // origin_url, username_element, username_value, password_element, and | 119 // origin_url, username_element, username_value, password_element, and |
| 496 // signon_realm. We then compare the result to the updated form. If they | 120 // signon_realm. We then compare the result to the updated form. If they |
| 497 // differ in any of the action, password_value, ssl_valid, or preferred | 121 // differ in any of the action, password_value, ssl_valid, or preferred |
| 498 // fields, then we remove the original, and then add the new entry. We'd add | 122 // fields, then we remove the original, and then add the new entry. We'd add |
| 499 // the new one first, and then delete the original, but then the delete might | 123 // the new one first, and then delete the original, but then the delete might |
| 500 // actually delete the newly-added entry! | 124 // actually delete the newly-added entry! |
| 501 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | 125 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| 502 GKRMethod method; | |
| 503 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 504 base::Bind(&GKRMethod::UpdateLoginSearch, | |
| 505 base::Unretained(&method), | |
| 506 form, app_string_.c_str())); | |
| 507 PasswordFormList forms; | 126 PasswordFormList forms; |
| 508 GnomeKeyringResult result = method.WaitResult(&forms); | 127 KeyringProxyClient::RequestContext context(&forms); |
| 509 if (result != GNOME_KEYRING_RESULT_OK) { | 128 proxy_client_->UpdateLoginSearch(form, app_string_, &context); |
| 510 LOG(ERROR) << "Keyring find failed: " | 129 context.event.Wait(); |
| 511 << gnome_keyring_result_to_message(result); | 130 if (context.result_code != GNOME_KEYRING_RESULT_OK) { |
| 131 LOG(ERROR) << "Keyring find failed: code " << context.result_code; |
| 512 return false; | 132 return false; |
| 513 } | 133 } |
| 514 | 134 |
| 515 // We try migration before updating the existing logins, since otherwise | 135 // We try migration before updating the existing logins, since otherwise |
| 516 // we'd do it after making some but not all of the changes below. | 136 // we'd do it after making some but not all of the changes below. |
| 517 if (forms.size() > 0 && !migrate_tried_) | 137 if (forms.size() > 0 && !migrate_tried_) |
| 518 MigrateToProfileSpecificLogins(); | 138 MigrateToProfileSpecificLogins(); |
| 519 | 139 |
| 520 bool ok = true; | 140 bool ok = true; |
| 521 for (size_t i = 0; i < forms.size(); ++i) { | 141 for (size_t i = 0; i < forms.size(); ++i) { |
| (...skipping 16 matching lines...) Expand all Loading... |
| 538 if (!RawAddLogin(*forms[i])) | 158 if (!RawAddLogin(*forms[i])) |
| 539 ok = false; | 159 ok = false; |
| 540 } | 160 } |
| 541 delete forms[i]; | 161 delete forms[i]; |
| 542 } | 162 } |
| 543 return ok; | 163 return ok; |
| 544 } | 164 } |
| 545 | 165 |
| 546 bool NativeBackendGnome::RemoveLogin(const PasswordForm& form) { | 166 bool NativeBackendGnome::RemoveLogin(const PasswordForm& form) { |
| 547 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | 167 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| 548 GKRMethod method; | 168 KeyringProxyClient::RequestContext context; |
| 549 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 169 proxy_client_->RemoveLogin(form, app_string_, &context); |
| 550 base::Bind(&GKRMethod::RemoveLogin, | 170 context.event.Wait(); |
| 551 base::Unretained(&method), | 171 if (context.result_code != GNOME_KEYRING_RESULT_OK) { |
| 552 form, app_string_.c_str())); | |
| 553 GnomeKeyringResult result = method.WaitResult(); | |
| 554 if (result != GNOME_KEYRING_RESULT_OK) { | |
| 555 // Warning, not error, because this can sometimes happen due to the user | 172 // Warning, not error, because this can sometimes happen due to the user |
| 556 // racing with the daemon to delete the password a second time. | 173 // racing with the daemon to delete the password a second time. |
| 557 LOG(WARNING) << "Keyring delete failed: " | 174 LOG(WARNING) << "Keyring delete failed: code " << context.result_code; |
| 558 << gnome_keyring_result_to_message(result); | |
| 559 return false; | 175 return false; |
| 560 } | 176 } |
| 561 // Successful write. Try migration if necessary. Note that presumably if we've | 177 // Successful write. Try migration if necessary. Note that presumably if we've |
| 562 // been asked to delete a login, it's because we returned it previously; thus, | 178 // been asked to delete a login, it's because we returned it previously; thus, |
| 563 // this will probably never happen since we'd have already tried migration. | 179 // this will probably never happen since we'd have already tried migration. |
| 564 if (!migrate_tried_) | 180 if (!migrate_tried_) |
| 565 MigrateToProfileSpecificLogins(); | 181 MigrateToProfileSpecificLogins(); |
| 566 return true; | 182 return true; |
| 567 } | 183 } |
| 568 | 184 |
| (...skipping 16 matching lines...) Expand all Loading... |
| 585 ok = false; | 201 ok = false; |
| 586 } | 202 } |
| 587 delete forms[i]; | 203 delete forms[i]; |
| 588 } | 204 } |
| 589 return ok; | 205 return ok; |
| 590 } | 206 } |
| 591 | 207 |
| 592 bool NativeBackendGnome::GetLogins(const PasswordForm& form, | 208 bool NativeBackendGnome::GetLogins(const PasswordForm& form, |
| 593 PasswordFormList* forms) { | 209 PasswordFormList* forms) { |
| 594 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | 210 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| 595 GKRMethod method; | 211 KeyringProxyClient::RequestContext context(forms); |
| 596 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 212 proxy_client_->GetLogins(form, app_string_, &context); |
| 597 base::Bind(&GKRMethod::GetLogins, | 213 context.event.Wait(); |
| 598 base::Unretained(&method), | 214 if (context.result_code == GNOME_KEYRING_RESULT_NO_MATCH) |
| 599 form, app_string_.c_str())); | |
| 600 GnomeKeyringResult result = method.WaitResult(forms); | |
| 601 if (result == GNOME_KEYRING_RESULT_NO_MATCH) | |
| 602 return true; | 215 return true; |
| 603 if (result != GNOME_KEYRING_RESULT_OK) { | 216 if (context.result_code != GNOME_KEYRING_RESULT_OK) { |
| 604 LOG(ERROR) << "Keyring find failed: " | 217 LOG(ERROR) << "Keyring find failed: code " << context.result_code; |
| 605 << gnome_keyring_result_to_message(result); | |
| 606 return false; | 218 return false; |
| 607 } | 219 } |
| 608 // Successful read of actual data. Try migration if necessary. | 220 // Successful read of actual data. Try migration if necessary. |
| 609 if (!migrate_tried_) | 221 if (!migrate_tried_) |
| 610 MigrateToProfileSpecificLogins(); | 222 MigrateToProfileSpecificLogins(); |
| 611 return true; | 223 return true; |
| 612 } | 224 } |
| 613 | 225 |
| 614 bool NativeBackendGnome::GetLoginsCreatedBetween(const base::Time& get_begin, | 226 bool NativeBackendGnome::GetLoginsCreatedBetween(const base::Time& get_begin, |
| 615 const base::Time& get_end, | 227 const base::Time& get_end, |
| (...skipping 26 matching lines...) Expand all Loading... |
| 642 bool NativeBackendGnome::GetBlacklistLogins(PasswordFormList* forms) { | 254 bool NativeBackendGnome::GetBlacklistLogins(PasswordFormList* forms) { |
| 643 return GetLoginsList(forms, false); | 255 return GetLoginsList(forms, false); |
| 644 } | 256 } |
| 645 | 257 |
| 646 bool NativeBackendGnome::GetLoginsList(PasswordFormList* forms, | 258 bool NativeBackendGnome::GetLoginsList(PasswordFormList* forms, |
| 647 bool autofillable) { | 259 bool autofillable) { |
| 648 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | 260 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| 649 | 261 |
| 650 uint32_t blacklisted_by_user = !autofillable; | 262 uint32_t blacklisted_by_user = !autofillable; |
| 651 | 263 |
| 652 GKRMethod method; | 264 KeyringProxyClient::RequestContext context(forms); |
| 653 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 265 proxy_client_->GetLoginsList(blacklisted_by_user, app_string_, &context); |
| 654 base::Bind(&GKRMethod::GetLoginsList, | 266 context.event.Wait(); |
| 655 base::Unretained(&method), | 267 if (context.result_code == GNOME_KEYRING_RESULT_NO_MATCH) |
| 656 blacklisted_by_user, app_string_.c_str())); | |
| 657 GnomeKeyringResult result = method.WaitResult(forms); | |
| 658 if (result == GNOME_KEYRING_RESULT_NO_MATCH) | |
| 659 return true; | 268 return true; |
| 660 if (result != GNOME_KEYRING_RESULT_OK) { | 269 if (context.result_code != GNOME_KEYRING_RESULT_OK) { |
| 661 LOG(ERROR) << "Keyring find failed: " | 270 LOG(ERROR) << "Keyring find failed: code " << context.result_code; |
| 662 << gnome_keyring_result_to_message(result); | |
| 663 return false; | 271 return false; |
| 664 } | 272 } |
| 665 // Successful read of actual data. Try migration if necessary. | 273 // Successful read of actual data. Try migration if necessary. |
| 666 if (!migrate_tried_) | 274 if (!migrate_tried_) |
| 667 MigrateToProfileSpecificLogins(); | 275 MigrateToProfileSpecificLogins(); |
| 668 return true; | 276 return true; |
| 669 } | 277 } |
| 670 | 278 |
| 671 bool NativeBackendGnome::GetAllLogins(PasswordFormList* forms) { | 279 bool NativeBackendGnome::GetAllLogins(PasswordFormList* forms) { |
| 672 GKRMethod method; | 280 KeyringProxyClient::RequestContext context(forms); |
| 673 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 281 proxy_client_->GetAllLogins(app_string_, &context); |
| 674 base::Bind(&GKRMethod::GetAllLogins, | 282 context.event.Wait(); |
| 675 base::Unretained(&method), | 283 if (context.result_code == GNOME_KEYRING_RESULT_NO_MATCH) |
| 676 app_string_.c_str())); | |
| 677 GnomeKeyringResult result = method.WaitResult(forms); | |
| 678 if (result == GNOME_KEYRING_RESULT_NO_MATCH) | |
| 679 return true; | 284 return true; |
| 680 if (result != GNOME_KEYRING_RESULT_OK) { | 285 if (context.result_code != GNOME_KEYRING_RESULT_OK) { |
| 681 LOG(ERROR) << "Keyring find failed: " | 286 LOG(ERROR) << "Keyring find failed: code " << context.result_code; |
| 682 << gnome_keyring_result_to_message(result); | |
| 683 return false; | 287 return false; |
| 684 } | 288 } |
| 685 // Successful read of actual data. Try migration if necessary. | 289 // Successful read of actual data. Try migration if necessary. |
| 686 if (!migrate_tried_) | 290 if (!migrate_tried_) |
| 687 MigrateToProfileSpecificLogins(); | 291 MigrateToProfileSpecificLogins(); |
| 688 return true; | 292 return true; |
| 689 } | 293 } |
| 690 | 294 |
| 691 std::string NativeBackendGnome::GetProfileSpecificAppString() const { | 295 std::string NativeBackendGnome::GetProfileSpecificAppString() const { |
| 692 // Originally, the application string was always just "chrome" and used only | 296 // Originally, the application string was always just "chrome" and used only |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 728 // Each other profile must be able to migrate the shared data as well, | 332 // Each other profile must be able to migrate the shared data as well, |
| 729 // so we must leave it alone. After a few releases, we'll add code to | 333 // so we must leave it alone. After a few releases, we'll add code to |
| 730 // delete them, and eventually remove this migration code. | 334 // delete them, and eventually remove this migration code. |
| 731 // TODO(mdm): follow through with the plan above. | 335 // TODO(mdm): follow through with the plan above. |
| 732 PasswordStoreX::SetPasswordsUseLocalProfileId(prefs_); | 336 PasswordStoreX::SetPasswordsUseLocalProfileId(prefs_); |
| 733 } else { | 337 } else { |
| 734 // We failed to migrate for some reason. Use the old app string. | 338 // We failed to migrate for some reason. Use the old app string. |
| 735 app_string_ = kGnomeKeyringAppString; | 339 app_string_ = kGnomeKeyringAppString; |
| 736 } | 340 } |
| 737 } | 341 } |
| OLD | NEW |