[Xfce4-commits] <thunar:master> Add secure desktop file launching (bug #5012).

Nick Schermer noreply at xfce.org
Wed Oct 3 21:40:01 CEST 2012


Updating branch refs/heads/master
         to 1ec8ff89ec5a3314fcd6a57f1475654ddecc9875 (commit)
       from 5c2bf770b6ad5a4d7259a60e692483d4a1ea087d (commit)

commit 1ec8ff89ec5a3314fcd6a57f1475654ddecc9875
Author: Nick Schermer <nick at xfce.org>
Date:   Wed Oct 3 21:38:32 2012 +0200

    Add secure desktop file launching (bug #5012).
    
    Only allow direct execution of desktop files in an XDG
    directory and if they are executable.

 thunar/thunar-chooser-dialog.c      |    1 +
 thunar/thunar-dialogs.c             |  102 ++++++++++++++++
 thunar/thunar-dialogs.h             |   45 ++++----
 thunar/thunar-dnd.c                 |    2 +-
 thunar/thunar-file.c                |  217 ++++++++++++++++++++--------------
 thunar/thunar-file.h                |    5 +-
 thunar/thunar-launcher.c            |   12 +--
 thunar/thunar-permissions-chooser.c |    1 +
 thunar/thunar-properties-dialog.c   |    2 +-
 9 files changed, 263 insertions(+), 124 deletions(-)

diff --git a/thunar/thunar-chooser-dialog.c b/thunar/thunar-chooser-dialog.c
index 7052d4f..1c778f2 100644
--- a/thunar/thunar-chooser-dialog.c
+++ b/thunar/thunar-chooser-dialog.c
@@ -483,6 +483,7 @@ thunar_chooser_dialog_response (GtkDialog *widget,
       /* create launch context */
       context = gdk_app_launch_context_new ();
       gdk_app_launch_context_set_screen (context, gtk_widget_get_screen (GTK_WIDGET (dialog)));
+      gdk_app_launch_context_set_timestamp (context, gtk_get_current_event_time ());
 
       /* create fake file list */
       list.data = thunar_file_get_file (dialog->file); list.next = list.prev = NULL;
diff --git a/thunar/thunar-dialogs.c b/thunar/thunar-dialogs.c
index 0021d2d..296bb0c 100644
--- a/thunar/thunar-dialogs.c
+++ b/thunar/thunar-dialogs.c
@@ -731,3 +731,105 @@ thunar_dialogs_show_job_error (GtkWindow *parent,
   g_string_free (primary, TRUE);
 }
 
+
+
+gboolean
+thunar_dialogs_show_insecure_program (gpointer     parent,
+                                      const gchar *primary,
+                                      ThunarFile  *file,
+                                      const gchar *command)
+{
+  GdkScreen      *screen;
+  GtkWindow      *window;
+  gint            response;
+  GtkWidget      *dialog;
+  GString        *secondary;
+  ThunarFileMode  old_mode;
+  ThunarFileMode  new_mode;
+  GFileInfo      *info;
+  GError         *err = NULL;
+
+  _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
+  _thunar_return_val_if_fail (g_utf8_validate (command, -1, NULL), FALSE);
+
+  /* parse the parent window and screen */
+  screen = thunar_util_parse_parent (parent, &window);
+
+  /* secondary text */
+  secondary = g_string_new (NULL);
+  g_string_append_printf (secondary, _("The desktop file \"%s\" is in an insecure location "
+                                       "and not marked as executable. If you do not trust "
+                                       "this program, click Cancel."),
+                                       thunar_file_get_display_name (file));
+  g_string_append (secondary, "\n\n");
+  if (exo_str_looks_like_an_uri (command))
+    g_string_append_printf (secondary, G_KEY_FILE_DESKTOP_KEY_URL"=%s", command);
+  else
+    g_string_append_printf (secondary, G_KEY_FILE_DESKTOP_KEY_EXEC"=%s", command);
+
+  /* allocate and display the error message dialog */
+  dialog = gtk_message_dialog_new (window,
+                                   GTK_DIALOG_MODAL |
+                                   GTK_DIALOG_DESTROY_WITH_PARENT,
+                                   GTK_MESSAGE_WARNING,
+                                   GTK_BUTTONS_NONE,
+                                   "%s", primary);
+  gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Launch Anyway"), GTK_RESPONSE_OK);
+  if (thunar_file_is_chmodable (file))
+    gtk_dialog_add_button (GTK_DIALOG (dialog), _("Mark _Executable"), GTK_RESPONSE_APPLY);
+  gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
+  if (screen != NULL && window == NULL)
+    gtk_window_set_screen (GTK_WINDOW (dialog), screen);
+  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", secondary->str);
+  g_string_free (secondary, TRUE);
+  response = gtk_dialog_run (GTK_DIALOG (dialog));
+  gtk_widget_destroy (dialog);
+
+  /* check if we should make the file executable */
+  if (response == GTK_RESPONSE_APPLY)
+    {
+      /* try to query information about the file */
+      info = g_file_query_info (thunar_file_get_file (file),
+                                G_FILE_ATTRIBUTE_UNIX_MODE,
+                                G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                NULL, &err);
+
+      if (G_LIKELY (info != NULL))
+        {
+          if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_UNIX_MODE))
+            {
+              /* determine the current mode */
+              old_mode = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE);
+
+              /* generate the new mode */
+              new_mode = old_mode | THUNAR_FILE_MODE_USR_EXEC | THUNAR_FILE_MODE_GRP_EXEC | THUNAR_FILE_MODE_OTH_EXEC;
+
+              if (old_mode != new_mode)
+                {
+                  g_file_set_attribute_uint32 (thunar_file_get_file (file),
+                                               G_FILE_ATTRIBUTE_UNIX_MODE, new_mode,
+                                               G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                               NULL, &err);
+                }
+            }
+          else
+            {
+              g_warning ("No %s attribute found", G_FILE_ATTRIBUTE_UNIX_MODE);
+            }
+
+          g_object_unref (info);
+        }
+
+      if (err != NULL)
+        {
+          thunar_dialogs_show_error (parent, err, ("Unable to mark launcher executable"));
+          g_error_free (err);
+        }
+
+      /* just launch */
+      response = GTK_RESPONSE_OK;
+    }
+
+  return (response == GTK_RESPONSE_OK);
+}
diff --git a/thunar/thunar-dialogs.h b/thunar/thunar-dialogs.h
index d476cb6..74c4317 100644
--- a/thunar/thunar-dialogs.h
+++ b/thunar/thunar-dialogs.h
@@ -26,29 +26,28 @@
 
 G_BEGIN_DECLS;
 
