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 |