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

Side by Side Diff: chrome/browser/password_manager/password_store_gnome.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_gnome.h"
6
7 #if defined(DLOPEN_GNOME_KEYRING)
8 #include <dlfcn.h>
9 #endif
10
11 #include <map>
12 #include <string>
13
14 #include "base/logging.h"
15 #include "base/string_util.h"
16 #include "base/task.h"
17 #include "base/time.h"
18 #include "base/utf_string_conversions.h"
19
20 using std::map;
21 using std::string;
22 using std::vector;
23 using webkit_glue::PasswordForm;
24
25 /* Many of the gnome_keyring_* functions use variable arguments, which makes
26 * them difficult if not impossible to wrap in C. Therefore, we want the
27 * actual uses below to either call the functions directly (if we are linking
28 * against libgnome-keyring), or call them via appropriately-typed function
29 * pointers (if we are dynamically loading libgnome-keyring).
30 *
31 * Thus, instead of making a wrapper class with two implementations, we use
32 * the preprocessor to rename the calls below in the dynamic load case, and
33 * provide a function to initialize a set of function pointers that have the
34 * alternate names. We also make sure the types are correct, since otherwise
35 * dynamic loading like this would leave us vulnerable to signature changes. */
36
37 #if defined(DLOPEN_GNOME_KEYRING)
38
39 namespace {
40
41 gboolean (*wrap_gnome_keyring_is_available)();
42 GnomeKeyringResult (*wrap_gnome_keyring_store_password_sync)( // NOLINT
43 const GnomeKeyringPasswordSchema* schema, const gchar* keyring,
44 const gchar* display_name, const gchar* password, ...);
45 GnomeKeyringResult (*wrap_gnome_keyring_delete_password_sync)( // NOLINT
46 const GnomeKeyringPasswordSchema* schema, ...);
47 GnomeKeyringResult (*wrap_gnome_keyring_find_itemsv_sync)( // NOLINT
48 GnomeKeyringItemType type, GList** found, ...);
49 const gchar* (*wrap_gnome_keyring_result_to_message)(GnomeKeyringResult res);
50 void (*wrap_gnome_keyring_found_list_free)(GList* found_list);
51
52 /* Cause the compiler to complain if the types of the above function pointers
53 * do not correspond to the types of the actual gnome_keyring_* functions. */
54 #define GNOME_KEYRING_VERIFY_TYPE(name) \
55 typeof(&gnome_keyring_##name) name = wrap_gnome_keyring_##name; name = name
56
57 inline void VerifyGnomeKeyringTypes() {
58 GNOME_KEYRING_VERIFY_TYPE(is_available);
59 GNOME_KEYRING_VERIFY_TYPE(store_password_sync);
60 GNOME_KEYRING_VERIFY_TYPE(delete_password_sync);
61 GNOME_KEYRING_VERIFY_TYPE(find_itemsv_sync);
62 GNOME_KEYRING_VERIFY_TYPE(result_to_message);
63 GNOME_KEYRING_VERIFY_TYPE(found_list_free);
64 }
65 #undef GNOME_KEYRING_VERIFY_TYPE
66
67 /* Make it easy to initialize the function pointers above with a loop below. */
68 #define GNOME_KEYRING_FUNCTION(name) \
69 {#name, reinterpret_cast<void**>(&wrap_##name)}
70 const struct {
71 const char* name;
72 void** pointer;
73 } gnome_keyring_functions[] = {
74 GNOME_KEYRING_FUNCTION(gnome_keyring_is_available),
75 GNOME_KEYRING_FUNCTION(gnome_keyring_store_password_sync),
76 GNOME_KEYRING_FUNCTION(gnome_keyring_delete_password_sync),
77 GNOME_KEYRING_FUNCTION(gnome_keyring_find_itemsv_sync),
78 GNOME_KEYRING_FUNCTION(gnome_keyring_result_to_message),
79 GNOME_KEYRING_FUNCTION(gnome_keyring_found_list_free),
80 {NULL, NULL}
81 };
82 #undef GNOME_KEYRING_FUNCTION
83
84 /* Allow application code below to use the normal function names, but actually
85 * end up using the function pointers above instead. */
86 #define gnome_keyring_is_available \
87 wrap_gnome_keyring_is_available
88 #define gnome_keyring_store_password_sync \
89 wrap_gnome_keyring_store_password_sync
90 #define gnome_keyring_delete_password_sync \
91 wrap_gnome_keyring_delete_password_sync
92 #define gnome_keyring_find_itemsv_sync \
93 wrap_gnome_keyring_find_itemsv_sync
94 #define gnome_keyring_result_to_message \
95 wrap_gnome_keyring_result_to_message
96 #define gnome_keyring_found_list_free \
97 wrap_gnome_keyring_found_list_free
98
99 /* Load the library and initialize the function pointers. */
100 bool LoadGnomeKeyring() {
101 void* handle = dlopen("libgnome-keyring.so.0", RTLD_NOW | RTLD_GLOBAL);
102 if (!handle) {
103 LOG(INFO) << "Could not find libgnome-keyring.so.0";
104 return false;
105 }
106 for (size_t i = 0; gnome_keyring_functions[i].name; ++i) {
107 dlerror();
108 *gnome_keyring_functions[i].pointer =
109 dlsym(handle, gnome_keyring_functions[i].name);
110 const char* error = dlerror();
111 if (error) {
112 LOG(ERROR) << "Unable to load symbol " <<
113 gnome_keyring_functions[i].name << ": " << error;
114 dlclose(handle);
115 return false;
116 }
117 }
118 // We leak the library handle. That's OK: this function is called only once.
119 return true;
120 }
121
122 } // namespace
123
124 #else // DLOPEN_GNOME_KEYRING
125
126 namespace {
127
128 bool LoadGnomeKeyring() {
129 return true;
130 }
131
132 } // namespace
133
134 #endif // DLOPEN_GNOME_KEYRING
135
136 #define GNOME_KEYRING_APPLICATION_CHROME "chrome"
137
138 // Schema is analagous to the fields in PasswordForm.
139 const GnomeKeyringPasswordSchema PasswordStoreGnome::kGnomeSchema = {
140 GNOME_KEYRING_ITEM_GENERIC_SECRET, {
141 { "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
142 { "action_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
143 { "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
144 { "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
145 { "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
146 { "submit_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
147 { "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
148 { "ssl_valid", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
149 { "preferred", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
150 { "date_created", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
151 { "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
152 { "scheme", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
153 // This field is always "chrome" so that we can search for it.
154 { "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
155 { NULL }
156 }
157 };
158
159 PasswordStoreGnome::PasswordStoreGnome(LoginDatabase* login_db,
160 Profile* profile,
161 WebDataService* web_data_service) {
162 }
163
164 PasswordStoreGnome::~PasswordStoreGnome() {
165 }
166
167 bool PasswordStoreGnome::Init() {
168 return PasswordStore::Init() &&
169 LoadGnomeKeyring() &&
170 gnome_keyring_is_available();
171 }
172
173 void PasswordStoreGnome::AddLoginImpl(const PasswordForm& form) {
174 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
175 AddLoginHelper(form, base::Time::Now());
176 }
177
178 void PasswordStoreGnome::UpdateLoginImpl(const PasswordForm& form) {
179 // Based on LoginDatabase::UpdateLogin(), we search for forms to update by
180 // origin_url, username_element, username_value, password_element, and
181 // signon_realm. We then compare the result to the updated form. If they
182 // differ in any of the action, password_value, ssl_valid, or preferred
183 // fields, then we add a new login with those fields updated and only delete
184 // the original on success.
185 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
186 GList* found = NULL;
187 // Search gnome keyring for matching passwords.
188 GnomeKeyringResult result = gnome_keyring_find_itemsv_sync(
189 GNOME_KEYRING_ITEM_GENERIC_SECRET,
190 &found,
191 "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
192 form.origin.spec().c_str(),
193 "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
194 UTF16ToUTF8(form.username_element).c_str(),
195 "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
196 UTF16ToUTF8(form.username_value).c_str(),
197 "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
198 UTF16ToUTF8(form.password_element).c_str(),
199 "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
200 form.signon_realm.c_str(),
201 "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
202 GNOME_KEYRING_APPLICATION_CHROME,
203 NULL);
204 vector<PasswordForm*> forms;
205 if (result == GNOME_KEYRING_RESULT_OK) {
206 FillFormVector(found, &forms);
207 for (size_t i = 0; i < forms.size(); ++i) {
208 if (forms[i]->action != form.action ||
209 forms[i]->password_value != form.password_value ||
210 forms[i]->ssl_valid != form.ssl_valid ||
211 forms[i]->preferred != form.preferred) {
212 PasswordForm updated = *forms[i];
213 updated.action = form.action;
214 updated.password_value = form.password_value;
215 updated.ssl_valid = form.ssl_valid;
216 updated.preferred = form.preferred;
217 if (AddLoginHelper(updated, updated.date_created))
218 RemoveLoginImpl(*forms[i]);
219 }
220 delete forms[i];
221 }
222 } else {
223 LOG(ERROR) << "Keyring find failed: "
224 << gnome_keyring_result_to_message(result);
225 }
226 }
227
228 void PasswordStoreGnome::RemoveLoginImpl(const PasswordForm& form) {
229 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
230 // We find forms using the same fields as LoginDatabase::RemoveLogin().
231 GnomeKeyringResult result = gnome_keyring_delete_password_sync(
232 &kGnomeSchema,
233 "origin_url", form.origin.spec().c_str(),
234 "action_url", form.action.spec().c_str(),
235 "username_element", UTF16ToUTF8(form.username_element).c_str(),
236 "username_value", UTF16ToUTF8(form.username_value).c_str(),
237 "password_element", UTF16ToUTF8(form.password_element).c_str(),
238 "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
239 "signon_realm", form.signon_realm.c_str(),
240 NULL);
241 if (result != GNOME_KEYRING_RESULT_OK) {
242 LOG(ERROR) << "Keyring delete failed: "
243 << gnome_keyring_result_to_message(result);
244 }
245 }
246
247 void PasswordStoreGnome::RemoveLoginsCreatedBetweenImpl(
248 const base::Time& delete_begin,
249 const base::Time& delete_end) {
250 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
251 GList* found = NULL;
252 // Search GNOME keyring for all passwords, then delete the ones in the range.
253 // We need to search for something, otherwise we get no results - so we search
254 // for the fixed application string.
255 GnomeKeyringResult result = gnome_keyring_find_itemsv_sync(
256 GNOME_KEYRING_ITEM_GENERIC_SECRET,
257 &found,
258 "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
259 GNOME_KEYRING_APPLICATION_CHROME,
260 NULL);
261 if (result == GNOME_KEYRING_RESULT_OK) {
262 // We could walk the list and delete items as we find them, but it is much
263 // easier to build the vector and use RemoveLoginImpl() to delete them.
264 vector<PasswordForm*> forms;
265 FillFormVector(found, &forms);
266 for (size_t i = 0; i < forms.size(); ++i) {
267 if (delete_begin <= forms[i]->date_created &&
268 (delete_end.is_null() || forms[i]->date_created < delete_end)) {
269 RemoveLoginImpl(*forms[i]);
270 }
271 delete forms[i];
272 }
273 } else if (result != GNOME_KEYRING_RESULT_NO_MATCH) {
274 LOG(ERROR) << "Keyring find failed: "
275 << gnome_keyring_result_to_message(result);
276 }
277 }
278
279 void PasswordStoreGnome::GetLoginsImpl(GetLoginsRequest* request,
280 const PasswordForm& form) {
281 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
282 GList* found = NULL;
283 // Search gnome keyring for matching passwords.
284 GnomeKeyringResult result = gnome_keyring_find_itemsv_sync(
285 GNOME_KEYRING_ITEM_GENERIC_SECRET,
286 &found,
287 "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
288 form.signon_realm.c_str(),
289 "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
290 GNOME_KEYRING_APPLICATION_CHROME,
291 NULL);
292 vector<PasswordForm*> forms;
293 if (result == GNOME_KEYRING_RESULT_OK) {
294 FillFormVector(found, &forms);
295 } else if (result != GNOME_KEYRING_RESULT_NO_MATCH) {
296 LOG(ERROR) << "Keyring find failed: "
297 << gnome_keyring_result_to_message(result);
298 }
299 NotifyConsumer(request, forms);
300 }
301
302 void PasswordStoreGnome::GetAutofillableLoginsImpl(
303 GetLoginsRequest* request) {
304 std::vector<PasswordForm*> forms;
305 FillAutofillableLogins(&forms);
306 NotifyConsumer(request, forms);
307 }
308
309 void PasswordStoreGnome::GetBlacklistLoginsImpl(
310 GetLoginsRequest* request) {
311 std::vector<PasswordForm*> forms;
312 FillBlacklistLogins(&forms);
313 NotifyConsumer(request, forms);
314 }
315
316 bool PasswordStoreGnome::FillAutofillableLogins(
317 std::vector<PasswordForm*>* forms) {
318 return FillSomeLogins(true, forms);
319 }
320
321 bool PasswordStoreGnome::FillBlacklistLogins(
322 std::vector<PasswordForm*>* forms) {
323 return FillSomeLogins(false, forms);
324 }
325
326 bool PasswordStoreGnome::AddLoginHelper(const PasswordForm& form,
327 const base::Time& date_created) {
328 GnomeKeyringResult result = gnome_keyring_store_password_sync(
329 &kGnomeSchema,
330 NULL, // Default keyring.
331 form.origin.spec().c_str(), // Display name.
332 UTF16ToUTF8(form.password_value).c_str(),
333 "origin_url", form.origin.spec().c_str(),
334 "action_url", form.action.spec().c_str(),
335 "username_element", UTF16ToUTF8(form.username_element).c_str(),
336 "username_value", UTF16ToUTF8(form.username_value).c_str(),
337 "password_element", UTF16ToUTF8(form.password_element).c_str(),
338 "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
339 "signon_realm", form.signon_realm.c_str(),
340 "ssl_valid", form.ssl_valid,
341 "preferred", form.preferred,
342 "date_created", Int64ToString(date_created.ToTimeT()).c_str(),
343 "blacklisted_by_user", form.blacklisted_by_user,
344 "scheme", form.scheme,
345 "application", GNOME_KEYRING_APPLICATION_CHROME,
346 NULL);
347
348 if (result != GNOME_KEYRING_RESULT_OK) {
349 LOG(ERROR) << "Keyring save failed: "
350 << gnome_keyring_result_to_message(result);
351 return false;
352 }
353 return true;
354 }
355
356 bool PasswordStoreGnome::FillSomeLogins(
357 bool autofillable,
358 std::vector<PasswordForm*>* forms) {
359 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
360 GList* found = NULL;
361 uint32_t blacklisted_by_user = !autofillable;
362 // Search gnome keyring for matching passwords.
363 GnomeKeyringResult result = gnome_keyring_find_itemsv_sync(
364 GNOME_KEYRING_ITEM_GENERIC_SECRET,
365 &found,
366 "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32,
367 blacklisted_by_user,
368 "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
369 GNOME_KEYRING_APPLICATION_CHROME,
370 NULL);
371 if (result == GNOME_KEYRING_RESULT_OK) {
372 FillFormVector(found, forms);
373 } else if (result != GNOME_KEYRING_RESULT_NO_MATCH) {
374 LOG(ERROR) << "Keyring find failed: "
375 << gnome_keyring_result_to_message(result);
376 return false;
377 }
378 return true;
379 }
380
381 void PasswordStoreGnome::FillFormVector(GList* found,
382 std::vector<PasswordForm*>* forms) {
383 GList* element = g_list_first(found);
384 while (element != NULL) {
385 GnomeKeyringFound* data = static_cast<GnomeKeyringFound*>(element->data);
386 char* password = data->secret;
387
388 GnomeKeyringAttributeList* attributes = data->attributes;
389 // Read the string and int attributes into the appropriate map.
390 map<string, string> string_attribute_map;
391 map<string, uint32> uint_attribute_map;
392 for (unsigned int i = 0; i < attributes->len; ++i) {
393 GnomeKeyringAttribute attribute =
394 gnome_keyring_attribute_list_index(attributes, i);
395 if (attribute.type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING) {
396 string_attribute_map[string(attribute.name)] =
397 string(attribute.value.string);
398 } else if (attribute.type == GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32) {
399 uint_attribute_map[string(attribute.name)] = attribute.value.integer;
400 }
401 }
402
403 PasswordForm* form = new PasswordForm();
404 form->origin = GURL(string_attribute_map["origin_url"]);
405 form->action = GURL(string_attribute_map["action_url"]);
406 form->username_element =
407 UTF8ToUTF16(string(string_attribute_map["username_element"]));
408 form->username_value =
409 UTF8ToUTF16(string(string_attribute_map["username_value"]));
410 form->password_element =
411 UTF8ToUTF16(string(string_attribute_map["password_element"]));
412 form->password_value = UTF8ToUTF16(string(password));
413 form->submit_element =
414 UTF8ToUTF16(string(string_attribute_map["submit_element"]));
415 form->signon_realm = string_attribute_map["signon_realm"];
416 form->ssl_valid = uint_attribute_map["ssl_valid"];
417 form->preferred = uint_attribute_map["preferred"];
418 string date = string_attribute_map["date_created"];
419 int64 date_created = 0;
420 bool date_ok = StringToInt64(date, &date_created);
421 DCHECK(date_ok);
422 DCHECK_NE(date_created, 0);
423 form->date_created = base::Time::FromTimeT(date_created);
424 form->blacklisted_by_user = uint_attribute_map["blacklisted_by_user"];
425 form->scheme = static_cast<PasswordForm::Scheme>(
426 uint_attribute_map["scheme"]);
427
428 forms->push_back(form);
429
430 element = g_list_next(element);
431 }
432 gnome_keyring_found_list_free(found);
433 }
OLDNEW
« no previous file with comments | « chrome/browser/password_manager/password_store_gnome.h ('k') | chrome/browser/password_manager/password_store_kwallet.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698