Commit 8b1db18a authored by Bastien Nocera's avatar Bastien Nocera

thumbnail: Sandbox thumbnailers on Linux

On Linux systems, bubblewrap is now required to launch thumbnailers in a
restricted environment.

- Only /usr and the compilation ${prefix} of the gnome-desktop library
  will be available to the thumbnailer as read-only
- The network is disabled
- The filename of the file to thumbnail is hidden
- Bubblewrap is not used if the application is already sandboxed in
  Flatpak as all privileges to create a new namespace are dropped when
  the initial one is created.

https://bugzilla.gnome.org/show_bug.cgi?id=774497
parent c1956f35
......@@ -159,6 +159,14 @@ else
have_udev=no
fi
dnl Check for bubblewrap compatible platform
case $host_os in
linux*)
AC_DEFINE_UNQUOTED(HAVE_BWRAP, 1, [Define to 1 if Bubblewrap support is available])
AC_DEFINE_UNQUOTED(INSTALL_PREFIX, "$prefix", [Path to library install prefix])
;;
esac
dnl pkg-config dependency checks
PKG_CHECK_MODULES(GNOME_DESKTOP, gdk-pixbuf-2.0 >= $GDK_PIXBUF_REQUIRED
......
......@@ -36,8 +36,15 @@
#include "gnome-desktop-thumbnail-script.h"
typedef struct {
gboolean sandbox;
GArray *fd_array;
/* Input/output file paths outside the sandbox */
char *infile;
char *outfile;
char *outdir; /* outdir is outfile's parent dir, if it needs to be deleted */
/* I/O file paths inside the sandbox */
char *s_infile;
char *s_outfile;
} ScriptExec;
static char *
......@@ -100,11 +107,99 @@ expand_thumbnailing_elem (const char *elem,
return g_string_free (str, FALSE);
}
/* From https://github.com/flatpak/flatpak/blob/master/common/flatpak-run.c */
G_GNUC_NULL_TERMINATED
static void
add_args (GPtrArray *argv_array, ...)
{
va_list args;
const gchar *arg;
va_start (args, argv_array);
while ((arg = va_arg (args, const gchar *)))
g_ptr_array_add (argv_array, g_strdup (arg));
va_end (args);
}
static char *
get_extension (const char *path)
{
g_autofree char *basename = NULL;
char *p;
basename = g_path_get_basename (path);
p = strrchr (basename, '.');
if (p == NULL)
return NULL;
return g_strdup (p + 1);
}
#ifdef HAVE_BWRAP
static gboolean
add_bwrap (GPtrArray *array,
ScriptExec *script)
{
char *fd_str;
int fd;
g_return_val_if_fail (script->outdir != NULL, FALSE);
g_return_val_if_fail (script->s_infile != NULL, FALSE);
add_args (array,
"bwrap",
"--ro-bind", "/usr", "/usr",
"--proc", "/proc",
"--dev", "/dev",
"--symlink", "usr/lib", "/lib",
"--symlink", "usr/lib64", "/lib64",
"--symlink", "usr/bin", "/bin",
"--symlink", "usr/sbin", "/sbin",
"--chdir", "/",
"--unshare-all",
"--die-with-parent",
NULL);
/* Add gnome-desktop's install prefix if needed */
if (g_strcmp0 (INSTALL_PREFIX, "") != 0 &&
g_strcmp0 (INSTALL_PREFIX, "/usr") != 0 &&
g_strcmp0 (INSTALL_PREFIX, "/usr/") != 0)
{
add_args (array,
"--ro-bind", INSTALL_PREFIX, INSTALL_PREFIX,
NULL);
}
g_ptr_array_add (array, g_strdup ("--bind"));
g_ptr_array_add (array, g_strdup (script->outdir));
g_ptr_array_add (array, g_strdup ("/tmp"));
/* Open the infile so we can pass the fd through bwrap,
* we make sure to also re-use the original file's original
* extension in case it's useful for the thumbnailer to
* identify the file type */
fd = open (script->infile, O_RDONLY | O_CLOEXEC);
if (fd == -1)
goto bail;
fd_str = g_strdup_printf ("%d", fd);
g_ptr_array_add (array, g_strdup ("--file"));
g_ptr_array_add (array, fd_str);
g_ptr_array_add (array, g_strdup (script->s_infile));
g_array_append_val (script->fd_array, fd);
return TRUE;
bail:
g_ptr_array_set_size (array, 0);
return FALSE;
}
#endif /* HAVE_BWRAP */
static char **
expand_thumbnailing_script (const char *cmd,
ScriptExec *script,
int size,
GError **error)
expand_thumbnailing_cmd (const char *cmd,
ScriptExec *script,
int size,
GError **error)
{
GPtrArray *array;
g_auto(GStrv) cmd_elems = NULL;
......@@ -117,6 +212,18 @@ expand_thumbnailing_script (const char *cmd,
array = g_ptr_array_new_with_free_func (g_free);
#ifdef HAVE_BWRAP
if (script->sandbox)
{
if (!add_bwrap (array, script))
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Bubblewrap setup failed");
goto bail;
}
}
#endif
got_in = got_out = FALSE;
for (i = 0; cmd_elems[i] != NULL; i++)
{
......@@ -124,8 +231,8 @@ expand_thumbnailing_script (const char *cmd,
expanded = expand_thumbnailing_elem (cmd_elems[i],
size,
script->infile,
script->outfile,
script->s_infile ? script->s_infile : script->infile,
script->s_outfile ? script->s_outfile : script->outfile,
&got_in,
&got_out);
......@@ -154,6 +261,21 @@ bail:
return NULL;
}
static void
child_setup (gpointer user_data)
{
GArray *fd_array = user_data;
int i;
/* If no fd_array was specified, don't care. */
if (fd_array == NULL)
return;
/* Otherwise, mark not - close-on-exec all the fds in the array */
for (i = 0; i < fd_array->len; i++)
fcntl (g_array_index (fd_array, int, i), F_SETFD, 0);
}
static void
script_exec_free (ScriptExec *exec)
{
......@@ -163,29 +285,78 @@ script_exec_free (ScriptExec *exec)
g_unlink (exec->outfile);
g_free (exec->outfile);
}
if (exec->outdir)
{
g_rmdir (exec->outdir);
g_free (exec->outdir);
}
g_free (exec->s_infile);
g_free (exec->s_outfile);
if (exec->fd_array)
g_array_free (exec->fd_array, TRUE);
g_free (exec);
}
static void
clear_fd (gpointer data)
{
int *fd_p = data;
if (fd_p != NULL && *fd_p != -1)
close (*fd_p);
}
static ScriptExec *
script_exec_new (const char *uri)
{
ScriptExec *exec;
g_autoptr(GFile) file = NULL;
int fd;
g_autofree char *tmpname = NULL;
exec = g_new0 (ScriptExec, 1);
#ifdef HAVE_BWRAP
/* Bubblewrap is not used if the application is already sandboxed in
* Flatpak as all privileges to create a new namespace are dropped when
* the initial one is created. */
if (!g_file_test ("/.flatpak-info", G_FILE_TEST_IS_REGULAR))
exec->sandbox = TRUE;
#endif
file = g_file_new_for_uri (uri);
exec->infile = g_file_get_path (file);
if (!exec->infile)
goto bail;
fd = g_file_open_tmp (".gnome_desktop_thumbnail.XXXXXX", &tmpname, NULL);
if (fd == -1)
goto bail;
close (fd);
exec->outfile = g_steal_pointer (&tmpname);
#ifdef HAVE_BWRAP
if (exec->sandbox)
{
char *tmpl;
g_autofree char *ext = NULL;
exec->fd_array = g_array_new (FALSE, TRUE, sizeof (int));
g_array_set_clear_func (exec->fd_array, clear_fd);
tmpl = g_strdup ("/tmp/gnome-desktop-thumbnailer-XXXXXX");
exec->outdir = g_mkdtemp (tmpl);
if (!exec->outdir)
goto bail;
exec->outfile = g_build_filename (exec->outdir, "gnome-desktop-thumbnailer.png", NULL);
ext = get_extension (exec->infile);
exec->s_infile = g_strdup_printf ("/tmp/gnome-desktop-file-to-thumbnail.%s", ext);
exec->s_outfile = g_strdup ("/tmp/gnome-desktop-thumbnailer.png");
}
else
#endif
{
int fd;
g_autofree char *tmpname = NULL;
fd = g_file_open_tmp (".gnome_desktop_thumbnail.XXXXXX", &tmpname, NULL);
if (fd == -1)
goto bail;
close (fd);
exec->outfile = g_steal_pointer (&tmpname);
}
return exec;
......@@ -208,7 +379,7 @@ gnome_desktop_thumbnail_script_exec (const char *cmd,
ScriptExec *exec;
exec = script_exec_new (uri);
expanded_script = expand_thumbnailing_script (cmd, exec, size, error);
expanded_script = expand_thumbnailing_cmd (cmd, exec, size, error);
if (expanded_script == NULL)
goto out;
......@@ -222,7 +393,7 @@ gnome_desktop_thumbnail_script_exec (const char *cmd,
#endif
ret = g_spawn_sync (NULL, expanded_script, NULL, G_SPAWN_SEARCH_PATH,
NULL, NULL, NULL, &error_out,
child_setup, exec->fd_array, NULL, &error_out,
&exit_status, error);
if (ret && g_spawn_check_exit_status (exit_status, error))
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment