From 041d3eeb39b5d703259b1ddff88cc5abbae254a1 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Wed, 19 Oct 2016 20:39:17 +0200 Subject: [PATCH] Add generic static permissions to metadata This allows you do do something like flatpak build-finish --add-policy=subsystem.key=v1 --add-policy=subsystem.key=v2 Which maps to this metadata keys: [Policy subsystem] key=v1;v2; You can also --remove-policy to remove values from a key. The policy values are parsed from the app and runtime metadata, and are overridable by per-app overrides and on the command line, however the values are never used by flatpak. They do end up in the flatpak-info file for the running application though, so external agents can look at them. These --- common/flatpak-run.c | 176 +++++++++++++++++++++++++++++++++++++++++++ common/flatpak-run.h | 1 + 2 files changed, 177 insertions(+) diff --git a/common/flatpak-run.c b/common/flatpak-run.c index 3843e34c..f92f60ed 100644 --- a/common/flatpak-run.c +++ b/common/flatpak-run.c @@ -130,6 +130,7 @@ struct FlatpakContext GHashTable *filesystems; GHashTable *session_bus_policy; GHashTable *system_bus_policy; + GHashTable *generic_policy; }; FlatpakContext * @@ -143,6 +144,8 @@ flatpak_context_new (void) context->filesystems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); context->session_bus_policy = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); context->system_bus_policy = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + context->generic_policy = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify)g_strfreev); return context; } @@ -155,6 +158,7 @@ flatpak_context_free (FlatpakContext *context) g_hash_table_destroy (context->filesystems); g_hash_table_destroy (context->session_bus_policy); g_hash_table_destroy (context->system_bus_policy); + g_hash_table_destroy (context->generic_policy); g_slice_free (FlatpakContext, context); } @@ -477,6 +481,38 @@ flatpak_context_set_system_bus_policy (FlatpakContext *context, g_hash_table_insert (context->system_bus_policy, g_strdup (name), GINT_TO_POINTER (policy)); } +void +flatpak_context_apply_generic_policy (FlatpakContext *context, + const char *key, + const char *value) +{ + GPtrArray *new = g_ptr_array_new (); + const char **old_v; + int i; + + g_assert (strchr (key, '.') != NULL); + + old_v = g_hash_table_lookup (context->generic_policy, key); + for (i = 0; old_v != NULL && old_v[i] != NULL; i++) + { + const char *old = old_v[i]; + const char *cmp1 = old; + const char *cmp2 = value; + if (*cmp1 == '!') + cmp1++; + if (*cmp2 == '!') + cmp2++; + if (strcmp (cmp1, cmp2) != 0) + g_ptr_array_add (new, g_strdup (old)); + } + + g_ptr_array_add (new, g_strdup (value)); + g_ptr_array_add (new, NULL); + + g_hash_table_insert (context->generic_policy, g_strdup (key), + g_ptr_array_free (new, FALSE)); +} + static void flatpak_context_set_persistent (FlatpakContext *context, const char *path) @@ -693,6 +729,21 @@ flatpak_context_merge (FlatpakContext *context, g_hash_table_iter_init (&iter, other->system_bus_policy); while (g_hash_table_iter_next (&iter, &key, &value)) g_hash_table_insert (context->system_bus_policy, g_strdup (key), value); + + g_hash_table_iter_init (&iter, other->system_bus_policy); + while (g_hash_table_iter_next (&iter, &key, &value)) + g_hash_table_insert (context->system_bus_policy, g_strdup (key), value); + + g_hash_table_iter_init (&iter, other->generic_policy); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + const char **policy_values = (const char **)value; + int i; + + for (i = 0; policy_values[i] != NULL; i++) + flatpak_context_apply_generic_policy (context, (char *)key, policy_values[i]); + } + } static gboolean @@ -950,6 +1001,63 @@ option_system_talk_name_cb (const gchar *option_name, return TRUE; } +static gboolean +option_add_generic_policy_cb (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + FlatpakContext *context = data; + char *t; + g_autofree char *key = NULL; + const char *policy_value; + + t = strchr (value, '='); + if (t == NULL) + return flatpak_fail (error, "--policy arguments must be in the form SUBSYSTEM.KEY=[!]VALUE"); + policy_value = t + 1; + key = g_strndup (value, t - value); + if (strchr (key, '.') == NULL) + return flatpak_fail (error, "--policy arguments must be in the form SUBSYSTEM.KEY=[!]VALUE"); + + if (policy_value[0] == '!') + return flatpak_fail (error, "--policy values can't start with \"!\""); + + flatpak_context_apply_generic_policy (context, key, policy_value); + + return TRUE; +} + +static gboolean +option_remove_generic_policy_cb (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + FlatpakContext *context = data; + char *t; + g_autofree char *key = NULL; + const char *policy_value; + g_autofree char *extended_value = NULL; + + t = strchr (value, '='); + if (t == NULL) + return flatpak_fail (error, "--policy arguments must be in the form SUBSYSTEM.KEY=[!]VALUE"); + policy_value = t + 1; + key = g_strndup (value, t - value); + if (strchr (key, '.') == NULL) + return flatpak_fail (error, "--policy arguments must be in the form SUBSYSTEM.KEY=[!]VALUE"); + + if (policy_value[0] == '!') + return flatpak_fail (error, "--policy values can't start with \"!\""); + + extended_value = g_strconcat ("!", policy_value, NULL); + + flatpak_context_apply_generic_policy (context, key, extended_value); + + return TRUE; +} + static gboolean option_persist_cb (const gchar *option_name, const gchar *value, @@ -980,6 +1088,8 @@ static GOptionEntry context_options[] = { { "talk-name", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_talk_name_cb, N_("Allow app to talk to name on the session bus"), N_("DBUS_NAME") }, { "system-own-name", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_system_own_name_cb, N_("Allow app to own name on the system bus"), N_("DBUS_NAME") }, { "system-talk-name", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_system_talk_name_cb, N_("Allow app to talk to name on the system bus"), N_("DBUS_NAME") }, + { "add-policy", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_add_generic_policy_cb, N_("Add generic policy option"), N_("SUBSYSTEM.KEY=VALUE") }, + { "remove-policy", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_remove_generic_policy_cb, N_("Remove generic policy option"), N_("SUBSYSTEM.KEY=VALUE") }, { "persist", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_persist_cb, N_("Persist home directory"), N_("FILENAME") }, /* This is not needed/used anymore, so hidden, but we accept it for backwards compat */ { "no-desktop", 0, G_OPTION_FLAG_IN_MAIN | G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &option_no_desktop_deprecated, N_("Don't require a running session (no cgroups creation)"), NULL }, @@ -1031,6 +1141,7 @@ flatpak_context_load_metadata (FlatpakContext *context, GError **error) { gboolean remove; + g_auto(GStrv) groups = NULL; int i; if (g_key_file_has_key (metakey, FLATPAK_METADATA_GROUP_CONTEXT, FLATPAK_METADATA_KEY_SHARED, NULL)) @@ -1204,6 +1315,33 @@ flatpak_context_load_metadata (FlatpakContext *context, } } + groups = g_key_file_get_groups (metakey, NULL); + for (i = 0; groups[i] != NULL; i++) + { + const char *group = groups[i]; + const char *subsystem; + int j; + + if (g_str_has_prefix (group, FLATPAK_METADATA_GROUP_PREFIX_POLICY)) + { + g_auto(GStrv) keys = NULL; + subsystem = group + strlen (FLATPAK_METADATA_GROUP_PREFIX_POLICY); + keys = g_key_file_get_keys (metakey, group, NULL, NULL); + for (j = 0; keys != NULL && keys[j] != NULL; j++) + { + const char *key = keys[j]; + g_autofree char *policy_key = g_strdup_printf ("%s.%s", subsystem, key); + g_auto(GStrv) values = NULL; + int k; + + values = g_key_file_get_string_list (metakey, group, key, NULL, NULL); + for (k = 0; values != NULL && values[k] != NULL; k++) + flatpak_context_apply_generic_policy (context, policy_key, + values[k]); + } + } + } + return TRUE; } @@ -1226,6 +1364,8 @@ flatpak_context_save_metadata (FlatpakContext *context, FlatpakContextDevices devices_valid = context->devices_valid; FlatpakContextFeatures features_mask = context->features; FlatpakContextFeatures features_valid = context->features; + g_auto(GStrv) groups = NULL; + int i; if (flatten) { @@ -1387,6 +1527,42 @@ flatpak_context_save_metadata (FlatpakContext *context, FLATPAK_METADATA_GROUP_ENVIRONMENT, (char *) key, (char *) value); } + + + groups = g_key_file_get_groups (metakey, NULL); + for (i = 0; groups[i] != NULL; i++) + { + const char *group = groups[i]; + if (g_str_has_prefix (group, FLATPAK_METADATA_GROUP_PREFIX_POLICY)) + g_key_file_remove_group (metakey, group, NULL); + } + + g_hash_table_iter_init (&iter, context->generic_policy); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + g_auto(GStrv) parts = g_strsplit ((const char *)key, ".", 2); + g_autofree char *group = NULL; + g_assert (parts[1] != NULL); + const char **policy_values = (const char **)value; + g_autoptr(GPtrArray) new = g_ptr_array_new (); + + for (i = 0; policy_values[i] != NULL; i++) + { + const char *policy_value = policy_values[i]; + + if (!flatten || policy_value[0] != '!') + g_ptr_array_add (new, (char *)policy_value); + } + + if (new->len > 0) + { + group = g_strconcat (FLATPAK_METADATA_GROUP_PREFIX_POLICY, + parts[0], NULL); + g_key_file_set_string_list (metakey, group, parts[1], + (const char * const*)new->pdata, + new->len); + } + } } void diff --git a/common/flatpak-run.h b/common/flatpak-run.h index 6da507de..c06636f8 100644 --- a/common/flatpak-run.h +++ b/common/flatpak-run.h @@ -32,6 +32,7 @@ gboolean flatpak_run_in_transient_unit (const char *app_id, #define FLATPAK_METADATA_GROUP_CONTEXT "Context" #define FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY "Session Bus Policy" #define FLATPAK_METADATA_GROUP_SYSTEM_BUS_POLICY "System Bus Policy" +#define FLATPAK_METADATA_GROUP_PREFIX_POLICY "Policy " #define FLATPAK_METADATA_GROUP_ENVIRONMENT "Environment" #define FLATPAK_METADATA_KEY_SHARED "shared" #define FLATPAK_METADATA_KEY_SOCKETS "sockets"