OLD | NEW |
| (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 } | |
OLD | NEW |