build_check_rule(); if (new_layer) { /* Should already be checked by landlock_merge_ruleset(). */ if (WARN_ON_ONCE(num_layers >= LANDLOCK_MAX_NUM_LAYERS)) return ERR_PTR(-E2BIG);
new_num_layers = num_layers + 1;
} else {
new_num_layers = num_layers;
}
new_rule = kzalloc(struct_size(new_rule, layers, new_num_layers),
GFP_KERNEL_ACCOUNT); if (!new_rule) return ERR_PTR(-ENOMEM);
RB_CLEAR_NODE(&new_rule->node); if (is_object_pointer(id.type)) { /* This should have been caught by insert_rule(). */
WARN_ON_ONCE(!id.key.object);
landlock_get_object(id.key.object);
}
new_rule->key = id.key;
new_rule->num_layers = new_num_layers; /* Copies the original layer stack. */
memcpy(new_rule->layers, layers,
flex_array_size(new_rule, layers, num_layers)); if (new_layer) /* Adds a copy of @new_layer on the layer stack. */
new_rule->layers[new_rule->num_layers - 1] = *new_layer; return new_rule;
}
/** * insert_rule - Create and insert a rule in a ruleset * * @ruleset: The ruleset to be updated. * @id: The ID to build the new rule with. The underlying kernel object, if * any, must be held by the caller. * @layers: One or multiple layers to be copied into the new rule. * @num_layers: The number of @layers entries. * * When user space requests to add a new rule to a ruleset, @layers only * contains one entry and this entry is not assigned to any level. In this * case, the new rule will extend @ruleset, similarly to a boolean OR between * access rights. * * When merging a ruleset in a domain, or copying a domain, @layers will be * added to @ruleset as new constraints, similarly to a boolean AND between * access rights.
*/ staticint insert_rule(struct landlock_ruleset *const ruleset, conststruct landlock_id id, conststruct landlock_layer (*const layers)[], const size_t num_layers)
{ struct rb_node **walker_node; struct rb_node *parent_node = NULL; struct landlock_rule *new_rule; struct rb_root *root;
might_sleep();
lockdep_assert_held(&ruleset->lock); if (WARN_ON_ONCE(!layers)) return -ENOENT;
if (is_object_pointer(id.type) && WARN_ON_ONCE(!id.key.object)) return -ENOENT;
root = get_root(ruleset, id.type); if (IS_ERR(root)) return PTR_ERR(root);
/* Only a single-level layer should match an existing rule. */ if (WARN_ON_ONCE(num_layers != 1)) return -EINVAL;
/* If there is a matching rule, updates it. */ if ((*layers)[0].level == 0) { /* * Extends access rights when the request comes from * landlock_add_rule(2), i.e. @ruleset is not a domain.
*/ if (WARN_ON_ONCE(this->num_layers != 1)) return -EINVAL; if (WARN_ON_ONCE(this->layers[0].level != 0)) return -EINVAL;
this->layers[0].access |= (*layers)[0].access; return 0;
}
if (WARN_ON_ONCE(this->layers[0].level == 0)) return -EINVAL;
/* * Intersects access rights when it is a merge between a * ruleset and a domain.
*/
new_rule = create_rule(id, &this->layers, this->num_layers,
&(*layers)[0]); if (IS_ERR(new_rule)) return PTR_ERR(new_rule);
rb_replace_node(&this->node, &new_rule->node, root);
free_rule(this, id.type); return 0;
}
/* There is no match for @id. */
build_check_ruleset(); if (ruleset->num_rules >= LANDLOCK_MAX_NUM_RULES) return -E2BIG;
new_rule = create_rule(id, layers, num_layers, NULL); if (IS_ERR(new_rule)) return PTR_ERR(new_rule);
rb_link_node(&new_rule->node, parent_node, walker_node);
rb_insert_color(&new_rule->node, root);
ruleset->num_rules++; return 0;
}
might_sleep(); /* Should already be checked by landlock_merge_ruleset() */ if (WARN_ON_ONCE(!src)) return 0; /* Only merge into a domain. */ if (WARN_ON_ONCE(!dst || !dst->hierarchy)) return -EINVAL;
/* Locks @dst first because we are its only owner. */
mutex_lock(&dst->lock);
mutex_lock_nested(&src->lock, SINGLE_DEPTH_NESTING);
/* Stacks the new layer. */ if (WARN_ON_ONCE(src->num_layers != 1 || dst->num_layers < 1)) {
err = -EINVAL; goto out_unlock;
}
dst->access_masks[dst->num_layers - 1] =
landlock_upgrade_handled_access_masks(src->access_masks[0]);
/* Merges the @src inode tree. */
err = merge_tree(dst, src, LANDLOCK_KEY_INODE); if (err) goto out_unlock;
#if IS_ENABLED(CONFIG_INET) /* Merges the @src network port tree. */
err = merge_tree(dst, src, LANDLOCK_KEY_NET_PORT); if (err) goto out_unlock; #endif/* IS_ENABLED(CONFIG_INET) */
/* Locks @child first because we are its only owner. */
mutex_lock(&child->lock);
mutex_lock_nested(&parent->lock, SINGLE_DEPTH_NESTING);
/* Copies the @parent inode tree. */
err = inherit_tree(parent, child, LANDLOCK_KEY_INODE); if (err) goto out_unlock;
#if IS_ENABLED(CONFIG_INET) /* Copies the @parent network port tree. */
err = inherit_tree(parent, child, LANDLOCK_KEY_NET_PORT); if (err) goto out_unlock; #endif/* IS_ENABLED(CONFIG_INET) */
if (WARN_ON_ONCE(child->num_layers <= parent->num_layers)) {
err = -EINVAL; goto out_unlock;
} /* Copies the parent layer stack and leaves a space for the new layer. */
memcpy(child->access_masks, parent->access_masks,
flex_array_size(parent, access_masks, parent->num_layers));
/* Only called by hook_cred_free(). */ void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset)
{ if (ruleset && refcount_dec_and_test(&ruleset->usage)) {
INIT_WORK(&ruleset->work_free, free_ruleset_work);
schedule_work(&ruleset->work_free);
}
}
/** * landlock_merge_ruleset - Merge a ruleset with a domain * * @parent: Parent domain. * @ruleset: New ruleset to be merged. * * The current task is requesting to be restricted. The subjective credentials * must not be in an overridden state. cf. landlock_init_hierarchy_log(). * * Returns the intersection of @parent and @ruleset, or returns @parent if * @ruleset is empty, or returns a duplicate of @ruleset if @parent is empty.
*/ struct landlock_ruleset *
landlock_merge_ruleset(struct landlock_ruleset *const parent, struct landlock_ruleset *const ruleset)
{ struct landlock_ruleset *new_dom __free(landlock_put_ruleset) = NULL;
u32 num_layers; int err;
might_sleep(); if (WARN_ON_ONCE(!ruleset || parent == ruleset)) return ERR_PTR(-EINVAL);
/* Creates a new domain... */
new_dom = create_ruleset(num_layers); if (IS_ERR(new_dom)) return new_dom;
new_dom->hierarchy =
kzalloc(sizeof(*new_dom->hierarchy), GFP_KERNEL_ACCOUNT); if (!new_dom->hierarchy) return ERR_PTR(-ENOMEM);
refcount_set(&new_dom->hierarchy->usage, 1);
/* ...as a child of @parent... */
err = inherit_ruleset(parent, new_dom); if (err) return ERR_PTR(err);
/* ...and including @ruleset. */
err = merge_ruleset(new_dom, ruleset); if (err) return ERR_PTR(err);
err = landlock_init_hierarchy_log(new_dom->hierarchy); if (err) return ERR_PTR(err);
return no_free_ptr(new_dom);
}
/* * The returned access has the same lifetime as @ruleset.
*/ conststruct landlock_rule *
landlock_find_rule(conststruct landlock_ruleset *const ruleset, conststruct landlock_id id)
{ conststruct rb_root *root; conststruct rb_node *node;
if (this->key.data == id.key.data) returnthis; if (this->key.data < id.key.data)
node = node->rb_right; else
node = node->rb_left;
} return NULL;
}
/* * @layer_masks is read and may be updated according to the access request and * the matching rule. * @masks_array_size must be equal to ARRAY_SIZE(*layer_masks). * * Returns true if the request is allowed (i.e. relevant layer masks for the * request are empty).
*/ bool landlock_unmask_layers(conststruct landlock_rule *const rule, const access_mask_t access_request,
layer_mask_t (*const layer_masks)[], const size_t masks_array_size)
{
size_t layer_level;
if (!access_request || !layer_masks) returntrue; if (!rule) returnfalse;
/* * An access is granted if, for each policy layer, at least one rule * encountered on the pathwalk grants the requested access, * regardless of its position in the layer stack. We must then check * the remaining layers for each inode, from the first added layer to * the last one. When there is multiple requested accesses, for each * policy layer, the full set of requested accesses may not be granted * by only one rule, but by the union (binary OR) of multiple rules. * E.g. /a/b <execute> + /a <read> => /a/b <execute + read>
*/ for (layer_level = 0; layer_level < rule->num_layers; layer_level++) { conststruct landlock_layer *const layer =
&rule->layers[layer_level]; const layer_mask_t layer_bit = BIT_ULL(layer->level - 1); constunsignedlong access_req = access_request; unsignedlong access_bit; bool is_empty;
/* * Records in @layer_masks which layer grants access to each * requested access.
*/
is_empty = true;
for_each_set_bit(access_bit, &access_req, masks_array_size) { if (layer->access & BIT_ULL(access_bit))
(*layer_masks)[access_bit] &= ~layer_bit;
is_empty = is_empty && !(*layer_masks)[access_bit];
} if (is_empty) returntrue;
} returnfalse;
}
/** * landlock_init_layer_masks - Initialize layer masks from an access request * * Populates @layer_masks such that for each access right in @access_request, * the bits for all the layers are set where this access right is handled. * * @domain: The domain that defines the current restrictions. * @access_request: The requested access rights to check. * @layer_masks: It must contain %LANDLOCK_NUM_ACCESS_FS or * %LANDLOCK_NUM_ACCESS_NET elements according to @key_type. * @key_type: The key type to switch between access masks of different types. * * Returns: An access mask where each access right bit is set which is handled * in any of the active layers in @domain.
*/
access_mask_t
landlock_init_layer_masks(conststruct landlock_ruleset *const domain, const access_mask_t access_request,
layer_mask_t (*const layer_masks)[], constenum landlock_key_type key_type)
{
access_mask_t handled_accesses = 0;
size_t layer_level, num_access;
get_access_mask_t *get_access_mask;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.