summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Catanzaro <mcatanzaro@igalia.com>2018-06-02 21:54:55 -0500
committerMichael Catanzaro <mcatanzaro@igalia.com>2018-06-02 22:10:18 -0500
commit0f88a4d45de1ec1f6974b9e487d660710afa22f9 (patch)
treebe5d3806e954f5a2a1068b43aeea6b22219ad7ba
parent006e94c85bb13665536da2e9806476c0d21dc119 (diff)
downloadepiphany-wip/#11.tar.gz
Fix password manager crash on chase.comwip/#11
We need to be way more careful when converting URIs to security origins. This can fail. In this case, Chase used a valid URI javascript:void(0); for its form action, which of course does not have any hostname and therefore cannot be converted to a security origin. This is a legitimate technique in order to prevent the web browser from actually submitting the form. That doesn't even matter, because this is untrusted HTML and the website can put whatever bogus data it wants there, so we'd better be prepared to handle it. The solution is to always use the page's origin for the target origin if the form action cannot be converted to an origin. This is correct because the target origin is just used as a heuristic when detecting and filling the forms. The same problem could, in theory, exist with the actual origin of the page containing a form. It's not guaranteed that every page will have a sane URI, e.g. when pages are opened by JavaScript. So, although I don't have a test case to trigger this, we ought to be careful about this, too. In this case, there's nothing really we can do, so we should fail to create the EphyEmbedFormAuth and abort whatever we were trying to do with it. Finally, move computation of the origin and target_origin into ephy_embed_form_auth_new() in order to simplify the implementation of these EphyWebExtension functions and reduce the chances of future errors. Fixes #11.
-rw-r--r--embed/web-extension/ephy-embed-form-auth.c35
-rw-r--r--embed/web-extension/ephy-embed-form-auth.h4
-rw-r--r--embed/web-extension/ephy-web-extension.c160
3 files changed, 73 insertions, 126 deletions
diff --git a/embed/web-extension/ephy-embed-form-auth.c b/embed/web-extension/ephy-embed-form-auth.c
index dcd02a4f1..31a29ab68 100644
--- a/embed/web-extension/ephy-embed-form-auth.c
+++ b/embed/web-extension/ephy-embed-form-auth.c
@@ -21,11 +21,13 @@
#include <config.h>
#include "ephy-embed-form-auth.h"
+#include "ephy-uri-helpers.h"
+
struct _EphyEmbedFormAuth {
GObject parent_instance;
guint64 page_id;
- SoupURI *uri;
+ char *origin;
char *target_origin;
WebKitDOMNode *username_node;
WebKitDOMNode *password_node;
@@ -41,10 +43,9 @@ ephy_embed_form_auth_finalize (GObject *object)
{
EphyEmbedFormAuth *form_auth = EPHY_EMBED_FORM_AUTH (object);
- if (form_auth->uri)
- soup_uri_free (form_auth->uri);
g_free (form_auth->username);
g_free (form_auth->password);
+ g_free (form_auth->origin);
g_free (form_auth->target_origin);
g_clear_object (&form_auth->username_node);
g_clear_object (&form_auth->password_node);
@@ -67,30 +68,46 @@ ephy_embed_form_auth_class_init (EphyEmbedFormAuthClass *klass)
EphyEmbedFormAuth *
ephy_embed_form_auth_new (WebKitWebPage *web_page,
- const char *target_origin,
+ const char *form_action,
WebKitDOMNode *username_node,
WebKitDOMNode *password_node,
const char *username,
const char *password)
{
EphyEmbedFormAuth *form_auth;
+ char *origin;
g_assert (WEBKIT_DOM_IS_NODE (password_node));
+ origin = ephy_uri_to_security_origin (webkit_web_page_get_uri (web_page));
+ if (!origin)
+ return NULL;
+
form_auth = EPHY_EMBED_FORM_AUTH (g_object_new (EPHY_TYPE_EMBED_FORM_AUTH, NULL));
form_auth->page_id = webkit_web_page_get_id (web_page);
- form_auth->uri = soup_uri_new (webkit_web_page_get_uri (web_page));
- form_auth->target_origin = g_strdup (target_origin);
+ form_auth->origin = origin;
form_auth->username_node = username_node;
form_auth->password_node = password_node;
form_auth->username = g_strdup (username);
form_auth->password = g_strdup (password);
+ if (form_action)
+ form_auth->target_origin = ephy_uri_to_security_origin (form_action);
+
+ if (!form_auth->target_origin)
+ form_auth->target_origin = g_strdup (form_auth->origin);
+
return form_auth;
}
const char *
+ephy_embed_form_auth_get_origin (EphyEmbedFormAuth *form_auth)
+{
+ return form_auth->origin;
+}
+
+const char *
ephy_embed_form_auth_get_target_origin (EphyEmbedFormAuth *form_auth)
{
return form_auth->target_origin;
@@ -108,12 +125,6 @@ ephy_embed_form_auth_get_password_node (EphyEmbedFormAuth *form_auth)
return form_auth->password_node;
}
-SoupURI *
-ephy_embed_form_auth_get_uri (EphyEmbedFormAuth *form_auth)
-{
- return form_auth->uri;
-}
-
guint64
ephy_embed_form_auth_get_page_id (EphyEmbedFormAuth *form_auth)
{
diff --git a/embed/web-extension/ephy-embed-form-auth.h b/embed/web-extension/ephy-embed-form-auth.h
index 002bd2b60..a7c0668f0 100644
--- a/embed/web-extension/ephy-embed-form-auth.h
+++ b/embed/web-extension/ephy-embed-form-auth.h
@@ -31,15 +31,15 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (EphyEmbedFormAuth, ephy_embed_form_auth, EPHY, EMBED_FORM_AUTH, GObject)
EphyEmbedFormAuth *ephy_embed_form_auth_new (WebKitWebPage *web_page,
- const char *target_origin,
+ const char *form_action,
WebKitDOMNode *username_node,
WebKitDOMNode *password_node,
const char *username,
const char *password);
WebKitDOMNode *ephy_embed_form_auth_get_username_node (EphyEmbedFormAuth *form_auth);
WebKitDOMNode *ephy_embed_form_auth_get_password_node (EphyEmbedFormAuth *form_auth);
+const char *ephy_embed_form_auth_get_origin (EphyEmbedFormAuth *form_auth);
const char *ephy_embed_form_auth_get_target_origin (EphyEmbedFormAuth *form_auth);
-SoupURI *ephy_embed_form_auth_get_uri (EphyEmbedFormAuth *form_auth);
guint64 ephy_embed_form_auth_get_page_id (EphyEmbedFormAuth *form_auth);
const char *ephy_embed_form_auth_get_username (EphyEmbedFormAuth *form_auth);
const char *ephy_embed_form_auth_get_password (EphyEmbedFormAuth *form_auth);
diff --git a/embed/web-extension/ephy-web-extension.c b/embed/web-extension/ephy-web-extension.c
index 359d9427f..6b51546fd 100644
--- a/embed/web-extension/ephy-web-extension.c
+++ b/embed/web-extension/ephy-web-extension.c
@@ -224,14 +224,10 @@ form_auth_data_save_request_new_id (void)
static void
store_password (EphyEmbedFormAuth *form_auth)
{
- SoupURI *uri;
- char *uri_str;
- char *origin;
char *username_field_name = NULL;
char *password_field_name = NULL;
const char *username = NULL;
const char *password = NULL;
- const char *target_origin;
gboolean password_updated;
WebKitDOMNode *username_node;
EphyWebExtension *extension = ephy_web_extension_get ();
@@ -250,22 +246,16 @@ store_password (EphyEmbedFormAuth *form_auth)
NULL);
password = ephy_embed_form_auth_get_password (form_auth);
- uri = ephy_embed_form_auth_get_uri (form_auth);
- uri_str = soup_uri_to_string (uri, FALSE);
- origin = ephy_uri_to_security_origin (uri_str);
- target_origin = ephy_embed_form_auth_get_target_origin (form_auth);
password_updated = ephy_embed_form_auth_get_password_updated (form_auth);
ephy_password_manager_save (extension->password_manager,
- origin,
- target_origin,
+ ephy_embed_form_auth_get_origin (form_auth),
+ ephy_embed_form_auth_get_target_origin (form_auth),
username,
password,
username_field_name,
password_field_name,
!password_updated);
- g_free (uri_str);
- g_free (origin);
g_free (username_field_name);
g_free (password_field_name);
}
@@ -274,28 +264,16 @@ static void
request_decision_on_storing (EphyEmbedFormAuth *form_auth)
{
char *username_field_value = NULL;
+ char *message = NULL;
guint request_id;
- SoupURI *uri;
WebKitDOMNode *username_node;
WebKitDOMDOMWindow *dom_window = NULL;
GVariant *variant;
- char *message = NULL;
- char *uri_string = NULL;
- char *origin = NULL;
dom_window = webkit_dom_document_get_default_view (ephy_embed_form_auth_get_owner_document (form_auth));
if (dom_window == NULL)
goto out;
- uri = ephy_embed_form_auth_get_uri (form_auth);
- if (uri == NULL)
- goto out;
-
- uri_string = soup_uri_to_string (uri, FALSE);
- origin = ephy_uri_to_security_origin (uri_string);
- if (origin == NULL)
- goto out;
-
request_id = form_auth_data_save_request_new_id ();
username_node = ephy_embed_form_auth_get_username_node (form_auth);
@@ -305,7 +283,7 @@ request_decision_on_storing (EphyEmbedFormAuth *form_auth)
variant = g_variant_new ("(utss)",
request_id,
ephy_embed_form_auth_get_page_id (form_auth),
- origin,
+ ephy_embed_form_auth_get_origin (form_auth),
username_field_value ? username_field_value : "");
g_free (username_field_value);
@@ -327,10 +305,6 @@ out:
g_object_unref (form_auth);
if (message != NULL)
g_free (message);
- if (uri_string != NULL)
- g_free (uri_string);
- if (origin != NULL)
- g_free (origin);
}
static void
@@ -340,26 +314,15 @@ should_store_cb (GList *records,
EphyEmbedFormAuth *form_auth = EPHY_EMBED_FORM_AUTH (user_data);
EphyWebExtension *web_extension;
EphyPermission permission;
- SoupURI *uri;
- char *uri_string;
const char *password;
- char *origin = NULL;
if (!g_settings_get_boolean (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_REMEMBER_PASSWORDS))
return;
- uri = ephy_embed_form_auth_get_uri (form_auth);
- uri_string = soup_uri_to_string (uri, FALSE);
- if (uri_string == NULL)
- return;
- origin = ephy_uri_to_security_origin (uri_string);
- if (origin == NULL)
- goto out;
-
web_extension = ephy_web_extension_get ();
permission = ephy_permissions_manager_get_permission (web_extension->permissions_manager,
EPHY_PERMISSION_TYPE_SAVE_PASSWORD,
- origin);
+ ephy_embed_form_auth_get_origin (form_auth));
if (permission == EPHY_PERMISSION_DENY) {
LOG ("User/password storage permission previously denied. Not asking about storing.");
@@ -402,9 +365,6 @@ should_store_cb (GList *records,
}
out:
- if (origin != NULL)
- g_free (origin);
- g_free (uri_string);
g_object_unref (form_auth);
g_list_free_full (records, g_object_unref);
}
@@ -415,17 +375,13 @@ handle_form_submission (WebKitWebPage *web_page,
{
EphyWebExtension *extension = ephy_web_extension_get ();
EphyEmbedFormAuth *form_auth;
- SoupURI *uri;
- char *target_origin;
WebKitDOMNode *username_node = NULL;
WebKitDOMNode *password_node = NULL;
char *username_field_name = NULL;
char *username_field_value = NULL;
char *password_field_name = NULL;
char *password_field_value = NULL;
- char *uri_str;
- char *origin;
- char *form_action;
+ char *form_action = NULL;
if (!extension->password_manager)
return;
@@ -438,52 +394,49 @@ handle_form_submission (WebKitWebPage *web_page,
if (username_node) {
g_object_get (username_node,
+ "name", &username_field_name,
+ NULL);
+ g_object_get (username_node,
"value", &username_field_value,
NULL);
}
+
+ g_object_get (password_node,
+ "name", &password_field_name,
+ NULL);
g_object_get (password_node,
"value", &password_field_value,
NULL);
form_action = webkit_dom_html_form_element_get_action (dom_form);
- if (form_action == NULL)
- form_action = g_strdup (webkit_web_page_get_uri (web_page));
- target_origin = ephy_uri_to_security_origin (form_action);
/* EphyEmbedFormAuth takes ownership of the nodes */
form_auth = ephy_embed_form_auth_new (web_page,
- target_origin,
+ form_action,
username_node,
password_node,
username_field_value,
password_field_value);
- uri = ephy_embed_form_auth_get_uri (form_auth);
- soup_uri_set_query (uri, NULL);
-
- if (username_node)
- g_object_get (username_node, "name", &username_field_name, NULL);
- g_object_get (password_node, "name", &password_field_name, NULL);
- uri_str = soup_uri_to_string (uri, FALSE);
- origin = ephy_uri_to_security_origin (uri_str);
-
- ephy_password_manager_query (extension->password_manager,
- NULL,
- origin,
- target_origin,
- username_field_value,
- username_field_name,
- password_field_name,
- should_store_cb,
- form_auth);
+ if (form_auth) {
+ ephy_password_manager_query (extension->password_manager,
+ NULL,
+ ephy_embed_form_auth_get_origin (form_auth),
+ ephy_embed_form_auth_get_target_origin (form_auth),
+ username_field_value,
+ username_field_name,
+ password_field_name,
+ should_store_cb,
+ form_auth);
+ } else {
+ LOG ("URI %s cannot be used to form a valid security origin, not storing password",
+ webkit_web_page_get_uri (web_page));
+ }
g_free (form_action);
- g_free (target_origin);
g_free (username_field_name);
g_free (username_field_value);
g_free (password_field_name);
g_free (password_field_value);
- g_free (uri_str);
- g_free (origin);
}
static void
@@ -545,21 +498,13 @@ fill_form_cb (GList *records,
static void
pre_fill_form (EphyEmbedFormAuth *form_auth)
{
- SoupURI *uri;
- char *uri_str;
- char *origin;
char *username = NULL;
char *username_field_name = NULL;
char *password_field_name = NULL;
- const char *target_origin;
WebKitDOMNode *username_node;
WebKitDOMNode *password_node;
EphyWebExtension *extension;
- uri = ephy_embed_form_auth_get_uri (form_auth);
- if (!uri)
- return;
-
extension = ephy_web_extension_get ();
if (!extension->password_manager)
return;
@@ -569,30 +514,24 @@ pre_fill_form (EphyEmbedFormAuth *form_auth)
g_object_get (username_node, "name", &username_field_name, NULL);
g_object_get (username_node, "value", &username, NULL);
}
+
password_node = ephy_embed_form_auth_get_password_node (form_auth);
- if (password_node)
- g_object_get (password_node, "name", &password_field_name, NULL);
+ g_object_get (password_node, "name", &password_field_name, NULL);
/* The username node is empty, so pre-fill with the default. */
if (!g_strcmp0 (username, ""))
g_clear_pointer (&username, g_free);
- uri_str = soup_uri_to_string (uri, FALSE);
- origin = ephy_uri_to_security_origin (uri_str);
- target_origin = ephy_embed_form_auth_get_target_origin (form_auth);
-
ephy_password_manager_query (extension->password_manager,
NULL,
- origin,
- target_origin,
+ ephy_embed_form_auth_get_origin (form_auth),
+ ephy_embed_form_auth_get_target_origin (form_auth),
username,
username_field_name,
password_field_name,
fill_form_cb,
form_auth);
- g_free (uri_str);
- g_free (origin);
g_free (username);
g_free (username_field_name);
g_free (password_field_name);
@@ -1128,31 +1067,30 @@ web_page_form_controls_associated (WebKitWebPage *web_page,
AUTH_CACHE_AUTOFILL)) {
EphyEmbedFormAuth *form_auth;
GList *cached_users;
- const char *uri;
- char *origin;
char *form_action;
- char *target_origin;
-
- uri = webkit_web_page_get_uri (web_page);
form_action = webkit_dom_html_form_element_get_action (form);
- if (form_action == NULL)
- form_action = g_strdup (uri);
- target_origin = ephy_uri_to_security_origin (form_action);
-
- LOG ("Hooking and pre-filling a form");
/* EphyEmbedFormAuth takes ownership of the nodes */
form_auth = ephy_embed_form_auth_new (web_page,
- target_origin,
+ form_action,
username_node,
password_node,
NULL,
NULL);
+ g_free (form_action);
+
+ if (!form_auth) {
+ LOG ("URI %s cannot be used to form a valid security origin, not hooking form",
+ webkit_web_page_get_uri (web_page));
+ continue;
+ }
+
+ LOG ("Hooking and pre-filling a form");
/* Plug in the user autocomplete */
- origin = ephy_uri_to_security_origin (uri);
- cached_users = ephy_password_manager_get_cached_users (extension->password_manager, origin);
+ cached_users = ephy_password_manager_get_cached_users (extension->password_manager,
+ ephy_embed_form_auth_get_origin (form_auth));
if (cached_users && cached_users->next && username_node) {
LOG ("More than 1 password saved, hooking menu for choosing which on focus");
@@ -1174,17 +1112,15 @@ web_page_form_controls_associated (WebKitWebPage *web_page,
webkit_dom_event_target_add_event_listener (WEBKIT_DOM_EVENT_TARGET (username_node), "blur",
G_CALLBACK (username_node_changed_cb), FALSE,
web_page);
- } else
+ } else {
LOG ("No items or a single item in cached_users, not hooking menu for choosing.");
+ }
pre_fill_form (form_auth);
-
- g_free (origin);
- g_free (form_action);
- g_free (target_origin);
g_object_weak_ref (G_OBJECT (form), form_destroyed_cb, form_auth);
- } else
+ } else {
LOG ("No pre-fillable/hookable form found");
+ }
}
}