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

Side by Side Diff: chrome/browser/password_manager/proxy/chrome_keyring_proxy.cc

Issue 8509038: Linux: split GNOME Keyring integration into a separate process. Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: everything works Created 9 years, 1 month 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
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2011 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/proxy/chrome_keyring_proxy.h"
6
7 // This file is part of a very small helper binary that runs without any
8 // dependencies on base. Thus we can't use normal logging; we use assert.
9 #include <assert.h>
10 #include <errno.h>
11 #include <glib.h>
12 #include <glib-object.h>
13 #include <gnome-keyring.h>
14 #include <inttypes.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18
19 #include <map>
20 #include <string>
21 #include <vector>
22
23 #include "chrome/browser/password_manager/proxy/keyring_loader.h"
24 #include "chrome/browser/password_manager/proxy/message_reader.h"
25
26 namespace {
27
28 // This is the approximation of PasswordForm that we store in
29 // GNOME Keyring, using the schema in kGnomeSchema below.
30 struct BasicPasswordForm {
31 std::string origin;
32 std::string action;
33 std::string username_element;
34 std::string username_value;
35 std::string password_element;
36 std::string password_value;
37 std::string submit_element;
38 std::string signon_realm;
39 uint32_t ssl_valid;
40 uint32_t preferred;
41 std::string date_created;
42 uint32_t blacklisted_by_user;
43 uint32_t scheme;
44 };
45
46 // Convert the attributes of a given keyring entry into a BasicPasswordForm.
47 // Note: does *not* get the actual password, as that is not a key attribute!
48 void FormFromAttributes(GnomeKeyringAttributeList* attrs,
49 BasicPasswordForm* form) {
50 // Read the string and int attributes into the appropriate map.
51 std::map<std::string, std::string> string_attr_map;
52 std::map<std::string, uint32_t> uint_attr_map;
53 for (guint i = 0; i < attrs->len; ++i) {
54 GnomeKeyringAttribute attr = gnome_keyring_attribute_list_index(attrs, i);
55 if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING)
56 string_attr_map[attr.name] = attr.value.string;
57 else if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32)
58 uint_attr_map[attr.name] = attr.value.integer;
59 }
60
61 form->origin = string_attr_map["origin_url"];
62 form->action = string_attr_map["action_url"];
63 form->username_element = string_attr_map["username_element"];
64 form->username_value = string_attr_map["username_value"];
65 form->password_element = string_attr_map["password_element"];
66 form->submit_element = string_attr_map["submit_element"];
67 form->signon_realm = string_attr_map["signon_realm"];
68 form->ssl_valid = uint_attr_map["ssl_valid"];
69 form->preferred = uint_attr_map["preferred"];
70 form->date_created = string_attr_map["date_created"];
71 form->blacklisted_by_user = uint_attr_map["blacklisted_by_user"];
72 form->scheme = uint_attr_map["scheme"];
73 }
74
75 // Parse all the results from the given GList into a list of BasicPasswordForms.
76 void ConvertFormList(GList* found, std::vector<BasicPasswordForm>* forms) {
77 GList* element = g_list_first(found);
78 while (element != NULL) {
79 GnomeKeyringFound* data = static_cast<GnomeKeyringFound*>(element->data);
80 GnomeKeyringAttributeList* attrs = data->attributes;
81
82 forms->resize(forms->size() + 1);
83 FormFromAttributes(attrs, &forms->back());
84 if (data->secret)
85 forms->back().password_value = data->secret;
86
87 element = g_list_next(element);
88 }
89 }
90
91 // Schema is analagous to the fields in BasicPasswordForm.
92 const GnomeKeyringPasswordSchema kGnomeSchema = {
93 GNOME_KEYRING_ITEM_GENERIC_SECRET, {
94 { "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
95 { "action_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
96 { "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
97 { "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
98 { "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
99 { "submit_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
100 { "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
101 { "ssl_valid", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
102 { "preferred", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
103 { "date_created", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
104 { "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
105 { "scheme", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
106 // This field is used to disambiguate passwords for different profiles.
107 { "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
108 { NULL }
109 }
110 };
111
112 // A simple class that encapsulates the boilerplate needed to tell
113 // GLib how to watch a file descriptor for reading.
114 class FDReadSource : public GSource {
115 public:
116 static GSource* Allocate(int fd) {
117 GSource* source = g_source_new(&functions, sizeof(FDReadSource));
118 FDReadSource* fdsource = static_cast<FDReadSource*>(source);
119 fdsource->pollfd.fd = fd;
120 fdsource->pollfd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
121 g_source_add_poll(source, &fdsource->pollfd);
122 return source;
123 }
124
125 private:
126 // We don't allocate or free FDReadSource ourselves. Instead, g_source_new()
127 // will do that for us. So, make all the constructors/destructors private.
128 FDReadSource();
129 FDReadSource(const FDReadSource&);
130 void operator=(const FDReadSource&);
131 ~FDReadSource();
132
133 static gboolean Prepare(GSource* source, gint* timeout) {
134 *timeout = -1;
135 return FALSE;
136 }
137
138 static gboolean Check(GSource* source) {
139 FDReadSource* fdsource = static_cast<FDReadSource*>(source);
140 return !!(fdsource->pollfd.revents & (G_IO_IN | G_IO_HUP | G_IO_ERR));
141 }
142
143 static gboolean Dispatch(GSource* source,
144 GSourceFunc callback,
145 gpointer user_data) {
146 if (!callback)
147 return FALSE; // No callback? Remove the source so it doesn't spin.
148 return callback(user_data);
149 }
150
151 static GSourceFuncs functions;
152
153 GPollFD pollfd;
154 };
155
156 GSourceFuncs FDReadSource::functions = {
157 Prepare, Check, Dispatch, NULL // .finalize
158 };
159
160 } // anonymous namespace
161
162 class MessageProcessor : public GnomeKeyringLoader {
163 public:
164 explicit MessageProcessor(FILE* output) : output_(output) {}
165
166 bool Process(const std::vector<std::string>& lines) {
167 if (lines.size() < 1) {
168 // Although an empty message is technically an error, we choose to be
169 // lenient and ignore it. It makes manual testing easier.
170 return true;
171 }
172 if (lines[0].length() < 2) {
173 // The first line must have a command character followed by an id.
174 return false;
175 }
176 int id = atoi(&lines[0].c_str()[1]);
177 if (id <= 0) {
178 // The id must be at least 1.
179 return false;
180 }
181 if (lines[0][0] == ChromeKeyringProxyCommands::kAddLogin) {
182 // Store a password. (AddLogin)
183 if (lines.size() != 16)
184 return false;
185 Request* request = AllocateRequest(id);
186 if (!request)
187 return false;
188 gnome_keyring_store_password(
189 &kGnomeSchema,
190 NULL, // Default keyring.
191 &lines[1].c_str()[1], // Display name.
192 &lines[2].c_str()[1], // Password.
193 OnOperationDone,
194 request, // data
195 NULL, // destroy_data
196 "origin_url", &lines[3].c_str()[1],
197 "action_url", &lines[4].c_str()[1],
198 "username_element", &lines[5].c_str()[1],
199 "username_value", &lines[6].c_str()[1],
200 "password_element", &lines[7].c_str()[1],
201 "submit_element", &lines[8].c_str()[1],
202 "signon_realm", &lines[9].c_str()[1],
203 "ssl_valid", lines[10] != "0",
204 "preferred", lines[11] != "0",
205 "date_created", &lines[12].c_str()[1],
206 "blacklisted_by_user", lines[13] != "0",
207 "scheme", atoi(lines[14].c_str()),
208 "application", lines[15].c_str(),
209 NULL);
210 } else if (lines[0][0] == ChromeKeyringProxyCommands::kRemoveLogin) {
211 // Delete a password. (RemoveLogin)
212 if (lines.size() != 9)
213 return false;
214 Request* request = AllocateRequest(id);
215 if (!request)
216 return false;
217 gnome_keyring_delete_password(
218 &kGnomeSchema,
219 OnOperationDone,
220 request, // data
221 NULL, // destroy_data
222 "origin_url", &lines[1].c_str()[1],
223 "action_url", &lines[2].c_str()[1],
224 "username_element", &lines[3].c_str()[1],
225 "username_value", &lines[4].c_str()[1],
226 "password_element", &lines[5].c_str()[1],
227 "submit_element", &lines[6].c_str()[1],
228 "signon_realm", &lines[7].c_str()[1],
229 "application", lines[8].c_str(),
230 NULL);
231 } else if (lines[0][0] == ChromeKeyringProxyCommands::kAddLoginSearch) {
232 // Find passwords before adding a new one. (AddLoginSearch)
233 if (lines.size() != 8)
234 return false;
235 Request* request = AllocateRequest(id);
236 if (!request)
237 return false;
238 gnome_keyring_find_itemsv(
239 GNOME_KEYRING_ITEM_GENERIC_SECRET,
240 OnFindDone,
241 request, // data
242 NULL, // destroy_data
243 "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
244 &lines[1].c_str()[1],
245 "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
246 &lines[2].c_str()[1],
247 "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
248 &lines[3].c_str()[1],
249 "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
250 &lines[4].c_str()[1],
251 "submit_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
252 &lines[5].c_str()[1],
253 "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
254 &lines[6].c_str()[1],
255 "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
256 lines[7].c_str(),
257 NULL);
258 } else if (lines[0][0] == ChromeKeyringProxyCommands::kUpdateLoginSearch) {
259 // Find passwords to update. (UpdateLoginSearch)
260 if (lines.size() != 7)
261 return false;
262 Request* request = AllocateRequest(id);
263 if (!request)
264 return false;
265 gnome_keyring_find_itemsv(
266 GNOME_KEYRING_ITEM_GENERIC_SECRET,
267 OnFindDone,
268 request, // data
269 NULL, // destroy_data
270 "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
271 &lines[1].c_str()[1],
272 "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
273 &lines[2].c_str()[1],
274 "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
275 &lines[3].c_str()[1],
276 "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
277 &lines[4].c_str()[1],
278 "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
279 &lines[5].c_str()[1],
280 "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
281 lines[6].c_str(),
282 NULL);
283 } else if (lines[0][0] == ChromeKeyringProxyCommands::kGetLogins) {
284 // Find passwords to autofill. (GetLogins)
285 if (lines.size() != 3)
286 return false;
287 Request* request = AllocateRequest(id);
288 if (!request)
289 return false;
290 gnome_keyring_find_itemsv(
291 GNOME_KEYRING_ITEM_GENERIC_SECRET,
292 OnFindDone,
293 request, // data
294 NULL, // destroy_data
295 "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
296 &lines[1].c_str()[1],
297 "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
298 lines[2].c_str(),
299 NULL);
300 } else if (lines[0][0] == ChromeKeyringProxyCommands::kGetLoginsList) {
301 // Find passwords to show in the password manager. (GetLoginsList)
302 if (lines.size() != 3)
303 return false;
304 Request* request = AllocateRequest(id);
305 if (!request)
306 return false;
307 gnome_keyring_find_itemsv(
308 GNOME_KEYRING_ITEM_GENERIC_SECRET,
309 OnFindDone,
310 request, // data
311 NULL, // destroy_data
312 "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32,
313 lines[1] != "0",
314 "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
315 lines[2].c_str(),
316 NULL);
317 } else if (lines[0][0] == ChromeKeyringProxyCommands::kGetAllLogins) {
318 // Find all passwords. (GetAllLogins)
319 if (lines.size() != 2)
320 return false;
321 Request* request = AllocateRequest(id);
322 if (!request)
323 return false;
324 // We need to search for something, otherwise we get no results - so
325 // we search for the fixed application string.
326 gnome_keyring_find_itemsv(
327 GNOME_KEYRING_ITEM_GENERIC_SECRET,
328 OnFindDone,
329 request, // data
330 NULL, // destroy_data
331 "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
332 lines[1].c_str(),
333 NULL);
334 } else {
335 // Unexpected command line.
336 return false;
337 }
338 return true;
339 }
340
341 private:
342 struct Request {
343 int id;
344 MessageProcessor* processor;
345
346 Request(int id, MessageProcessor* processor)
347 : id(id), processor(processor) {
348 }
349 };
350
351 Request* AllocateRequest(int id) {
352 std::pair<std::map<int, Request>::iterator, bool> result =
353 requests_.insert(std::make_pair(id, Request(id, this)));
354 return result.second ? &result.first->second : NULL;
355 }
356
357 void ReplyAndFreeRequest(Request* request,
358 const std::vector<std::string>& reply) {
359 for (size_t i = 0; i < reply.size(); ++i)
360 fprintf(output_, "%s\n", reply[i].c_str());
361 fprintf(output_, "\n");
362 fflush(output_);
363 requests_.erase(request->id);
364 }
365
366 static void OnOperationDone(GnomeKeyringResult result, gpointer data) {
367 Request* request = static_cast<Request*>(data);
368 std::vector<std::string> reply;
369 char line[32];
370 snprintf(line, sizeof(line), "%d %d", request->id, result);
371 reply.push_back(line);
372 request->processor->ReplyAndFreeRequest(request, reply);
373 }
374
375 static void OnFindDone(GnomeKeyringResult result,
376 GList* list, gpointer data) {
377 Request* request = static_cast<Request*>(data);
378 std::vector<std::string> reply;
379 char line[32];
380 snprintf(line, sizeof(line), "%d %d", request->id, result);
381 reply.push_back(line);
382 // |list| will be freed after this callback returns, so convert it now.
383 std::vector<BasicPasswordForm> forms;
384 ConvertFormList(list, &forms);
385 for (size_t i = 0; i < forms.size(); ++i) {
386 reply.push_back("+" + forms[i].origin);
387 reply.push_back("+" + forms[i].action);
388 reply.push_back("+" + forms[i].username_element);
389 reply.push_back("+" + forms[i].username_value);
390 reply.push_back("+" + forms[i].password_element);
391 reply.push_back("+" + forms[i].password_value);
392 reply.push_back("+" + forms[i].submit_element);
393 reply.push_back("+" + forms[i].signon_realm);
394 reply.push_back(forms[i].ssl_valid ? "1" : "0");
395 reply.push_back(forms[i].preferred ? "1" : "0");
396 reply.push_back("+" + forms[i].date_created);
397 reply.push_back(forms[i].blacklisted_by_user ? "1" : "0");
398 snprintf(line, sizeof(line), "%u", forms[i].scheme);
399 reply.push_back(line);
400 }
401 request->processor->ReplyAndFreeRequest(request, reply);
402 }
403
404 FILE* output_;
405 std::map<int, Request> requests_;
406 };
407
408 ChromeKeyringProxy::ChromeKeyringProxy(int fd, FILE* output)
409 : fd_(fd), processor_(new MessageProcessor(output)) {
410 main_loop_ = g_main_loop_new(NULL, FALSE); // is_running
411 input_ = FDReadSource::Allocate(fd_);
412 g_source_set_callback(input_, DataReady, this, NULL);
413 g_source_attach(input_, NULL);
414 }
415
416 ChromeKeyringProxy::~ChromeKeyringProxy() {
417 g_source_remove_by_user_data(this);
418 g_source_unref(input_);
419 g_main_loop_unref(main_loop_);
420 }
421
422 bool ChromeKeyringProxy::Init() {
423 return LoadGnomeKeyring() && gnome_keyring_is_available();
424 }
425
426 void ChromeKeyringProxy::Run() {
427 g_main_loop_run(main_loop_);
428 }
429
430 void ChromeKeyringProxy::Stop() {
431 if (!g_main_loop_is_running(main_loop_))
432 fprintf(stderr, "%s:%d: main loop not running!\n", __FILE__, __LINE__);
433 g_main_loop_quit(main_loop_);
434 }
435
436 void ChromeKeyringProxy::ReadData() {
437 // We don't need to read all the available data. If there is still data left
438 // after we return, then we'll get called again because the descriptor will
439 // still be ready to read. So just read a fixed amount, for simplicity.
440 char buffer[256];
441 ssize_t available = read(fd_, buffer, sizeof(buffer));
442 if (available > 0) {
443 // Got some data. Handle it. Note that this may not be a complete message.
444 ssize_t used = 0;
445 while (used < available) {
446 ssize_t handled = reader_.HandleData(&buffer[used], available);
447 assert(handled > 0);
448 used += handled;
449 available -= handled;
450 if (reader_.is_complete()) {
451 if (!processor_->Process(reader_.lines())) {
452 // A fatal processing error occurred. Shut down the proxy.
453 Stop();
454 }
455 reader_.Reset();
456 } else {
457 assert(available == 0);
458 }
459 }
460 } else if (available == 0 || errno != EINTR) {
461 // EOF, or an unexpected error. Shut down the proxy.
462 Stop();
463 }
464 }
465
466 // static
467 gboolean ChromeKeyringProxy::DataReady(gpointer data) {
468 static_cast<ChromeKeyringProxy*>(data)->ReadData();
469 // We always keep this source. If we run out of input, we should quit.
470 return TRUE;
471 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698