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
3 // found in the LICENSE file.
5 #include "chrome/browser/password_manager/native_backend_gnome_x.h"
8 #include <gnome-keyring.h>
14 #include "base/basictypes.h"
15 #include "base/logging.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/metrics/histogram.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_piece.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/synchronization/waitable_event.h"
24 #include "base/time/time.h"
25 #include "components/autofill/core/common/password_form.h"
26 #include "components/password_manager/core/browser/psl_matching_helper.h"
27 #include "content/public/browser/browser_thread.h"
29 using autofill::PasswordForm
;
30 using base::UTF8ToUTF16
;
31 using base::UTF16ToUTF8
;
32 using content::BrowserThread
;
33 using password_manager::PSLMatchingHelper
;
35 #define GNOME_KEYRING_DEFINE_POINTER(name) \
36 typeof(&::gnome_keyring_##name) GnomeKeyringLoader::gnome_keyring_##name;
37 GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_DEFINE_POINTER
)
38 #undef GNOME_KEYRING_DEFINE_POINTER
40 bool GnomeKeyringLoader::keyring_loaded
= false;
42 #if defined(DLOPEN_GNOME_KEYRING)
44 #define GNOME_KEYRING_FUNCTION_INFO(name) \
45 {"gnome_keyring_"#name, reinterpret_cast<void**>(&gnome_keyring_##name)},
46 const GnomeKeyringLoader::FunctionInfo
GnomeKeyringLoader::functions
[] = {
47 GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_FUNCTION_INFO
)
50 #undef GNOME_KEYRING_FUNCTION_INFO
52 /* Load the library and initialize the function pointers. */
53 bool GnomeKeyringLoader::LoadGnomeKeyring() {
57 void* handle
= dlopen("libgnome-keyring.so.0", RTLD_NOW
| RTLD_GLOBAL
);
59 // We wanted to use GNOME Keyring, but we couldn't load it. Warn, because
60 // either the user asked for this, or we autodetected it incorrectly. (Or
61 // the system has broken libraries, which is also good to warn about.)
62 LOG(WARNING
) << "Could not load libgnome-keyring.so.0: " << dlerror();
66 for (size_t i
= 0; functions
[i
].name
; ++i
) {
68 *functions
[i
].pointer
= dlsym(handle
, functions
[i
].name
);
69 const char* error
= dlerror();
71 LOG(ERROR
) << "Unable to load symbol "
72 << functions
[i
].name
<< ": " << error
;
78 keyring_loaded
= true;
79 // We leak the library handle. That's OK: this function is called only once.
83 #else // defined(DLOPEN_GNOME_KEYRING)
85 bool GnomeKeyringLoader::LoadGnomeKeyring() {
88 #define GNOME_KEYRING_ASSIGN_POINTER(name) \
89 gnome_keyring_##name = &::gnome_keyring_##name;
90 GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_ASSIGN_POINTER
)
91 #undef GNOME_KEYRING_ASSIGN_POINTER
92 keyring_loaded
= true;
96 #endif // defined(DLOPEN_GNOME_KEYRING)
100 const char kGnomeKeyringAppString
[] = "chrome";
102 // Convert the attributes of a given keyring entry into a new PasswordForm.
103 // Note: does *not* get the actual password, as that is not a key attribute!
104 // Returns NULL if the attributes are for the wrong application.
105 scoped_ptr
<PasswordForm
> FormFromAttributes(GnomeKeyringAttributeList
* attrs
) {
106 // Read the string and int attributes into the appropriate map.
107 std::map
<std::string
, std::string
> string_attr_map
;
108 std::map
<std::string
, uint32_t> uint_attr_map
;
109 for (guint i
= 0; i
< attrs
->len
; ++i
) {
110 GnomeKeyringAttribute attr
= gnome_keyring_attribute_list_index(attrs
, i
);
111 if (attr
.type
== GNOME_KEYRING_ATTRIBUTE_TYPE_STRING
)
112 string_attr_map
[attr
.name
] = attr
.value
.string
;
113 else if (attr
.type
== GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32
)
114 uint_attr_map
[attr
.name
] = attr
.value
.integer
;
116 // Check to make sure this is a password we care about.
117 const std::string
& app_value
= string_attr_map
["application"];
118 if (!base::StringPiece(app_value
).starts_with(kGnomeKeyringAppString
))
119 return scoped_ptr
<PasswordForm
>();
121 scoped_ptr
<PasswordForm
> form(new PasswordForm());
122 form
->origin
= GURL(string_attr_map
["origin_url"]);
123 form
->action
= GURL(string_attr_map
["action_url"]);
124 form
->username_element
= UTF8ToUTF16(string_attr_map
["username_element"]);
125 form
->username_value
= UTF8ToUTF16(string_attr_map
["username_value"]);
126 form
->password_element
= UTF8ToUTF16(string_attr_map
["password_element"]);
127 form
->submit_element
= UTF8ToUTF16(string_attr_map
["submit_element"]);
128 form
->signon_realm
= string_attr_map
["signon_realm"];
129 form
->ssl_valid
= uint_attr_map
["ssl_valid"];
130 form
->preferred
= uint_attr_map
["preferred"];
131 int64 date_created
= 0;
132 bool date_ok
= base::StringToInt64(string_attr_map
["date_created"],
135 form
->date_created
= base::Time::FromTimeT(date_created
);
136 form
->blacklisted_by_user
= uint_attr_map
["blacklisted_by_user"];
137 form
->type
= static_cast<PasswordForm::Type
>(uint_attr_map
["type"]);
138 form
->times_used
= uint_attr_map
["times_used"];
139 form
->scheme
= static_cast<PasswordForm::Scheme
>(uint_attr_map
["scheme"]);
144 // Parse all the results from the given GList into a PasswordFormList, and free
145 // the GList. PasswordForms are allocated on the heap, and should be deleted by
146 // the consumer. If not NULL, |lookup_form| is used to filter out results --
147 // only credentials with signon realms passing the PSL matching (done by
148 // |helper|) against |lookup_form->signon_realm| will be kept. PSL matched
149 // results get their signon_realm, origin, and action rewritten to those of
150 // |lookup_form_|, with the original signon_realm saved into the result's
151 // original_signon_realm data member.
152 void ConvertFormList(GList
* found
,
153 const PasswordForm
* lookup_form
,
154 const PSLMatchingHelper
& helper
,
155 NativeBackendGnome::PasswordFormList
* forms
) {
156 PSLMatchingHelper::PSLDomainMatchMetric psl_domain_match_metric
=
157 PSLMatchingHelper::PSL_DOMAIN_MATCH_NONE
;
158 for (GList
* element
= g_list_first(found
); element
!= NULL
;
159 element
= g_list_next(element
)) {
160 GnomeKeyringFound
* data
= static_cast<GnomeKeyringFound
*>(element
->data
);
161 GnomeKeyringAttributeList
* attrs
= data
->attributes
;
163 scoped_ptr
<PasswordForm
> form(FormFromAttributes(attrs
));
165 if (lookup_form
&& form
->signon_realm
!= lookup_form
->signon_realm
) {
166 // This is not an exact match, we try PSL matching.
167 if (lookup_form
->scheme
!= PasswordForm::SCHEME_HTML
||
168 form
->scheme
!= PasswordForm::SCHEME_HTML
||
169 !(PSLMatchingHelper::IsPublicSuffixDomainMatch(
170 lookup_form
->signon_realm
, form
->signon_realm
))) {
173 psl_domain_match_metric
= PSLMatchingHelper::PSL_DOMAIN_MATCH_FOUND
;
174 form
->original_signon_realm
= form
->signon_realm
;
175 form
->signon_realm
= lookup_form
->signon_realm
;
176 form
->origin
= lookup_form
->origin
;
177 form
->action
= lookup_form
->action
;
180 form
->password_value
= UTF8ToUTF16(data
->secret
);
182 LOG(WARNING
) << "Unable to access password from list element!";
184 forms
->push_back(form
.release());
186 LOG(WARNING
) << "Could not initialize PasswordForm from attributes!";
190 UMA_HISTOGRAM_ENUMERATION(
191 "PasswordManager.PslDomainMatchTriggering",
192 helper
.IsMatchingEnabled()
193 ? psl_domain_match_metric
194 : PSLMatchingHelper::PSL_DOMAIN_MATCH_DISABLED
,
195 PSLMatchingHelper::PSL_DOMAIN_MATCH_COUNT
);
199 // Schema is analagous to the fields in PasswordForm.
200 // TODO(gcasto): Adding 'form_data' would be nice, but we would need to
201 // serialize in a way that is guaranteed to not have any embedded NULLs. Pickle
202 // doesn't make this guarantee, so we just don't serialize this field. Since
203 // it's only used to crowd source data collection it doesn't matter that much
204 // if it's not available on this platform.
205 const GnomeKeyringPasswordSchema kGnomeSchema
= {
206 GNOME_KEYRING_ITEM_GENERIC_SECRET
, {
207 { "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING
},
208 { "action_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING
},
209 { "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING
},
210 { "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING
},
211 { "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING
},
212 { "submit_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING
},
213 { "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING
},
214 { "ssl_valid", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32
},
215 { "preferred", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32
},
216 { "date_created", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING
},
217 { "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32
},
218 { "scheme", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32
},
219 { "type", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32
},
220 { "times_used", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32
},
221 // This field is always "chrome" so that we can search for it.
222 { "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING
},
227 // Sadly, PasswordStore goes to great lengths to switch from the originally
228 // calling thread to the DB thread, and to provide an asynchronous API to
229 // callers while using a synchronous (virtual) API provided by subclasses like
230 // PasswordStoreX -- but GNOME Keyring really wants to be on the GLib main
231 // thread, which is the UI thread to us. So we end up having to switch threads
232 // again, possibly back to the very same thread (in case the UI thread is the
233 // caller, e.g. in the password management UI), and *block* the DB thread
234 // waiting for a response from the UI thread to provide the synchronous API
235 // PasswordStore expects of us. (It will then in turn switch back to the
236 // original caller to send the asynchronous reply to the original request.)
238 // This class represents a call to a GNOME Keyring method. A RunnableMethod
239 // should be posted to the UI thread to call one of its action methods, and then
240 // a WaitResult() method should be called to wait for the result. Each instance
241 // supports only one outstanding method at a time, though multiple instances may
242 // be used in parallel.
243 class GKRMethod
: public GnomeKeyringLoader
{
245 typedef NativeBackendGnome::PasswordFormList PasswordFormList
;
247 GKRMethod() : event_(false, false), result_(GNOME_KEYRING_RESULT_CANCELLED
) {}
249 // Action methods. These call gnome_keyring_* functions. Call from UI thread.
250 // See GetProfileSpecificAppString() for more information on the app string.
251 void AddLogin(const PasswordForm
& form
, const char* app_string
);
252 void AddLoginSearch(const PasswordForm
& form
, const char* app_string
);
253 void UpdateLoginSearch(const PasswordForm
& form
, const char* app_string
);
254 void RemoveLogin(const PasswordForm
& form
, const char* app_string
);
255 void GetLogins(const PasswordForm
& form
, const char* app_string
);
256 void GetLoginsList(uint32_t blacklisted_by_user
, const char* app_string
);
257 void GetAllLogins(const char* app_string
);
259 // Use after AddLogin, RemoveLogin.
260 GnomeKeyringResult
WaitResult();
262 // Use after AddLoginSearch, UpdateLoginSearch, GetLogins, GetLoginsList,
264 GnomeKeyringResult
WaitResult(PasswordFormList
* forms
);
267 struct GnomeKeyringAttributeListFreeDeleter
{
268 inline void operator()(void* list
) const {
269 gnome_keyring_attribute_list_free(
270 static_cast<GnomeKeyringAttributeList
*>(list
));
274 typedef scoped_ptr
<GnomeKeyringAttributeList
,
275 GnomeKeyringAttributeListFreeDeleter
> ScopedAttributeList
;
277 // Helper methods to abbreviate Gnome Keyring long API names.
278 static void AppendString(ScopedAttributeList
* list
,
281 static void AppendString(ScopedAttributeList
* list
,
283 const std::string
& value
);
284 static void AppendUint32(ScopedAttributeList
* list
,
288 // All these callbacks are called on UI thread.
289 static void OnOperationDone(GnomeKeyringResult result
, gpointer data
);
291 static void OnOperationGetList(GnomeKeyringResult result
, GList
* list
,
294 base::WaitableEvent event_
;
295 GnomeKeyringResult result_
;
296 NativeBackendGnome::PasswordFormList forms_
;
297 // If the credential search is specified by a single form and needs to use PSL
298 // matching, then the specifying form is stored in |lookup_form_|. If PSL
299 // matching is used to find a result, then the results signon realm, origin
300 // and action are stored are replaced by those of |lookup_form_|.
301 // Additionally, |lookup_form_->signon_realm| is also used to narrow down the
302 // found logins to those which indeed PSL-match the look-up. And finally,
303 // |lookup_form_| set to NULL means that PSL matching is not required.
304 scoped_ptr
<PasswordForm
> lookup_form_
;
305 const PSLMatchingHelper helper_
;
308 void GKRMethod::AddLogin(const PasswordForm
& form
, const char* app_string
) {
309 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
310 time_t date_created
= form
.date_created
.ToTimeT();
311 // If we are asked to save a password with 0 date, use the current time.
312 // We don't want to actually save passwords as though on January 1, 1970.
314 date_created
= time(NULL
);
315 gnome_keyring_store_password(
317 NULL
, // Default keyring.
318 form
.origin
.spec().c_str(), // Display name.
319 UTF16ToUTF8(form
.password_value
).c_str(),
322 NULL
, // destroy_data
323 "origin_url", form
.origin
.spec().c_str(),
324 "action_url", form
.action
.spec().c_str(),
325 "username_element", UTF16ToUTF8(form
.username_element
).c_str(),
326 "username_value", UTF16ToUTF8(form
.username_value
).c_str(),
327 "password_element", UTF16ToUTF8(form
.password_element
).c_str(),
328 "submit_element", UTF16ToUTF8(form
.submit_element
).c_str(),
329 "signon_realm", form
.signon_realm
.c_str(),
330 "ssl_valid", form
.ssl_valid
,
331 "preferred", form
.preferred
,
332 "date_created", base::Int64ToString(date_created
).c_str(),
333 "blacklisted_by_user", form
.blacklisted_by_user
,
335 "times_used", form
.times_used
,
336 "scheme", form
.scheme
,
337 "application", app_string
,
341 void GKRMethod::AddLoginSearch(const PasswordForm
& form
,
342 const char* app_string
) {
343 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
344 lookup_form_
.reset(NULL
);
345 // Search GNOME Keyring for matching passwords to update.
346 ScopedAttributeList
attrs(gnome_keyring_attribute_list_new());
347 AppendString(&attrs
, "origin_url", form
.origin
.spec());
348 AppendString(&attrs
, "username_element", UTF16ToUTF8(form
.username_element
));
349 AppendString(&attrs
, "username_value", UTF16ToUTF8(form
.username_value
));
350 AppendString(&attrs
, "password_element", UTF16ToUTF8(form
.password_element
));
351 AppendString(&attrs
, "submit_element", UTF16ToUTF8(form
.submit_element
));
352 AppendString(&attrs
, "signon_realm", form
.signon_realm
);
353 AppendString(&attrs
, "application", app_string
);
354 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET
,
358 /*destroy_data=*/NULL
);
361 void GKRMethod::UpdateLoginSearch(const PasswordForm
& form
,
362 const char* app_string
) {
363 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
364 lookup_form_
.reset(NULL
);
365 // Search GNOME Keyring for matching passwords to update.
366 ScopedAttributeList
attrs(gnome_keyring_attribute_list_new());
367 AppendString(&attrs
, "origin_url", form
.origin
.spec());
368 AppendString(&attrs
, "username_element", UTF16ToUTF8(form
.username_element
));
369 AppendString(&attrs
, "username_value", UTF16ToUTF8(form
.username_value
));
370 AppendString(&attrs
, "password_element", UTF16ToUTF8(form
.password_element
));
371 AppendString(&attrs
, "signon_realm", form
.signon_realm
);
372 AppendString(&attrs
, "application", app_string
);
373 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET
,
377 /*destroy_data=*/NULL
);
380 void GKRMethod::RemoveLogin(const PasswordForm
& form
, const char* app_string
) {
381 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
382 // We find forms using the same fields as LoginDatabase::RemoveLogin().
383 gnome_keyring_delete_password(
387 NULL
, // destroy_data
388 "origin_url", form
.origin
.spec().c_str(),
389 "action_url", form
.action
.spec().c_str(),
390 "username_element", UTF16ToUTF8(form
.username_element
).c_str(),
391 "username_value", UTF16ToUTF8(form
.username_value
).c_str(),
392 "password_element", UTF16ToUTF8(form
.password_element
).c_str(),
393 "submit_element", UTF16ToUTF8(form
.submit_element
).c_str(),
394 "signon_realm", form
.signon_realm
.c_str(),
395 "application", app_string
,
399 void GKRMethod::GetLogins(const PasswordForm
& form
, const char* app_string
) {
400 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
401 lookup_form_
.reset(new PasswordForm(form
));
402 // Search GNOME Keyring for matching passwords.
403 ScopedAttributeList
attrs(gnome_keyring_attribute_list_new());
404 if (!helper_
.ShouldPSLDomainMatchingApply(
405 PSLMatchingHelper::GetRegistryControlledDomain(
406 GURL(form
.signon_realm
)))) {
407 AppendString(&attrs
, "signon_realm", form
.signon_realm
);
409 AppendString(&attrs
, "application", app_string
);
410 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET
,
414 /*destroy_data=*/NULL
);
417 void GKRMethod::GetLoginsList(uint32_t blacklisted_by_user
,
418 const char* app_string
) {
419 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
420 lookup_form_
.reset(NULL
);
421 // Search GNOME Keyring for matching passwords.
422 ScopedAttributeList
attrs(gnome_keyring_attribute_list_new());
423 AppendUint32(&attrs
, "blacklisted_by_user", blacklisted_by_user
);
424 AppendString(&attrs
, "application", app_string
);
425 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET
,
429 /*destroy_data=*/NULL
);
432 void GKRMethod::GetAllLogins(const char* app_string
) {
433 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
434 lookup_form_
.reset(NULL
);
435 // We need to search for something, otherwise we get no results - so
436 // we search for the fixed application string.
437 ScopedAttributeList
attrs(gnome_keyring_attribute_list_new());
438 AppendString(&attrs
, "application", app_string
);
439 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET
,
443 /*destroy_data=*/NULL
);
446 GnomeKeyringResult
GKRMethod::WaitResult() {
447 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB
));
452 GnomeKeyringResult
GKRMethod::WaitResult(PasswordFormList
* forms
) {
453 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB
));
455 if (forms
->empty()) {
456 // Normal case. Avoid extra allocation by swapping.
459 // Rare case. Append forms_ to *forms.
460 forms
->insert(forms
->end(), forms_
.begin(), forms_
.end());
467 void GKRMethod::AppendString(GKRMethod::ScopedAttributeList
* list
,
470 gnome_keyring_attribute_list_append_string(list
->get(), name
, value
);
474 void GKRMethod::AppendString(GKRMethod::ScopedAttributeList
* list
,
476 const std::string
& value
) {
477 AppendString(list
, name
, value
.c_str());
481 void GKRMethod::AppendUint32(GKRMethod::ScopedAttributeList
* list
,
484 gnome_keyring_attribute_list_append_uint32(list
->get(), name
, value
);
488 void GKRMethod::OnOperationDone(GnomeKeyringResult result
, gpointer data
) {
489 GKRMethod
* method
= static_cast<GKRMethod
*>(data
);
490 method
->result_
= result
;
491 method
->event_
.Signal();
495 void GKRMethod::OnOperationGetList(GnomeKeyringResult result
, GList
* list
,
497 GKRMethod
* method
= static_cast<GKRMethod
*>(data
);
498 method
->result_
= result
;
499 method
->forms_
.clear();
500 // |list| will be freed after this callback returns, so convert it now.
502 list
, method
->lookup_form_
.get(), method
->helper_
, &method
->forms_
);
503 method
->lookup_form_
.reset(NULL
);
504 method
->event_
.Signal();
509 NativeBackendGnome::NativeBackendGnome(LocalProfileId id
)
511 app_string_
= GetProfileSpecificAppString();
514 NativeBackendGnome::~NativeBackendGnome() {
517 bool NativeBackendGnome::Init() {
518 return LoadGnomeKeyring() && gnome_keyring_is_available();
521 bool NativeBackendGnome::RawAddLogin(const PasswordForm
& form
) {
522 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB
));
524 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
525 base::Bind(&GKRMethod::AddLogin
,
526 base::Unretained(&method
),
527 form
, app_string_
.c_str()));
528 GnomeKeyringResult result
= method
.WaitResult();
529 if (result
!= GNOME_KEYRING_RESULT_OK
) {
530 LOG(ERROR
) << "Keyring save failed: "
531 << gnome_keyring_result_to_message(result
);
537 password_manager::PasswordStoreChangeList
NativeBackendGnome::AddLogin(
538 const PasswordForm
& form
) {
539 // Based on LoginDatabase::AddLogin(), we search for an existing match based
540 // on origin_url, username_element, username_value, password_element, submit
541 // element, and signon_realm first, remove that, and then add the new entry.
542 // We'd add the new one first, and then delete the original, but then the
543 // delete might actually delete the newly-added entry!
544 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB
));
546 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
547 base::Bind(&GKRMethod::AddLoginSearch
,
548 base::Unretained(&method
),
549 form
, app_string_
.c_str()));
550 ScopedVector
<autofill::PasswordForm
> forms
;
551 GnomeKeyringResult result
= method
.WaitResult(&forms
.get());
552 if (result
!= GNOME_KEYRING_RESULT_OK
&&
553 result
!= GNOME_KEYRING_RESULT_NO_MATCH
) {
554 LOG(ERROR
) << "Keyring find failed: "
555 << gnome_keyring_result_to_message(result
);
556 return password_manager::PasswordStoreChangeList();
558 password_manager::PasswordStoreChangeList changes
;
559 if (forms
.size() > 0) {
560 if (forms
.size() > 1) {
561 LOG(WARNING
) << "Adding login when there are " << forms
.size()
562 << " matching logins already! Will replace only the first.";
565 if (RemoveLogin(*forms
[0])) {
566 changes
.push_back(password_manager::PasswordStoreChange(
567 password_manager::PasswordStoreChange::REMOVE
, *forms
[0]));
570 if (RawAddLogin(form
)) {
571 changes
.push_back(password_manager::PasswordStoreChange(
572 password_manager::PasswordStoreChange::ADD
, form
));
577 bool NativeBackendGnome::UpdateLogin(
578 const PasswordForm
& form
,
579 password_manager::PasswordStoreChangeList
* changes
) {
580 // Based on LoginDatabase::UpdateLogin(), we search for forms to update by
581 // origin_url, username_element, username_value, password_element, and
582 // signon_realm. We then compare the result to the updated form. If they
583 // differ in any of the mutable fields, then we remove the original, and
584 // then add the new entry. We'd add the new one first, and then delete the
585 // original, but then the delete might actually delete the newly-added entry!
586 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB
));
590 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
591 base::Bind(&GKRMethod::UpdateLoginSearch
,
592 base::Unretained(&method
),
593 form
, app_string_
.c_str()));
594 ScopedVector
<autofill::PasswordForm
> forms
;
595 GnomeKeyringResult result
= method
.WaitResult(&forms
.get());
596 if (result
!= GNOME_KEYRING_RESULT_OK
) {
597 LOG(ERROR
) << "Keyring find failed: "
598 << gnome_keyring_result_to_message(result
);
602 bool removed
= false;
603 for (size_t i
= 0; i
< forms
.size(); ++i
) {
604 if (*forms
[i
] != form
) {
605 RemoveLogin(*forms
[i
]);
612 if (RawAddLogin(form
)) {
613 password_manager::PasswordStoreChange
change(
614 password_manager::PasswordStoreChange::UPDATE
, form
);
615 changes
->push_back(change
);
621 bool NativeBackendGnome::RemoveLogin(const PasswordForm
& form
) {
622 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB
));
624 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
625 base::Bind(&GKRMethod::RemoveLogin
,
626 base::Unretained(&method
),
627 form
, app_string_
.c_str()));
628 GnomeKeyringResult result
= method
.WaitResult();
629 if (result
!= GNOME_KEYRING_RESULT_OK
) {
630 // Warning, not error, because this can sometimes happen due to the user
631 // racing with the daemon to delete the password a second time.
632 LOG(WARNING
) << "Keyring delete failed: "
633 << gnome_keyring_result_to_message(result
);
639 bool NativeBackendGnome::RemoveLoginsCreatedBetween(
640 const base::Time
& delete_begin
,
641 const base::Time
& delete_end
) {
642 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB
));
644 // We could walk the list and delete items as we find them, but it is much
645 // easier to build the list and use RemoveLogin() to delete them.
646 PasswordFormList forms
;
647 if (!GetAllLogins(&forms
))
650 for (size_t i
= 0; i
< forms
.size(); ++i
) {
651 if (delete_begin
<= forms
[i
]->date_created
&&
652 (delete_end
.is_null() || forms
[i
]->date_created
< delete_end
)) {
653 if (!RemoveLogin(*forms
[i
]))
661 bool NativeBackendGnome::GetLogins(const PasswordForm
& form
,
662 PasswordFormList
* forms
) {
663 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB
));
665 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
666 base::Bind(&GKRMethod::GetLogins
,
667 base::Unretained(&method
),
668 form
, app_string_
.c_str()));
669 GnomeKeyringResult result
= method
.WaitResult(forms
);
670 if (result
== GNOME_KEYRING_RESULT_NO_MATCH
)
672 if (result
!= GNOME_KEYRING_RESULT_OK
) {
673 LOG(ERROR
) << "Keyring find failed: "
674 << gnome_keyring_result_to_message(result
);
680 bool NativeBackendGnome::GetLoginsCreatedBetween(const base::Time
& get_begin
,
681 const base::Time
& get_end
,
682 PasswordFormList
* forms
) {
683 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB
));
684 // We could walk the list and add items as we find them, but it is much
685 // easier to build the list and then filter the results.
686 PasswordFormList all_forms
;
687 if (!GetAllLogins(&all_forms
))
690 forms
->reserve(forms
->size() + all_forms
.size());
691 for (size_t i
= 0; i
< all_forms
.size(); ++i
) {
692 if (get_begin
<= all_forms
[i
]->date_created
&&
693 (get_end
.is_null() || all_forms
[i
]->date_created
< get_end
)) {
694 forms
->push_back(all_forms
[i
]);
703 bool NativeBackendGnome::GetAutofillableLogins(PasswordFormList
* forms
) {
704 return GetLoginsList(forms
, true);
707 bool NativeBackendGnome::GetBlacklistLogins(PasswordFormList
* forms
) {
708 return GetLoginsList(forms
, false);
711 bool NativeBackendGnome::GetLoginsList(PasswordFormList
* forms
,
713 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB
));
715 uint32_t blacklisted_by_user
= !autofillable
;
718 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
719 base::Bind(&GKRMethod::GetLoginsList
,
720 base::Unretained(&method
),
721 blacklisted_by_user
, app_string_
.c_str()));
722 GnomeKeyringResult result
= method
.WaitResult(forms
);
723 if (result
== GNOME_KEYRING_RESULT_NO_MATCH
)
725 if (result
!= GNOME_KEYRING_RESULT_OK
) {
726 LOG(ERROR
) << "Keyring find failed: "
727 << gnome_keyring_result_to_message(result
);
733 bool NativeBackendGnome::GetAllLogins(PasswordFormList
* forms
) {
735 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
736 base::Bind(&GKRMethod::GetAllLogins
,
737 base::Unretained(&method
),
738 app_string_
.c_str()));
739 GnomeKeyringResult result
= method
.WaitResult(forms
);
740 if (result
== GNOME_KEYRING_RESULT_NO_MATCH
)
742 if (result
!= GNOME_KEYRING_RESULT_OK
) {
743 LOG(ERROR
) << "Keyring find failed: "
744 << gnome_keyring_result_to_message(result
);
750 std::string
NativeBackendGnome::GetProfileSpecificAppString() const {
751 // Originally, the application string was always just "chrome" and used only
752 // so that we had *something* to search for since GNOME Keyring won't search
753 // for nothing. Now we use it to distinguish passwords for different profiles.
754 return base::StringPrintf("%s-%d", kGnomeKeyringAppString
, profile_id_
);