OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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::forms::PasswordForm; | 19 using webkit::forms::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 // TODO(mdm): after a few more releases, remove the code which is now dead due | 36 // TODO(mdm): after a few more releases, remove the code which is now dead due |
420 // to the true || here, and simplify this code. We don't do it yet to make it | 37 // to the true || here, and simplify this code. We don't do it yet to make it |
421 // easier to revert if necessary. | 38 // easier to revert if necessary. |
422 if (true || PasswordStoreX::PasswordsUseLocalProfileId(prefs)) { | 39 if (true || PasswordStoreX::PasswordsUseLocalProfileId(prefs)) { |
423 app_string_ = GetProfileSpecificAppString(); | 40 app_string_ = GetProfileSpecificAppString(); |
424 // We already did the migration previously. Don't try again. | 41 // We already did the migration previously. Don't try again. |
425 migrate_tried_ = true; | 42 migrate_tried_ = true; |
426 } else { | 43 } else { |
427 app_string_ = kGnomeKeyringAppString; | 44 app_string_ = kGnomeKeyringAppString; |
428 migrate_tried_ = false; | 45 migrate_tried_ = false; |
429 } | 46 } |
430 } | 47 } |
431 | 48 |
432 NativeBackendGnome::~NativeBackendGnome() { | 49 NativeBackendGnome::~NativeBackendGnome() { |
433 } | 50 } |
434 | 51 |
435 bool NativeBackendGnome::Init() { | 52 bool NativeBackendGnome::Init() { |
436 return LoadGnomeKeyring() && gnome_keyring_is_available(); | 53 // We don't need to determine with absolute certainty that we can use GNOME |
| 54 // Keyring here. PasswordStoreX is conservative and will back off if the first |
| 55 // attempt to use it fails. Since that will conveniently occur on the DB |
| 56 // thread, where we need to finish initialization by watching the IPC file |
| 57 // descriptor, we merely start the proxy here and claim success if that works. |
| 58 DCHECK(!proxy_client_.get()); |
| 59 scoped_ptr<KeyringProxyClient> client(new KeyringProxyClient); |
| 60 if (!client->Connect()) |
| 61 return false; |
| 62 proxy_client_.reset(client.release()); |
| 63 return true; |
| 64 } |
| 65 |
| 66 void NativeBackendGnome::InitForTesting(KeyringProxyClient* client) { |
| 67 DCHECK(!proxy_client_.get()); |
| 68 proxy_client_.reset(client); |
437 } | 69 } |
438 | 70 |
439 bool NativeBackendGnome::RawAddLogin(const PasswordForm& form) { | 71 bool NativeBackendGnome::RawAddLogin(const PasswordForm& form) { |
440 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | 72 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
441 GKRMethod method; | 73 KeyringProxyClient::RequestContext context; |
442 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 74 proxy_client_->AddLogin(form, app_string_, &context); |
443 base::Bind(&GKRMethod::AddLogin, | 75 context.event.Wait(); |
444 base::Unretained(&method), | 76 if (context.result_code != GNOME_KEYRING_RESULT_OK) { |
445 form, app_string_.c_str())); | 77 LOG(ERROR) << "Keyring save failed: code " << context.result_code; |
446 GnomeKeyringResult result = method.WaitResult(); | |
447 if (result != GNOME_KEYRING_RESULT_OK) { | |
448 LOG(ERROR) << "Keyring save failed: " | |
449 << gnome_keyring_result_to_message(result); | |
450 return false; | 78 return false; |
451 } | 79 } |
452 // Successful write. Try migration if necessary. | 80 // Successful write. Try migration if necessary. |
453 if (!migrate_tried_) | 81 if (!migrate_tried_) |
454 MigrateToProfileSpecificLogins(); | 82 MigrateToProfileSpecificLogins(); |
455 return true; | 83 return true; |
456 } | 84 } |
457 | 85 |
458 bool NativeBackendGnome::AddLogin(const PasswordForm& form) { | 86 bool NativeBackendGnome::AddLogin(const PasswordForm& form) { |
459 // Based on LoginDatabase::AddLogin(), we search for an existing match based | 87 // Based on LoginDatabase::AddLogin(), we search for an existing match based |
460 // on origin_url, username_element, username_value, password_element, submit | 88 // on origin_url, username_element, username_value, password_element, submit |
461 // element, and signon_realm first, remove that, and then add the new entry. | 89 // element, and signon_realm first, remove that, and then add the new entry. |
462 // We'd add the new one first, and then delete the original, but then the | 90 // We'd add the new one first, and then delete the original, but then the |
463 // delete might actually delete the newly-added entry! | 91 // delete might actually delete the newly-added entry! |
464 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | 92 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
465 GKRMethod method; | |
466 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
467 base::Bind(&GKRMethod::AddLoginSearch, | |
468 base::Unretained(&method), | |
469 form, app_string_.c_str())); | |
470 PasswordFormList forms; | 93 PasswordFormList forms; |
471 GnomeKeyringResult result = method.WaitResult(&forms); | 94 KeyringProxyClient::RequestContext context(&forms); |
472 if (result != GNOME_KEYRING_RESULT_OK && | 95 proxy_client_->AddLoginSearch(form, app_string_, &context); |
473 result != GNOME_KEYRING_RESULT_NO_MATCH) { | 96 context.event.Wait(); |
474 LOG(ERROR) << "Keyring find failed: " | 97 if (context.result_code != GNOME_KEYRING_RESULT_OK && |
475 << gnome_keyring_result_to_message(result); | 98 context.result_code != GNOME_KEYRING_RESULT_NO_MATCH) { |
| 99 LOG(ERROR) << "Keyring find failed: code " << context.result_code; |
476 return false; | 100 return false; |
477 } | 101 } |
478 if (forms.size() > 0) { | 102 if (forms.size() > 0) { |
479 if (forms.size() > 1) { | 103 if (forms.size() > 1) { |
480 LOG(WARNING) << "Adding login when there are " << forms.size() | 104 LOG(WARNING) << "Adding login when there are " << forms.size() |
481 << " matching logins already! Will replace only the first."; | 105 << " matching logins already! Will replace only the first."; |
482 } | 106 } |
483 | 107 |
484 // We try migration before updating the existing logins, since otherwise | 108 // We try migration before updating the existing logins, since otherwise |
485 // we'd do it after making some but not all of the changes below. | 109 // we'd do it after making some but not all of the changes below. |
486 if (forms.size() > 0 && !migrate_tried_) | 110 if (forms.size() > 0 && !migrate_tried_) |
487 MigrateToProfileSpecificLogins(); | 111 MigrateToProfileSpecificLogins(); |
488 | 112 |
489 RemoveLogin(*forms[0]); | 113 RemoveLogin(*forms[0]); |
490 for (size_t i = 0; i < forms.size(); ++i) | 114 for (size_t i = 0; i < forms.size(); ++i) |
491 delete forms[i]; | 115 delete forms[i]; |
492 } | 116 } |
493 return RawAddLogin(form); | 117 return RawAddLogin(form); |
494 } | 118 } |
495 | 119 |
496 bool NativeBackendGnome::UpdateLogin(const PasswordForm& form) { | 120 bool NativeBackendGnome::UpdateLogin(const PasswordForm& form) { |
497 // Based on LoginDatabase::UpdateLogin(), we search for forms to update by | 121 // Based on LoginDatabase::UpdateLogin(), we search for forms to update by |
498 // origin_url, username_element, username_value, password_element, and | 122 // origin_url, username_element, username_value, password_element, and |
499 // signon_realm. We then compare the result to the updated form. If they | 123 // signon_realm. We then compare the result to the updated form. If they |
500 // differ in any of the action, password_value, ssl_valid, or preferred | 124 // differ in any of the action, password_value, ssl_valid, or preferred |
501 // fields, then we remove the original, and then add the new entry. We'd add | 125 // fields, then we remove the original, and then add the new entry. We'd add |
502 // the new one first, and then delete the original, but then the delete might | 126 // the new one first, and then delete the original, but then the delete might |
503 // actually delete the newly-added entry! | 127 // actually delete the newly-added entry! |
504 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | 128 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
505 GKRMethod method; | |
506 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
507 base::Bind(&GKRMethod::UpdateLoginSearch, | |
508 base::Unretained(&method), | |
509 form, app_string_.c_str())); | |
510 PasswordFormList forms; | 129 PasswordFormList forms; |
511 GnomeKeyringResult result = method.WaitResult(&forms); | 130 KeyringProxyClient::RequestContext context(&forms); |
512 if (result != GNOME_KEYRING_RESULT_OK) { | 131 proxy_client_->UpdateLoginSearch(form, app_string_, &context); |
513 LOG(ERROR) << "Keyring find failed: " | 132 context.event.Wait(); |
514 << gnome_keyring_result_to_message(result); | 133 if (context.result_code != GNOME_KEYRING_RESULT_OK) { |
| 134 LOG(ERROR) << "Keyring find failed: code " << context.result_code; |
515 return false; | 135 return false; |
516 } | 136 } |
517 | 137 |
518 // We try migration before updating the existing logins, since otherwise | 138 // We try migration before updating the existing logins, since otherwise |
519 // we'd do it after making some but not all of the changes below. | 139 // we'd do it after making some but not all of the changes below. |
520 if (forms.size() > 0 && !migrate_tried_) | 140 if (forms.size() > 0 && !migrate_tried_) |
521 MigrateToProfileSpecificLogins(); | 141 MigrateToProfileSpecificLogins(); |
522 | 142 |
523 bool ok = true; | 143 bool ok = true; |
524 for (size_t i = 0; i < forms.size(); ++i) { | 144 for (size_t i = 0; i < forms.size(); ++i) { |
(...skipping 16 matching lines...) Expand all Loading... |
541 if (!RawAddLogin(*forms[i])) | 161 if (!RawAddLogin(*forms[i])) |
542 ok = false; | 162 ok = false; |
543 } | 163 } |
544 delete forms[i]; | 164 delete forms[i]; |
545 } | 165 } |
546 return ok; | 166 return ok; |
547 } | 167 } |
548 | 168 |
549 bool NativeBackendGnome::RemoveLogin(const PasswordForm& form) { | 169 bool NativeBackendGnome::RemoveLogin(const PasswordForm& form) { |
550 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | 170 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
551 GKRMethod method; | 171 KeyringProxyClient::RequestContext context; |
552 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 172 proxy_client_->RemoveLogin(form, app_string_, &context); |
553 base::Bind(&GKRMethod::RemoveLogin, | 173 context.event.Wait(); |
554 base::Unretained(&method), | 174 if (context.result_code != GNOME_KEYRING_RESULT_OK) { |
555 form, app_string_.c_str())); | |
556 GnomeKeyringResult result = method.WaitResult(); | |
557 if (result != GNOME_KEYRING_RESULT_OK) { | |
558 // Warning, not error, because this can sometimes happen due to the user | 175 // Warning, not error, because this can sometimes happen due to the user |
559 // racing with the daemon to delete the password a second time. | 176 // racing with the daemon to delete the password a second time. |
560 LOG(WARNING) << "Keyring delete failed: " | 177 LOG(WARNING) << "Keyring delete failed: code " << context.result_code; |
561 << gnome_keyring_result_to_message(result); | |
562 return false; | 178 return false; |
563 } | 179 } |
564 // Successful write. Try migration if necessary. Note that presumably if we've | 180 // Successful write. Try migration if necessary. Note that presumably if we've |
565 // been asked to delete a login, it's because we returned it previously; thus, | 181 // been asked to delete a login, it's because we returned it previously; thus, |
566 // this will probably never happen since we'd have already tried migration. | 182 // this will probably never happen since we'd have already tried migration. |
567 if (!migrate_tried_) | 183 if (!migrate_tried_) |
568 MigrateToProfileSpecificLogins(); | 184 MigrateToProfileSpecificLogins(); |
569 return true; | 185 return true; |
570 } | 186 } |
571 | 187 |
(...skipping 16 matching lines...) Expand all Loading... |
588 ok = false; | 204 ok = false; |
589 } | 205 } |
590 delete forms[i]; | 206 delete forms[i]; |
591 } | 207 } |
592 return ok; | 208 return ok; |
593 } | 209 } |
594 | 210 |
595 bool NativeBackendGnome::GetLogins(const PasswordForm& form, | 211 bool NativeBackendGnome::GetLogins(const PasswordForm& form, |
596 PasswordFormList* forms) { | 212 PasswordFormList* forms) { |
597 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | 213 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
598 GKRMethod method; | 214 KeyringProxyClient::RequestContext context(forms); |
599 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 215 proxy_client_->GetLogins(form, app_string_, &context); |
600 base::Bind(&GKRMethod::GetLogins, | 216 context.event.Wait(); |
601 base::Unretained(&method), | 217 if (context.result_code == GNOME_KEYRING_RESULT_NO_MATCH) |
602 form, app_string_.c_str())); | |
603 GnomeKeyringResult result = method.WaitResult(forms); | |
604 if (result == GNOME_KEYRING_RESULT_NO_MATCH) | |
605 return true; | 218 return true; |
606 if (result != GNOME_KEYRING_RESULT_OK) { | 219 if (context.result_code != GNOME_KEYRING_RESULT_OK) { |
607 LOG(ERROR) << "Keyring find failed: " | 220 LOG(ERROR) << "Keyring find failed: code " << context.result_code; |
608 << gnome_keyring_result_to_message(result); | |
609 return false; | 221 return false; |
610 } | 222 } |
611 // Successful read of actual data. Try migration if necessary. | 223 // Successful read of actual data. Try migration if necessary. |
612 if (!migrate_tried_) | 224 if (!migrate_tried_) |
613 MigrateToProfileSpecificLogins(); | 225 MigrateToProfileSpecificLogins(); |
614 return true; | 226 return true; |
615 } | 227 } |
616 | 228 |
617 bool NativeBackendGnome::GetLoginsCreatedBetween(const base::Time& get_begin, | 229 bool NativeBackendGnome::GetLoginsCreatedBetween(const base::Time& get_begin, |
618 const base::Time& get_end, | 230 const base::Time& get_end, |
(...skipping 26 matching lines...) Expand all Loading... |
645 bool NativeBackendGnome::GetBlacklistLogins(PasswordFormList* forms) { | 257 bool NativeBackendGnome::GetBlacklistLogins(PasswordFormList* forms) { |
646 return GetLoginsList(forms, false); | 258 return GetLoginsList(forms, false); |
647 } | 259 } |
648 | 260 |
649 bool NativeBackendGnome::GetLoginsList(PasswordFormList* forms, | 261 bool NativeBackendGnome::GetLoginsList(PasswordFormList* forms, |
650 bool autofillable) { | 262 bool autofillable) { |
651 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | 263 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
652 | 264 |
653 uint32_t blacklisted_by_user = !autofillable; | 265 uint32_t blacklisted_by_user = !autofillable; |
654 | 266 |
655 GKRMethod method; | 267 KeyringProxyClient::RequestContext context(forms); |
656 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 268 proxy_client_->GetLoginsList(blacklisted_by_user, app_string_, &context); |
657 base::Bind(&GKRMethod::GetLoginsList, | 269 context.event.Wait(); |
658 base::Unretained(&method), | 270 if (context.result_code == GNOME_KEYRING_RESULT_NO_MATCH) |
659 blacklisted_by_user, app_string_.c_str())); | |
660 GnomeKeyringResult result = method.WaitResult(forms); | |
661 if (result == GNOME_KEYRING_RESULT_NO_MATCH) | |
662 return true; | 271 return true; |
663 if (result != GNOME_KEYRING_RESULT_OK) { | 272 if (context.result_code != GNOME_KEYRING_RESULT_OK) { |
664 LOG(ERROR) << "Keyring find failed: " | 273 LOG(ERROR) << "Keyring find failed: code " << context.result_code; |
665 << gnome_keyring_result_to_message(result); | |
666 return false; | 274 return false; |
667 } | 275 } |
668 // Successful read of actual data. Try migration if necessary. | 276 // Successful read of actual data. Try migration if necessary. |
669 if (!migrate_tried_) | 277 if (!migrate_tried_) |
670 MigrateToProfileSpecificLogins(); | 278 MigrateToProfileSpecificLogins(); |
671 return true; | 279 return true; |
672 } | 280 } |
673 | 281 |
674 bool NativeBackendGnome::GetAllLogins(PasswordFormList* forms) { | 282 bool NativeBackendGnome::GetAllLogins(PasswordFormList* forms) { |
675 GKRMethod method; | 283 KeyringProxyClient::RequestContext context(forms); |
676 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 284 proxy_client_->GetAllLogins(app_string_, &context); |
677 base::Bind(&GKRMethod::GetAllLogins, | 285 context.event.Wait(); |
678 base::Unretained(&method), | 286 if (context.result_code == GNOME_KEYRING_RESULT_NO_MATCH) |
679 app_string_.c_str())); | |
680 GnomeKeyringResult result = method.WaitResult(forms); | |
681 if (result == GNOME_KEYRING_RESULT_NO_MATCH) | |
682 return true; | 287 return true; |
683 if (result != GNOME_KEYRING_RESULT_OK) { | 288 if (context.result_code != GNOME_KEYRING_RESULT_OK) { |
684 LOG(ERROR) << "Keyring find failed: " | 289 LOG(ERROR) << "Keyring find failed: code " << context.result_code; |
685 << gnome_keyring_result_to_message(result); | |
686 return false; | 290 return false; |
687 } | 291 } |
688 // Successful read of actual data. Try migration if necessary. | 292 // Successful read of actual data. Try migration if necessary. |
689 if (!migrate_tried_) | 293 if (!migrate_tried_) |
690 MigrateToProfileSpecificLogins(); | 294 MigrateToProfileSpecificLogins(); |
691 return true; | 295 return true; |
692 } | 296 } |
693 | 297 |
694 std::string NativeBackendGnome::GetProfileSpecificAppString() const { | 298 std::string NativeBackendGnome::GetProfileSpecificAppString() const { |
695 // Originally, the application string was always just "chrome" and used only | 299 // Originally, the application string was always just "chrome" and used only |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
731 // Each other profile must be able to migrate the shared data as well, | 335 // Each other profile must be able to migrate the shared data as well, |
732 // so we must leave it alone. After a few releases, we'll add code to | 336 // so we must leave it alone. After a few releases, we'll add code to |
733 // delete them, and eventually remove this migration code. | 337 // delete them, and eventually remove this migration code. |
734 // TODO(mdm): follow through with the plan above. | 338 // TODO(mdm): follow through with the plan above. |
735 PasswordStoreX::SetPasswordsUseLocalProfileId(prefs_); | 339 PasswordStoreX::SetPasswordsUseLocalProfileId(prefs_); |
736 } else { | 340 } else { |
737 // We failed to migrate for some reason. Use the old app string. | 341 // We failed to migrate for some reason. Use the old app string. |
738 app_string_ = kGnomeKeyringAppString; | 342 app_string_ = kGnomeKeyringAppString; |
739 } | 343 } |
740 } | 344 } |
OLD | NEW |