[gvfs] sftp: Handle host key / IP mismatch



commit 336fe869f06c8e792aa20170e6278b57c1c38c05
Author: Ross Lagerwall <rosslagerwall gmail com>
Date:   Fri Mar 13 23:25:21 2015 +0000

    sftp: Handle host key / IP mismatch
    
    Handle the following SSH login question by asking the user whether to
    continue or not:
    
    Warning: the ECDSA/RSA host key for 'hostname' differs from the key for the IP address '...'
    Offending key for IP in /home/username/.ssh/known_hosts:???
    Matching host key in /home/username/.ssh/known_hosts:???
    Are you sure you want to continue connecting (yes/no)? yes
    
    Based on a patch by Carlos Garcia Campos.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=545445

 daemon/gvfsbackendsftp.c |  159 +++++++++++++++++++++++++++++++++++-----------
 1 files changed, 121 insertions(+), 38 deletions(-)
---
diff --git a/daemon/gvfsbackendsftp.c b/daemon/gvfsbackendsftp.c
index 576b74e..e6c1d5b 100644
--- a/daemon/gvfsbackendsftp.c
+++ b/daemon/gvfsbackendsftp.c
@@ -828,6 +828,94 @@ get_hostname_and_fingerprint_from_line (const gchar *buffer,
   return TRUE;
 }
 
+static gboolean
+get_hostname_and_ip_address (const gchar *buffer,
+                             gchar      **hostname_out,
+                             gchar      **ip_address_out)
+{
+  char *startpos, *endpos;
+
+  /* Parse a line that looks like:
+   * Warning: the ECDSA/RSA host key for 'hostname' differs from the key for the IP address '...'
+   * First get the hostname.
+   */
+  startpos = strchr (buffer, '\'') + 1;
+  if (!startpos)
+    return FALSE;
+
+  endpos = strchr (startpos, '\'');
+  if (!endpos)
+    return FALSE;
+
+  *hostname_out = g_strndup (startpos, endpos - startpos);
+
+  /* Then get the ip address. */
+  startpos = strchr (endpos + 1, '\'') + 1;
+  if (!startpos)
+    {
+      g_free (hostname_out);
+      return FALSE;
+    }
+
+  endpos = strchr (startpos, '\'');
+  if (!endpos)
+    {
+      g_free (hostname_out);
+      return FALSE;
+    }
+
+  *ip_address_out = g_strndup (startpos, endpos - startpos);
+
+  return TRUE;
+}
+
+static gboolean
+login_answer_yes_no (GMountSource *mount_source,
+                     char *message,
+                     GOutputStream *reply_stream,
+                     GError **error)
+{
+  const char *choices[] = {_("Log In Anyway"), _("Cancel Login"), NULL};
+  const char *choice_string;
+  int choice;
+  gboolean aborted = FALSE;
+  gsize bytes_written;
+
+  if (!g_mount_source_ask_question (mount_source,
+                                    message,
+                                    choices,
+                                    &aborted,
+                                    &choice) ||
+      aborted)
+    {
+      g_set_error_literal (error,
+                           G_IO_ERROR, G_IO_ERROR_FAILED,
+                           _("Login dialog cancelled"));
+      g_free (message);
+      return FALSE;
+    }
+  g_free (message);
+
+  choice_string = (choice == 0) ? "yes" : "no";
+  if (!g_output_stream_write_all (reply_stream,
+                                  choice_string,
+                                  strlen (choice_string),
+                                  &bytes_written,
+                                  NULL, NULL) ||
+      !g_output_stream_write_all (reply_stream,
+                                  "\n", 1,
+                                  &bytes_written,
+                                  NULL, NULL))
+    {
+      g_set_error_literal (error,
+                           G_IO_ERROR, G_IO_ERROR_FAILED,
+                           _("Can't send host identity confirmation"));
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
 static const gchar *
 get_authtype_from_password_line (const char *password_line)
 {
@@ -874,11 +962,9 @@ handle_login (GVfsBackend *backend,
   struct pollfd fds[2];
   char buffer[1024];
   gsize len;
-  gboolean aborted = FALSE;
   gboolean ret_val;
   char *new_password = NULL;
   char *new_user = NULL;
-  gsize bytes_written;
   gboolean password_in_keyring = FALSE;
   const gchar *authtype = NULL;
   gchar *object = NULL;
@@ -963,6 +1049,9 @@ handle_login (GVfsBackend *backend,
           g_str_has_prefix (buffer, "Enter Kerberos password") ||
           g_str_has_prefix (buffer, "Enter passphrase for key"))
         {
+          gboolean aborted = FALSE;
+          gsize bytes_written;
+
          authtype = get_authtype_from_password_line (buffer);
          object = get_object_from_password_line (buffer);
 
@@ -1103,11 +1192,8 @@ handle_login (GVfsBackend *backend,
       else if (g_str_has_prefix (buffer, "The authenticity of host '") ||
                strstr (buffer, "Key fingerprint:") != NULL)
         {
-         const gchar *choices[] = {_("Log In Anyway"), _("Cancel Login"), NULL};
-         const gchar *choice_string;
          gchar *hostname = NULL;
          gchar *fingerprint = NULL;
-         gint choice;
          gchar *message;
 
           DEBUG ("handle_login #%d - confirming authenticity of host...\n", i);
@@ -1124,40 +1210,37 @@ handle_login (GVfsBackend *backend,
          g_free (hostname);
          g_free (fingerprint);
 
-         if (!g_mount_source_ask_question (mount_source,
-                                           message,
-                                           choices,
-                                           &aborted,
-                                           &choice) || 
-             aborted)
-           {
-             g_set_error_literal (error,
-                                  G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
-                                  _("Login dialog cancelled"));
-             g_free (message);
-             ret_val = FALSE;
-             break;
-           }
-         g_free (message); 
-
-         choice_string = (choice == 0) ? "yes" : "no";
-         if (!g_output_stream_write_all (reply_stream,
-                                         choice_string,
-                                         strlen (choice_string),
-                                         &bytes_written,
-                                         NULL, NULL) ||
-             !g_output_stream_write_all (reply_stream,
-                                         "\n", 1,
-                                         &bytes_written,
-                                         NULL, NULL))
-           {
-             g_set_error_literal (error,
-                                  G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
-                                  _("Can't send host identity confirmation"));
-             ret_val = FALSE;
-             break;
-           }
+          if (!login_answer_yes_no (mount_source, message, reply_stream, error))
+            {
+              ret_val = FALSE;
+              break;
+            }
        }
+      else if (strstr (buffer, "differs from the key for the IP address"))
+        {
+          gchar *hostname = NULL;
+          gchar *ip_address = NULL;
+          gchar *message;
+
+          DEBUG ("handle_login #%d - host key / IP mismatch ...\n", i);
+
+          get_hostname_and_ip_address (buffer, &hostname, &ip_address);
+
+          message = g_strdup_printf (_("The host key for ā€œ%sā€ differs from the key for the IP address ā€œ%sā€\n"
+                                       "If you want to be absolutely sure it is safe to continue, "
+                                       "contact the system administrator."),
+                                     hostname ? hostname : op_backend->host,
+                                     ip_address ? ip_address : "???");
+
+          g_free (hostname);
+          g_free (ip_address);
+
+          if (!login_answer_yes_no (mount_source, message, reply_stream, error))
+            {
+              ret_val = FALSE;
+              break;
+            }
+        }
     }
   
   if (ret_val)


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