From f5cadc018b47a985e43a2cd6ef4fa4149b9df647 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 26 Nov 2015 17:22:37 +0100 Subject: [PATCH] Support defining read-only filesystem access If you do something like "--filesystem=host:ro" you get a read-only mount of the specified filesystem location. --- lib/xdg-app-helper.c | 35 ++++++++++------ lib/xdg-app-run.c | 95 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 96 insertions(+), 34 deletions(-) diff --git a/lib/xdg-app-helper.c b/lib/xdg-app-helper.c index 26c089fa..17501ce2 100644 --- a/lib/xdg-app-helper.c +++ b/lib/xdg-app-helper.c @@ -469,17 +469,18 @@ usage (char **argv) fprintf (stderr, " -a Specify path for application (mounted at /app)\n" - " -b DEST[=SOURCE] Bind extra source path readonly into DEST\n" + " -b DEST[=SOURCE] Bind extra source path read-only into DEST\n" " -B DEST[=SOURCE] Bind extra source path into DEST\n" " -M DEST[=SOURCE] Bind extra source path into DEST and remove original\n" " -d SOCKETPATH Use SOCKETPATH as dbus session bus\n" " -D SOCKETPATH Use SOCKETPATH as dbus system bus\n" " -e Make /app/exports writable\n" " -E Make /etc a pure symlink to /usr/etc\n" - " -f Mount the host filesystems\n" + " -F Mount the host filesystems\n" + " -f Mount the host filesystems read-only\n" " -g Allow use of direct rendering graphics\n" - " -F Mount the host filesystems read-only\n" - " -H Mount the users home directory (implied by -f)\n" + " -H Mount the users home directory\n" + " -h Mount the users home directory read-only\n" " -i Share IPC namespace with session\n" " -I APPID Set app id (used to find app data)\n" " -l Lock .ref files in all mounts\n" @@ -1565,7 +1566,7 @@ mount_extra_root_dirs (int readonly) } static void -create_homedir (int mount_real_home, const char *app_id) +create_homedir (int mount_real_home, int mount_home_ro, const char *app_id) { const char *home; const char *relative_home; @@ -1586,10 +1587,12 @@ create_homedir (int mount_real_home, const char *app_id) if (mount_real_home) { writable_home = home; - if (bind_mount (writable_home, relative_home, BIND_RECURSIVE)) + if (bind_mount (writable_home, relative_home, BIND_RECURSIVE | (mount_home_ro ? BIND_READONLY : 0))) die_with_error ("unable to mount %s", home); } - else if (app_id != NULL) + + if (app_id != NULL && + (!mount_real_home || mount_home_ro)) { app_id_dir = strconcat3 (home, "/.var/app/", app_id); if (stat (app_id_dir, &st) == 0 && S_ISDIR (st.st_mode)) @@ -2022,6 +2025,7 @@ main (int argc, bool mount_host_fs = FALSE; bool mount_host_fs_ro = FALSE; bool mount_home = FALSE; + bool mount_home_ro = FALSE; bool lock_files = FALSE; bool writable = FALSE; bool writable_app = FALSE; @@ -2044,7 +2048,7 @@ main (int argc, clean_argv (argc, argv); - while ((c = getopt (argc, argv, "+inWwceEsfFHra:m:M:b:B:p:x:ly:d:D:v:I:gS:")) >= 0) + while ((c = getopt (argc, argv, "+inWwceEsfFHhra:m:M:b:B:p:x:ly:d:D:v:I:gS:")) >= 0) { switch (c) { @@ -2110,11 +2114,11 @@ main (int argc, create_etc_dir = FALSE; break; - case 'f': + case 'F': mount_host_fs = TRUE; break; - case 'F': + case 'f': mount_host_fs = TRUE; mount_host_fs_ro = TRUE; break; @@ -2127,6 +2131,11 @@ main (int argc, mount_home = TRUE; break; + case 'h': + mount_home = TRUE; + mount_home_ro = TRUE; + break; + case 'i': ipc = TRUE; break; @@ -2439,8 +2448,8 @@ main (int argc, bind_mount ("/run/media", "run/media", BIND_RECURSIVE | (mount_host_fs_ro ? BIND_READONLY : 0)); } - if (!mount_host_fs) - create_homedir (mount_home, app_id); + if (!mount_host_fs || mount_host_fs_ro) + create_homedir (mount_home, mount_home_ro, app_id); if (mount_host_fs || mount_home) { @@ -2461,7 +2470,7 @@ main (int argc, } /* If the user has homedir access, also allow dconf run dir access */ - bind_mount (dconf_run_path, get_relative_path (dconf_run_path), 0); + bind_mount (dconf_run_path, get_relative_path (dconf_run_path), BIND_READONLY); free (dconf_run_path); } diff --git a/lib/xdg-app-run.c b/lib/xdg-app-run.c index 4501213b..e622c1eb 100644 --- a/lib/xdg-app-run.c +++ b/lib/xdg-app-run.c @@ -43,6 +43,12 @@ typedef enum { XDG_APP_CONTEXT_SHARED_IPC = 1 << 1, } XdgAppContextShares; +typedef enum { + XDG_APP_FILESYSTEM_MODE_READ_WRITE = 1, + XDG_APP_FILESYSTEM_MODE_READ_ONLY = 2, +} XdgAppFilesystemMode; + + /* Same order as enum */ static const char *xdg_app_context_shares[] = { "network", @@ -376,10 +382,36 @@ get_user_dir_from_string (const char *filesystem) return -1; } +static char * +parse_filesystem_flags (const char *filesystem, XdgAppFilesystemMode *mode) +{ + gsize len = strlen (filesystem); + + if (mode) + *mode = XDG_APP_FILESYSTEM_MODE_READ_WRITE; + + if (g_str_has_suffix (filesystem, ":ro")) + { + len -= 3; + if (mode) + *mode = XDG_APP_FILESYSTEM_MODE_READ_ONLY; + } + else if (g_str_has_suffix (filesystem, ":rw")) + { + len -= 3; + if (mode) + *mode = XDG_APP_FILESYSTEM_MODE_READ_WRITE; + } + + return g_strndup (filesystem, len); +} + static gboolean -xdg_app_context_verify_filesystem (const char *filesystem, +xdg_app_context_verify_filesystem (const char *filesystem_and_mode, GError **error) { + char *filesystem = parse_filesystem_flags (filesystem_and_mode, NULL); + if (strcmp (filesystem, "host") == 0) return TRUE; if (strcmp (filesystem, "home") == 0) @@ -400,14 +432,19 @@ static void xdg_app_context_add_filesystem (XdgAppContext *context, const char *what) { - g_hash_table_insert (context->filesystems, g_strdup (what), GINT_TO_POINTER (1)); + XdgAppFilesystemMode mode; + char *fs = parse_filesystem_flags (what, &mode); + + g_hash_table_insert (context->filesystems, fs, GINT_TO_POINTER (mode)); } static void xdg_app_context_remove_filesystem (XdgAppContext *context, const char *what) { - g_hash_table_insert (context->filesystems, g_strdup (what), NULL); + g_hash_table_insert (context->filesystems, + parse_filesystem_flags (what, NULL), + NULL); } void @@ -650,7 +687,7 @@ static GOptionEntry context_options[] = { { "nosocket", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_nosocket_cb, "Don't expose socket to app", "SOCKET" }, { "device", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_device_cb, "Expose device to app", "DEVICE" }, { "nodevice", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_nodevice_cb, "Don't expose device to app", "DEVICE" }, - { "filesystem", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_filesystem_cb, "Expose filesystem to app", "FILESYSTEM" }, + { "filesystem", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_filesystem_cb, "Expose filesystem to app (:ro for read-only)", "FILESYSTEM[:ro]" }, { "nofilesystem", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_nofilesystem_cb, "Don't expose filesystem to app", "FILESYSTEM" }, { "env", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_env_cb, "Set environment variable", "VAR=VALUE" }, { "own-name", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_own_name_cb, "Allow app to own name on the session bus", "DBUS_NAME" }, @@ -873,13 +910,17 @@ xdg_app_context_save_metadata (XdgAppContext *context, if (g_hash_table_size (context->filesystems) > 0) { - GPtrArray *array = g_ptr_array_new (); + g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func (g_free); g_hash_table_iter_init (&iter, context->filesystems); while (g_hash_table_iter_next (&iter, &key, &value)) { - if (value != NULL) - g_ptr_array_add (array, key); + XdgAppFilesystemMode mode = GPOINTER_TO_INT (value); + + if (mode == XDG_APP_FILESYSTEM_MODE_READ_ONLY) + g_ptr_array_add (array, g_strconcat (key, ":ro", NULL)); + else if (value != NULL) + g_ptr_array_add (array, g_strdup (key)); } g_key_file_set_string_list (metakey, @@ -1287,6 +1328,7 @@ xdg_app_run_add_environment_args (GPtrArray *argv_array, gboolean home_access = FALSE; GString *xdg_dirs_conf = NULL; char opts[16]; + XdgAppFilesystemMode fs_mode, home_mode; int i; i = 0; @@ -1310,19 +1352,29 @@ xdg_app_run_add_environment_args (GPtrArray *argv_array, opts[i++] = 'g'; } - if (g_hash_table_lookup (context->filesystems, "host")) + fs_mode = (XdgAppFilesystemMode)g_hash_table_lookup (context->filesystems, "host"); + if (fs_mode != 0) { g_debug ("Allowing host-fs access"); - opts[i++] = 'f'; + if (fs_mode == XDG_APP_FILESYSTEM_MODE_READ_WRITE) + opts[i++] = 'F'; + else + opts[i++] = 'f'; home_access = TRUE; } - else if (g_hash_table_lookup (context->filesystems, "home")) + + home_mode = (XdgAppFilesystemMode)g_hash_table_lookup (context->filesystems, "home"); + if (home_mode != 0) { g_debug ("Allowing homedir access"); - opts[i++] = 'H'; + if (home_mode == XDG_APP_FILESYSTEM_MODE_READ_WRITE) + opts[i++] = 'H'; + else + opts[i++] = 'h'; home_access = TRUE; } - else + + if (!home_access) { /* Enable persistant mapping only if no access to real home dir */ @@ -1351,20 +1403,24 @@ xdg_app_run_add_environment_args (GPtrArray *argv_array, while (g_hash_table_iter_next (&iter, &key, &value)) { const char *filesystem = key; + XdgAppFilesystemMode mode = GPOINTER_TO_INT(value); + const char *mode_arg; if (value == NULL || strcmp (filesystem, "host") == 0 || strcmp (filesystem, "home") == 0) continue; + if (mode == XDG_APP_FILESYSTEM_MODE_READ_WRITE) + mode_arg = "-B"; + else + mode_arg = "-b"; + if (g_str_has_prefix (filesystem, "xdg-")) { const char *path; int dir = get_user_dir_from_string (filesystem); - if (home_access) - continue; - if (dir < 0) { g_warning ("Unsupported xdg dir %s\n", filesystem); @@ -1388,7 +1444,7 @@ xdg_app_run_add_environment_args (GPtrArray *argv_array, g_string_append_printf (xdg_dirs_conf, "%s=\"%s\"\n", get_user_dir_config_key (dir), path); - g_ptr_array_add (argv_array, g_strdup ("-B")); + g_ptr_array_add (argv_array, g_strdup (mode_arg)); g_ptr_array_add (argv_array, g_strdup_printf ("%s", path)); } } @@ -1396,13 +1452,10 @@ xdg_app_run_add_environment_args (GPtrArray *argv_array, { g_autofree char *path = NULL; - if (home_access) - continue; - path = g_build_filename (g_get_home_dir(), filesystem+2, NULL); if (g_file_test (path, G_FILE_TEST_EXISTS)) { - g_ptr_array_add (argv_array, g_strdup ("-B")); + g_ptr_array_add (argv_array, g_strdup (mode_arg)); g_ptr_array_add (argv_array, g_strdup (path)); } } @@ -1410,7 +1463,7 @@ xdg_app_run_add_environment_args (GPtrArray *argv_array, { if (g_file_test (filesystem, G_FILE_TEST_EXISTS)) { - g_ptr_array_add (argv_array, g_strdup ("-B")); + g_ptr_array_add (argv_array, g_strdup (mode_arg)); g_ptr_array_add (argv_array, g_strdup_printf ("%s", filesystem)); } }