Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8)

Side by Side Diff: chrome/browser/password_manager/password_store_kwallet.cc

Issue 2806002: Linux: refactor GNOME Keyring and KWallet integration to allow migration. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 10 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2010 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/password_store_kwallet.h"
6
7 #include <sstream>
8
9 #include "base/logging.h"
10 #include "base/md5.h"
11 #include "base/pickle.h"
12 #include "base/stl_util-inl.h"
13 #include "base/string_util.h"
14 #include "base/task.h"
15
16 using std::string;
17 using std::vector;
18 using webkit_glue::PasswordForm;
19
20 // We could localize these strings, but then changing your locale would cause
21 // you to lose access to all your stored passwords. Maybe best not to do that.
22 const char* PasswordStoreKWallet::kAppId = "Chrome";
23 const char* PasswordStoreKWallet::kKWalletFolder = "Chrome Form Data";
24
25 const char* PasswordStoreKWallet::kKWalletServiceName = "org.kde.kwalletd";
26 const char* PasswordStoreKWallet::kKWalletPath = "/modules/kwalletd";
27 const char* PasswordStoreKWallet::kKWalletInterface = "org.kde.KWallet";
28 const char* PasswordStoreKWallet::kKLauncherServiceName = "org.kde.klauncher";
29 const char* PasswordStoreKWallet::kKLauncherPath = "/KLauncher";
30 const char* PasswordStoreKWallet::kKLauncherInterface = "org.kde.KLauncher";
31
32 PasswordStoreKWallet::PasswordStoreKWallet(LoginDatabase* login_db,
33 Profile* profile,
34 WebDataService* web_data_service)
35 : error_(NULL),
36 connection_(NULL),
37 proxy_(NULL) {
38 }
39
40 PasswordStoreKWallet::~PasswordStoreKWallet() {
41 if (proxy_) {
42 g_object_unref(proxy_);
43 }
44 }
45
46 bool PasswordStoreKWallet::Init() {
47 // Initialize threading in dbus-glib - it should be fine for
48 // dbus_g_thread_init to be called multiple times.
49 if (!g_thread_supported())
50 g_thread_init(NULL);
51 dbus_g_thread_init();
52
53 // Get a connection to the session bus.
54 connection_ = dbus_g_bus_get(DBUS_BUS_SESSION, &error_);
55 if (CheckError())
56 return false;
57
58 if (!InitWallet()) {
59 // kwalletd may not be running. Try to start it and try again.
60 if (!StartKWalletd() || !InitWallet())
61 return false;
62 }
63
64 return true;
65 }
66
67 bool PasswordStoreKWallet::StartKWalletd() {
68 // Sadly kwalletd doesn't use DBUS activation, so we have to make a call to
69 // klauncher to start it.
70 DBusGProxy* klauncher_proxy =
71 dbus_g_proxy_new_for_name(connection_, kKLauncherServiceName,
72 kKLauncherPath, kKLauncherInterface);
73
74 char* empty_string_list = NULL;
75 int ret = 1;
76 char* error = NULL;
77 dbus_g_proxy_call(klauncher_proxy, "start_service_by_desktop_name", &error_,
78 G_TYPE_STRING, "kwalletd", // serviceName
79 G_TYPE_STRV, &empty_string_list, // urls
80 G_TYPE_STRV, &empty_string_list, // envs
81 G_TYPE_STRING, "", // startup_id
82 G_TYPE_BOOLEAN, (gboolean) false, // blind
83 G_TYPE_INVALID,
84 G_TYPE_INT, &ret, // result
85 G_TYPE_STRING, NULL, // dubsName
86 G_TYPE_STRING, &error, // error
87 G_TYPE_INT, NULL, // pid
88 G_TYPE_INVALID);
89
90 if (error && *error) {
91 LOG(ERROR) << "Error launching kwalletd: " << error;
92 ret = 1; // Make sure we return false after freeing.
93 }
94
95 g_free(error);
96 g_object_unref(klauncher_proxy);
97
98 if (CheckError() || ret != 0)
99 return false;
100 return true;
101 }
102
103 bool PasswordStoreKWallet::InitWallet() {
104 // Make a proxy to KWallet.
105 proxy_ = dbus_g_proxy_new_for_name(connection_, kKWalletServiceName,
106 kKWalletPath, kKWalletInterface);
107
108 // Check KWallet is enabled.
109 gboolean is_enabled = false;
110 dbus_g_proxy_call(proxy_, "isEnabled", &error_,
111 G_TYPE_INVALID,
112 G_TYPE_BOOLEAN, &is_enabled,
113 G_TYPE_INVALID);
114 if (CheckError() || !is_enabled)
115 return false;
116
117 // Get the wallet name.
118 char* wallet_name = NULL;
119 dbus_g_proxy_call(proxy_, "networkWallet", &error_,
120 G_TYPE_INVALID,
121 G_TYPE_STRING, &wallet_name,
122 G_TYPE_INVALID);
123 if (CheckError() || !wallet_name)
124 return false;
125
126 wallet_name_.assign(wallet_name);
127 g_free(wallet_name);
128
129 return true;
130 }
131
132 void PasswordStoreKWallet::AddLoginImpl(const PasswordForm& form) {
133 AutoLock l(kwallet_lock_);
134 int wallet_handle = WalletHandle();
135 if (wallet_handle == kInvalidKWalletHandle)
136 return;
137
138 PasswordFormList forms;
139 GetLoginsList(&forms, form.signon_realm, wallet_handle);
140
141 forms.push_back(new PasswordForm(form));
142 SetLoginsList(forms, form.signon_realm, wallet_handle);
143
144 STLDeleteElements(&forms);
145 }
146
147 void PasswordStoreKWallet::UpdateLoginImpl(const PasswordForm& form) {
148 AutoLock l(kwallet_lock_);
149 int wallet_handle = WalletHandle();
150 if (wallet_handle == kInvalidKWalletHandle)
151 return;
152
153 PasswordFormList forms;
154 GetLoginsList(&forms, form.signon_realm, wallet_handle);
155
156 for (size_t i = 0; i < forms.size(); ++i) {
157 if (CompareForms(form, *forms[i], true))
158 *forms[i] = form;
159 }
160
161 SetLoginsList(forms, form.signon_realm, wallet_handle);
162
163 STLDeleteElements(&forms);
164 }
165
166 void PasswordStoreKWallet::RemoveLoginImpl(const PasswordForm& form) {
167 AutoLock l(kwallet_lock_);
168 int wallet_handle = WalletHandle();
169 if (wallet_handle == kInvalidKWalletHandle)
170 return;
171
172 PasswordFormList all_forms;
173 GetLoginsList(&all_forms, form.signon_realm, wallet_handle);
174
175 PasswordFormList kept_forms;
176 kept_forms.reserve(all_forms.size());
177 for (size_t i = 0; i < all_forms.size(); ++i) {
178 if (CompareForms(form, *all_forms[i], false))
179 delete all_forms[i];
180 else
181 kept_forms.push_back(all_forms[i]);
182 }
183
184 // Update the entry in the wallet.
185 SetLoginsList(kept_forms, form.signon_realm, wallet_handle);
186 STLDeleteElements(&kept_forms);
187 }
188
189 void PasswordStoreKWallet::RemoveLoginsCreatedBetweenImpl(
190 const base::Time& delete_begin,
191 const base::Time& delete_end) {
192 AutoLock l(kwallet_lock_);
193 int wallet_handle = WalletHandle();
194 if (wallet_handle == kInvalidKWalletHandle)
195 return;
196
197 // We could probably also use readEntryList here.
198 char** realm_list = NULL;
199 dbus_g_proxy_call(proxy_, "entryList", &error_,
200 G_TYPE_INT, wallet_handle, // handle
201 G_TYPE_STRING, kKWalletFolder, // folder
202 G_TYPE_STRING, kAppId, // appid
203 G_TYPE_INVALID,
204 G_TYPE_STRV, &realm_list,
205 G_TYPE_INVALID);
206 if (CheckError())
207 return;
208
209 for (char** realm = realm_list; *realm; ++realm) {
210 GArray* byte_array = NULL;
211 dbus_g_proxy_call(proxy_, "readEntry", &error_,
212 G_TYPE_INT, wallet_handle, // handle
213 G_TYPE_STRING, kKWalletFolder, // folder
214 G_TYPE_STRING, *realm, // key
215 G_TYPE_STRING, kAppId, // appid
216 G_TYPE_INVALID,
217 DBUS_TYPE_G_UCHAR_ARRAY, &byte_array,
218 G_TYPE_INVALID);
219
220 if (CheckError() || !byte_array || !byte_array->len)
221 continue;
222
223 string signon_realm(*realm);
224 Pickle pickle(byte_array->data, byte_array->len);
225 PasswordFormList all_forms;
226 DeserializeValue(signon_realm, pickle, &all_forms);
227 g_array_free(byte_array, true);
228
229 PasswordFormList kept_forms;
230 kept_forms.reserve(all_forms.size());
231 for (size_t i = 0; i < all_forms.size(); ++i) {
232 if (delete_begin <= all_forms[i]->date_created &&
233 (delete_end.is_null() || all_forms[i]->date_created < delete_end)) {
234 delete all_forms[i];
235 } else {
236 kept_forms.push_back(all_forms[i]);
237 }
238 }
239
240 SetLoginsList(kept_forms, signon_realm, wallet_handle);
241 STLDeleteElements(&kept_forms);
242 }
243 g_strfreev(realm_list);
244 }
245
246 void PasswordStoreKWallet::GetLoginsImpl(GetLoginsRequest* request,
247 const PasswordForm& form) {
248 PasswordFormList forms;
249
250 AutoLock l(kwallet_lock_);
251 int wallet_handle = WalletHandle();
252 if (wallet_handle != kInvalidKWalletHandle)
253 GetLoginsList(&forms, form.signon_realm, wallet_handle);
254
255 NotifyConsumer(request, forms);
256 }
257
258 void PasswordStoreKWallet::GetAutofillableLoginsImpl(
259 GetLoginsRequest* request) {
260 std::vector<PasswordForm*> forms;
261 FillAutofillableLogins(&forms);
262 NotifyConsumer(request, forms);
263 }
264
265 void PasswordStoreKWallet::GetBlacklistLoginsImpl(
266 GetLoginsRequest* request) {
267 std::vector<PasswordForm*> forms;
268 FillBlacklistLogins(&forms);
269 NotifyConsumer(request, forms);
270 }
271
272 bool PasswordStoreKWallet::FillAutofillableLogins(
273 std::vector<PasswordForm*>* forms) {
274 return FillSomeLogins(true, forms);
275 }
276
277 bool PasswordStoreKWallet::FillBlacklistLogins(
278 std::vector<PasswordForm*>* forms) {
279 return FillSomeLogins(false, forms);
280 }
281
282 void PasswordStoreKWallet::GetLoginsList(PasswordFormList* forms,
283 const string& signon_realm,
284 int wallet_handle) {
285 // Is there an entry in the wallet?
286 gboolean has_entry = false;
287 dbus_g_proxy_call(proxy_, "hasEntry", &error_,
288 G_TYPE_INT, wallet_handle, // handle
289 G_TYPE_STRING, kKWalletFolder, // folder
290 G_TYPE_STRING, signon_realm.c_str(), // key
291 G_TYPE_STRING, kAppId, // appid
292 G_TYPE_INVALID,
293 G_TYPE_BOOLEAN, &has_entry,
294 G_TYPE_INVALID);
295
296 if (CheckError() || !has_entry)
297 return;
298
299 GArray* byte_array = NULL;
300 dbus_g_proxy_call(proxy_, "readEntry", &error_,
301 G_TYPE_INT, wallet_handle, // handle
302 G_TYPE_STRING, kKWalletFolder, // folder
303 G_TYPE_STRING, signon_realm.c_str(), // key
304 G_TYPE_STRING, kAppId, // appid
305 G_TYPE_INVALID,
306 DBUS_TYPE_G_UCHAR_ARRAY, &byte_array,
307 G_TYPE_INVALID);
308
309 if (CheckError() || !byte_array || !byte_array->len)
310 return;
311
312 Pickle pickle(byte_array->data, byte_array->len);
313 DeserializeValue(signon_realm, pickle, forms);
314 g_array_free(byte_array, true);
315 }
316
317 void PasswordStoreKWallet::SetLoginsList(const PasswordFormList& forms,
318 const string& signon_realm,
319 int wallet_handle) {
320 if (forms.empty()) {
321 // No items left? Remove the entry from the wallet.
322 int ret = 0;
323 dbus_g_proxy_call(proxy_, "removeEntry", &error_,
324 G_TYPE_INT, wallet_handle, // handle
325 G_TYPE_STRING, kKWalletFolder, // folder
326 G_TYPE_STRING, signon_realm.c_str(), // key
327 G_TYPE_STRING, kAppId, // appid
328 G_TYPE_INVALID,
329 G_TYPE_INT, &ret,
330 G_TYPE_INVALID);
331 CheckError();
332 if (ret != 0)
333 LOG(ERROR) << "Bad return code " << ret << " from kwallet removeEntry";
334 return;
335 }
336
337 Pickle value;
338 SerializeValue(forms, &value);
339
340 // Convert the pickled bytes to a GByteArray.
341 GArray* byte_array = g_array_sized_new(false, false, sizeof(char),
342 value.size());
343 g_array_append_vals(byte_array, value.data(), value.size());
344
345 // Make the call.
346 int ret = 0;
347 dbus_g_proxy_call(proxy_, "writeEntry", &error_,
348 G_TYPE_INT, wallet_handle, // handle
349 G_TYPE_STRING, kKWalletFolder, // folder
350 G_TYPE_STRING, signon_realm.c_str(), // key
351 DBUS_TYPE_G_UCHAR_ARRAY, byte_array, // value
352 G_TYPE_STRING, kAppId, // appid
353 G_TYPE_INVALID,
354 G_TYPE_INT, &ret,
355 G_TYPE_INVALID);
356 g_array_free(byte_array, true);
357
358 CheckError();
359 if (ret != 0)
360 LOG(ERROR) << "Bad return code " << ret << " from kwallet writeEntry";
361 }
362
363 bool PasswordStoreKWallet::FillSomeLogins(bool autofillable,
364 PasswordFormList* forms) {
365 AutoLock l(kwallet_lock_);
366 int wallet_handle = WalletHandle();
367 if (wallet_handle == kInvalidKWalletHandle)
368 return false;
369
370 // We could probably also use readEntryList here.
371 char** realm_list = NULL;
372 dbus_g_proxy_call(proxy_, "entryList", &error_,
373 G_TYPE_INT, wallet_handle, // handle
374 G_TYPE_STRING, kKWalletFolder, // folder
375 G_TYPE_STRING, kAppId, // appid
376 G_TYPE_INVALID,
377 G_TYPE_STRV, &realm_list,
378 G_TYPE_INVALID);
379 if (CheckError())
380 return false;
381
382 PasswordFormList all_forms;
383 for (char** realm = realm_list; *realm; ++realm) {
384 GArray* byte_array = NULL;
385 dbus_g_proxy_call(proxy_, "readEntry", &error_,
386 G_TYPE_INT, wallet_handle, // handle
387 G_TYPE_STRING, kKWalletFolder, // folder
388 G_TYPE_STRING, *realm, // key
389 G_TYPE_STRING, kAppId, // appid
390 G_TYPE_INVALID,
391 DBUS_TYPE_G_UCHAR_ARRAY, &byte_array,
392 G_TYPE_INVALID);
393
394 if (CheckError() || !byte_array || !byte_array->len)
395 continue;
396
397 Pickle pickle(byte_array->data, byte_array->len);
398 DeserializeValue(*realm, pickle, &all_forms);
399 g_array_free(byte_array, true);
400 }
401 g_strfreev(realm_list);
402
403 // We have to read all the entries, and then filter them here.
404 forms->reserve(forms->size() + all_forms.size());
405 for (size_t i = 0; i < all_forms.size(); ++i) {
406 if (all_forms[i]->blacklisted_by_user == !autofillable)
407 forms->push_back(all_forms[i]);
408 else
409 delete all_forms[i];
410 }
411
412 return true;
413 }
414
415 bool PasswordStoreKWallet::CompareForms(const PasswordForm& a,
416 const PasswordForm& b,
417 bool update_check) {
418 // An update check doesn't care about the submit element.
419 if (!update_check && a.submit_element != b.submit_element)
420 return false;
421 return a.origin == b.origin &&
422 a.password_element == b.password_element &&
423 a.signon_realm == b.signon_realm &&
424 a.username_element == b.username_element &&
425 a.username_value == b.username_value;
426 }
427
428 void PasswordStoreKWallet::SerializeValue(const PasswordFormList& forms,
429 Pickle* pickle) {
430 pickle->WriteInt(kPickleVersion);
431 pickle->WriteSize(forms.size());
432 for (PasswordFormList::const_iterator it = forms.begin() ;
433 it != forms.end() ; ++it) {
434 const PasswordForm* form = *it;
435 pickle->WriteInt(form->scheme);
436 pickle->WriteString(form->origin.spec());
437 pickle->WriteString(form->action.spec());
438 pickle->WriteString16(form->username_element);
439 pickle->WriteString16(form->username_value);
440 pickle->WriteString16(form->password_element);
441 pickle->WriteString16(form->password_value);
442 pickle->WriteString16(form->submit_element);
443 pickle->WriteBool(form->ssl_valid);
444 pickle->WriteBool(form->preferred);
445 pickle->WriteBool(form->blacklisted_by_user);
446 pickle->WriteInt64(form->date_created.ToTimeT());
447 }
448 }
449
450 void PasswordStoreKWallet::DeserializeValue(const string& signon_realm,
451 const Pickle& pickle,
452 PasswordFormList* forms) {
453 void* iter = NULL;
454
455 int version = -1;
456 pickle.ReadInt(&iter, &version);
457 if (version != kPickleVersion) {
458 // This is the only version so far, so anything else is an error.
459 return;
460 }
461
462 size_t count = 0;
463 pickle.ReadSize(&iter, &count);
464
465 forms->reserve(forms->size() + count);
466 for (size_t i = 0; i < count; ++i) {
467 PasswordForm* form = new PasswordForm();
468 form->signon_realm.assign(signon_realm);
469
470 int scheme = 0;
471 pickle.ReadInt(&iter, &scheme);
472 form->scheme = static_cast<PasswordForm::Scheme>(scheme);
473 ReadGURL(pickle, &iter, &form->origin);
474 ReadGURL(pickle, &iter, &form->action);
475 pickle.ReadString16(&iter, &form->username_element);
476 pickle.ReadString16(&iter, &form->username_value);
477 pickle.ReadString16(&iter, &form->password_element);
478 pickle.ReadString16(&iter, &form->password_value);
479 pickle.ReadString16(&iter, &form->submit_element);
480 pickle.ReadBool(&iter, &form->ssl_valid);
481 pickle.ReadBool(&iter, &form->preferred);
482 pickle.ReadBool(&iter, &form->blacklisted_by_user);
483 int64 date_created = 0;
484 pickle.ReadInt64(&iter, &date_created);
485 form->date_created = base::Time::FromTimeT(date_created);
486 forms->push_back(form);
487 }
488 }
489
490 void PasswordStoreKWallet::ReadGURL(const Pickle& pickle, void** iter,
491 GURL* url) {
492 string url_string;
493 pickle.ReadString(iter, &url_string);
494 *url = GURL(url_string);
495 }
496
497 bool PasswordStoreKWallet::CheckError() {
498 if (error_) {
499 LOG(ERROR) << "Failed to complete KWallet call: " << error_->message;
500 g_error_free(error_);
501 error_ = NULL;
502 return true;
503 }
504 return false;
505 }
506
507 int PasswordStoreKWallet::WalletHandle() {
508 // Open the wallet.
509 int handle = kInvalidKWalletHandle;
510 dbus_g_proxy_call(proxy_, "open", &error_,
511 G_TYPE_STRING, wallet_name_.c_str(), // wallet
512 G_TYPE_INT64, 0LL, // wid
513 G_TYPE_STRING, kAppId, // appid
514 G_TYPE_INVALID,
515 G_TYPE_INT, &handle,
516 G_TYPE_INVALID);
517 if (CheckError() || handle == kInvalidKWalletHandle)
518 return kInvalidKWalletHandle;
519
520 // Check if our folder exists.
521 gboolean has_folder = false;
522 dbus_g_proxy_call(proxy_, "hasFolder", &error_,
523 G_TYPE_INT, handle, // handle
524 G_TYPE_STRING, kKWalletFolder, // folder
525 G_TYPE_STRING, kAppId, // appid
526 G_TYPE_INVALID,
527 G_TYPE_BOOLEAN, &has_folder,
528 G_TYPE_INVALID);
529 if (CheckError())
530 return kInvalidKWalletHandle;
531
532 // Create it if it didn't.
533 if (!has_folder) {
534 gboolean success = false;
535 dbus_g_proxy_call(proxy_, "createFolder", &error_,
536 G_TYPE_INT, handle, // handle
537 G_TYPE_STRING, kKWalletFolder, // folder
538 G_TYPE_STRING, kAppId, // appid
539 G_TYPE_INVALID,
540 G_TYPE_BOOLEAN, &success,
541 G_TYPE_INVALID);
542 if (CheckError() || !success)
543 return kInvalidKWalletHandle;
544 }
545
546 return handle;
547 }
OLDNEW
« no previous file with comments | « chrome/browser/password_manager/password_store_kwallet.h ('k') | chrome/browser/password_manager/password_store_x.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698