mirror of
https://github.com/flatpak/flatpak.git
synced 2026-01-10 00:38:01 -05:00
context: Add new FlatpakPermission(s) type
This adds a new type that is meant to track more complex permissions than a pure bitmask, including conditional dependencies. It is not yet used, but it will be used for at least the socket and device permissions. For each possible permission we track whether the permission is unconditionally allowed, unconditionally disallowed, or if it is conditionally allowed (allowed if some conditions are met). Additionally we track for each permission whether stacking the context on top of another will reset permissions in the layer below. This is a new feature, because previously merging layers *always* overrode the value from below, whereas conditional permissions can either stack on top of, or replace the underlying layer. In terms of the keyfile, there are 4 possible types of layers: 1) Add a permission, removes all partial permissions below socket=pipewire 2) Remove access, removes both partial and full permissions below socket=!pipewire 3) Adds a partial permission, keeping whatever is already there: socket=pipewire;pipewire:if:has-wayland Note: This adds a plain `pipewire` for backwards compat. Note: If parent has full pipewire access, this is a no-op. 4) Adds a partial permission, remove all previous access socket=!pipewire;pipewire;pipewire:if:has-wayland Note: This seems weird as it has both !pipewire and pipewire, but older versions will read these in order and get the right result. Additionally, partial permissions can have multiple conditions: socket=pipewire;pipewire:if:has-something;pipewire:if:has-other; In such a case the socket will be accessible if any condition matches. Conditions can also be negated: socket=pipewire;pipewire:if:!has-something; Due to backwards compatibility we have to add the non-conditional permission as well as the conditional, as older flatpak will ignore the conditional. This is handle when serializing/deserializing the permissions, and internally we don't have to care about this.
This commit is contained in:
committed by
Sebastian Wick
parent
5852e6f5e6
commit
b93b58a44e
@@ -80,6 +80,13 @@ typedef enum {
|
||||
FLATPAK_CONTEXT_FEATURE_PER_APP_DEV_SHM = 1 << 4,
|
||||
} FlatpakContextFeatures;
|
||||
|
||||
typedef enum {
|
||||
FLATPAK_CONTEXT_CONDITION_TRUE = 1 << 0,
|
||||
FLATPAK_CONTEXT_CONDITION_FALSE = 1 << 1,
|
||||
FLATPAK_CONTEXT_CONDITION_HAS_INPUT_DEV = 1 << 2,
|
||||
FLATPAK_CONTEXT_CONDITION_HAS_WAYLAND = 1 << 3,
|
||||
} FlatpakContextConditions;
|
||||
|
||||
struct FlatpakContext
|
||||
{
|
||||
FlatpakContextShares shares;
|
||||
@@ -101,6 +108,9 @@ struct FlatpakContext
|
||||
GHashTable *hidden_usb_devices;
|
||||
};
|
||||
|
||||
/* Gets a single condition as param and returns whether the condition is true. */
|
||||
typedef gboolean (*FlatpakContextConditionEvaluator) (FlatpakContextConditions condition);
|
||||
|
||||
extern const char *flatpak_context_sockets[];
|
||||
extern const char *flatpak_context_devices[];
|
||||
extern const char *flatpak_context_features[];
|
||||
|
||||
@@ -98,6 +98,842 @@ const char *flatpak_context_special_filesystems[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *flatpak_context_conditions[] = {
|
||||
"true",
|
||||
"false",
|
||||
"has-input-device",
|
||||
"has-wayland",
|
||||
NULL
|
||||
};
|
||||
|
||||
FlatpakContextConditions flatpak_context_true_conditions =
|
||||
FLATPAK_CONTEXT_CONDITION_TRUE |
|
||||
FLATPAK_CONTEXT_CONDITION_HAS_INPUT_DEV;
|
||||
|
||||
static const char *parse_negated (const char *option, gboolean *negated);
|
||||
static guint32 flatpak_context_bitmask_from_string (const char *name, const char **names);
|
||||
|
||||
typedef struct FlatpakPermission FlatpakPermission;
|
||||
|
||||
struct FlatpakPermission {
|
||||
/* Is the permission unconditionally allowed */
|
||||
gboolean allowed;
|
||||
/* When layering, reset all permissions below */
|
||||
gboolean reset;
|
||||
/* Assumes allowed is false */
|
||||
GPtrArray *conditionals;
|
||||
|
||||
/* Only used during deserialization */
|
||||
gboolean disallow_if_conditional;
|
||||
gboolean disallow_if_conditional_original_reset;
|
||||
GPtrArray *disallow_if_conditional_original_conditionals;
|
||||
};
|
||||
|
||||
|
||||
static FlatpakPermission *
|
||||
flatpak_permission_new (void)
|
||||
{
|
||||
FlatpakPermission *permission;
|
||||
|
||||
permission = g_slice_new0 (FlatpakPermission);
|
||||
permission->conditionals = g_ptr_array_new_with_free_func (g_free);
|
||||
permission->disallow_if_conditional_original_conditionals =
|
||||
g_ptr_array_new_with_free_func (g_free);
|
||||
|
||||
return permission;
|
||||
};
|
||||
|
||||
static void
|
||||
flatpak_permission_free (FlatpakPermission *permission)
|
||||
{
|
||||
g_ptr_array_free (permission->conditionals, TRUE);
|
||||
g_ptr_array_free (permission->disallow_if_conditional_original_conditionals,
|
||||
TRUE);
|
||||
g_slice_free (FlatpakPermission, permission);
|
||||
}
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakPermission, flatpak_permission_free)
|
||||
|
||||
static FlatpakPermission *
|
||||
flatpak_permission_dup (FlatpakPermission *permission)
|
||||
{
|
||||
FlatpakPermission *copy = NULL;
|
||||
|
||||
copy = flatpak_permission_new ();
|
||||
copy->allowed = permission->allowed;
|
||||
copy->reset = permission->reset;
|
||||
|
||||
for (size_t i = 0; i < permission->conditionals->len; i++) {
|
||||
const char *condition = permission->conditionals->pdata[i];
|
||||
|
||||
g_ptr_array_add (copy->conditionals, g_strdup (condition));
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_permission_set_not_allowed (FlatpakPermission *permission)
|
||||
{
|
||||
permission->allowed = FALSE;
|
||||
permission->reset = TRUE;
|
||||
g_ptr_array_set_size (permission->conditionals, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_permission_set_allowed (FlatpakPermission *permission)
|
||||
{
|
||||
permission->allowed = TRUE;
|
||||
/* We reset even when allowed, because lower layer conditionals being added
|
||||
* at merge would make this non-conditional layer conditional. */
|
||||
permission->reset = TRUE;
|
||||
g_ptr_array_set_size (permission->conditionals, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_permission_set_allowed_if (FlatpakPermission *permission,
|
||||
const char *condition)
|
||||
{
|
||||
/* If we are already unconditionally allowed, don't add useless conditionals */
|
||||
if (permission->allowed)
|
||||
return;
|
||||
|
||||
/* Check if its already there */
|
||||
if (g_ptr_array_find_with_equal_func (permission->conditionals,
|
||||
condition,
|
||||
g_str_equal, NULL))
|
||||
return;
|
||||
|
||||
g_ptr_array_add (permission->conditionals, g_strdup (condition));
|
||||
g_ptr_array_sort (permission->conditionals, flatpak_strcmp0_ptr);
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_permission_serialize (FlatpakPermission *permission,
|
||||
const char *name,
|
||||
GPtrArray *res,
|
||||
gboolean flatten)
|
||||
{
|
||||
if (permission->allowed)
|
||||
{
|
||||
/* Completely allowed */
|
||||
|
||||
g_ptr_array_add (res, g_strdup (name));
|
||||
g_assert (permission->conditionals->len == 0);
|
||||
/* A non-conditional add always implies reset, so no need to serialize that */
|
||||
}
|
||||
else if (permission->conditionals->len > 0)
|
||||
{
|
||||
/* Partially allowed */
|
||||
|
||||
if (permission->reset && !flatten)
|
||||
g_ptr_array_add (res, g_strdup_printf ("!%s", name));
|
||||
|
||||
/* As backwards compat for pre-conditional flatpaks we unconditionally
|
||||
* add this first. New versions will ignore this if there are
|
||||
* any conditionals.
|
||||
* Note: This may result in both "!foo" and "foo", but that
|
||||
* is fine as the "foo" is last and wins for older flatpaks.
|
||||
*/
|
||||
g_ptr_array_add (res, g_strdup (name));
|
||||
|
||||
for (size_t i = 0; i < permission->conditionals->len; i++)
|
||||
{
|
||||
const char *conditional = permission->conditionals->pdata[i];
|
||||
|
||||
g_ptr_array_add (res, g_strdup_printf ("if:%s:%s", name, conditional));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Completely disallowed */
|
||||
|
||||
if (!flatten)
|
||||
g_ptr_array_add (res, g_strdup_printf ("!%s", name));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_permission_to_args (FlatpakPermission *permission,
|
||||
const char *argname,
|
||||
const char *name,
|
||||
GPtrArray *args)
|
||||
{
|
||||
if (permission->allowed)
|
||||
{
|
||||
/* Completely allowed */
|
||||
|
||||
g_ptr_array_add (args, g_strdup_printf ("--%s=%s", argname, name));
|
||||
}
|
||||
else if (permission->conditionals->len > 0)
|
||||
{
|
||||
/* Partially allowed */
|
||||
|
||||
if (permission->reset)
|
||||
g_ptr_array_add (args, g_strdup_printf ("--no%s=%s", argname, name));
|
||||
|
||||
for (size_t i = 0; i < permission->conditionals->len; i++)
|
||||
{
|
||||
const char *conditional = permission->conditionals->pdata[i];
|
||||
|
||||
g_ptr_array_add (args, g_strdup_printf ("--%s-if=%s:%s",
|
||||
argname, name, conditional));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Completely disallowed */
|
||||
|
||||
g_ptr_array_add (args, g_strdup_printf ("--no%s=%s", argname, name));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_permission_deserialize (FlatpakPermission *permission,
|
||||
gboolean negated,
|
||||
const char *maybe_condition)
|
||||
{
|
||||
/* This can't use the flatpak_permission_set_ helpers, because we
|
||||
* have to be wary of the backward compat non-conditional permission
|
||||
* in case conditionals are used. */
|
||||
|
||||
if (maybe_condition == NULL)
|
||||
{
|
||||
/* Non-conditional option, these are always before conditionals,
|
||||
* but if non-negated could be backwards compat for later conditional. */
|
||||
if (negated)
|
||||
{
|
||||
permission->allowed = FALSE;
|
||||
permission->reset = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
GPtrArray *tmp;
|
||||
|
||||
/* Allow us to revert this if it is a backwards compat */
|
||||
permission->disallow_if_conditional = TRUE;
|
||||
permission->disallow_if_conditional_original_reset = permission->reset;
|
||||
tmp = permission->conditionals;
|
||||
permission->conditionals =
|
||||
permission->disallow_if_conditional_original_conditionals;
|
||||
permission->disallow_if_conditional_original_conditionals = tmp;
|
||||
|
||||
permission->allowed = TRUE;
|
||||
permission->reset = TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Conditional option */
|
||||
if (permission->disallow_if_conditional)
|
||||
{
|
||||
GPtrArray *tmp;
|
||||
|
||||
/* Previous allow was a backward compat, revert it */
|
||||
permission->allowed = FALSE;
|
||||
permission->reset = permission->disallow_if_conditional_original_reset;
|
||||
permission->disallow_if_conditional = FALSE;
|
||||
tmp = permission->disallow_if_conditional_original_conditionals;
|
||||
permission->disallow_if_conditional_original_conditionals =
|
||||
permission->conditionals;
|
||||
permission->conditionals = tmp;
|
||||
g_ptr_array_set_size (
|
||||
permission->disallow_if_conditional_original_conditionals, 0);
|
||||
}
|
||||
|
||||
g_ptr_array_add (permission->conditionals, g_strdup (maybe_condition));
|
||||
g_ptr_array_sort (permission->conditionals, flatpak_strcmp0_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_permission_merge (FlatpakPermission *permission,
|
||||
FlatpakPermission *other_permission)
|
||||
{
|
||||
if (other_permission->reset)
|
||||
{
|
||||
permission->reset = TRUE;
|
||||
g_ptr_array_set_size (permission->conditionals, 0);
|
||||
}
|
||||
|
||||
permission->allowed = other_permission->allowed;
|
||||
|
||||
for (size_t i = 0; i < other_permission->conditionals->len; i++)
|
||||
{
|
||||
const char *conditional = other_permission->conditionals->pdata[i];
|
||||
|
||||
/* Check if its already there */
|
||||
if (g_ptr_array_find_with_equal_func (permission->conditionals,
|
||||
conditional,
|
||||
g_str_equal, NULL))
|
||||
return;
|
||||
|
||||
g_ptr_array_add (permission->conditionals, g_strdup (conditional));
|
||||
}
|
||||
|
||||
g_ptr_array_sort (permission->conditionals, flatpak_strcmp0_ptr);
|
||||
|
||||
/* Internal consistency check */
|
||||
if (permission->allowed)
|
||||
g_assert (permission->conditionals->len == 0);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
flatpak_permission_compute_allowed (FlatpakPermission *permission,
|
||||
FlatpakContextConditionEvaluator evaluator)
|
||||
{
|
||||
if (permission->allowed)
|
||||
return TRUE;
|
||||
|
||||
for (size_t i = 0; i < permission->conditionals->len; i++)
|
||||
{
|
||||
const char *conditional = permission->conditionals->pdata[i];
|
||||
gboolean negated;
|
||||
const char *condition_str;
|
||||
guint32 condition;
|
||||
|
||||
condition_str = parse_negated (conditional, &negated);
|
||||
condition =
|
||||
flatpak_context_bitmask_from_string (condition_str,
|
||||
flatpak_context_conditions);
|
||||
|
||||
/* If condition is 0 it means this version of flatpak doesn't know
|
||||
* about the condition and it cannot be satisfied. */
|
||||
if (condition == 0)
|
||||
continue;
|
||||
|
||||
/* Conditions which are always true in this version of flatpak */
|
||||
if ((condition & flatpak_context_true_conditions) && !negated)
|
||||
return TRUE;
|
||||
|
||||
/* Conditions which need runtime evaluation */
|
||||
if (evaluator && evaluator (condition) == !negated)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* No condition evaluated to TRUE, so disable the thing */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
flatpak_permission_adds_permissions (FlatpakPermission *old,
|
||||
FlatpakPermission *new)
|
||||
{
|
||||
size_t i = 0, j = 0;
|
||||
|
||||
if (old->allowed)
|
||||
return FALSE;
|
||||
|
||||
if (new->allowed)
|
||||
return TRUE;
|
||||
|
||||
if (new->conditionals->len > old->conditionals->len)
|
||||
return TRUE;
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
const char *old_cond = old->conditionals->pdata[i];
|
||||
const char *new_cond = new->conditionals->pdata[j];
|
||||
int res;
|
||||
|
||||
if (old_cond == NULL)
|
||||
return new_cond != NULL;
|
||||
|
||||
if (new_cond == NULL)
|
||||
return FALSE;
|
||||
|
||||
res = strcmp (old_cond, new_cond);
|
||||
if (res == 0) /* Same conditional */
|
||||
{
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
else if (res < 0) /* Old conditional was removed */
|
||||
{
|
||||
i++;
|
||||
}
|
||||
else /* new conditional */
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static GHashTable *
|
||||
flatpak_permissions_new (void)
|
||||
{
|
||||
return g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
(GDestroyNotify) g_free,
|
||||
(GDestroyNotify) flatpak_permission_free);
|
||||
}
|
||||
|
||||
static GHashTable *
|
||||
flatpak_permissions_dup (GHashTable *old)
|
||||
{
|
||||
GHashTable *new;
|
||||
const char *name;
|
||||
FlatpakPermission *old_permission;
|
||||
GHashTableIter iter;
|
||||
|
||||
new = flatpak_permissions_new ();
|
||||
|
||||
g_hash_table_iter_init (&iter, old);
|
||||
while (g_hash_table_iter_next (&iter,
|
||||
(gpointer *) &name,
|
||||
(gpointer *) &old_permission))
|
||||
{
|
||||
g_hash_table_insert (new,
|
||||
g_strdup (name),
|
||||
flatpak_permission_dup (old_permission));
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
static FlatpakPermission *
|
||||
flatpak_permissions_ensure (GHashTable *permissions,
|
||||
const char *name)
|
||||
{
|
||||
FlatpakPermission *permission = g_hash_table_lookup (permissions, name);
|
||||
|
||||
if (permission == NULL)
|
||||
{
|
||||
permission = flatpak_permission_new ();
|
||||
g_hash_table_insert (permissions, g_strdup (name), permission);
|
||||
}
|
||||
|
||||
return permission;
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_permissions_set_not_allowed (GHashTable *permissions,
|
||||
const char *name)
|
||||
{
|
||||
flatpak_permission_set_not_allowed (flatpak_permissions_ensure (permissions,
|
||||
name));
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_permissions_set_allowed (GHashTable *permissions,
|
||||
const char *name)
|
||||
{
|
||||
flatpak_permission_set_allowed (flatpak_permissions_ensure (permissions,
|
||||
name));
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_permissions_set_allowed_if (GHashTable *permissions,
|
||||
const char *name,
|
||||
const char *condition)
|
||||
{
|
||||
flatpak_permission_set_allowed_if (flatpak_permissions_ensure (permissions,
|
||||
name),
|
||||
condition);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
flatpak_permissions_allows_unconditionally (GHashTable *permissions,
|
||||
const char *name)
|
||||
{
|
||||
FlatpakPermission *permission = g_hash_table_lookup (permissions, name);
|
||||
|
||||
if (permission)
|
||||
return permission->allowed;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_permissions_to_args (GHashTable *permissions,
|
||||
const char *argname,
|
||||
GPtrArray *args)
|
||||
{
|
||||
g_autoptr(GList) ordered_keys = NULL;
|
||||
|
||||
ordered_keys = g_hash_table_get_keys (permissions);
|
||||
ordered_keys = g_list_sort (ordered_keys, (GCompareFunc) strcmp);
|
||||
|
||||
for (GList *l = ordered_keys; l != NULL; l = l->next)
|
||||
{
|
||||
const char *name = l->data;
|
||||
FlatpakPermission *permission = g_hash_table_lookup (permissions, name);
|
||||
|
||||
flatpak_permission_to_args (permission, argname, name, args);
|
||||
}
|
||||
}
|
||||
|
||||
static char **
|
||||
flatpak_permissions_to_strv (GHashTable *permissions,
|
||||
gboolean flatten)
|
||||
{
|
||||
g_autoptr(GList) ordered_keys = NULL;
|
||||
g_autoptr(GPtrArray) res = g_ptr_array_new ();
|
||||
|
||||
ordered_keys = g_hash_table_get_keys (permissions);
|
||||
ordered_keys = g_list_sort (ordered_keys, (GCompareFunc) strcmp);
|
||||
|
||||
for (GList *l = ordered_keys; l != NULL; l = l->next)
|
||||
{
|
||||
const char *name = l->data;
|
||||
FlatpakPermission *permission = g_hash_table_lookup (permissions, name);
|
||||
|
||||
flatpak_permission_serialize (permission, name, res, flatten);
|
||||
}
|
||||
|
||||
g_ptr_array_add (res, NULL);
|
||||
return (char **)g_ptr_array_free (g_steal_pointer (&res), FALSE);
|
||||
}
|
||||
|
||||
static guint32
|
||||
flatpak_permissions_compute_allowed (GHashTable *permissions,
|
||||
const char **names,
|
||||
FlatpakContextConditionEvaluator evaluator)
|
||||
{
|
||||
guint32 bitmask = 0;
|
||||
|
||||
for (size_t i = 0; names[i] != NULL; i++)
|
||||
{
|
||||
const char *name = names[i];
|
||||
FlatpakPermission *permission = g_hash_table_lookup (permissions, name);
|
||||
|
||||
if (permission &&
|
||||
flatpak_permission_compute_allowed (permission, evaluator))
|
||||
bitmask |= 1 << i;
|
||||
}
|
||||
|
||||
return bitmask;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
flatpak_permissions_from_strv (GHashTable *permissions,
|
||||
const char **strv,
|
||||
GError **error)
|
||||
{
|
||||
for (size_t i = 0; strv[i] != NULL; i++)
|
||||
{
|
||||
g_auto(GStrv) tokens = g_strsplit (strv[i], ":", 3);
|
||||
const char *name = NULL;
|
||||
gboolean negated = FALSE;
|
||||
const char *condition = NULL;
|
||||
FlatpakPermission *permission;
|
||||
|
||||
if (strcmp (tokens[0], "if") == 0)
|
||||
{
|
||||
if (g_strv_length (tokens) != 3)
|
||||
{
|
||||
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
|
||||
_("Invalid permission syntax: %s"), strv[i]);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
name = tokens[1];
|
||||
condition = tokens[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (g_strv_length (tokens) != 1)
|
||||
{
|
||||
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
|
||||
_("Invalid permission syntax: %s"), strv[i]);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
name = parse_negated (tokens[0], &negated);
|
||||
}
|
||||
|
||||
permission = flatpak_permissions_ensure (permissions, name);
|
||||
flatpak_permission_deserialize (permission, negated, condition);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_permissions_merge (GHashTable *permissions,
|
||||
GHashTable *other)
|
||||
{
|
||||
const char *name;
|
||||
FlatpakPermission *other_permission;
|
||||
GHashTableIter iter;
|
||||
|
||||
g_hash_table_iter_init (&iter, other);
|
||||
while (g_hash_table_iter_next (&iter,
|
||||
(gpointer *) &name,
|
||||
(gpointer *) &other_permission))
|
||||
{
|
||||
FlatpakPermission *permission = g_hash_table_lookup (permissions, name);
|
||||
|
||||
if (permission)
|
||||
{
|
||||
flatpak_permission_merge (permission, other_permission);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_hash_table_insert (permissions,
|
||||
g_strdup (name),
|
||||
flatpak_permission_dup (other_permission));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
flatpak_permissions_adds_permissions (GHashTable *old,
|
||||
GHashTable *new)
|
||||
{
|
||||
const char *name;
|
||||
FlatpakPermission *new_permission;
|
||||
GHashTableIter iter;
|
||||
|
||||
g_hash_table_iter_init (&iter, new);
|
||||
while (g_hash_table_iter_next (&iter,
|
||||
(gpointer *) &name,
|
||||
(gpointer *) &new_permission))
|
||||
{
|
||||
FlatpakPermission *old_permission = g_hash_table_lookup (old, name);
|
||||
|
||||
if (old_permission)
|
||||
{
|
||||
if (flatpak_permission_adds_permissions (old_permission,
|
||||
new_permission))
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (new_permission->allowed ||
|
||||
new_permission->conditionals->len > 0)
|
||||
return TRUE; /* new is completely new permission */
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#ifdef INCLUDE_INTERNAL_TESTS
|
||||
static void flatpak_permissions_test_basic (void)
|
||||
{
|
||||
/* This is in canonical form, so must be kept sorted by name */
|
||||
const char *perms_strv[] =
|
||||
{
|
||||
/* Regular unconditional allowed (resets) */
|
||||
"allowed",
|
||||
|
||||
/* conditional allowed with two conditions (doesn't reset) */
|
||||
"cond1", /* backwards compat */
|
||||
"if:cond1:check1",
|
||||
"if:cond1:check2",
|
||||
|
||||
/* conditional allowed with one conditions (doesn't reset) */
|
||||
"cond2", /* backwards compat */
|
||||
"if:cond2:check3",
|
||||
|
||||
/* conditional allowed (resets) */
|
||||
"!cond3", /* reset */
|
||||
"cond3", /* backwards compat */
|
||||
"if:cond3:check3",
|
||||
|
||||
/* Regular unconditional disallowed (resets) */
|
||||
"!disallowed",
|
||||
|
||||
NULL,
|
||||
};
|
||||
|
||||
const char *perms_args[] =
|
||||
{
|
||||
"--socket=allowed",
|
||||
|
||||
"--socket-if=cond1:check1",
|
||||
"--socket-if=cond1:check2",
|
||||
|
||||
"--socket-if=cond2:check3",
|
||||
|
||||
/* conditional allowed (resets) */
|
||||
"--nosocket=cond3",
|
||||
"--socket-if=cond3:check3",
|
||||
|
||||
/* Regular unconditional disallowed (resets) */
|
||||
"--nosocket=disallowed",
|
||||
|
||||
NULL,
|
||||
};
|
||||
|
||||
GError *error = NULL;
|
||||
|
||||
/* Test parsing */
|
||||
g_autoptr(GHashTable) perms = flatpak_permissions_new ();
|
||||
gboolean ok = flatpak_permissions_from_strv (perms, perms_strv, &error);
|
||||
g_assert_true(ok);
|
||||
g_assert_no_error(error);
|
||||
g_assert_nonnull(perms);
|
||||
|
||||
g_assert_cmpint(g_hash_table_size (perms), ==, 5);
|
||||
|
||||
FlatpakPermission *allowed = g_hash_table_lookup (perms, "allowed");
|
||||
g_assert_nonnull(allowed);
|
||||
g_assert_true(allowed->allowed);
|
||||
g_assert_true(allowed->reset);
|
||||
g_assert(allowed->conditionals->len == 0);
|
||||
|
||||
FlatpakPermission *disallowed = g_hash_table_lookup (perms, "disallowed");
|
||||
g_assert_nonnull(disallowed);
|
||||
g_assert_false(disallowed->allowed);
|
||||
g_assert_true(disallowed->reset);
|
||||
g_assert(disallowed->conditionals->len == 0);
|
||||
|
||||
FlatpakPermission *cond1 = g_hash_table_lookup (perms, "cond1");
|
||||
g_assert_nonnull(cond1);
|
||||
g_assert_false(cond1->allowed);
|
||||
g_assert_false(cond1->reset);
|
||||
g_assert(cond1->conditionals->len == 2);
|
||||
g_assert_cmpstr(cond1->conditionals->pdata[0], ==, "check1");
|
||||
g_assert_cmpstr(cond1->conditionals->pdata[1], ==, "check2");
|
||||
|
||||
FlatpakPermission *cond2 = g_hash_table_lookup (perms, "cond2");
|
||||
g_assert_nonnull(cond2);
|
||||
g_assert_false(cond2->allowed);
|
||||
g_assert_false(cond2->reset);
|
||||
g_assert(cond2->conditionals->len == 1);
|
||||
g_assert_cmpstr(cond2->conditionals->pdata[0], ==, "check3");
|
||||
|
||||
FlatpakPermission *cond3 = g_hash_table_lookup (perms, "cond3");
|
||||
g_assert_nonnull(cond3);
|
||||
g_assert_false(cond3->allowed);
|
||||
g_assert_true(cond3->reset);
|
||||
g_assert(cond3->conditionals->len == 1);
|
||||
g_assert_cmpstr(cond3->conditionals->pdata[0], ==, "check3");
|
||||
|
||||
/* Test roundtrip */
|
||||
g_auto(GStrv) new_strv = flatpak_permissions_to_strv (perms, FALSE);
|
||||
g_assert_cmpstrv (perms_strv, new_strv);
|
||||
|
||||
g_autoptr(GPtrArray) args = g_ptr_array_new_with_free_func (g_free);
|
||||
flatpak_permissions_to_args (perms, "socket", args);
|
||||
g_ptr_array_add(args, NULL);
|
||||
g_assert_cmpstrv (perms_args, args->pdata);
|
||||
|
||||
/* Test copy */
|
||||
g_autoptr(FlatpakPermission) cond1_copy = flatpak_permission_dup(cond1);
|
||||
g_assert_nonnull(cond1_copy);
|
||||
g_assert_false(cond1_copy->allowed);
|
||||
g_assert_false(cond1_copy->reset);
|
||||
g_assert(cond1_copy->conditionals->len == 2);
|
||||
g_assert_cmpstr(cond1_copy->conditionals->pdata[0], ==, "check1");
|
||||
g_assert_cmpstr(cond1_copy->conditionals->pdata[1], ==, "check2");
|
||||
|
||||
/* Test setters: */
|
||||
{
|
||||
g_autoptr(FlatpakPermission) copy = flatpak_permission_dup(cond1);
|
||||
flatpak_permission_set_allowed (copy);
|
||||
g_assert_true (copy->allowed);
|
||||
g_assert_true (copy->reset);
|
||||
g_assert(copy->conditionals->len == 0);
|
||||
}
|
||||
|
||||
{
|
||||
g_autoptr(FlatpakPermission) copy = flatpak_permission_dup(cond1);
|
||||
flatpak_permission_set_not_allowed (copy);
|
||||
g_assert_false (copy->allowed);
|
||||
g_assert_true (copy->reset);
|
||||
g_assert(copy->conditionals->len == 0);
|
||||
}
|
||||
|
||||
{
|
||||
g_autoptr(FlatpakPermission) copy = flatpak_permission_dup(cond1);
|
||||
flatpak_permission_set_allowed_if (copy, "check0");
|
||||
g_assert_false (copy->allowed);
|
||||
g_assert_false (copy->reset);
|
||||
g_assert(copy->conditionals->len == 3);
|
||||
g_assert_cmpstr(copy->conditionals->pdata[0], ==, "check0");
|
||||
g_assert_cmpstr(copy->conditionals->pdata[1], ==, "check1");
|
||||
g_assert_cmpstr(copy->conditionals->pdata[2], ==, "check2");
|
||||
}
|
||||
|
||||
/* Test merge */
|
||||
{
|
||||
g_autoptr(FlatpakPermission) copy = flatpak_permission_dup(cond1);
|
||||
flatpak_permission_merge (copy, allowed);
|
||||
g_assert_true (copy->allowed);
|
||||
g_assert_true (copy->reset);
|
||||
g_assert(copy->conditionals->len == 0);
|
||||
}
|
||||
{
|
||||
g_autoptr(FlatpakPermission) copy = flatpak_permission_dup(cond1);
|
||||
flatpak_permission_merge (copy, disallowed);
|
||||
g_assert_false (copy->allowed);
|
||||
g_assert_true (copy->reset);
|
||||
g_assert(copy->conditionals->len == 0);
|
||||
}
|
||||
{
|
||||
/* Merge from non-reset conditional */
|
||||
g_autoptr(FlatpakPermission) copy = flatpak_permission_dup(cond1);
|
||||
flatpak_permission_merge (copy, cond2);
|
||||
g_assert_false (copy->allowed);
|
||||
g_assert_false (copy->reset);
|
||||
g_assert(copy->conditionals->len == 3);
|
||||
}
|
||||
{
|
||||
/* Merge from reset conditional */
|
||||
g_autoptr(FlatpakPermission) copy = flatpak_permission_dup(cond1);
|
||||
flatpak_permission_merge (copy, cond3);
|
||||
g_assert_false (copy->allowed);
|
||||
g_assert_true (copy->reset);
|
||||
g_assert(copy->conditionals->len == 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void flatpak_permissions_test_backwards_compat (void)
|
||||
{
|
||||
{
|
||||
/* Deserialize if:wayland:foo;wayland
|
||||
* The last wayland makes it unconditional. */
|
||||
g_autoptr(FlatpakPermission) perm = flatpak_permission_new ();
|
||||
|
||||
flatpak_permission_deserialize (perm, FALSE, "foo");
|
||||
flatpak_permission_deserialize (perm, FALSE, NULL);
|
||||
g_assert_true (perm->allowed);
|
||||
g_assert_true (perm->reset);
|
||||
g_assert_cmpint (perm->conditionals->len, ==, 0);
|
||||
}
|
||||
|
||||
{
|
||||
/* Deserialize wayland;if:wayland:foo;wayland
|
||||
* Should be the same as the one above. The first wayland is just for
|
||||
* backwards compat. */
|
||||
g_autoptr(FlatpakPermission) perm = flatpak_permission_new ();
|
||||
|
||||
flatpak_permission_deserialize (perm, FALSE, NULL);
|
||||
flatpak_permission_deserialize (perm, FALSE, "foo");
|
||||
flatpak_permission_deserialize (perm, FALSE, NULL);
|
||||
g_assert_true (perm->allowed);
|
||||
g_assert_true (perm->reset);
|
||||
g_assert_cmpint (perm->conditionals->len, ==, 0);
|
||||
}
|
||||
|
||||
{
|
||||
/* Deserialize if:wayland:foo;wayland;if:wayland:bar
|
||||
* Now the wayland is before a conditional, so it acts as backwards
|
||||
* compat. */
|
||||
g_autoptr(FlatpakPermission) perm = flatpak_permission_new ();
|
||||
|
||||
flatpak_permission_deserialize (perm, FALSE, "foo");
|
||||
flatpak_permission_deserialize (perm, FALSE, NULL);
|
||||
flatpak_permission_deserialize (perm, FALSE, "bar");
|
||||
g_assert_false (perm->allowed);
|
||||
g_assert_false (perm->reset);
|
||||
g_assert_cmpint (perm->conditionals->len, ==, 2);
|
||||
g_assert_cmpstr (perm->conditionals->pdata[0], ==, "bar");
|
||||
g_assert_cmpstr (perm->conditionals->pdata[1], ==, "foo");
|
||||
}
|
||||
}
|
||||
|
||||
FLATPAK_INTERNAL_TEST("/context/permissions/basic",
|
||||
flatpak_permissions_test_basic);
|
||||
FLATPAK_INTERNAL_TEST("/context/permissions/backwards-compat",
|
||||
flatpak_permissions_test_backwards_compat);
|
||||
|
||||
#endif /* INCLUDE_INTERNAL_TESTS */
|
||||
|
||||
FlatpakContext *
|
||||
flatpak_context_new (void)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user