-ThunarJob         *thunar_dialogs_show_rename_file     (gpointer              parent,
-                                                        ThunarFile           *file);
-
-void               thunar_dialogs_show_about           (GtkWindow            *parent,
-                                                        const gchar          *title,
-                                                        const gchar          *format,
-                                                        ...) G_GNUC_PRINTF (3, 4);
-
-void               thunar_dialogs_show_error           (gpointer              parent,
-                                                        const GError         *error,
-                                                        const gchar          *format,
-                                                        ...) G_GNUC_PRINTF (3, 4);
-
-ThunarJobResponse  thunar_dialogs_show_job_ask         (GtkWindow            *parent,
-                                                        const gchar          *question,
-                                                        ThunarJobResponse     choices);
-
-ThunarJobResponse  thunar_dialogs_show_job_ask_replace (GtkWindow            *parent,
-                                                        ThunarFile           *src_file,
-                                                        ThunarFile           *dst_file);
-
-void               thunar_dialogs_show_job_error       (GtkWindow            *parent,
-                                                        GError               *error);
+ThunarJob         *thunar_dialogs_show_rename_file      (gpointer              parent,
+                                                         ThunarFile           *file);
+void               thunar_dialogs_show_about            (GtkWindow            *parent,
+                                                         const gchar          *title,
+                                                         const gchar          *format,
+                                                         ...) G_GNUC_PRINTF (3, 4);
+void               thunar_dialogs_show_error            (gpointer              parent,
+                                                         const GError         *error,
+                                                         const gchar          *format,
+                                                         ...) G_GNUC_PRINTF (3, 4);
+ThunarJobResponse  thunar_dialogs_show_job_ask          (GtkWindow            *parent,
+                                                         const gchar          *question,
+                                                         ThunarJobResponse     choices);
+ThunarJobResponse  thunar_dialogs_show_job_ask_replace  (GtkWindow            *parent,
+                                                         ThunarFile           *src_file,
+                                                         ThunarFile           *dst_file);
+void               thunar_dialogs_show_job_error        (GtkWindow            *parent,
+                                                         GError               *error);
+gboolean           thunar_dialogs_show_insecure_program (gpointer              parent,
+                                                         const gchar          *title,
+                                                         ThunarFile           *file,
+                                                         const gchar          *command);
 
 G_END_DECLS;
 
