[epiphany/gnome-3-28] Fix password manager crash on chase.com



commit 2f575a062258e2ac6dfc35b5136258e43e36e089
Author: Michael Catanzaro <mcatanzaro igalia com>
Date:   Sat Jun 2 21:54:55 2018 -0500

    Fix password manager crash on chase.com
    
    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 on the gnome-3-28 branch. The code is completely different in
    master and will need to be fixed separately there.

 embed/web-extension/ephy-embed-form-auth.c |  35 +++++---
 embed/web-extension/ephy-embed-form-auth.h |   4 +-
 embed/web-extension/ephy-web-extension.c   | 124 +++++++++++------------------
 3 files changed, 70 insertions(+), 93 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,29 +68,45 @@ 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)
 {
@@ -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..258ae4ad5 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);
 }
@@ -415,17 +405,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;
@@ -437,53 +423,50 @@ handle_form_submission (WebKitWebPage            *web_page,
     return;
 
   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 +528,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 +544,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 +1097,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 +1142,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");
+    }
   }
 }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]