context: Introduce new --nofilesystem=host:reset

This reintroduces the special case that existed in Flatpak 1.12.3, but
under a different name, so that it will be backwards-compatible. With
this change, flatpak-builder will be able to resolve CVE-2022-21682 by
using --filesystem=host:reset.

We want to implement this as a suffix rather than as a new keyword,
because unknown suffixes are ignored with a warning, rather than causing
a fatal error. This means that the new version of flatpak-builder will
be able to run against older versions of flatpak: it will still be
vulnerable to CVE-2022-21682 in that situation, but at least it will run.

Co-authored-by: Alexander Larsson <alexl@redhat.com>
(cherry picked from commit 5709f1aaed)
This commit is contained in:
Simon McVittie
2022-01-16 12:42:30 +00:00
committed by Simon McVittie
parent 47247b0987
commit 4eb3c2addd
5 changed files with 173 additions and 18 deletions

View File

@@ -84,6 +84,7 @@ extern const char *flatpak_context_features[];
extern const char *flatpak_context_shares[];
gboolean flatpak_context_parse_filesystem (const char *filesystem_and_mode,
gboolean negated,
char **filesystem_out,
FlatpakFilesystemMode *mode_out,
GError **error);

View File

@@ -87,6 +87,7 @@ const char *flatpak_context_special_filesystems[] = {
"host",
"host-etc",
"host-os",
"host-reset",
NULL
};
@@ -704,6 +705,12 @@ unparse_filesystem_flags (const char *path,
case FLATPAK_FILESYSTEM_MODE_NONE:
g_string_insert_c (s, 0, '!');
if (g_str_has_suffix (s->str, "-reset"))
{
g_string_truncate (s, s->len - 6);
g_string_append (s, ":reset");
}
break;
default:
@@ -716,11 +723,14 @@ unparse_filesystem_flags (const char *path,
static char *
parse_filesystem_flags (const char *filesystem,
FlatpakFilesystemMode *mode_out)
gboolean negated,
FlatpakFilesystemMode *mode_out,
GError **error)
{
g_autoptr(GString) s = g_string_new ("");
const char *p, *suffix;
FlatpakFilesystemMode mode;
gboolean reset = FALSE;
p = filesystem;
while (*p != 0 && *p != ':')
@@ -735,7 +745,31 @@ parse_filesystem_flags (const char *filesystem,
g_string_append_c (s, *p++);
}
mode = FLATPAK_FILESYSTEM_MODE_READ_WRITE;
if (negated)
mode = FLATPAK_FILESYSTEM_MODE_NONE;
else
mode = FLATPAK_FILESYSTEM_MODE_READ_WRITE;
if (g_str_equal (s->str, "host-reset"))
{
reset = TRUE;
if (!negated)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
"Filesystem token \"%s\" is only applicable for --nofilesystem",
s->str);
return NULL;
}
if (*p != '\0')
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
"Filesystem token \"%s\" cannot be used with a suffix",
s->str);
return NULL;
}
}
if (*p == ':')
{
@@ -747,10 +781,63 @@ parse_filesystem_flags (const char *filesystem,
mode = FLATPAK_FILESYSTEM_MODE_READ_WRITE;
else if (strcmp (suffix, "create") == 0)
mode = FLATPAK_FILESYSTEM_MODE_CREATE;
else if (strcmp (suffix, "reset") == 0)
reset = TRUE;
else if (*suffix != 0)
g_warning ("Unexpected filesystem suffix %s, ignoring", suffix);
if (negated && mode != FLATPAK_FILESYSTEM_MODE_NONE)
{
g_warning ("Filesystem suffix \"%s\" is not applicable for --nofilesystem",
suffix);
mode = FLATPAK_FILESYSTEM_MODE_NONE;
}
if (reset)
{
if (!negated)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
"Filesystem suffix \"%s\" only applies to --nofilesystem",
suffix);
return NULL;
}
if (!g_str_equal (s->str, "host"))
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
"Filesystem suffix \"%s\" can only be applied to "
"--nofilesystem=host",
suffix);
return NULL;
}
/* We internally handle host:reset (etc) as host-reset, only exposing it as a flag in the public
part to allow it to be ignored (with a warning) for old flatpak versions */
g_string_append (s, "-reset");
}
}
/* Postcondition check: the code above should make some results
* impossible */
if (negated)
{
g_assert (mode == FLATPAK_FILESYSTEM_MODE_NONE);
}
else
{
g_assert (mode > FLATPAK_FILESYSTEM_MODE_NONE);
/* This flag is only applicable to --nofilesystem */
g_assert (!reset);
}
/* Postcondition check: filesystem token is host-reset iff reset flag
* was found */
if (reset)
g_assert (g_str_equal (s->str, "host-reset"));
else
g_assert (!g_str_equal (s->str, "host-reset"));
if (mode_out)
*mode_out = mode;
@@ -759,13 +846,18 @@ parse_filesystem_flags (const char *filesystem,
gboolean
flatpak_context_parse_filesystem (const char *filesystem_and_mode,
gboolean negated,
char **filesystem_out,
FlatpakFilesystemMode *mode_out,
GError **error)
{
g_autofree char *filesystem = parse_filesystem_flags (filesystem_and_mode, mode_out);
g_autofree char *filesystem = NULL;
char *slash;
filesystem = parse_filesystem_flags (filesystem_and_mode, negated, mode_out, error);
if (filesystem == NULL)
return FALSE;
slash = strchr (filesystem, '/');
/* Forbid /../ in paths */
@@ -857,6 +949,14 @@ flatpak_context_take_filesystem (FlatpakContext *context,
char *fs,
FlatpakFilesystemMode mode)
{
/* Special case: --nofilesystem=host-reset implies --nofilesystem=host.
* --filesystem=host-reset (or host:reset) is not allowed. */
if (g_str_equal (fs, "host-reset"))
{
g_return_if_fail (mode == FLATPAK_FILESYSTEM_MODE_NONE);
g_hash_table_insert (context->filesystems, g_strdup ("host"), GINT_TO_POINTER (mode));
}
g_hash_table_insert (context->filesystems, fs, GINT_TO_POINTER (mode));
}
@@ -888,6 +988,14 @@ flatpak_context_merge (FlatpakContext *context,
while (g_hash_table_iter_next (&iter, &key, &value))
g_hash_table_insert (context->persistent, g_strdup (key), value);
/* We first handle host:reset, as it overrides all other keys from the parent */
if (g_hash_table_lookup_extended (other->filesystems, "host-reset", NULL, &value))
{
g_warn_if_fail (GPOINTER_TO_INT (value) == FLATPAK_FILESYSTEM_MODE_NONE);
g_hash_table_remove_all (context->filesystems);
}
/* Then set the new ones, which includes propagating host:reset. */
g_hash_table_iter_init (&iter, other->filesystems);
while (g_hash_table_iter_next (&iter, &key, &value))
g_hash_table_insert (context->filesystems, g_strdup (key), value);
@@ -1075,7 +1183,7 @@ option_filesystem_cb (const gchar *option_name,
g_autofree char *fs = NULL;
FlatpakFilesystemMode mode;
if (!flatpak_context_parse_filesystem (value, &fs, &mode, error))
if (!flatpak_context_parse_filesystem (value, FALSE, &fs, &mode, error))
return FALSE;
flatpak_context_take_filesystem (context, g_steal_pointer (&fs), mode);
@@ -1092,7 +1200,7 @@ option_nofilesystem_cb (const gchar *option_name,
g_autofree char *fs = NULL;
FlatpakFilesystemMode mode;
if (!flatpak_context_parse_filesystem (value, &fs, &mode, error))
if (!flatpak_context_parse_filesystem (value, TRUE, &fs, &mode, error))
return FALSE;
flatpak_context_take_filesystem (context, g_steal_pointer (&fs),
@@ -1597,15 +1705,13 @@ flatpak_context_load_metadata (FlatpakContext *context,
g_autofree char *filesystem = NULL;
FlatpakFilesystemMode mode;
if (!flatpak_context_parse_filesystem (fs, &filesystem, &mode, NULL))
if (!flatpak_context_parse_filesystem (fs, remove,
&filesystem, &mode, NULL))
g_debug ("Unknown filesystem type %s", filesystems[i]);
else
{
if (remove)
flatpak_context_take_filesystem (context, g_steal_pointer (&filesystem),
FLATPAK_FILESYSTEM_MODE_NONE);
else
flatpak_context_take_filesystem (context, g_steal_pointer (&filesystem), mode);
g_assert (mode == FLATPAK_FILESYSTEM_MODE_NONE || !remove);
flatpak_context_take_filesystem (context, g_steal_pointer (&filesystem), mode);
}
}
}
@@ -1851,11 +1957,24 @@ flatpak_context_save_metadata (FlatpakContext *context,
{
g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func (g_free);
/* Serialize host-reset first, because order can matter in
* corner cases. */
if (g_hash_table_lookup_extended (context->filesystems, "host-reset",
NULL, &value))
{
g_warn_if_fail (GPOINTER_TO_INT (value) == FLATPAK_FILESYSTEM_MODE_NONE);
g_ptr_array_add (array, g_strdup ("!host:reset"));
}
g_hash_table_iter_init (&iter, context->filesystems);
while (g_hash_table_iter_next (&iter, &key, &value))
{
FlatpakFilesystemMode mode = GPOINTER_TO_INT (value);
/* We already did this */
if (g_str_equal (key, "host-reset"))
continue;
g_ptr_array_add (array, unparse_filesystem_flags (key, mode));
}
@@ -1994,7 +2113,8 @@ flatpak_context_save_metadata (FlatpakContext *context,
void
flatpak_context_allow_host_fs (FlatpakContext *context)
{
flatpak_context_take_filesystem (context, g_strdup ("host"), FLATPAK_FILESYSTEM_MODE_READ_WRITE);
flatpak_context_take_filesystem (context, g_strdup ("host"),
FLATPAK_FILESYSTEM_MODE_READ_WRITE);
}
gboolean
@@ -2185,18 +2305,36 @@ flatpak_context_to_args (FlatpakContext *context,
g_ptr_array_add (args, g_strdup_printf ("--system-%s-name=%s", flatpak_policy_to_string (policy), name));
}
/* Serialize host-reset first, because order can matter in
* corner cases. */
if (g_hash_table_lookup_extended (context->filesystems, "host-reset",
NULL, &value))
{
g_warn_if_fail (GPOINTER_TO_INT (value) == FLATPAK_FILESYSTEM_MODE_NONE);
g_ptr_array_add (args, g_strdup ("--nofilesystem=host:reset"));
}
g_hash_table_iter_init (&iter, context->filesystems);
while (g_hash_table_iter_next (&iter, &key, &value))
{
g_autofree char *fs = NULL;
FlatpakFilesystemMode mode = GPOINTER_TO_INT (value);
/* We already did this */
if (g_str_equal (key, "host-reset"))
continue;
fs = unparse_filesystem_flags (key, mode);
if (mode != FLATPAK_FILESYSTEM_MODE_NONE)
{
g_autofree char *fs = unparse_filesystem_flags (key, mode);
g_ptr_array_add (args, g_strdup_printf ("--filesystem=%s", fs));
}
else
g_ptr_array_add (args, g_strdup_printf ("--nofilesystem=%s", (char *) key));
{
g_assert (fs[0] == '!');
g_ptr_array_add (args, g_strdup_printf ("--nofilesystem=%s", &fs[1]));
}
}
}

View File

@@ -248,6 +248,14 @@
accessing most of the home directory, but it will still
be allowed to access
<filename>$XDG_CONFIG_HOME/MyApp</filename>.
</para><para>
As a special case,
<option>--nofilesystem=host:reset</option>
will ignore all <option>--filesystem</option>
permissions inherited from the app manifest or a
lower-precedence layer of overrides, in addition to
having the behaviour of
<option>--nofilesystem=host</option>.
</para></listitem>
</varlistentry>

View File

@@ -389,6 +389,14 @@
accessing most of the home directory, but it will still
be allowed to access
<filename>$XDG_CONFIG_HOME/MyApp</filename>.
</para><para>
As a special case,
<option>--nofilesystem=host:reset</option>
will ignore all <option>--filesystem</option>
permissions inherited from the app manifest or
<citerefentry><refentrytitle>flatpak-override</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
in addition to having the behaviour of
<option>--nofilesystem=host</option>.
</para></listitem>
</varlistentry>

View File

@@ -607,8 +607,8 @@ test_filesystems (void)
gboolean ret;
g_test_message ("%s", fs->input);
ret = flatpak_context_parse_filesystem (fs->input, &normalized, &mode,
&error);
ret = flatpak_context_parse_filesystem (fs->input, FALSE,
&normalized, &mode, &error);
g_assert_no_error (error);
g_assert_true (ret);
@@ -629,8 +629,8 @@ test_filesystems (void)
gboolean ret;
g_test_message ("%s", not->input);
ret = flatpak_context_parse_filesystem (not->input, &normalized, &mode,
&error);
ret = flatpak_context_parse_filesystem (not->input, FALSE,
&normalized, &mode, &error);
g_test_message ("-> %s", error ? error->message : "(no error)");
g_assert_error (error, G_OPTION_ERROR, not->code);
g_assert_false (ret);