diff --git a/thunar/thunar-dnd.c b/thunar/thunar-dnd.c
index 2205b94..6a4339f 100644
--- a/thunar/thunar-dnd.c
+++ b/thunar/thunar-dnd.c
@@ -247,7 +247,7 @@ thunar_dnd_perform (GtkWidget    *widget,
   else if (thunar_file_is_executable (file))
     {
       /* TODO any chance to determine the working dir here? */
-      succeed = thunar_file_execute (file, NULL, gtk_widget_get_screen (widget), file_list, &error);
+      succeed = thunar_file_execute (file, NULL, widget, file_list, &error);
       if (G_UNLIKELY (!succeed))
         {
           /* display an error to the user */
diff --git a/thunar/thunar-file.c b/thunar/thunar-file.c
index 9f7cae2..2ceff15 100644
--- a/thunar/thunar-file.c
+++ b/thunar/thunar-file.c
@@ -65,6 +65,7 @@
 #include <thunar/thunar-private.h>
 #include <thunar/thunar-user.h>
 #include <thunar/thunar-util.h>
+#include <thunar/thunar-dialogs.h>
 
 
 
@@ -712,6 +713,7 @@ thunar_file_load (ThunarFile   *file,
   gchar       *p;
   gchar       *thumbnail_dir_path;
   const gchar *display_name;
+  gboolean     is_secure = FALSE;
 
   _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
   _thunar_return_val_if_fail (error == NULL || *error == NULL, FALSE);
@@ -786,7 +788,7 @@ thunar_file_load (ThunarFile   *file,
     }
 
   /* check if this file is a desktop entry */
-  if (thunar_file_is_desktop_file (file))
+  if (thunar_file_is_desktop_file (file, &is_secure) && is_secure)
     {
       /* determine the custom icon and display name for .desktop files */
 
@@ -955,7 +957,7 @@ thunar_file_get_parent (const ThunarFile *file,
  * @file              : a #ThunarFile instance.
  * @working_directory : the working directory used to resolve relative filenames 
  *                      in @file_list.
- * @screen            : a #GdkScreen.
+ * @parent            : %NULL, a #GdkScreen or #GtkWidget.
  * @file_list         : the list of #GFile<!---->s to supply to @file on execution.
  * @error             : return location for errors or %NULL.
  *
@@ -968,36 +970,36 @@ thunar_file_get_parent (const ThunarFile *file,
 gboolean
 thunar_file_execute (ThunarFile  *file,
                      GFile       *working_directory,
-                     GdkScreen   *screen,
+                     gpointer     parent,
                      GList       *file_list,
                      GError     **error)
 {
-  gboolean  snotify = FALSE;
-  gboolean  terminal;
-  gboolean  result = FALSE;
-  GKeyFile *key_file;
-  GError   *err = NULL;
-  GFile    *parent;
-  gchar    *icon = NULL;
-  gchar    *name;
-  gchar    *type;
-  gchar    *url;
-  gchar    *location;
-  gchar    *escaped_location;
-  gchar   **argv = NULL;
-  gchar    *exec;
-  gchar    *directory = NULL;
+  gboolean    snotify = FALSE;
+  gboolean    terminal;
+  gboolean    result = FALSE;
+  GKeyFile   *key_file;
+  GError     *err = NULL;
+  GFile      *file_parent;
+  gchar      *icon_name = NULL;
+  gchar      *name;
+  gchar      *type;
+  gchar      *url;
+  gchar      *location;
+  gchar      *escaped_location;
+  gchar     **argv = NULL;
+  gchar      *exec;
+  gchar      *directory = NULL;
+  gboolean    is_secure = FALSE;
 
   _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
-  _thunar_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
   _thunar_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
   location = thunar_g_file_get_location (file->gfile);
 
-  if (thunar_file_is_desktop_file (file))
+  if (thunar_file_is_desktop_file (file, &is_secure))
     {
+      /* parse file first, even if it is insecure */
       key_file = thunar_g_file_query_key_file (file->gfile, NULL, &err);
-
       if (key_file == NULL)
         {
           g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL,
@@ -1006,69 +1008,69 @@ thunar_file_execute (ThunarFile  *file,
           return FALSE;
         }
 
-      type = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
-                                    G_KEY_FILE_DESKTOP_KEY_TYPE, NULL);
-
+      type = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TYPE, NULL);
       if (G_LIKELY (exo_str_is_equal (type, "Application")))
         {
-          exec = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
-                                        G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
+          exec = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
           if (G_LIKELY (exec != NULL))
             {
-              /* parse other fields */
-              name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
-                                                   G_KEY_FILE_DESKTOP_KEY_NAME, NULL,
-                                                   NULL);
-              icon = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
-                                            G_KEY_FILE_DESKTOP_KEY_ICON, NULL);
-              directory = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
-                                                 G_KEY_FILE_DESKTOP_KEY_PATH, NULL);
-              terminal = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
-                                                 G_KEY_FILE_DESKTOP_KEY_TERMINAL, NULL);
-              snotify = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
-                                                G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY, 
-                                                NULL);
-
-              result = thunar_exec_parse (exec, file_list, icon, name, location, 
-                                          terminal, NULL, &argv, error);
-              
-              g_free (name);
-              g_free (icon);
+              /* if the .desktop file is not secure, ask user what to do */
+              if (is_secure || thunar_dialogs_show_insecure_program (parent, _("Untrusted application launcher"), file, exec))
+                {
+                  /* parse other fields */
+                  name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, NULL, NULL);
+                  icon_name = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, NULL);
+                  directory = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_PATH, NULL);
+                  terminal = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TERMINAL, NULL);
+                  snotify = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY, NULL);
+
+                  result = thunar_exec_parse (exec, file_list, icon_name, name, location, terminal, NULL, &argv, error);
+
+                  g_free (name);
+                }
+              else
+                {
+                  /* fall-through to free value and leave without execution */
+                  result = TRUE;
+                }
+
               g_free (exec);
             }
           else
             {
-              /* TRANSLATORS: `Exec' is a field name in a .desktop file. 
-               * Don't translate it. */
-              g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL, 
+              /* TRANSLATORS: `Exec' is a field name in a .desktop file. Don't translate it. */
+              g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL,
                            _("No Exec field specified"));
             }
         }
       else if (exo_str_is_equal (type, "Link"))
         {
-          url = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
-                                       G_KEY_FILE_DESKTOP_KEY_URL, NULL);
+          url = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_URL, NULL);
           if (G_LIKELY (url != NULL))
             {
-              /* pass the URL to exo-open which will fire up the appropriate viewer */
-              argv = g_new (gchar *, 3);
-              argv[0] = g_strdup ("exo-open");
-              argv[1] = url;
-              argv[2] = NULL;
+              /* if the .desktop file is not secure, ask user what to do */
+              if (is_secure || thunar_dialogs_show_insecure_program (parent, _("Untrusted link launcher"), file, url))
+                {
+                  /* pass the URL to the webbrowser, this could be a bit strange,
+                   * but then at least we are on the secure side */
+                  argv = g_new (gchar *, 3);
+                  argv[0] = g_strdup ("exo-open");
+                  argv[1] = url;
+                  argv[2] = NULL;
+                }
+
               result = TRUE;
             }
           else
             {
-              /* TRANSLATORS: `URL' is a field name in a .desktop file.
-               * Don't translate it. */
+              /* TRANSLATORS: `URL' is a field name in a .desktop file. Don't translate it. */
               g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL, 
                            _("No URL field specified"));
             }
         }
       else
         {
-          g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL, 
-                       _("Invalid desktop file"));
+          g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_INVAL, _("Invalid desktop file"));
         }
 
       g_free (type);
