- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / password_manager / native_backend_kwallet_x.cc
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.
4
5 #include "chrome/browser/password_manager/native_backend_kwallet_x.h"
6
7 #include <vector>
8
9 #include "base/bind.h"
10 #include "base/logging.h"
11 #include "base/pickle.h"
12 #include "base/stl_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/synchronization/waitable_event.h"
15 #include "base/threading/thread_restrictions.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "dbus/bus.h"
18 #include "dbus/message.h"
19 #include "dbus/object_path.h"
20 #include "dbus/object_proxy.h"
21 #include "grit/chromium_strings.h"
22 #include "ui/base/l10n/l10n_util.h"
23
24 using autofill::PasswordForm;
25 using content::BrowserThread;
26
27 namespace {
28
29 // We could localize this string, but then changing your locale would cause
30 // you to lose access to all your stored passwords. Maybe best not to do that.
31 // Name of the folder to store passwords in.
32 const char kKWalletFolder[] = "Chrome Form Data";
33
34 // DBus service, path, and interface names for klauncher and kwalletd.
35 const char kKWalletServiceName[] = "org.kde.kwalletd";
36 const char kKWalletPath[] = "/modules/kwalletd";
37 const char kKWalletInterface[] = "org.kde.KWallet";
38 const char kKLauncherServiceName[] = "org.kde.klauncher";
39 const char kKLauncherPath[] = "/KLauncher";
40 const char kKLauncherInterface[] = "org.kde.KLauncher";
41
42 // Compares two PasswordForms and returns true if they are the same.
43 // If |update_check| is false, we only check the fields that are checked by
44 // LoginDatabase::UpdateLogin() when updating logins; otherwise, we check the
45 // fields that are checked by LoginDatabase::RemoveLogin() for removing them.
46 bool CompareForms(const autofill::PasswordForm& a,
47                   const autofill::PasswordForm& b,
48                   bool update_check) {
49   // An update check doesn't care about the submit element.
50   if (!update_check && a.submit_element != b.submit_element)
51     return false;
52   return a.origin           == b.origin &&
53          a.password_element == b.password_element &&
54          a.signon_realm     == b.signon_realm &&
55          a.username_element == b.username_element &&
56          a.username_value   == b.username_value;
57 }
58
59 // Checks a serialized list of PasswordForms for sanity. Returns true if OK.
60 // Note that |realm| is only used for generating a useful warning message.
61 bool CheckSerializedValue(const uint8_t* byte_array,
62                           size_t length,
63                           const std::string& realm) {
64   const Pickle::Header* header =
65       reinterpret_cast<const Pickle::Header*>(byte_array);
66   if (length < sizeof(*header) ||
67       header->payload_size > length - sizeof(*header)) {
68     LOG(WARNING) << "Invalid KWallet entry detected (realm: " << realm << ")";
69     return false;
70   }
71   return true;
72 }
73
74 // Convenience function to read a GURL from a Pickle. Assumes the URL has
75 // been written as a UTF-8 string. Returns true on success.
76 bool ReadGURL(PickleIterator* iter, bool warn_only, GURL* url) {
77   std::string url_string;
78   if (!iter->ReadString(&url_string)) {
79     if (!warn_only)
80       LOG(ERROR) << "Failed to deserialize URL.";
81     *url = GURL();
82     return false;
83   }
84   *url = GURL(url_string);
85   return true;
86 }
87
88 }  // namespace
89
90 NativeBackendKWallet::NativeBackendKWallet(LocalProfileId id,
91                                            PrefService* prefs)
92     : profile_id_(id),
93       prefs_(prefs),
94       kwallet_proxy_(NULL),
95       app_name_(l10n_util::GetStringUTF8(IDS_PRODUCT_NAME)) {
96   // TODO(mdm): after a few more releases, remove the code which is now dead due
97   // to the true || here, and simplify this code. We don't do it yet to make it
98   // easier to revert if necessary.
99   if (true || PasswordStoreX::PasswordsUseLocalProfileId(prefs)) {
100     folder_name_ = GetProfileSpecificFolderName();
101     // We already did the migration previously. Don't try again.
102     migrate_tried_ = true;
103   } else {
104     folder_name_ = kKWalletFolder;
105     migrate_tried_ = false;
106   }
107 }
108
109 NativeBackendKWallet::~NativeBackendKWallet() {
110   // This destructor is called on the thread that is destroying the Profile
111   // containing the PasswordStore that owns this NativeBackend. Generally that
112   // won't be the DB thread; it will be the UI thread. So we post a message to
113   // shut it down on the DB thread, and it will be destructed afterward when the
114   // scoped_refptr<dbus::Bus> goes out of scope. The NativeBackend will be
115   // destroyed before that occurs, but that's OK.
116   if (session_bus_.get()) {
117     BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
118                             base::Bind(&dbus::Bus::ShutdownAndBlock,
119                                        session_bus_.get()));
120   }
121 }
122
123 bool NativeBackendKWallet::Init() {
124   // Without the |optional_bus| parameter, a real bus will be instantiated.
125   return InitWithBus(scoped_refptr<dbus::Bus>());
126 }
127
128 bool NativeBackendKWallet::InitWithBus(scoped_refptr<dbus::Bus> optional_bus) {
129   // We must synchronously do a few DBus calls to figure out if initialization
130   // succeeds, but later, we'll want to do most work on the DB thread. So we
131   // have to do the initialization on the DB thread here too, and wait for it.
132   bool success = false;
133   base::WaitableEvent event(false, false);
134   // NativeBackendKWallet isn't reference counted, but we wait for InitWithBus
135   // to finish, so we can safely use base::Unretained here.
136   BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
137                           base::Bind(&NativeBackendKWallet::InitOnDBThread,
138                                      base::Unretained(this),
139                                      optional_bus, &event, &success));
140
141   // This ScopedAllowWait should not be here. http://crbug.com/125331
142   base::ThreadRestrictions::ScopedAllowWait allow_wait;
143   event.Wait();
144   return success;
145 }
146
147 void NativeBackendKWallet::InitOnDBThread(scoped_refptr<dbus::Bus> optional_bus,
148                                           base::WaitableEvent* event,
149                                           bool* success) {
150   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
151   DCHECK(!session_bus_.get());
152   if (optional_bus.get()) {
153     // The optional_bus parameter is given when this method is called in tests.
154     session_bus_ = optional_bus;
155   } else {
156     // Get a (real) connection to the session bus.
157     dbus::Bus::Options options;
158     options.bus_type = dbus::Bus::SESSION;
159     options.connection_type = dbus::Bus::PRIVATE;
160     session_bus_ = new dbus::Bus(options);
161   }
162   kwallet_proxy_ =
163       session_bus_->GetObjectProxy(kKWalletServiceName,
164                                    dbus::ObjectPath(kKWalletPath));
165   // kwalletd may not be running. If we get a temporary failure initializing it,
166   // try to start it and then try again. (Note the short-circuit evaluation.)
167   const InitResult result = InitWallet();
168   *success = (result == INIT_SUCCESS ||
169               (result == TEMPORARY_FAIL &&
170                StartKWalletd() && InitWallet() == INIT_SUCCESS));
171   event->Signal();
172 }
173
174 bool NativeBackendKWallet::StartKWalletd() {
175   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
176   // Sadly kwalletd doesn't use DBus activation, so we have to make a call to
177   // klauncher to start it.
178   dbus::ObjectProxy* klauncher =
179       session_bus_->GetObjectProxy(kKLauncherServiceName,
180                                    dbus::ObjectPath(kKLauncherPath));
181
182   dbus::MethodCall method_call(kKLauncherInterface,
183                                "start_service_by_desktop_name");
184   dbus::MessageWriter builder(&method_call);
185   std::vector<std::string> empty;
186   builder.AppendString("kwalletd");     // serviceName
187   builder.AppendArrayOfStrings(empty);  // urls
188   builder.AppendArrayOfStrings(empty);  // envs
189   builder.AppendString(std::string());  // startup_id
190   builder.AppendBool(false);            // blind
191   scoped_ptr<dbus::Response> response(
192       klauncher->CallMethodAndBlock(
193           &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
194   if (!response.get()) {
195     LOG(ERROR) << "Error contacting klauncher to start kwalletd";
196     return false;
197   }
198   dbus::MessageReader reader(response.get());
199   int32_t ret = -1;
200   std::string dbus_name;
201   std::string error;
202   int32_t pid = -1;
203   if (!reader.PopInt32(&ret) || !reader.PopString(&dbus_name) ||
204       !reader.PopString(&error) || !reader.PopInt32(&pid)) {
205     LOG(ERROR) << "Error reading response from klauncher to start kwalletd: "
206                << response->ToString();
207     return false;
208   }
209   if (!error.empty() || ret) {
210     LOG(ERROR) << "Error launching kwalletd: error '" << error << "' "
211                << " (code " << ret << ")";
212     return false;
213   }
214
215   return true;
216 }
217
218 NativeBackendKWallet::InitResult NativeBackendKWallet::InitWallet() {
219   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
220   {
221     // Check that KWallet is enabled.
222     dbus::MethodCall method_call(kKWalletInterface, "isEnabled");
223     scoped_ptr<dbus::Response> response(
224         kwallet_proxy_->CallMethodAndBlock(
225             &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
226     if (!response.get()) {
227       LOG(ERROR) << "Error contacting kwalletd (isEnabled)";
228       return TEMPORARY_FAIL;
229     }
230     dbus::MessageReader reader(response.get());
231     bool enabled = false;
232     if (!reader.PopBool(&enabled)) {
233       LOG(ERROR) << "Error reading response from kwalletd (isEnabled): "
234                  << response->ToString();
235       return PERMANENT_FAIL;
236     }
237     // Not enabled? Don't use KWallet. But also don't warn here.
238     if (!enabled) {
239       VLOG(1) << "kwalletd reports that KWallet is not enabled.";
240       return PERMANENT_FAIL;
241     }
242   }
243
244   {
245     // Get the wallet name.
246     dbus::MethodCall method_call(kKWalletInterface, "networkWallet");
247     scoped_ptr<dbus::Response> response(
248         kwallet_proxy_->CallMethodAndBlock(
249             &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
250     if (!response.get()) {
251       LOG(ERROR) << "Error contacting kwalletd (networkWallet)";
252       return TEMPORARY_FAIL;
253     }
254     dbus::MessageReader reader(response.get());
255     if (!reader.PopString(&wallet_name_)) {
256       LOG(ERROR) << "Error reading response from kwalletd (networkWallet): "
257                  << response->ToString();
258       return PERMANENT_FAIL;
259     }
260   }
261
262   return INIT_SUCCESS;
263 }
264
265 bool NativeBackendKWallet::AddLogin(const PasswordForm& form) {
266   int wallet_handle = WalletHandle();
267   if (wallet_handle == kInvalidKWalletHandle)
268     return false;
269
270   PasswordFormList forms;
271   GetLoginsList(&forms, form.signon_realm, wallet_handle);
272
273   // We search for a login to update, rather than unconditionally appending the
274   // login, because in some cases (especially involving sync) we can be asked to
275   // add a login that already exists. In these cases we want to just update.
276   bool updated = false;
277   for (size_t i = 0; i < forms.size(); ++i) {
278     // Use the more restrictive removal comparison, so that we never have
279     // duplicate logins that would all be removed together by RemoveLogin().
280     if (CompareForms(form, *forms[i], false)) {
281       *forms[i] = form;
282       updated = true;
283     }
284   }
285   if (!updated)
286     forms.push_back(new PasswordForm(form));
287
288   bool ok = SetLoginsList(forms, form.signon_realm, wallet_handle);
289
290   STLDeleteElements(&forms);
291   return ok;
292 }
293
294 bool NativeBackendKWallet::UpdateLogin(const PasswordForm& form) {
295   int wallet_handle = WalletHandle();
296   if (wallet_handle == kInvalidKWalletHandle)
297     return false;
298
299   PasswordFormList forms;
300   GetLoginsList(&forms, form.signon_realm, wallet_handle);
301
302   for (size_t i = 0; i < forms.size(); ++i) {
303     if (CompareForms(form, *forms[i], true))
304       *forms[i] = form;
305   }
306
307   bool ok = SetLoginsList(forms, form.signon_realm, wallet_handle);
308
309   STLDeleteElements(&forms);
310   return ok;
311 }
312
313 bool NativeBackendKWallet::RemoveLogin(const PasswordForm& form) {
314   int wallet_handle = WalletHandle();
315   if (wallet_handle == kInvalidKWalletHandle)
316     return false;
317
318   PasswordFormList all_forms;
319   GetLoginsList(&all_forms, form.signon_realm, wallet_handle);
320
321   PasswordFormList kept_forms;
322   kept_forms.reserve(all_forms.size());
323   for (size_t i = 0; i < all_forms.size(); ++i) {
324     if (CompareForms(form, *all_forms[i], false))
325       delete all_forms[i];
326     else
327       kept_forms.push_back(all_forms[i]);
328   }
329
330   // Update the entry in the wallet, possibly deleting it.
331   bool ok = SetLoginsList(kept_forms, form.signon_realm, wallet_handle);
332
333   STLDeleteElements(&kept_forms);
334   return ok;
335 }
336
337 bool NativeBackendKWallet::RemoveLoginsCreatedBetween(
338     const base::Time& delete_begin,
339     const base::Time& delete_end) {
340   int wallet_handle = WalletHandle();
341   if (wallet_handle == kInvalidKWalletHandle)
342     return false;
343
344   // We could probably also use readEntryList here.
345   std::vector<std::string> realm_list;
346   {
347     dbus::MethodCall method_call(kKWalletInterface, "entryList");
348     dbus::MessageWriter builder(&method_call);
349     builder.AppendInt32(wallet_handle);  // handle
350     builder.AppendString(folder_name_);  // folder
351     builder.AppendString(app_name_);     // appid
352     scoped_ptr<dbus::Response> response(
353         kwallet_proxy_->CallMethodAndBlock(
354             &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
355     if (!response.get()) {
356       LOG(ERROR) << "Error contacting kwalletd (entryList)";
357       return false;
358     }
359     dbus::MessageReader reader(response.get());
360     dbus::MessageReader array(response.get());
361     if (!reader.PopArray(&array)) {
362       LOG(ERROR) << "Error reading response from kwalletd (entryList): "
363                  << response->ToString();
364       return false;
365     }
366     while (array.HasMoreData()) {
367       std::string realm;
368       if (!array.PopString(&realm)) {
369         LOG(ERROR) << "Error reading response from kwalletd (entryList): "
370                    << response->ToString();
371         return false;
372       }
373       realm_list.push_back(realm);
374     }
375   }
376
377   bool ok = true;
378   for (size_t i = 0; i < realm_list.size(); ++i) {
379     const std::string& signon_realm = realm_list[i];
380     dbus::MethodCall method_call(kKWalletInterface, "readEntry");
381     dbus::MessageWriter builder(&method_call);
382     builder.AppendInt32(wallet_handle);  // handle
383     builder.AppendString(folder_name_);  // folder
384     builder.AppendString(signon_realm);  // key
385     builder.AppendString(app_name_);     // appid
386     scoped_ptr<dbus::Response> response(
387         kwallet_proxy_->CallMethodAndBlock(
388             &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
389     if (!response.get()) {
390       LOG(ERROR) << "Error contacting kwalletd (readEntry)";
391       continue;
392     }
393     dbus::MessageReader reader(response.get());
394     uint8_t* bytes = NULL;
395     size_t length = 0;
396     if (!reader.PopArrayOfBytes(&bytes, &length)) {
397       LOG(ERROR) << "Error reading response from kwalletd (readEntry): "
398                  << response->ToString();
399       continue;
400     }
401     if (!bytes || !CheckSerializedValue(bytes, length, signon_realm))
402       continue;
403
404     // Can't we all just agree on whether bytes are signed or not? Please?
405     Pickle pickle(reinterpret_cast<const char*>(bytes), length);
406     PasswordFormList all_forms;
407     DeserializeValue(signon_realm, pickle, &all_forms);
408
409     PasswordFormList kept_forms;
410     kept_forms.reserve(all_forms.size());
411     for (size_t i = 0; i < all_forms.size(); ++i) {
412       if (delete_begin <= all_forms[i]->date_created &&
413           (delete_end.is_null() || all_forms[i]->date_created < delete_end)) {
414         delete all_forms[i];
415       } else {
416         kept_forms.push_back(all_forms[i]);
417       }
418     }
419
420     if (!SetLoginsList(kept_forms, signon_realm, wallet_handle))
421       ok = false;
422     STLDeleteElements(&kept_forms);
423   }
424   return ok;
425 }
426
427 bool NativeBackendKWallet::GetLogins(const PasswordForm& form,
428                                      PasswordFormList* forms) {
429   int wallet_handle = WalletHandle();
430   if (wallet_handle == kInvalidKWalletHandle)
431     return false;
432   return GetLoginsList(forms, form.signon_realm, wallet_handle);
433 }
434
435 bool NativeBackendKWallet::GetLoginsCreatedBetween(const base::Time& get_begin,
436                                                    const base::Time& get_end,
437                                                    PasswordFormList* forms) {
438   int wallet_handle = WalletHandle();
439   if (wallet_handle == kInvalidKWalletHandle)
440     return false;
441   return GetLoginsList(forms, get_begin, get_end, wallet_handle);
442 }
443
444 bool NativeBackendKWallet::GetAutofillableLogins(PasswordFormList* forms) {
445   int wallet_handle = WalletHandle();
446   if (wallet_handle == kInvalidKWalletHandle)
447     return false;
448   return GetLoginsList(forms, true, wallet_handle);
449 }
450
451 bool NativeBackendKWallet::GetBlacklistLogins(PasswordFormList* forms) {
452   int wallet_handle = WalletHandle();
453   if (wallet_handle == kInvalidKWalletHandle)
454     return false;
455   return GetLoginsList(forms, false, wallet_handle);
456 }
457
458 bool NativeBackendKWallet::GetLoginsList(PasswordFormList* forms,
459                                          const std::string& signon_realm,
460                                          int wallet_handle) {
461   // Is there an entry in the wallet?
462   {
463     dbus::MethodCall method_call(kKWalletInterface, "hasEntry");
464     dbus::MessageWriter builder(&method_call);
465     builder.AppendInt32(wallet_handle);  // handle
466     builder.AppendString(folder_name_);  // folder
467     builder.AppendString(signon_realm);  // key
468     builder.AppendString(app_name_);     // appid
469     scoped_ptr<dbus::Response> response(
470         kwallet_proxy_->CallMethodAndBlock(
471             &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
472     if (!response.get()) {
473       LOG(ERROR) << "Error contacting kwalletd (hasEntry)";
474       return false;
475     }
476     dbus::MessageReader reader(response.get());
477     bool has_entry = false;
478     if (!reader.PopBool(&has_entry)) {
479       LOG(ERROR) << "Error reading response from kwalletd (hasEntry): "
480                  << response->ToString();
481       return false;
482     }
483     if (!has_entry) {
484       // This is not an error. There just isn't a matching entry.
485       return true;
486     }
487   }
488
489   {
490     dbus::MethodCall method_call(kKWalletInterface, "readEntry");
491     dbus::MessageWriter builder(&method_call);
492     builder.AppendInt32(wallet_handle);  // handle
493     builder.AppendString(folder_name_);  // folder
494     builder.AppendString(signon_realm);  // key
495     builder.AppendString(app_name_);     // appid
496     scoped_ptr<dbus::Response> response(
497         kwallet_proxy_->CallMethodAndBlock(
498             &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
499     if (!response.get()) {
500       LOG(ERROR) << "Error contacting kwalletd (readEntry)";
501       return false;
502     }
503     dbus::MessageReader reader(response.get());
504     uint8_t* bytes = NULL;
505     size_t length = 0;
506     if (!reader.PopArrayOfBytes(&bytes, &length)) {
507       LOG(ERROR) << "Error reading response from kwalletd (readEntry): "
508                  << response->ToString();
509       return false;
510     }
511     if (!bytes)
512       return false;
513     if (!CheckSerializedValue(bytes, length, signon_realm)) {
514       // This is weird, but we choose not to call it an error. There is an
515       // invalid entry somehow, but by just ignoring it, we make it easier to
516       // repair without having to delete it using kwalletmanager (that is, by
517       // just saving a new password within this realm to overwrite it).
518       return true;
519     }
520
521     // Can't we all just agree on whether bytes are signed or not? Please?
522     Pickle pickle(reinterpret_cast<const char*>(bytes), length);
523     PasswordFormList all_forms;
524     DeserializeValue(signon_realm, pickle, forms);
525   }
526
527   return true;
528 }
529
530 bool NativeBackendKWallet::GetLoginsList(PasswordFormList* forms,
531                                          bool autofillable,
532                                          int wallet_handle) {
533   PasswordFormList all_forms;
534   if (!GetAllLogins(&all_forms, wallet_handle))
535     return false;
536
537   // We have to read all the entries, and then filter them here.
538   forms->reserve(forms->size() + all_forms.size());
539   for (size_t i = 0; i < all_forms.size(); ++i) {
540     if (all_forms[i]->blacklisted_by_user == !autofillable)
541       forms->push_back(all_forms[i]);
542     else
543       delete all_forms[i];
544   }
545
546   return true;
547 }
548
549 bool NativeBackendKWallet::GetLoginsList(PasswordFormList* forms,
550                                          const base::Time& begin,
551                                          const base::Time& end,
552                                          int wallet_handle) {
553   PasswordFormList all_forms;
554   if (!GetAllLogins(&all_forms, wallet_handle))
555     return false;
556
557   // We have to read all the entries, and then filter them here.
558   forms->reserve(forms->size() + all_forms.size());
559   for (size_t i = 0; i < all_forms.size(); ++i) {
560     if (begin <= all_forms[i]->date_created &&
561         (end.is_null() || all_forms[i]->date_created < end)) {
562       forms->push_back(all_forms[i]);
563     } else {
564       delete all_forms[i];
565     }
566   }
567
568   return true;
569 }
570
571 bool NativeBackendKWallet::GetAllLogins(PasswordFormList* forms,
572                                         int wallet_handle) {
573   // We could probably also use readEntryList here.
574   std::vector<std::string> realm_list;
575   {
576     dbus::MethodCall method_call(kKWalletInterface, "entryList");
577     dbus::MessageWriter builder(&method_call);
578     builder.AppendInt32(wallet_handle);  // handle
579     builder.AppendString(folder_name_);  // folder
580     builder.AppendString(app_name_);     // appid
581     scoped_ptr<dbus::Response> response(
582         kwallet_proxy_->CallMethodAndBlock(
583             &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
584     if (!response.get()) {
585       LOG(ERROR) << "Error contacting kwalletd (entryList)";
586       return false;
587     }
588     dbus::MessageReader reader(response.get());
589     if (!reader.PopArrayOfStrings(&realm_list)) {
590       LOG(ERROR) << "Error reading response from kwalletd (entryList): "
591                  << response->ToString();
592       return false;
593     }
594   }
595
596   for (size_t i = 0; i < realm_list.size(); ++i) {
597     const std::string& signon_realm = realm_list[i];
598     dbus::MethodCall method_call(kKWalletInterface, "readEntry");
599     dbus::MessageWriter builder(&method_call);
600     builder.AppendInt32(wallet_handle);  // handle
601     builder.AppendString(folder_name_);  // folder
602     builder.AppendString(signon_realm);  // key
603     builder.AppendString(app_name_);     // appid
604     scoped_ptr<dbus::Response> response(
605         kwallet_proxy_->CallMethodAndBlock(
606             &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
607     if (!response.get()) {
608       LOG(ERROR) << "Error contacting kwalletd (readEntry)";
609       continue;
610     }
611     dbus::MessageReader reader(response.get());
612     uint8_t* bytes = NULL;
613     size_t length = 0;
614     if (!reader.PopArrayOfBytes(&bytes, &length)) {
615       LOG(ERROR) << "Error reading response from kwalletd (readEntry): "
616                  << response->ToString();
617       continue;
618     }
619     if (!bytes || !CheckSerializedValue(bytes, length, signon_realm))
620       continue;
621
622     // Can't we all just agree on whether bytes are signed or not? Please?
623     Pickle pickle(reinterpret_cast<const char*>(bytes), length);
624     PasswordFormList all_forms;
625     DeserializeValue(signon_realm, pickle, forms);
626   }
627   return true;
628 }
629
630 bool NativeBackendKWallet::SetLoginsList(const PasswordFormList& forms,
631                                          const std::string& signon_realm,
632                                          int wallet_handle) {
633   if (forms.empty()) {
634     // No items left? Remove the entry from the wallet.
635     dbus::MethodCall method_call(kKWalletInterface, "removeEntry");
636     dbus::MessageWriter builder(&method_call);
637     builder.AppendInt32(wallet_handle);  // handle
638     builder.AppendString(folder_name_);  // folder
639     builder.AppendString(signon_realm);  // key
640     builder.AppendString(app_name_);     // appid
641     scoped_ptr<dbus::Response> response(
642         kwallet_proxy_->CallMethodAndBlock(
643             &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
644     if (!response.get()) {
645       LOG(ERROR) << "Error contacting kwalletd (removeEntry)";
646       return kInvalidKWalletHandle;
647     }
648     dbus::MessageReader reader(response.get());
649     int ret = 0;
650     if (!reader.PopInt32(&ret)) {
651       LOG(ERROR) << "Error reading response from kwalletd (removeEntry): "
652                  << response->ToString();
653       return false;
654     }
655     if (ret != 0)
656       LOG(ERROR) << "Bad return code " << ret << " from KWallet removeEntry";
657     return ret == 0;
658   }
659
660   Pickle value;
661   SerializeValue(forms, &value);
662
663   dbus::MethodCall method_call(kKWalletInterface, "writeEntry");
664   dbus::MessageWriter builder(&method_call);
665   builder.AppendInt32(wallet_handle);  // handle
666   builder.AppendString(folder_name_);  // folder
667   builder.AppendString(signon_realm);  // key
668   builder.AppendArrayOfBytes(static_cast<const uint8_t*>(value.data()),
669                              value.size());  // value
670   builder.AppendString(app_name_);     // appid
671   scoped_ptr<dbus::Response> response(
672       kwallet_proxy_->CallMethodAndBlock(
673           &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
674   if (!response.get()) {
675     LOG(ERROR) << "Error contacting kwalletd (writeEntry)";
676     return kInvalidKWalletHandle;
677   }
678   dbus::MessageReader reader(response.get());
679   int ret = 0;
680   if (!reader.PopInt32(&ret)) {
681     LOG(ERROR) << "Error reading response from kwalletd (writeEntry): "
682                << response->ToString();
683     return false;
684   }
685   if (ret != 0)
686     LOG(ERROR) << "Bad return code " << ret << " from KWallet writeEntry";
687   return ret == 0;
688 }
689
690 // static
691 void NativeBackendKWallet::SerializeValue(const PasswordFormList& forms,
692                                           Pickle* pickle) {
693   pickle->WriteInt(kPickleVersion);
694   pickle->WriteUInt64(forms.size());
695   for (PasswordFormList::const_iterator it = forms.begin();
696        it != forms.end(); ++it) {
697     const PasswordForm* form = *it;
698     pickle->WriteInt(form->scheme);
699     pickle->WriteString(form->origin.spec());
700     pickle->WriteString(form->action.spec());
701     pickle->WriteString16(form->username_element);
702     pickle->WriteString16(form->username_value);
703     pickle->WriteString16(form->password_element);
704     pickle->WriteString16(form->password_value);
705     pickle->WriteString16(form->submit_element);
706     pickle->WriteBool(form->ssl_valid);
707     pickle->WriteBool(form->preferred);
708     pickle->WriteBool(form->blacklisted_by_user);
709     pickle->WriteInt64(form->date_created.ToTimeT());
710   }
711 }
712
713 // static
714 bool NativeBackendKWallet::DeserializeValueSize(const std::string& signon_realm,
715                                                 const PickleIterator& init_iter,
716                                                 bool size_32, bool warn_only,
717                                                 PasswordFormList* forms) {
718   PickleIterator iter = init_iter;
719
720   uint64_t count = 0;
721   if (size_32) {
722     uint32_t count_32 = 0;
723     if (!iter.ReadUInt32(&count_32)) {
724       LOG(ERROR) << "Failed to deserialize KWallet entry "
725                  << "(realm: " << signon_realm << ")";
726       return false;
727     }
728     count = count_32;
729   } else {
730     if (!iter.ReadUInt64(&count)) {
731       LOG(ERROR) << "Failed to deserialize KWallet entry "
732                  << "(realm: " << signon_realm << ")";
733       return false;
734     }
735   }
736
737   if (count > 0xFFFF) {
738     // Trying to pin down the cause of http://crbug.com/80728 (or fix it).
739     // This is a very large number of passwords to be saved for a single realm.
740     // It is almost certainly a corrupt pickle and not real data. Ignore it.
741     // This very well might actually be http://crbug.com/107701, so if we're
742     // reading an old pickle, we don't even log this the first time we try to
743     // read it. (That is, when we're reading the native architecture size.)
744     if (!warn_only) {
745       LOG(ERROR) << "Suspiciously large number of entries in KWallet entry "
746                  << "(" << count << "; realm: " << signon_realm << ")";
747     }
748     return false;
749   }
750
751   forms->reserve(forms->size() + count);
752   for (uint64_t i = 0; i < count; ++i) {
753     scoped_ptr<PasswordForm> form(new PasswordForm());
754     form->signon_realm.assign(signon_realm);
755
756     int scheme = 0;
757     int64 date_created = 0;
758     // Note that these will be read back in the order listed due to
759     // short-circuit evaluation. This is important.
760     if (!iter.ReadInt(&scheme) ||
761         !ReadGURL(&iter, warn_only, &form->origin) ||
762         !ReadGURL(&iter, warn_only, &form->action) ||
763         !iter.ReadString16(&form->username_element) ||
764         !iter.ReadString16(&form->username_value) ||
765         !iter.ReadString16(&form->password_element) ||
766         !iter.ReadString16(&form->password_value) ||
767         !iter.ReadString16(&form->submit_element) ||
768         !iter.ReadBool(&form->ssl_valid) ||
769         !iter.ReadBool(&form->preferred) ||
770         !iter.ReadBool(&form->blacklisted_by_user) ||
771         !iter.ReadInt64(&date_created)) {
772       if (warn_only) {
773         LOG(WARNING) << "Failed to deserialize version 0 KWallet entry "
774                      << "(realm: " << signon_realm << ") with native "
775                      << "architecture size; will try alternate size.";
776       } else {
777         LOG(ERROR) << "Failed to deserialize KWallet entry "
778                    << "(realm: " << signon_realm << ")";
779       }
780       return false;
781     }
782     form->scheme = static_cast<PasswordForm::Scheme>(scheme);
783     form->date_created = base::Time::FromTimeT(date_created);
784     forms->push_back(form.release());
785   }
786
787   return true;
788 }
789
790 // static
791 void NativeBackendKWallet::DeserializeValue(const std::string& signon_realm,
792                                             const Pickle& pickle,
793                                             PasswordFormList* forms) {
794   PickleIterator iter(pickle);
795
796   int version = -1;
797   if (!iter.ReadInt(&version) ||
798       version < 0 || version > kPickleVersion) {
799     LOG(ERROR) << "Failed to deserialize KWallet entry "
800                << "(realm: " << signon_realm << ")";
801     return;
802   }
803
804   if (version == kPickleVersion) {
805     // In current pickles, we expect 64-bit sizes. Failure is an error.
806     DeserializeValueSize(signon_realm, iter, false, false, forms);
807     return;
808   }
809
810   const size_t saved_forms_size = forms->size();
811   const bool size_32 = sizeof(size_t) == sizeof(uint32_t);
812   if (!DeserializeValueSize(signon_realm, iter, size_32, true, forms)) {
813     // We failed to read the pickle using the native architecture of the system.
814     // Try again with the opposite architecture. Note that we do this even on
815     // 32-bit machines, in case we're reading a 64-bit pickle. (Probably rare,
816     // since mostly we expect upgrades, not downgrades, but both are possible.)
817     forms->resize(saved_forms_size);
818     DeserializeValueSize(signon_realm, iter, !size_32, false, forms);
819   }
820 }
821
822 int NativeBackendKWallet::WalletHandle() {
823   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
824
825   // Open the wallet.
826   // TODO(mdm): Are we leaking these handles? Find out.
827   int32_t handle = kInvalidKWalletHandle;
828   {
829     dbus::MethodCall method_call(kKWalletInterface, "open");
830     dbus::MessageWriter builder(&method_call);
831     builder.AppendString(wallet_name_);  // wallet
832     builder.AppendInt64(0);              // wid
833     builder.AppendString(app_name_);     // appid
834     scoped_ptr<dbus::Response> response(
835         kwallet_proxy_->CallMethodAndBlock(
836             &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
837     if (!response.get()) {
838       LOG(ERROR) << "Error contacting kwalletd (open)";
839       return kInvalidKWalletHandle;
840     }
841     dbus::MessageReader reader(response.get());
842     if (!reader.PopInt32(&handle)) {
843       LOG(ERROR) << "Error reading response from kwalletd (open): "
844                  << response->ToString();
845       return kInvalidKWalletHandle;
846     }
847     if (handle == kInvalidKWalletHandle) {
848       LOG(ERROR) << "Error obtaining KWallet handle";
849       return kInvalidKWalletHandle;
850     }
851   }
852
853   // Check if our folder exists.
854   bool has_folder = false;
855   {
856     dbus::MethodCall method_call(kKWalletInterface, "hasFolder");
857     dbus::MessageWriter builder(&method_call);
858     builder.AppendInt32(handle);         // handle
859     builder.AppendString(folder_name_);  // folder
860     builder.AppendString(app_name_);     // appid
861     scoped_ptr<dbus::Response> response(
862         kwallet_proxy_->CallMethodAndBlock(
863             &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
864     if (!response.get()) {
865       LOG(ERROR) << "Error contacting kwalletd (hasFolder)";
866       return kInvalidKWalletHandle;
867     }
868     dbus::MessageReader reader(response.get());
869     if (!reader.PopBool(&has_folder)) {
870       LOG(ERROR) << "Error reading response from kwalletd (hasFolder): "
871                  << response->ToString();
872       return kInvalidKWalletHandle;
873     }
874   }
875
876   // Create it if it didn't.
877   if (!has_folder) {
878     dbus::MethodCall method_call(kKWalletInterface, "createFolder");
879     dbus::MessageWriter builder(&method_call);
880     builder.AppendInt32(handle);         // handle
881     builder.AppendString(folder_name_);  // folder
882     builder.AppendString(app_name_);     // appid
883     scoped_ptr<dbus::Response> response(
884         kwallet_proxy_->CallMethodAndBlock(
885             &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
886     if (!response.get()) {
887       LOG(ERROR) << "Error contacting kwalletd (createFolder)";
888       return kInvalidKWalletHandle;
889     }
890     dbus::MessageReader reader(response.get());
891     bool success = false;
892     if (!reader.PopBool(&success)) {
893       LOG(ERROR) << "Error reading response from kwalletd (createFolder): "
894                  << response->ToString();
895       return kInvalidKWalletHandle;
896     }
897     if (!success) {
898       LOG(ERROR) << "Error creating KWallet folder";
899       return kInvalidKWalletHandle;
900     }
901   }
902
903   // Successful initialization. Try migration if necessary.
904   if (!migrate_tried_)
905     MigrateToProfileSpecificLogins();
906
907   return handle;
908 }
909
910 std::string NativeBackendKWallet::GetProfileSpecificFolderName() const {
911   // Originally, the folder name was always just "Chrome Form Data".
912   // Now we use it to distinguish passwords for different profiles.
913   return base::StringPrintf("%s (%d)", kKWalletFolder, profile_id_);
914 }
915
916 void NativeBackendKWallet::MigrateToProfileSpecificLogins() {
917   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
918
919   DCHECK(!migrate_tried_);
920   DCHECK_EQ(folder_name_, kKWalletFolder);
921
922   // Record the fact that we've attempted migration already right away, so that
923   // we don't get recursive calls back to MigrateToProfileSpecificLogins().
924   migrate_tried_ = true;
925
926   // First get all the logins, using the old folder name.
927   int wallet_handle = WalletHandle();
928   if (wallet_handle == kInvalidKWalletHandle)
929     return;
930   PasswordFormList forms;
931   if (!GetAllLogins(&forms, wallet_handle))
932     return;
933
934   // Now switch to a profile-specific folder name.
935   folder_name_ = GetProfileSpecificFolderName();
936
937   // Try to add all the logins with the new folder name.
938   // This could be done more efficiently by grouping by signon realm and using
939   // SetLoginsList(), but we do this for simplicity since it is only done once.
940   // Note, however, that we do need another call to WalletHandle() to create
941   // this folder if necessary.
942   bool ok = true;
943   for (size_t i = 0; i < forms.size(); ++i) {
944     if (!AddLogin(*forms[i]))
945       ok = false;
946     delete forms[i];
947   }
948   if (forms.empty()) {
949     // If there were no logins to migrate, we do an extra call to WalletHandle()
950     // for its side effect of attempting to create the profile-specific folder.
951     // This is not strictly necessary, but it's safe and helps in testing.
952     wallet_handle = WalletHandle();
953     if (wallet_handle == kInvalidKWalletHandle)
954       ok = false;
955   }
956
957   if (ok) {
958     // All good! Keep the new app string and set a persistent pref.
959     // NOTE: We explicitly don't delete the old passwords yet. They are
960     // potentially shared with other profiles and other user data dirs!
961     // Each other profile must be able to migrate the shared data as well,
962     // so we must leave it alone. After a few releases, we'll add code to
963     // delete them, and eventually remove this migration code.
964     // TODO(mdm): follow through with the plan above.
965     PasswordStoreX::SetPasswordsUseLocalProfileId(prefs_);
966   } else {
967     // We failed to migrate for some reason. Use the old folder name.
968     folder_name_ = kKWalletFolder;
969   }
970 }