Move password management out of web process
This is done to move all libsecret usage out of the webprocess for sandboxing. This also makes it more robust against vulernabilities such as Spectre moving it out of process.
... | ... | @@ -25,12 +25,9 @@ |
#include "ephy-dbus-util.h" | ||
#include "ephy-debug.h" | ||
#include "ephy-file-helpers.h" | ||
#include "ephy-password-manager.h" | ||
#include "ephy-permissions-manager.h" | ||
#include "ephy-prefs.h" | ||
#include "ephy-settings.h" | ||
#include "ephy-sync-service.h" | ||
#include "ephy-sync-utils.h" | ||
#include "ephy-uri-helpers.h" | ||
#include "ephy-uri-tester.h" | ||
#include "ephy-web-overview-model.h" | ||
... | ... | @@ -53,14 +50,14 @@ struct _EphyWebExtension { |
GDBusConnection *dbus_connection; | ||
GArray *page_created_signals_pending; | ||
EphySyncService *sync_service; | ||
EphyPasswordManager *password_manager; | ||
GHashTable *form_auth_data_save_requests; | ||
EphyWebOverviewModel *overview_model; | ||
EphyPermissionsManager *permissions_manager; | ||
EphyUriTester *uri_tester; | ||
WebKitScriptWorld *script_world; | ||
WebKitWebPage *web_page; | ||
gboolean is_private_profile; | ||
}; | ||
static const char introspection_xml[] = | ||
... | ... | @@ -69,10 +66,6 @@ static const char introspection_xml[] = |
" <signal name='PageCreated'>" | ||
" <arg type='t' name='page_id' direction='out'/>" | ||
" </signal>" | ||
" <method name='FormAuthDataSaveConfirmationResponse'>" | ||
" <arg type='u' name='request_id' direction='in'/>" | ||
" <arg type='b' name='should_store' direction='in'/>" | ||
" </method>" | ||
" <method name='HistorySetURLs'>" | ||
" <arg type='a(ss)' name='urls' direction='in'/>" | ||
" </method>" | ||
... | ... | @@ -91,6 +84,15 @@ static const char introspection_xml[] = |
" <arg type='s' name='host' direction='in'/>" | ||
" </method>" | ||
" <method name='HistoryClear'/>" | ||
" <method name='PasswordQueryResponse'>" | ||
" <arg type='s' name='username' direction='in'/>" | ||
" <arg type='s' name='password' direction='in'/>" | ||
" <arg type='i' name='id' direction='in'/>" | ||
" </method>" | ||
" <method name='PasswordQueryUsernamesResponse'>" | ||
" <arg type='as' name='users' direction='in'/>" | ||
" <arg type='i' name='id' direction='in'/>" | ||
" </method>" | ||
" </interface>" | ||
"</node>"; | ||
... | ... | @@ -187,106 +189,6 @@ web_page_send_request (WebKitWebPage *web_page, |
return FALSE; | ||
} | ||
typedef struct { | ||
char *origin; | ||
char *target_origin; | ||
char *username; | ||
char *password; | ||
char *username_field_name; | ||
char *password_field_name; | ||
gboolean is_new; | ||
} SaveAuthRequest; | ||
static SaveAuthRequest * | ||
save_auth_request_new (const char *origin, | ||
const char *target_origin, | ||
const char *username, | ||
const char *password, | ||
const char *username_field_name, | ||
const char *password_field_name, | ||
gboolean is_new) | ||
{ | ||
SaveAuthRequest *request; | ||
request = g_new (SaveAuthRequest, 1); | ||
request->origin = g_strdup (origin); | ||
request->target_origin = g_strdup (target_origin); | ||
request->username = g_strdup (username); | ||
request->password = g_strdup (password); | ||
request->username_field_name = g_strdup (username_field_name); | ||
request->password_field_name = g_strdup (password_field_name); | ||
request->is_new = is_new; | ||
return request; | ||
} | ||
static void | ||
save_auth_request_free (SaveAuthRequest *request) | ||
{ | ||
g_free (request->origin); | ||
g_free (request->target_origin); | ||
g_free (request->username); | ||
g_free (request->password); | ||
g_free (request->username_field_name); | ||
g_free (request->password_field_name); | ||
g_free (request); | ||
} | ||
static GHashTable * | ||
ephy_web_extension_get_form_auth_data_save_requests (EphyWebExtension *extension) | ||
{ | ||
if (!extension->form_auth_data_save_requests) { | ||
extension->form_auth_data_save_requests = | ||
g_hash_table_new_full (g_direct_hash, | ||
g_direct_equal, | ||
NULL, | ||
(GDestroyNotify)save_auth_request_free); | ||
} | ||
return extension->form_auth_data_save_requests; | ||
} | ||
static guint | ||
form_auth_data_save_request_new_id (void) | ||
{ | ||
static guint form_auth_data_save_request_id = 0; | ||
return ++form_auth_data_save_request_id; | ||
} | ||
static char * | ||
save_auth_requester (guint64 page_id, | ||
const char *origin, | ||
const char *target_origin, | ||
const char *username, | ||
const char *password, | ||
const char *username_field_name, | ||
const char *password_field_name, | ||
gboolean is_new) | ||
{ | ||
GVariant *variant; | ||
guint request_id; | ||
char *retval; | ||
request_id = form_auth_data_save_request_new_id (); | ||
variant = g_variant_new ("(utss)", | ||
request_id, | ||
page_id, | ||
origin, | ||
username ? username : ""); | ||
retval = g_variant_print (variant, FALSE); | ||
g_variant_unref (variant); | ||
g_hash_table_insert (ephy_web_extension_get_form_auth_data_save_requests (ephy_web_extension_get ()), | ||
GINT_TO_POINTER (request_id), save_auth_request_new (origin, target_origin, username, | ||
password, username_field_name, | ||
password_field_name, is_new)); | ||
return retval; | ||
} | ||
static void | ||
web_page_will_submit_form (WebKitWebPage *web_page, | ||
WebKitDOMHTMLFormElement *dom_form, | ||
... | ... | @@ -301,7 +203,6 @@ web_page_will_submit_form (WebKitWebPage *web_page, |
JSCContext *js_context; | ||
JSCValue *js_ephy; | ||
JSCValue *js_form; | ||
JSCValue *js_requester; | ||
JSCValue *js_result; | ||
form_submit_handled = | ||
... | ... | @@ -318,21 +219,12 @@ web_page_will_submit_form (WebKitWebPage *web_page, |
js_context = webkit_frame_get_js_context_for_script_world (source_frame, extension->script_world); | ||
js_ephy = jsc_context_get_value (js_context, "Ephy"); | ||
js_form = webkit_frame_get_js_value_for_dom_object_in_script_world (frame, WEBKIT_DOM_OBJECT (dom_form), extension->script_world); | ||
js_requester = jsc_value_new_function (js_context, | ||
"saveAuthRequester", | ||
G_CALLBACK (save_auth_requester), NULL, NULL, | ||
G_TYPE_STRING, 8, | ||
G_TYPE_UINT64, G_TYPE_STRING, G_TYPE_STRING, | ||
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, | ||
G_TYPE_STRING, G_TYPE_BOOLEAN); | ||
js_result = jsc_value_object_invoke_method (js_ephy, | ||
"handleFormSubmission", | ||
G_TYPE_UINT64, webkit_web_page_get_id (web_page), | ||
JSC_TYPE_VALUE, js_form, | ||
JSC_TYPE_VALUE, js_requester, | ||
G_TYPE_NONE); | ||
g_object_unref (js_result); | ||
g_object_unref (js_requester); | ||
g_object_unref (js_form); | ||
g_object_unref (js_ephy); | ||
g_object_unref (js_context); | ||
... | ... | @@ -382,7 +274,7 @@ web_page_form_controls_associated (WebKitWebPage *web_page, |
G_CALLBACK (sensitive_form_message_serializer), NULL, NULL, | ||
G_TYPE_STRING, 2, | ||
G_TYPE_UINT64, G_TYPE_BOOLEAN); | ||
remember_passwords = extension->password_manager && | ||
remember_passwords = !extension->is_private_profile && | ||
g_settings_get_boolean (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_REMEMBER_PASSWORDS); | ||
js_result = jsc_value_object_invoke_method (js_ephy, | ||
"formControlsAssociated", | ||
... | ... | @@ -496,6 +388,7 @@ ephy_web_extension_page_created_cb (EphyWebExtension *extension, |
g_object_unref (js_context); | ||
page_id = webkit_web_page_get_id (web_page); | ||
extension->web_page = web_page; | ||
|
||
if (extension->dbus_connection) | ||
ephy_web_extension_emit_page_created (extension, page_id); | ||
else | ||
... | ... | @@ -515,6 +408,18 @@ ephy_web_extension_page_created_cb (EphyWebExtension *extension, |
extension); | ||
} | ||
static JSCValue * | ||
get_password_manager (EphyWebExtension *self) | ||
{ | ||
g_assert (self->web_page); | ||
WebKitFrame *frame = webkit_web_page_get_main_frame (self->web_page); | ||
JSCContext *context = webkit_frame_get_js_context_for_script_world (frame, | ||
self->script_world); | ||
g_autoptr(JSCValue) ephy = jsc_context_get_value (context, "Ephy"); | ||
return jsc_value_object_get_property (ephy, "passwordManager"); | ||
} | ||
static void | ||
handle_method_call (GDBusConnection *connection, | ||
const char *sender, | ||
... | ... | @@ -530,32 +435,7 @@ handle_method_call (GDBusConnection *connection, |
if (g_strcmp0 (interface_name, EPHY_WEB_EXTENSION_INTERFACE) != 0) | ||
return; | ||
if (g_strcmp0 (method_name, "FormAuthDataSaveConfirmationResponse") == 0) { | ||
SaveAuthRequest *request; | ||
guint request_id; | ||
gboolean should_store; | ||
GHashTable *requests; | ||
requests = ephy_web_extension_get_form_auth_data_save_requests (extension); | ||
g_variant_get (parameters, "(ub)", &request_id, &should_store); | ||
request = g_hash_table_lookup (requests, GINT_TO_POINTER (request_id)); | ||
if (!request) | ||
return; | ||
if (should_store) { | ||
ephy_password_manager_save (extension->password_manager, | ||
request->origin, | ||
request->target_origin, | ||
request->username, | ||
request->password, | ||
request->username_field_name, | ||
request->password_field_name, | ||
request->is_new); | ||
} | ||
g_hash_table_remove (requests, GINT_TO_POINTER (request_id)); | ||
} else if (g_strcmp0 (method_name, "HistorySetURLs") == 0) { | ||
if (g_strcmp0 (method_name, "HistorySetURLs") == 0) { | ||
if (extension->overview_model) { | ||
GVariantIter iter; | ||
GVariant *array; | ||
... | ... | @@ -611,6 +491,29 @@ handle_method_call (GDBusConnection *connection, |
if (extension->overview_model) | ||
ephy_web_overview_model_clear (extension->overview_model); | ||
g_dbus_method_invocation_return_value (invocation, NULL); | ||
} else if (g_strcmp0 (method_name, "PasswordQueryUsernamesResponse") == 0) { | ||
g_autofree const char **users; | ||
g_autoptr(JSCValue) ret; | ||
gint32 id; | ||
users = g_variant_get_strv (g_variant_get_child_value (parameters, 0), NULL); | ||
g_variant_get_child (parameters, 1, "i", &id); | ||
g_autoptr(JSCValue) password_manager = get_password_manager (extension); | ||
ret = jsc_value_object_invoke_method (password_manager, "_onQueryUsernamesResponse", | ||
G_TYPE_STRV, users, G_TYPE_INT, id, G_TYPE_NONE); | ||