@@ -1079,13 +1081,12 @@ thunar_file_execute (ThunarFile  *file,
       /* fake the Exec line */
       escaped_location = g_shell_quote (location);
       exec = g_strconcat (escaped_location, " %F", NULL);
-      result = thunar_exec_parse (exec, file_list, NULL, NULL, NULL, FALSE, NULL, &argv,
-                                  error);
+      result = thunar_exec_parse (exec, file_list, NULL, NULL, NULL, FALSE, NULL, &argv, error);
       g_free (escaped_location);
       g_free (exec);
     }
 
-  if (G_LIKELY (result))
+  if (G_LIKELY (result && argv != NULL))
     {
       /* use other directory if the Path from the desktop file was not set */
       if (G_LIKELY (directory == NULL))
@@ -1099,9 +1100,9 @@ thunar_file_execute (ThunarFile  *file,
           else if (file_list != NULL)
             {
               /* use the directory of the first list item */
-              parent = g_file_get_parent (file_list->data);
-              directory = (parent != NULL) ? thunar_g_file_get_location (parent) : NULL;
-              g_object_unref (parent);
+              file_parent = g_file_get_parent (file_list->data);
+              directory = (file_parent != NULL) ? thunar_g_file_get_location (file_parent) : NULL;
+              g_object_unref (file_parent);
             }
           else
             {
@@ -1113,14 +1114,16 @@ thunar_file_execute (ThunarFile  *file,
         }
 
       /* execute the command */
-      result = xfce_spawn_on_screen (screen, directory, argv, NULL, G_SPAWN_SEARCH_PATH,
-                                     snotify, gtk_get_current_event_time (), icon, error);
+      result = xfce_spawn_on_screen (thunar_util_parse_parent (parent, NULL), 
+                                     directory, argv, NULL, G_SPAWN_SEARCH_PATH,
+                                     snotify, gtk_get_current_event_time (), icon_name, error);
     }
 
   /* clean up */
   g_strfreev (argv);
   g_free (location);
   g_free (directory);
+  g_free (icon_name);
 
   return result;
 }
@@ -1156,22 +1159,16 @@ thunar_file_launch (ThunarFile  *file,
 {
   GdkAppLaunchContext *context;
   ThunarApplication   *application;
-  GdkScreen           *screen;
   GAppInfo            *app_info;
   gboolean             succeed;
   GList                path_list;
+  GdkScreen           *screen;
 
   _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
   _thunar_return_val_if_fail (error == NULL || *error == NULL, FALSE);
   _thunar_return_val_if_fail (parent == NULL || GDK_IS_SCREEN (parent) || GTK_IS_WIDGET (parent), FALSE);
-  
-  /* determine the screen for the parent */
-  if (G_UNLIKELY (parent == NULL))
-    screen = gdk_screen_get_default ();
-  else if (GTK_IS_WIDGET (parent))
-    screen = gtk_widget_get_screen (parent);
-  else
-    screen = GDK_SCREEN (parent);
+
+  screen = thunar_util_parse_parent (parent, NULL);
 
   /* check if we have a folder here */
   if (thunar_file_is_directory (file))
@@ -1184,7 +1181,7 @@ thunar_file_launch (ThunarFile  *file,
 
   /* check if we should execute the file */
   if (thunar_file_is_executable (file))
-    return thunar_file_execute (file, NULL, screen, NULL, error);
+    return thunar_file_execute (file, NULL, parent, NULL, error);
 
   /* determine the default application to open the file */
   /* TODO We should probably add a cancellable argument to thunar_file_launch() */
@@ -1216,6 +1213,7 @@ thunar_file_launch (ThunarFile  *file,
   /* create a launch context */
   context = gdk_app_launch_context_new ();
   gdk_app_launch_context_set_screen (context, screen);
+  gdk_app_launch_context_set_timestamp (context, gtk_get_current_event_time ());
 
   /* otherwise try to execute the application */
   succeed = g_app_info_launch (app_info, &path_list, G_APP_LAUNCH_CONTEXT (context), error);
@@ -1269,7 +1267,7 @@ thunar_file_rename (ThunarFile   *file,
   _thunar_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
   /* check if this file is a desktop entry */
-  if (thunar_file_is_desktop_file (file))
+  if (thunar_file_is_desktop_file (file, NULL))
     {
       /* try to load the desktop entry into a key file */
       key_file = thunar_g_file_query_key_file (file->gfile, cancellable, &err);
@@ -2236,7 +2234,7 @@ thunar_file_is_executable (const ThunarFile *file)
         }
     }
 
-  return can_execute || thunar_file_is_desktop_file (file);
+  return can_execute || thunar_file_is_desktop_file (file, NULL);
 }
 
 
@@ -2370,30 +2368,71 @@ thunar_file_is_trashed (const ThunarFile *file)
 
 /**
  * thunar_file_is_desktop_file:
- * @file : a #ThunarFile.
+ * @file      : a #ThunarFile.
+ * @is_secure : if %NULL do a simple check, else it will set this boolean
+ *              to indicate if the desktop file is safe see bug #5012
+ *              for more info.
  *
- * Returns %TRUE if @file is a .desktop file, but not a .directory file.
+ * Returns %TRUE if @file is a .desktop file. The @is_secure return value
+ * will tell if the .desktop file is also secure.
  *
  * Return value: %TRUE if @file is a .desktop file.
  **/
 gboolean
-thunar_file_is_desktop_file (const ThunarFile *file)
+thunar_file_is_desktop_file (const ThunarFile *file,
+                             gboolean         *is_secure)
 {
-  const gchar *content_type;
-  gboolean     is_desktop_file = FALSE;
+  const gchar * const *data_dirs;
+  guint                n;
+  gchar               *path;
 
   _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
 
   if (file->info == NULL)
     return FALSE;
 
-  content_type = g_file_info_get_content_type (file->info);
+  /* only allow regular files with a .desktop extension */
+  if (!g_str_has_suffix (thunar_file_get_basename (file), ".desktop")
+      || g_file_info_get_file_type (file->info) != G_FILE_TYPE_REGULAR)
+    return FALSE;
 
-  if (content_type != NULL)
-    is_desktop_file = g_content_type_equals (content_type, "application/x-desktop");
+  /* don't check more if not needed */
+  if (is_secure == NULL)
+    return TRUE;
+
+  /* desktop files outside xdg directories need to be executable for security reasons */
+  if (g_file_info_get_attribute_boolean (file->info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE))
+    {
+      /* has +x */
+      *is_secure = TRUE;
+    }
+  else
+    {
+      /* assume the file is not safe */
+      *is_secure = FALSE;
+
+      /* deskopt files in xdg directories are also fine... */
+      if (g_file_is_native (thunar_file_get_file (file)))
+        {
+          data_dirs = g_get_system_data_dirs ();
+          if (G_LIKELY (data_dirs != NULL))
+            {
+              path = g_file_get_path (thunar_file_get_file (file));
+              for (n = 0; data_dirs[n] != NULL; n++)
+                {
+                  if (g_str_has_prefix (path, data_dirs[n]))
+                    {
+                      /* has known prefix, can launch without problems */
+                      *is_secure = TRUE;
+                      break;
+                    }
+                }
+              g_free (path);
+            }
+        }
+    }
 
-  return is_desktop_file 
-    && !g_str_has_suffix (thunar_file_get_basename (file), ".directory");
+  return TRUE;
 }
 
 
diff --git a/thunar/thunar-file.h b/thunar/thunar-file.h
index 9f6e23d..0c8085f 100644
--- a/thunar/thunar-file.h
+++ b/thunar/thunar-file.h
@@ -134,7 +134,7 @@ ThunarFile       *thunar_file_get_parent           (const ThunarFile       *file
 
 gboolean          thunar_file_execute              (ThunarFile             *file,
                                                     GFile                  *working_directory,
-                                                    GdkScreen              *screen,
+                                                    gpointer                parent,
                                                     GList                  *path_list,
                                                     GError                **error);
 
@@ -195,7 +195,8 @@ gboolean          thunar_file_is_hidden            (const ThunarFile       *file
 gboolean          thunar_file_is_home              (const ThunarFile       *file);
 gboolean          thunar_file_is_regular           (const ThunarFile       *file);
 gboolean          thunar_file_is_trashed           (const ThunarFile       *file);
-gboolean          thunar_file_is_desktop_file      (const ThunarFile       *file);
+gboolean          thunar_file_is_desktop_file      (const ThunarFile       *file,
+                                                    gboolean               *is_secure);
 const gchar      *thunar_file_get_display_name     (const ThunarFile       *file);
 
 gchar            *thunar_file_get_deletion_date    (const ThunarFile       *file,
diff --git a/thunar/thunar-launcher.c b/thunar/thunar-launcher.c
index a821681..6bfe366 100644
--- a/thunar/thunar-launcher.c
+++ b/thunar/thunar-launcher.c
@@ -521,20 +521,16 @@ static void
 thunar_launcher_execute_files (ThunarLauncher *launcher,
                                GList          *files)
 {
-  GdkScreen *screen;
-  GError    *error = NULL;
-  GFile     *working_directory;
-  GList     *lp;
-
-  /* determine the screen on which to run the file(s) */
-  screen = (launcher->widget != NULL) ? gtk_widget_get_screen (launcher->widget) : NULL;
+  GError *error = NULL;
+  GFile  *working_directory;
+  GList  *lp;
 
   /* execute all selected files */
   for (lp = files; lp != NULL; lp = lp->next)
     {
       working_directory = thunar_file_get_file (launcher->current_directory);
 
-      if (!thunar_file_execute (lp->data, working_directory, screen, NULL, &error))
+      if (!thunar_file_execute (lp->data, working_directory, launcher->widget, NULL, &error))
         {
           /* display an error message to the user */
           thunar_dialogs_show_error (launcher->widget, error, _("Failed to execute file \"%s\""), thunar_file_get_display_name (lp->data));
diff --git a/thunar/thunar-permissions-chooser.c b/thunar/thunar-permissions-chooser.c
index 3140293..027e561 100644
--- a/thunar/thunar-permissions-chooser.c
+++ b/thunar/thunar-permissions-chooser.c
@@ -1011,6 +1011,7 @@ thunar_permissions_chooser_file_changed (ThunarPermissionsChooser *chooser)
   g_object_set (G_OBJECT (chooser->program_button), "visible", thunar_file_is_regular (file)
       && (thunarx_file_info_has_mime_type (THUNARX_FILE_INFO (file), "application/x-executable")
        || thunarx_file_info_has_mime_type (THUNARX_FILE_INFO (file), "application/x-shellscript")
+       || thunarx_file_info_has_mime_type (THUNARX_FILE_INFO (file), "application/x-desktop")
        || thunarx_file_info_has_mime_type (THUNARX_FILE_INFO (file), "application/x-ms-dos-executable")
        || thunarx_file_info_has_mime_type (THUNARX_FILE_INFO (file), "application/x-msi")), NULL);
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (chooser->program_button), (mode & 0111) != 0);
diff --git a/thunar/thunar-properties-dialog.c b/thunar/thunar-properties-dialog.c
index 8370a9b..33f4667 100644
--- a/thunar/thunar-properties-dialog.c
+++ b/thunar/thunar-properties-dialog.c
@@ -900,7 +900,7 @@ thunar_properties_dialog_update_single (ThunarPropertiesDialog *dialog)
   g_object_ref (G_OBJECT (dialog->icon_image));
   gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (dialog->icon_image)), dialog->icon_image);
   if (thunar_file_is_writable (file)
-      && thunar_file_is_desktop_file (file))
+      && thunar_file_is_desktop_file (file, NULL))
     {
       gtk_container_add (GTK_CONTAINER (dialog->icon_button), dialog->icon_image);
       gtk_widget_show (dialog->icon_button);


More information about the Xfce4-commits mailing list