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

Unified Diff: chrome/browser/password_manager/keyring_proxy/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: merge Created 8 years, 11 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/password_manager/keyring_proxy/keyring_proxy.cc
===================================================================
--- chrome/browser/password_manager/keyring_proxy/keyring_proxy.cc (revision 0)
+++ chrome/browser/password_manager/keyring_proxy/keyring_proxy.cc (revision 0)
@@ -0,0 +1,475 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/password_manager/keyring_proxy/keyring_proxy.h"
+
+// This file is part of a very small helper binary that runs without any
+// dependencies on base. Thus we can't use normal logging; we use assert.
+#include <assert.h>
+#include <errno.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <gnome-keyring.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "chrome/browser/password_manager/keyring_proxy/gnome_keyring_loader.h"
+#include "chrome/browser/password_manager/keyring_proxy/message_reader.h"
+
+namespace {
+
+// This is the approximation of PasswordForm that we store in
+// GNOME Keyring, using the schema in kGnomeSchema below.
+struct BasicPasswordForm {
+ std::string origin;
+ std::string action;
+ std::string username_element;
+ std::string username_value;
+ std::string password_element;
+ std::string password_value;
+ std::string submit_element;
+ std::string signon_realm;
+ uint32_t ssl_valid;
+ uint32_t preferred;
+ std::string date_created;
+ uint32_t blacklisted_by_user;
+ uint32_t scheme;
+};
+
+// Convert the attributes of a given keyring entry into a BasicPasswordForm.
+// Note: does *not* get the actual password, as that is not a key attribute!
+void FormFromAttributes(GnomeKeyringAttributeList* attrs,
+ BasicPasswordForm* form) {
+ // Read the string and int attributes into the appropriate map.
+ std::map<std::string, std::string> string_attr_map;
+ std::map<std::string, uint32_t> uint_attr_map;
+ for (guint i = 0; i < attrs->len; ++i) {
+ GnomeKeyringAttribute attr = gnome_keyring_attribute_list_index(attrs, i);
+ if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING)
+ string_attr_map[attr.name] = attr.value.string;
+ else if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32)
+ uint_attr_map[attr.name] = attr.value.integer;
+ }
+
+ form->origin = string_attr_map["origin_url"];
+ form->action = string_attr_map["action_url"];
+ form->username_element = string_attr_map["username_element"];
+ form->username_value = string_attr_map["username_value"];
+ form->password_element = string_attr_map["password_element"];
+ form->submit_element = string_attr_map["submit_element"];
+ form->signon_realm = string_attr_map["signon_realm"];
+ form->ssl_valid = uint_attr_map["ssl_valid"];
+ form->preferred = uint_attr_map["preferred"];
+ form->date_created = string_attr_map["date_created"];
+ form->blacklisted_by_user = uint_attr_map["blacklisted_by_user"];
+ form->scheme = uint_attr_map["scheme"];
+}
+
+// Parse all the results from the given GList into a list of BasicPasswordForms.
+void ConvertFormList(GList* found, std::vector<BasicPasswordForm>* forms) {
+ GList* element = g_list_first(found);
+ while (element != NULL) {
+ GnomeKeyringFound* data = static_cast<GnomeKeyringFound*>(element->data);
+ GnomeKeyringAttributeList* attrs = data->attributes;
+
+ forms->resize(forms->size() + 1);
+ FormFromAttributes(attrs, &forms->back());
+ if (data->secret)
+ forms->back().password_value = data->secret;
+
+ element = g_list_next(element);
+ }
+}
+
+// Schema is analagous to the fields in BasicPasswordForm.
+const GnomeKeyringPasswordSchema kGnomeSchema = {
+ GNOME_KEYRING_ITEM_GENERIC_SECRET, {
+ { "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
+ { "action_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
+ { "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
+ { "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
+ { "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
+ { "submit_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
+ { "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
+ { "ssl_valid", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
+ { "preferred", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
+ { "date_created", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
+ { "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
+ { "scheme", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
+ // This field is used to disambiguate passwords for different profiles.
+ { "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
+ { NULL }
+ }
+};
+
+// A simple class that encapsulates the boilerplate needed to tell
+// GLib how to watch a file descriptor for reading.
+class FDReadSource : public GSource {
+ public:
+ static GSource* Allocate(int fd) {
+ GSource* source = g_source_new(&functions, sizeof(FDReadSource));
+ FDReadSource* fdsource = static_cast<FDReadSource*>(source);
+ fdsource->pollfd.fd = fd;
+ fdsource->pollfd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
+ g_source_add_poll(source, &fdsource->pollfd);
+ return source;
+ }
+
+ private:
+ // We don't allocate or free FDReadSource ourselves. Instead, g_source_new()
+ // will do that for us. So, make all the constructors/destructors private.
+ FDReadSource();
+ FDReadSource(const FDReadSource&);
+ void operator=(const FDReadSource&);
+ ~FDReadSource();
+
+ static gboolean Prepare(GSource* source, gint* timeout) {
+ *timeout = -1;
+ return FALSE;
+ }
+
+ static gboolean Check(GSource* source) {
+ FDReadSource* fdsource = static_cast<FDReadSource*>(source);
+ return !!(fdsource->pollfd.revents & (G_IO_IN | G_IO_HUP | G_IO_ERR));
+ }
+
+ static gboolean Dispatch(GSource* source,
+ GSourceFunc callback,
+ gpointer user_data) {
+ if (!callback)
+ return FALSE; // No callback? Remove the source so it doesn't spin.
+ return callback(user_data);
+ }
+
+ static GSourceFuncs functions;
+
+ GPollFD pollfd;
+};
+
+GSourceFuncs FDReadSource::functions = {
+ Prepare, Check, Dispatch, NULL // .finalize
+};
+
+} // anonymous namespace
+
+namespace keyring_proxy {
+
+class MessageProcessor : public GnomeKeyringLoader {
+ public:
+ explicit MessageProcessor(FILE* output) : output_(output) {}
+
+ bool Process(const std::vector<std::string>& lines) {
+ if (lines.size() < 1) {
+ // Although an empty message is technically an error, we choose to be
+ // lenient and ignore it. It makes manual testing easier.
+ return true;
+ }
+ if (lines[0].length() < 2) {
+ // The first line must have a command character followed by an id.
+ return false;
+ }
+ int id = atoi(&lines[0].c_str()[1]);
+ if (id <= 0) {
+ // The id must be at least 1.
+ return false;
+ }
+ if (lines[0][0] == KeyringProxy::kAddLoginCommand) {
+ // Store a password. (AddLogin)
+ if (lines.size() != 16)
+ return false;
+ Request* request = AllocateRequest(id);
+ if (!request)
+ return false;
+ gnome_keyring_store_password(
+ &kGnomeSchema,
+ NULL, // Default keyring.
+ &lines[1].c_str()[1], // Display name.
+ &lines[2].c_str()[1], // Password.
+ OnOperationDone,
+ request, // data
+ NULL, // destroy_data
+ "origin_url", &lines[3].c_str()[1],
+ "action_url", &lines[4].c_str()[1],
+ "username_element", &lines[5].c_str()[1],
+ "username_value", &lines[6].c_str()[1],
+ "password_element", &lines[7].c_str()[1],
+ "submit_element", &lines[8].c_str()[1],
+ "signon_realm", &lines[9].c_str()[1],
+ "ssl_valid", lines[10] != "0",
+ "preferred", lines[11] != "0",
+ "date_created", &lines[12].c_str()[1],
+ "blacklisted_by_user", lines[13] != "0",
+ "scheme", atoi(lines[14].c_str()),
+ "application", lines[15].c_str(),
+ NULL);
+ } else if (lines[0][0] == KeyringProxy::kRemoveLoginCommand) {
+ // Delete a password. (RemoveLogin)
+ if (lines.size() != 9)
+ return false;
+ Request* request = AllocateRequest(id);
+ if (!request)
+ return false;
+ gnome_keyring_delete_password(
+ &kGnomeSchema,
+ OnOperationDone,
+ request, // data
+ NULL, // destroy_data
+ "origin_url", &lines[1].c_str()[1],
+ "action_url", &lines[2].c_str()[1],
+ "username_element", &lines[3].c_str()[1],
+ "username_value", &lines[4].c_str()[1],
+ "password_element", &lines[5].c_str()[1],
+ "submit_element", &lines[6].c_str()[1],
+ "signon_realm", &lines[7].c_str()[1],
+ "application", lines[8].c_str(),
+ NULL);
+ } else if (lines[0][0] == KeyringProxy::kAddLoginSearchCommand) {
+ // Find passwords before adding a new one. (AddLoginSearch)
+ if (lines.size() != 8)
+ return false;
+ Request* request = AllocateRequest(id);
+ if (!request)
+ return false;
+ gnome_keyring_find_itemsv(
+ GNOME_KEYRING_ITEM_GENERIC_SECRET,
+ OnFindDone,
+ request, // data
+ NULL, // destroy_data
+ "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+ &lines[1].c_str()[1],
+ "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+ &lines[2].c_str()[1],
+ "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+ &lines[3].c_str()[1],
+ "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+ &lines[4].c_str()[1],
+ "submit_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+ &lines[5].c_str()[1],
+ "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+ &lines[6].c_str()[1],
+ "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+ lines[7].c_str(),
+ NULL);
+ } else if (lines[0][0] == KeyringProxy::kUpdateLoginSearchCommand) {
+ // Find passwords to update. (UpdateLoginSearch)
+ if (lines.size() != 7)
+ return false;
+ Request* request = AllocateRequest(id);
+ if (!request)
+ return false;
+ gnome_keyring_find_itemsv(
+ GNOME_KEYRING_ITEM_GENERIC_SECRET,
+ OnFindDone,
+ request, // data
+ NULL, // destroy_data
+ "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+ &lines[1].c_str()[1],
+ "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+ &lines[2].c_str()[1],
+ "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+ &lines[3].c_str()[1],
+ "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+ &lines[4].c_str()[1],
+ "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+ &lines[5].c_str()[1],
+ "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+ lines[6].c_str(),
+ NULL);
+ } else if (lines[0][0] == KeyringProxy::kGetLoginsCommand) {
+ // Find passwords to autofill. (GetLogins)
+ if (lines.size() != 3)
+ return false;
+ Request* request = AllocateRequest(id);
+ if (!request)
+ return false;
+ gnome_keyring_find_itemsv(
+ GNOME_KEYRING_ITEM_GENERIC_SECRET,
+ OnFindDone,
+ request, // data
+ NULL, // destroy_data
+ "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+ &lines[1].c_str()[1],
+ "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+ lines[2].c_str(),
+ NULL);
+ } else if (lines[0][0] == KeyringProxy::kGetLoginsListCommand) {
+ // Find passwords to show in the password manager. (GetLoginsList)
+ if (lines.size() != 3)
+ return false;
+ Request* request = AllocateRequest(id);
+ if (!request)
+ return false;
+ gnome_keyring_find_itemsv(
+ GNOME_KEYRING_ITEM_GENERIC_SECRET,
+ OnFindDone,
+ request, // data
+ NULL, // destroy_data
+ "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32,
+ lines[1] != "0",
+ "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+ lines[2].c_str(),
+ NULL);
+ } else if (lines[0][0] == KeyringProxy::kGetAllLoginsCommand) {
+ // Find all passwords. (GetAllLogins)
+ if (lines.size() != 2)
+ return false;
+ Request* request = AllocateRequest(id);
+ if (!request)
+ return false;
+ // We need to search for something, otherwise we get no results - so
+ // we search for the fixed application string.
+ gnome_keyring_find_itemsv(
+ GNOME_KEYRING_ITEM_GENERIC_SECRET,
+ OnFindDone,
+ request, // data
+ NULL, // destroy_data
+ "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+ lines[1].c_str(),
+ NULL);
+ } else {
+ // Unexpected command line.
+ return false;
+ }
+ return true;
+ }
+
+ private:
+ struct Request {
+ int id;
+ MessageProcessor* processor;
+
+ Request(int id, MessageProcessor* processor)
+ : id(id), processor(processor) {
+ }
+ };
+
+ Request* AllocateRequest(int id) {
+ std::pair<std::map<int, Request>::iterator, bool> result =
+ requests_.insert(std::make_pair(id, Request(id, this)));
+ return result.second ? &result.first->second : NULL;
+ }
+
+ void ReplyAndFreeRequest(Request* request,
+ const std::vector<std::string>& reply) {
+ for (size_t i = 0; i < reply.size(); ++i)
+ fprintf(output_, "%s\n", reply[i].c_str());
+ fprintf(output_, "\n");
+ fflush(output_);
+ requests_.erase(request->id);
+ }
+
+ static void OnOperationDone(GnomeKeyringResult result, gpointer data) {
+ Request* request = static_cast<Request*>(data);
+ std::vector<std::string> reply;
+ char line[32];
+ snprintf(line, sizeof(line), "%d %d", request->id, result);
+ reply.push_back(line);
+ request->processor->ReplyAndFreeRequest(request, reply);
+ }
+
+ static void OnFindDone(GnomeKeyringResult result,
+ GList* list, gpointer data) {
+ Request* request = static_cast<Request*>(data);
+ std::vector<std::string> reply;
+ char line[32];
+ snprintf(line, sizeof(line), "%d %d", request->id, result);
+ reply.push_back(line);
+ // |list| will be freed after this callback returns, so convert it now.
+ std::vector<BasicPasswordForm> forms;
+ ConvertFormList(list, &forms);
+ for (size_t i = 0; i < forms.size(); ++i) {
+ reply.push_back("+" + forms[i].origin);
+ reply.push_back("+" + forms[i].action);
+ reply.push_back("+" + forms[i].username_element);
+ reply.push_back("+" + forms[i].username_value);
+ reply.push_back("+" + forms[i].password_element);
+ reply.push_back("+" + forms[i].password_value);
+ reply.push_back("+" + forms[i].submit_element);
+ reply.push_back("+" + forms[i].signon_realm);
+ reply.push_back(forms[i].ssl_valid ? "1" : "0");
+ reply.push_back(forms[i].preferred ? "1" : "0");
+ reply.push_back("+" + forms[i].date_created);
+ reply.push_back(forms[i].blacklisted_by_user ? "1" : "0");
+ snprintf(line, sizeof(line), "%u", forms[i].scheme);
+ reply.push_back(line);
+ }
+ request->processor->ReplyAndFreeRequest(request, reply);
+ }
+
+ FILE* output_;
+ std::map<int, Request> requests_;
+};
+
+KeyringProxy::KeyringProxy(int fd, FILE* output)
+ : fd_(fd), processor_(new MessageProcessor(output)) {
+ main_loop_ = g_main_loop_new(NULL, FALSE); // is_running
+ input_ = FDReadSource::Allocate(fd_);
+ g_source_set_callback(input_, DataReady, this, NULL);
+ g_source_attach(input_, NULL);
+}
+
+KeyringProxy::~KeyringProxy() {
+ g_source_remove_by_user_data(this);
+ g_source_unref(input_);
+ g_main_loop_unref(main_loop_);
+}
+
+bool KeyringProxy::Init() {
+ return LoadGnomeKeyring() && gnome_keyring_is_available();
+}
+
+void KeyringProxy::Run() {
+ g_main_loop_run(main_loop_);
+}
+
+void KeyringProxy::Stop() {
+ if (!g_main_loop_is_running(main_loop_))
+ fprintf(stderr, "%s:%d: main loop not running!\n", __FILE__, __LINE__);
+ g_main_loop_quit(main_loop_);
+}
+
+void KeyringProxy::ReadData() {
+ // We don't need to read all the available data. If there is still data left
+ // after we return, then we'll get called again because the descriptor will
+ // still be ready to read. So just read a fixed amount, for simplicity.
+ char buffer[256];
+ ssize_t available = read(fd_, buffer, sizeof(buffer));
+ if (available > 0) {
+ // Got some data. Handle it. Note that this may not be a complete message.
+ ssize_t used = 0;
+ while (used < available) {
+ ssize_t handled = reader_.HandleData(&buffer[used], available);
+ assert(handled > 0);
+ used += handled;
+ available -= handled;
+ if (reader_.is_complete()) {
+ if (!processor_->Process(reader_.lines())) {
+ // A fatal processing error occurred. Shut down the proxy.
+ Stop();
+ }
+ reader_.Reset();
+ } else {
+ assert(available == 0);
+ }
+ }
+ } else if (available == 0 || errno != EINTR) {
+ // EOF, or an unexpected error. Shut down the proxy.
+ Stop();
+ }
+}
+
+// static
+gboolean KeyringProxy::DataReady(gpointer data) {
+ static_cast<KeyringProxy*>(data)->ReadData();
+ // We always keep this source. If we run out of input, we should quit.
+ return TRUE;
+}
+
+} // namespace keyring_proxy
Property changes on: chrome/browser/password_manager/keyring_proxy/keyring_proxy.cc
___________________________________________________________________
Added: svn:eol-style
+ LF

Powered by Google App Engine
This is Rietveld 408576698