OLD | NEW |
| (Empty) |
1 // Copyright (c) 2006-2009 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/string_util.h" | |
13 #include "base/task.h" | |
14 | |
15 using std::string; | |
16 using std::vector; | |
17 | |
18 const char* PasswordStoreKWallet::kAppId = "Chrome"; | |
19 const char* PasswordStoreKWallet::kKWalletFolder = "Chrome Form Data"; | |
20 | |
21 const char* PasswordStoreKWallet::kKWalletServiceName = "org.kde.kwalletd"; | |
22 const char* PasswordStoreKWallet::kKWalletPath = "/modules/kwalletd"; | |
23 const char* PasswordStoreKWallet::kKWalletInterface = "org.kde.KWallet"; | |
24 const char* PasswordStoreKWallet::kKLauncherServiceName = "org.kde.klauncher"; | |
25 const char* PasswordStoreKWallet::kKLauncherPath = "/KLauncher"; | |
26 const char* PasswordStoreKWallet::kKLauncherInterface = "org.kde.KLauncher"; | |
27 | |
28 PasswordStoreKWallet::PasswordStoreKWallet() | |
29 : error_(NULL), | |
30 connection_(NULL), | |
31 proxy_(NULL) { | |
32 } | |
33 | |
34 PasswordStoreKWallet::~PasswordStoreKWallet() { | |
35 if (proxy_) { | |
36 g_object_unref(proxy_); | |
37 } | |
38 } | |
39 | |
40 bool PasswordStoreKWallet::Init() { | |
41 thread_.reset(new base::Thread("Chrome_KeyringThread")); | |
42 | |
43 if (!thread_->Start()) { | |
44 thread_.reset(NULL); | |
45 return false; | |
46 } | |
47 | |
48 // Initialize threading in dbus-glib - it should be fine for | |
49 // dbus_g_thread_init to be called multiple times. | |
50 if (!g_thread_supported()) | |
51 g_thread_init(NULL); | |
52 dbus_g_thread_init(); | |
53 | |
54 // Get a connection to the session bus. | |
55 connection_ = dbus_g_bus_get(DBUS_BUS_SESSION, &error_); | |
56 if (CheckError()) | |
57 return false; | |
58 | |
59 if (!StartKWalletd()) return false; | |
60 if (!InitWallet()) return false; | |
61 | |
62 return true; | |
63 } | |
64 | |
65 bool PasswordStoreKWallet::StartKWalletd() { | |
66 // Sadly kwalletd doesn't use DBUS activation, so we have to make a call to | |
67 // klauncher to start it. | |
68 DBusGProxy* klauncher_proxy = | |
69 dbus_g_proxy_new_for_name(connection_, kKLauncherServiceName, | |
70 kKLauncherPath, kKLauncherInterface); | |
71 | |
72 char* empty_string_list = NULL; | |
73 int ret = 1; | |
74 char* error = NULL; | |
75 dbus_g_proxy_call(klauncher_proxy, "start_service_by_desktop_name", &error_, | |
76 G_TYPE_STRING, "kwalletd", // serviceName | |
77 G_TYPE_STRV, &empty_string_list, // urls | |
78 G_TYPE_STRV, &empty_string_list, // envs | |
79 G_TYPE_STRING, "", // startup_id | |
80 G_TYPE_BOOLEAN, (gboolean) false, // blind | |
81 G_TYPE_INVALID, | |
82 G_TYPE_INT, &ret, // result | |
83 G_TYPE_STRING, NULL, // dubsName | |
84 G_TYPE_STRING, &error, // error | |
85 G_TYPE_INT, NULL, // pid | |
86 G_TYPE_INVALID); | |
87 | |
88 if (error && *error) { | |
89 LOG(ERROR) << "Error launching kwalletd: " << error; | |
90 ret = 1; // Make sure we return false after freeing. | |
91 } | |
92 | |
93 g_free(error); | |
94 g_object_unref(klauncher_proxy); | |
95 | |
96 if (CheckError() || ret != 0) | |
97 return false; | |
98 return true; | |
99 } | |
100 | |
101 bool PasswordStoreKWallet::InitWallet() { | |
102 // Make a proxy to KWallet. | |
103 proxy_ = dbus_g_proxy_new_for_name(connection_, kKWalletServiceName, | |
104 kKWalletPath, kKWalletInterface); | |
105 | |
106 // Check KWallet is enabled. | |
107 gboolean is_enabled = false; | |
108 dbus_g_proxy_call(proxy_, "isEnabled", &error_, | |
109 G_TYPE_INVALID, | |
110 G_TYPE_BOOLEAN, &is_enabled, | |
111 G_TYPE_INVALID); | |
112 if (CheckError() || !is_enabled) | |
113 return false; | |
114 | |
115 // Get the wallet name. | |
116 char* wallet_name = NULL; | |
117 dbus_g_proxy_call(proxy_, "networkWallet", &error_, | |
118 G_TYPE_INVALID, | |
119 G_TYPE_STRING, &wallet_name, | |
120 G_TYPE_INVALID); | |
121 if (CheckError() || !wallet_name) | |
122 return false; | |
123 | |
124 wallet_name_.assign(wallet_name); | |
125 g_free(wallet_name); | |
126 | |
127 return true; | |
128 } | |
129 | |
130 void PasswordStoreKWallet::AddLoginImpl(const PasswordForm& form) { | |
131 AutoLock l(kwallet_lock_); | |
132 int wallet_handle = WalletHandle(); | |
133 if (wallet_handle == kInvalidKWalletHandle) | |
134 return; | |
135 | |
136 PasswordFormList forms; | |
137 GetLoginsList(&forms, form, wallet_handle); | |
138 | |
139 forms.push_back(const_cast<PasswordForm*>(&form)); | |
140 | |
141 SetLoginsList(forms, form, wallet_handle); | |
142 } | |
143 | |
144 void PasswordStoreKWallet::UpdateLoginImpl(const PasswordForm& form) { | |
145 AutoLock l(kwallet_lock_); | |
146 int wallet_handle = WalletHandle(); | |
147 if (wallet_handle == kInvalidKWalletHandle) | |
148 return; | |
149 | |
150 PasswordFormList forms; | |
151 GetLoginsList(&forms, form, wallet_handle); | |
152 | |
153 for (uint i = 0; i < forms.size(); ++i) { | |
154 if (CompareForms(form, *forms[i])) { | |
155 forms.erase(forms.begin() + i); | |
156 forms.insert(forms.begin() + i, const_cast<PasswordForm*>(&form)); | |
157 } | |
158 } | |
159 | |
160 SetLoginsList(forms, form, wallet_handle); | |
161 } | |
162 | |
163 void PasswordStoreKWallet::RemoveLoginImpl(const PasswordForm& form) { | |
164 AutoLock l(kwallet_lock_); | |
165 int wallet_handle = WalletHandle(); | |
166 if (wallet_handle == kInvalidKWalletHandle) | |
167 return; | |
168 | |
169 PasswordFormList forms; | |
170 GetLoginsList(&forms, form, wallet_handle); | |
171 | |
172 for (uint i = 0; i < forms.size(); ++i) { | |
173 if (CompareForms(form, *forms[i])) { | |
174 forms.erase(forms.begin() + i); | |
175 --i; | |
176 } | |
177 } | |
178 | |
179 if (forms.empty()) { | |
180 // No items left? Remove the entry from the wallet. | |
181 int ret = 0; | |
182 dbus_g_proxy_call(proxy_, "removeEntry", &error_, | |
183 G_TYPE_INT, wallet_handle, // handle | |
184 G_TYPE_STRING, kKWalletFolder, // folder | |
185 G_TYPE_STRING, form.signon_realm.c_str(), // key | |
186 G_TYPE_STRING, kAppId, // appid | |
187 G_TYPE_INVALID, | |
188 G_TYPE_INT, &ret, | |
189 G_TYPE_INVALID); | |
190 CheckError(); | |
191 if (ret) | |
192 LOG(ERROR) << "Bad return code " << ret << " from kwallet removeEntry"; | |
193 } else { | |
194 // Otherwise update the entry in the wallet. | |
195 SetLoginsList(forms, form, wallet_handle); | |
196 } | |
197 } | |
198 | |
199 void PasswordStoreKWallet::GetLoginsImpl(GetLoginsRequest* request) { | |
200 PasswordFormList forms; | |
201 | |
202 AutoLock l(kwallet_lock_); | |
203 int wallet_handle = WalletHandle(); | |
204 if (wallet_handle != kInvalidKWalletHandle) | |
205 GetLoginsList(&forms, request->form, wallet_handle); | |
206 | |
207 NotifyConsumer(request, forms); | |
208 } | |
209 | |
210 void PasswordStoreKWallet::GetLoginsList(PasswordFormList* forms, | |
211 const PasswordForm& key, | |
212 int wallet_handle) { | |
213 // Is there an entry in the wallet? | |
214 gboolean has_entry = false; | |
215 dbus_g_proxy_call(proxy_, "hasEntry", &error_, | |
216 G_TYPE_INT, wallet_handle, // handle | |
217 G_TYPE_STRING, kKWalletFolder, // folder | |
218 G_TYPE_STRING, key.signon_realm.c_str(), // key | |
219 G_TYPE_STRING, kAppId, // appid | |
220 G_TYPE_INVALID, | |
221 G_TYPE_BOOLEAN, &has_entry, | |
222 G_TYPE_INVALID); | |
223 | |
224 if (CheckError() || !has_entry) | |
225 return; | |
226 | |
227 GArray* byte_array = NULL; | |
228 dbus_g_proxy_call(proxy_, "readEntry", &error_, | |
229 G_TYPE_INT, wallet_handle, // handle | |
230 G_TYPE_STRING, kKWalletFolder, // folder | |
231 G_TYPE_STRING, key.signon_realm.c_str(), // key | |
232 G_TYPE_STRING, kAppId, // appid | |
233 G_TYPE_INVALID, | |
234 DBUS_TYPE_G_UCHAR_ARRAY, &byte_array, | |
235 G_TYPE_INVALID); | |
236 | |
237 if (CheckError() || !byte_array || !byte_array->len) | |
238 return; | |
239 | |
240 Pickle pickle(byte_array->data, byte_array->len); | |
241 DeserializeValue(key, pickle, forms); | |
242 } | |
243 | |
244 void PasswordStoreKWallet::SetLoginsList(const PasswordFormList& forms, | |
245 const PasswordForm& key, | |
246 int wallet_handle) { | |
247 Pickle value; | |
248 SerializeValue(forms, &value); | |
249 | |
250 // Convert the pickled bytes to a GByteArray. | |
251 GArray* byte_array = g_array_sized_new(false, false, sizeof(char), | |
252 value.size()); | |
253 g_array_append_vals(byte_array, value.data(), value.size()); | |
254 | |
255 // Make the call. | |
256 int ret = 0; | |
257 dbus_g_proxy_call(proxy_, "writeEntry", &error_, | |
258 G_TYPE_INT, wallet_handle, // handle | |
259 G_TYPE_STRING, kKWalletFolder, // folder | |
260 G_TYPE_STRING, key.signon_realm.c_str(), // key | |
261 DBUS_TYPE_G_UCHAR_ARRAY, byte_array, // value | |
262 G_TYPE_STRING, kAppId, // appid | |
263 G_TYPE_INVALID, | |
264 G_TYPE_INT, &ret, | |
265 G_TYPE_INVALID); | |
266 g_array_free(byte_array, true); | |
267 | |
268 CheckError(); | |
269 if (ret) | |
270 LOG(ERROR) << "Bad return code " << ret << " from kwallet writeEntry"; | |
271 } | |
272 | |
273 bool PasswordStoreKWallet::CompareForms(const PasswordForm& a, | |
274 const PasswordForm& b) { | |
275 return a.origin == b.origin && | |
276 a.password_element == b.password_element && | |
277 a.signon_realm == b.signon_realm && | |
278 a.submit_element == b.submit_element && | |
279 a.username_element == b.username_element && | |
280 a.username_value == b.username_value; | |
281 } | |
282 | |
283 void PasswordStoreKWallet::SerializeValue(const PasswordFormList& forms, | |
284 Pickle* pickle) { | |
285 pickle->WriteInt(forms.size()); | |
286 for (PasswordFormList::const_iterator it = forms.begin() ; | |
287 it != forms.end() ; ++it) { | |
288 const PasswordForm* form = *it; | |
289 pickle->WriteInt(form->scheme); | |
290 pickle->WriteString(form->origin.spec()); | |
291 pickle->WriteString(form->action.spec()); | |
292 pickle->WriteWString(form->username_element); | |
293 pickle->WriteWString(form->username_value); | |
294 pickle->WriteWString(form->password_element); | |
295 pickle->WriteWString(form->password_value); | |
296 pickle->WriteWString(form->submit_element); | |
297 pickle->WriteBool(form->ssl_valid); | |
298 pickle->WriteBool(form->preferred); | |
299 pickle->WriteBool(form->blacklisted_by_user); | |
300 } | |
301 } | |
302 | |
303 void PasswordStoreKWallet::DeserializeValue(const PasswordForm& key, | |
304 const Pickle& pickle, | |
305 PasswordFormList* forms) { | |
306 void* iter = NULL; | |
307 | |
308 int count = 0; | |
309 pickle.ReadInt(&iter, &count); | |
310 | |
311 for (int i = 0; i < count; ++i) { | |
312 PasswordForm* form = new PasswordForm(); | |
313 form->signon_realm.assign(key.signon_realm); | |
314 | |
315 pickle.ReadInt(&iter, reinterpret_cast<int*>(&form->scheme)); | |
316 ReadGURL(pickle, &iter, &form->origin); | |
317 ReadGURL(pickle, &iter, &form->action); | |
318 pickle.ReadWString(&iter, &form->username_element); | |
319 pickle.ReadWString(&iter, &form->username_value); | |
320 pickle.ReadWString(&iter, &form->password_element); | |
321 pickle.ReadWString(&iter, &form->password_value); | |
322 pickle.ReadWString(&iter, &form->submit_element); | |
323 pickle.ReadBool(&iter, &form->ssl_valid); | |
324 pickle.ReadBool(&iter, &form->preferred); | |
325 pickle.ReadBool(&iter, &form->blacklisted_by_user); | |
326 forms->push_back(form); | |
327 } | |
328 } | |
329 | |
330 void PasswordStoreKWallet::ReadGURL(const Pickle& pickle, void** iter, | |
331 GURL* url) { | |
332 string url_string; | |
333 pickle.ReadString(iter, &url_string); | |
334 *url = GURL(url_string); | |
335 } | |
336 | |
337 bool PasswordStoreKWallet::CheckError() { | |
338 if (error_) { | |
339 LOG(ERROR) << "Failed to complete KWallet call: " << error_->message; | |
340 g_error_free(error_); | |
341 error_ = NULL; | |
342 return true; | |
343 } | |
344 return false; | |
345 } | |
346 | |
347 int PasswordStoreKWallet::WalletHandle() { | |
348 // Open the wallet. | |
349 int handle = kInvalidKWalletHandle; | |
350 dbus_g_proxy_call(proxy_, "open", &error_, | |
351 G_TYPE_STRING, wallet_name_.c_str(), // wallet | |
352 G_TYPE_INT64, 0LL, // wid | |
353 G_TYPE_STRING, kAppId, // appid | |
354 G_TYPE_INVALID, | |
355 G_TYPE_INT, &handle, | |
356 G_TYPE_INVALID); | |
357 if (CheckError() || handle == kInvalidKWalletHandle) | |
358 return kInvalidKWalletHandle; | |
359 | |
360 // Check if our folder exists. | |
361 gboolean has_folder = false; | |
362 dbus_g_proxy_call(proxy_, "hasFolder", &error_, | |
363 G_TYPE_INT, handle, // handle | |
364 G_TYPE_STRING, kKWalletFolder, // folder | |
365 G_TYPE_STRING, kAppId, // appid | |
366 G_TYPE_INVALID, | |
367 G_TYPE_BOOLEAN, &has_folder, | |
368 G_TYPE_INVALID); | |
369 if (CheckError()) | |
370 return kInvalidKWalletHandle; | |
371 | |
372 // Create it if it didn't. | |
373 if (!has_folder) { | |
374 gboolean success = false; | |
375 dbus_g_proxy_call(proxy_, "createFolder", &error_, | |
376 G_TYPE_INT, handle, // handle | |
377 G_TYPE_STRING, kKWalletFolder, // folder | |
378 G_TYPE_STRING, kAppId, // appid | |
379 G_TYPE_INVALID, | |
380 G_TYPE_BOOLEAN, &success, | |
381 G_TYPE_INVALID); | |
382 if (CheckError() || !success) | |
383 return kInvalidKWalletHandle; | |
384 } | |
385 | |
386 return handle; | |
387 } | |
OLD | NEW |