mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 14:04:27 -04:00
landlock: Add tracepoints for rule checking
Merge landlock_find_rule() into landlock_unmask_layers() so rule pointers stay inside the domain implementation while unmask checking gets the matched rule it needs for the check_rule tracepoint. landlock_unmask_layers() now takes a landlock_id and the domain instead of a rule pointer. A rename or link evaluates the same dentry against both renamed parents, so this path now looks the rule up once per parent; collapsing that back to a single lookup is left to a follow-up. Emit, via the per-type wrappers unmask_layers_fs() and unmask_layers_net(), the rights each matching rule grants at every domain layer. The events carry this as a dynamic per-layer array (up to LANDLOCK_MAX_NUM_LAYERS entries) reserved from the trace ring buffer, not the caller's stack, and rendered symbolically per layer. A WARN_ON_ONCE() in __trace_landlock_fill_layers() flags a rule whose layer levels fall outside the domain range or are unsorted, a cannot-happen case; the zero-filled slots keep the rendered output and the array bounds safe regardless. Setting allowed_parent2 to true for non-dom-check requests when get_inode_id() returns false preserves the pre-refactoring behavior: a negative dentry (no backing inode) has no matching rule, so the access is allowed at this path component. Before the refactoring, landlock_unmask_layers() with a NULL rule produced this result as a side effect; now the caller must set it explicitly. Name the trace-only check_rule fields so each printk label equals its ring-buffer field name and works directly as an ftrace filter: the request field is labelled access_request= and the per-layer array is named grants. Values audit also logs keep audit's label (domain=, ruleset=) so a single filter works across trace and audit. Cc: Günther Noack <gnoack@google.com> Cc: Justin Suess <utilityemal77@gmail.com> Cc: Masami Hiramatsu <mhiramat@kernel.org> Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Tingmao Wang <m@maowtm.org> Link: https://patch.msgid.link/20260811094338.288094-12-mic@digikod.net Signed-off-by: Mickaël Salaün <mic@digikod.net>
This commit is contained in:
@@ -16,8 +16,10 @@
|
||||
#include <linux/tracepoint.h>
|
||||
#include <linux/trace_seq.h>
|
||||
|
||||
struct dentry;
|
||||
struct landlock_domain;
|
||||
struct landlock_hierarchy;
|
||||
struct landlock_rule;
|
||||
struct landlock_ruleset;
|
||||
struct path;
|
||||
|
||||
@@ -63,6 +65,77 @@ __trace_print_untrusted_str(struct trace_seq *p, const char *src, size_t len)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fills the dense per-domain-layer array layers (one access mask per layer,
|
||||
* indexed by level - 1) from rule's sparse layer stack, keeping only the
|
||||
* requested rights (access_request). Layers with no matching rule entry get
|
||||
* a zero mask. Shared by the check_rule_fs and check_rule_net events.
|
||||
*
|
||||
* rule->layers is sorted by ascending level, with levels in the domain's
|
||||
* [1, num_layers] range (see landlock_merge_ruleset()), so every entry maps
|
||||
* to a slot. A leftover entry would be a malformed rule; the zero-filled
|
||||
* slots keep the output and the array bounds safe regardless.
|
||||
*/
|
||||
static inline void
|
||||
__trace_landlock_fill_layers(access_mask_t *const layers,
|
||||
const size_t num_layers,
|
||||
const struct landlock_rule *const rule,
|
||||
const access_mask_t access_request)
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
for (size_t level = 1; level <= num_layers; level++) {
|
||||
access_mask_t grants = 0;
|
||||
|
||||
if (i < rule->num_layers && level == rule->layers[i].level) {
|
||||
grants = rule->layers[i].access & access_request;
|
||||
i++;
|
||||
}
|
||||
layers[level - 1] = grants;
|
||||
}
|
||||
|
||||
/* A leftover entry means an out-of-range or unsorted rule level. */
|
||||
WARN_ON_ONCE(i < rule->num_layers);
|
||||
}
|
||||
|
||||
/*
|
||||
* Renders the dense per-domain-layer access array as symbolic flag names for
|
||||
* the grants field: layers wrapped in "{}", flags within a layer joined by
|
||||
* "|", layers separated by ",", an empty layer rendered as nothing.
|
||||
* Open-codes the flag walk because trace_print_flags_seq() NUL-terminates per
|
||||
* call and so cannot be chained into a single field. The shared names table
|
||||
* covers every access right, so masked bits are always named. Returns the
|
||||
* trace_seq position like __print_flags().
|
||||
*/
|
||||
static inline const char *__trace_landlock_print_layers(
|
||||
struct trace_seq *p, const access_mask_t *const layers,
|
||||
const size_t num_layers, const struct trace_print_flags *const names,
|
||||
const size_t names_size)
|
||||
{
|
||||
const char *const ret = trace_seq_buffer_ptr(p);
|
||||
|
||||
trace_seq_putc(p, '{');
|
||||
for (size_t i = 0; i < num_layers; i++) {
|
||||
access_mask_t mask = layers[i];
|
||||
bool first = true;
|
||||
|
||||
if (i)
|
||||
trace_seq_putc(p, ',');
|
||||
for (size_t j = 0; mask && j < names_size; j++) {
|
||||
if ((mask & names[j].mask) != names[j].mask)
|
||||
continue;
|
||||
if (!first)
|
||||
trace_seq_putc(p, '|');
|
||||
trace_seq_puts(p, names[j].name);
|
||||
mask &= ~names[j].mask;
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
trace_seq_putc(p, '}');
|
||||
trace_seq_putc(p, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* CREATE_TRACE_POINTS */
|
||||
|
||||
/* clang-format off */
|
||||
@@ -123,8 +196,34 @@ __trace_print_untrusted_str(struct trace_seq *p, const char *src, size_t len)
|
||||
* (e.g. network ports are __u64 in host endianness, like
|
||||
* landlock_net_port_attr.port). Per-event details, such as where a value
|
||||
* is byte-swapped, live in the field's own kdoc.
|
||||
*
|
||||
* Rule-check fields
|
||||
* ~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* The check_rule events fire during an access check, once per matching
|
||||
* rule, before the final allow-or-deny verdict. They share domain (the
|
||||
* enforcing domain being evaluated), access_request (the access mask being
|
||||
* checked), and rule (the matching rule, with per-layer access masks).
|
||||
*/
|
||||
|
||||
/*
|
||||
* Prints a per-layer access mask array (the dynamic array @array) as symbolic
|
||||
* flag names using the shared @flag_names list (a _LANDLOCK_*_NAMES macro).
|
||||
* Stays outside CREATE_TRACE_POINTS: TP_printk is expanded in the print-output
|
||||
* pass where that macro is undefined.
|
||||
*/
|
||||
#define __print_landlock_layers(array, flag_names...) \
|
||||
({ \
|
||||
static const struct trace_print_flags __layer_names[] = { \
|
||||
flag_names \
|
||||
}; \
|
||||
__trace_landlock_print_layers( \
|
||||
p, __get_dynamic_array(array), \
|
||||
__get_dynamic_array_len(array) / \
|
||||
sizeof(access_mask_t), \
|
||||
__layer_names, ARRAY_SIZE(__layer_names)); \
|
||||
})
|
||||
|
||||
/**
|
||||
* landlock_create_ruleset - New ruleset created
|
||||
*
|
||||
@@ -432,6 +531,103 @@ TRACE_EVENT(landlock_free_domain,
|
||||
__entry->domain_id, __entry->denials)
|
||||
);
|
||||
|
||||
/**
|
||||
* landlock_check_rule_fs - Filesystem rule evaluated during access check
|
||||
*
|
||||
* @domain: Enforcing domain (never NULL).
|
||||
* @rule: Matching rule with per-layer access masks (never NULL).
|
||||
* @access_request: Access mask evaluated against the rule (the domain's
|
||||
* handled mask during rename/link double-checks).
|
||||
* @dentry: Filesystem dentry being checked (never NULL).
|
||||
*
|
||||
* Emitted for each rule that matches during a filesystem access check.
|
||||
* The grants array shows the requested rights the rule grants at each
|
||||
* domain layer. See Documentation/trace/events-landlock.rst for how to
|
||||
* interpret it.
|
||||
*/
|
||||
TRACE_EVENT(landlock_check_rule_fs,
|
||||
|
||||
TP_PROTO(const struct landlock_domain *domain,
|
||||
const struct landlock_rule *rule,
|
||||
access_mask_t access_request, const struct dentry *dentry),
|
||||
|
||||
TP_ARGS(domain, rule, access_request, dentry),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( __u64, domain_id )
|
||||
__field( access_mask_t, access_request )
|
||||
__field( dev_t, dev )
|
||||
__field( ino_t, ino )
|
||||
__dynamic_array(access_mask_t, grants,
|
||||
domain->num_layers)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->domain_id = domain->hierarchy->id;
|
||||
__entry->access_request = access_request;
|
||||
__entry->dev = dentry->d_sb->s_dev;
|
||||
__entry->ino = d_backing_inode(dentry)->i_ino;
|
||||
|
||||
__trace_landlock_fill_layers(__get_dynamic_array(grants),
|
||||
__get_dynamic_array_len(grants) /
|
||||
sizeof(access_mask_t),
|
||||
rule, access_request);
|
||||
),
|
||||
|
||||
TP_printk("domain=%llx access_request=%s dev=%u:%u ino=%lu grants=%s",
|
||||
__entry->domain_id,
|
||||
__print_flags(__entry->access_request, "|", _LANDLOCK_ACCESS_FS_NAMES),
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev), __entry->ino,
|
||||
__print_landlock_layers(grants, _LANDLOCK_ACCESS_FS_NAMES))
|
||||
);
|
||||
|
||||
/**
|
||||
* landlock_check_rule_net - Network port rule evaluated during access check
|
||||
*
|
||||
* @domain: Enforcing domain (never NULL).
|
||||
* @rule: Matching rule with per-layer access masks (never NULL).
|
||||
* @access_request: Access mask being requested.
|
||||
* @port: Network port being checked (host endianness).
|
||||
*
|
||||
* Emitted for each rule that matches during a network access check. The
|
||||
* grants array shows the requested rights the rule grants at each domain
|
||||
* layer. See Documentation/trace/events-landlock.rst for how to
|
||||
* interpret it.
|
||||
*/
|
||||
TRACE_EVENT(landlock_check_rule_net,
|
||||
|
||||
TP_PROTO(const struct landlock_domain *domain,
|
||||
const struct landlock_rule *rule,
|
||||
access_mask_t access_request, __u64 port),
|
||||
|
||||
TP_ARGS(domain, rule, access_request, port),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( __u64, domain_id )
|
||||
__field( access_mask_t, access_request )
|
||||
__field( __u64, port )
|
||||
__dynamic_array(access_mask_t, grants,
|
||||
domain->num_layers)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->domain_id = domain->hierarchy->id;
|
||||
__entry->access_request = access_request;
|
||||
__entry->port = port;
|
||||
|
||||
__trace_landlock_fill_layers(__get_dynamic_array(grants),
|
||||
__get_dynamic_array_len(grants) /
|
||||
sizeof(access_mask_t),
|
||||
rule, access_request);
|
||||
),
|
||||
|
||||
TP_printk("domain=%llx access_request=%s port=%llu grants=%s",
|
||||
__entry->domain_id,
|
||||
__print_flags(__entry->access_request, "|", _LANDLOCK_ACCESS_NET_NAMES),
|
||||
__entry->port,
|
||||
__print_landlock_layers(grants, _LANDLOCK_ACCESS_NET_NAMES))
|
||||
);
|
||||
|
||||
#undef _LANDLOCK_NAME_ENTRY
|
||||
|
||||
#endif /* _TRACE_LANDLOCK_H */
|
||||
|
||||
@@ -98,9 +98,9 @@ void landlock_put_domain_deferred(struct landlock_domain *const domain)
|
||||
}
|
||||
|
||||
/* The returned access has the same lifetime as the domain. */
|
||||
const struct landlock_rule *
|
||||
landlock_find_rule(const struct landlock_domain *const domain,
|
||||
const struct landlock_id id)
|
||||
static const struct landlock_rule *
|
||||
find_rule(const struct landlock_domain *const domain,
|
||||
const struct landlock_id id)
|
||||
{
|
||||
const struct rb_root *root;
|
||||
const struct rb_node *node;
|
||||
@@ -127,26 +127,38 @@ landlock_find_rule(const struct landlock_domain *const domain,
|
||||
|
||||
/**
|
||||
* landlock_unmask_layers - Remove the access rights in @masks which are
|
||||
* granted in @rule
|
||||
* granted by a matching rule
|
||||
*
|
||||
* Updates the set of (per-layer) unfulfilled access rights @masks so that all
|
||||
* the access rights granted in @rule are removed from it (because they are now
|
||||
* fulfilled).
|
||||
* Looks up the rule matching @id in @domain, then updates the set of
|
||||
* (per-layer) unfulfilled access rights @masks so that all the access rights
|
||||
* granted by that rule are removed (because they are now fulfilled).
|
||||
*
|
||||
* @rule: A rule that grants a set of access rights for each layer.
|
||||
* @domain: The Landlock domain to search for a matching rule.
|
||||
* @id: Identifier for the rule target (e.g. inode, port).
|
||||
* @masks: A matrix of unfulfilled access rights for each layer.
|
||||
* @matched_rule: Optional output for the matched rule (for tracing); set to
|
||||
* the matching rule when non-NULL, unchanged otherwise.
|
||||
*
|
||||
* Return: True if the request is allowed (i.e. the access rights granted all
|
||||
* remaining unfulfilled access rights and masks has no leftover set bits).
|
||||
*/
|
||||
bool landlock_unmask_layers(const struct landlock_rule *const rule,
|
||||
struct layer_masks *masks)
|
||||
bool landlock_unmask_layers(const struct landlock_domain *const domain,
|
||||
const struct landlock_id id,
|
||||
struct layer_masks *masks,
|
||||
const struct landlock_rule **matched_rule)
|
||||
{
|
||||
const struct landlock_rule *rule;
|
||||
|
||||
if (!masks)
|
||||
return true;
|
||||
|
||||
rule = find_rule(domain, id);
|
||||
if (!rule)
|
||||
return false;
|
||||
|
||||
if (matched_rule)
|
||||
*matched_rule = rule;
|
||||
|
||||
/*
|
||||
* An access is granted if, for each policy layer, at least one rule
|
||||
* encountered on the pathwalk grants the requested access, regardless
|
||||
|
||||
@@ -306,12 +306,10 @@ struct landlock_domain *
|
||||
landlock_merge_ruleset(struct landlock_domain *const parent,
|
||||
struct landlock_ruleset *const ruleset);
|
||||
|
||||
const struct landlock_rule *
|
||||
landlock_find_rule(const struct landlock_domain *const domain,
|
||||
const struct landlock_id id);
|
||||
|
||||
bool landlock_unmask_layers(const struct landlock_rule *const rule,
|
||||
struct layer_masks *masks);
|
||||
bool landlock_unmask_layers(const struct landlock_domain *const domain,
|
||||
const struct landlock_id id,
|
||||
struct layer_masks *masks,
|
||||
const struct landlock_rule **matched_rule);
|
||||
|
||||
access_mask_t
|
||||
landlock_init_layer_masks(const struct landlock_domain *const domain,
|
||||
|
||||
@@ -377,31 +377,55 @@ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
|
||||
|
||||
/* Access-control management */
|
||||
|
||||
/*
|
||||
* The lifetime of the returned rule is tied to @domain.
|
||||
/**
|
||||
* get_inode_id - Look up the Landlock object for a dentry
|
||||
* @dentry: The dentry to look up.
|
||||
* @id: Filled with the inode's Landlock object pointer on success.
|
||||
*
|
||||
* Returns NULL if no rule is found or if @dentry is negative.
|
||||
* Extracts the Landlock object pointer from @dentry's inode security blob and
|
||||
* stores it in @id for use as a rule-tree lookup key.
|
||||
*
|
||||
* When this returns false (negative dentry or no Landlock object), no rule can
|
||||
* match this inode, so landlock_unmask_layers() need not be called. Callers
|
||||
* that gate landlock_unmask_layers() on this function must handle the NULL
|
||||
* masks case independently, since the !masks-returns-true early-return in
|
||||
* landlock_unmask_layers() will not be reached. See the allowed_parent2
|
||||
* initialization in is_access_to_paths_allowed().
|
||||
*
|
||||
* Return: True if a Landlock object exists for @dentry, false otherwise.
|
||||
*/
|
||||
static const struct landlock_rule *
|
||||
find_rule(const struct landlock_domain *const domain,
|
||||
const struct dentry *const dentry)
|
||||
static bool get_inode_id(const struct dentry *const dentry,
|
||||
struct landlock_id *id)
|
||||
{
|
||||
const struct landlock_rule *rule;
|
||||
const struct inode *inode;
|
||||
struct landlock_id id = {
|
||||
.type = LANDLOCK_KEY_INODE,
|
||||
};
|
||||
|
||||
/* Ignores nonexistent leafs. */
|
||||
if (d_is_negative(dentry))
|
||||
return NULL;
|
||||
return false;
|
||||
|
||||
inode = d_backing_inode(dentry);
|
||||
rcu_read_lock();
|
||||
id.key.object = rcu_dereference(landlock_inode(inode)->object);
|
||||
rule = landlock_find_rule(domain, id);
|
||||
rcu_read_unlock();
|
||||
return rule;
|
||||
/*
|
||||
* rcu_access_pointer() is sufficient: the pointer is used only as a
|
||||
* numeric comparison key for rule lookup, not dereferenced. The object
|
||||
* cannot be freed while the domain exists because the domain's rule
|
||||
* tree holds its own reference to it.
|
||||
*/
|
||||
id->key.object = rcu_access_pointer(
|
||||
landlock_inode(d_backing_inode(dentry))->object);
|
||||
return !!id->key.object;
|
||||
}
|
||||
|
||||
static bool unmask_layers_fs(const struct landlock_domain *const domain,
|
||||
const struct landlock_id id,
|
||||
const access_mask_t access_request,
|
||||
struct layer_masks *masks,
|
||||
const struct dentry *const dentry)
|
||||
{
|
||||
const struct landlock_rule *rule = NULL;
|
||||
bool ret;
|
||||
|
||||
ret = landlock_unmask_layers(domain, id, masks, &rule);
|
||||
if (rule)
|
||||
trace_landlock_check_rule_fs(domain, rule, access_request,
|
||||
dentry);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -782,6 +806,9 @@ is_access_to_paths_allowed(const struct landlock_domain *const domain,
|
||||
bool allowed_parent1 = false, allowed_parent2 = false, is_dom_check,
|
||||
child1_is_directory = true, child2_is_directory = true;
|
||||
struct path walker_path;
|
||||
struct landlock_id id = {
|
||||
.type = LANDLOCK_KEY_INODE,
|
||||
};
|
||||
access_mask_t access_masked_parent1, access_masked_parent2;
|
||||
struct layer_masks _layer_masks_child1, _layer_masks_child2;
|
||||
struct layer_masks *layer_masks_child1 = NULL,
|
||||
@@ -821,28 +848,46 @@ is_access_to_paths_allowed(const struct landlock_domain *const domain,
|
||||
/* For a simple request, only check for requested accesses. */
|
||||
access_masked_parent1 = access_request_parent1;
|
||||
access_masked_parent2 = access_request_parent2;
|
||||
/*
|
||||
* Simple requests have no parent2 to check, so parent2 is
|
||||
* trivially allowed. This must be set explicitly because the
|
||||
* get_inode_id() gate in the pathwalk loop may prevent
|
||||
* landlock_unmask_layers() from being called (which would
|
||||
* otherwise return true for NULL masks as a side effect).
|
||||
*/
|
||||
allowed_parent2 = true;
|
||||
is_dom_check = false;
|
||||
}
|
||||
|
||||
if (unlikely(dentry_child1)) {
|
||||
/*
|
||||
* Get the layer masks for the child dentries for use by domain
|
||||
* check later.
|
||||
*/
|
||||
if (landlock_init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
|
||||
&_layer_masks_child1,
|
||||
LANDLOCK_KEY_INODE))
|
||||
landlock_unmask_layers(find_rule(domain, dentry_child1),
|
||||
&_layer_masks_child1);
|
||||
struct landlock_id id = {
|
||||
.type = LANDLOCK_KEY_INODE,
|
||||
};
|
||||
access_mask_t handled;
|
||||
|
||||
handled = landlock_init_layer_masks(domain,
|
||||
LANDLOCK_MASK_ACCESS_FS,
|
||||
&_layer_masks_child1,
|
||||
LANDLOCK_KEY_INODE);
|
||||
if (handled && get_inode_id(dentry_child1, &id))
|
||||
unmask_layers_fs(domain, id, handled,
|
||||
&_layer_masks_child1, dentry_child1);
|
||||
layer_masks_child1 = &_layer_masks_child1;
|
||||
child1_is_directory = d_is_dir(dentry_child1);
|
||||
}
|
||||
if (unlikely(dentry_child2)) {
|
||||
if (landlock_init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
|
||||
&_layer_masks_child2,
|
||||
LANDLOCK_KEY_INODE))
|
||||
landlock_unmask_layers(find_rule(domain, dentry_child2),
|
||||
&_layer_masks_child2);
|
||||
struct landlock_id id = {
|
||||
.type = LANDLOCK_KEY_INODE,
|
||||
};
|
||||
access_mask_t handled;
|
||||
|
||||
handled = landlock_init_layer_masks(domain,
|
||||
LANDLOCK_MASK_ACCESS_FS,
|
||||
&_layer_masks_child2,
|
||||
LANDLOCK_KEY_INODE);
|
||||
if (handled && get_inode_id(dentry_child2, &id))
|
||||
unmask_layers_fs(domain, id, handled,
|
||||
&_layer_masks_child2, dentry_child2);
|
||||
layer_masks_child2 = &_layer_masks_child2;
|
||||
child2_is_directory = d_is_dir(dentry_child2);
|
||||
}
|
||||
@@ -854,8 +899,6 @@ is_access_to_paths_allowed(const struct landlock_domain *const domain,
|
||||
* restriction.
|
||||
*/
|
||||
while (true) {
|
||||
const struct landlock_rule *rule;
|
||||
|
||||
/*
|
||||
* If at least all accesses allowed on the destination are
|
||||
* already allowed on the source, respectively if there is at
|
||||
@@ -896,13 +939,20 @@ is_access_to_paths_allowed(const struct landlock_domain *const domain,
|
||||
break;
|
||||
}
|
||||
|
||||
rule = find_rule(domain, walker_path.dentry);
|
||||
allowed_parent1 =
|
||||
allowed_parent1 ||
|
||||
landlock_unmask_layers(rule, layer_masks_parent1);
|
||||
allowed_parent2 =
|
||||
allowed_parent2 ||
|
||||
landlock_unmask_layers(rule, layer_masks_parent2);
|
||||
if (get_inode_id(walker_path.dentry, &id)) {
|
||||
allowed_parent1 =
|
||||
allowed_parent1 ||
|
||||
unmask_layers_fs(domain, id,
|
||||
access_masked_parent1,
|
||||
layer_masks_parent1,
|
||||
walker_path.dentry);
|
||||
allowed_parent2 =
|
||||
allowed_parent2 ||
|
||||
unmask_layers_fs(domain, id,
|
||||
access_masked_parent2,
|
||||
layer_masks_parent2,
|
||||
walker_path.dentry);
|
||||
}
|
||||
|
||||
/* Stops when a rule from each layer grants access. */
|
||||
if (allowed_parent1 && allowed_parent2)
|
||||
@@ -1076,23 +1126,30 @@ static bool collect_domain_accesses(const struct landlock_domain *const domain,
|
||||
struct layer_masks *layer_masks_dom)
|
||||
{
|
||||
bool ret = false;
|
||||
access_mask_t access_masked_dom;
|
||||
|
||||
if (WARN_ON_ONCE(!domain || !mnt_root || !dir || !layer_masks_dom))
|
||||
return true;
|
||||
if (is_nouser_or_private(dir))
|
||||
return true;
|
||||
|
||||
if (!landlock_init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
|
||||
layer_masks_dom, LANDLOCK_KEY_INODE))
|
||||
access_masked_dom =
|
||||
landlock_init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
|
||||
layer_masks_dom, LANDLOCK_KEY_INODE);
|
||||
if (!access_masked_dom)
|
||||
return true;
|
||||
|
||||
dget(dir);
|
||||
while (true) {
|
||||
struct dentry *parent_dentry;
|
||||
struct landlock_id id = {
|
||||
.type = LANDLOCK_KEY_INODE,
|
||||
};
|
||||
|
||||
/* Gets all layers allowing all domain accesses. */
|
||||
if (landlock_unmask_layers(find_rule(domain, dir),
|
||||
layer_masks_dom)) {
|
||||
if (get_inode_id(dir, &id) &&
|
||||
unmask_layers_fs(domain, id, access_masked_dom,
|
||||
layer_masks_dom, dir)) {
|
||||
/*
|
||||
* Stops when all handled accesses are allowed by at
|
||||
* least one rule in each layer.
|
||||
|
||||
@@ -53,6 +53,22 @@ int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool unmask_layers_net(const struct landlock_domain *const domain,
|
||||
const struct landlock_id id,
|
||||
struct layer_masks *masks,
|
||||
access_mask_t access_request)
|
||||
{
|
||||
const struct landlock_rule *rule = NULL;
|
||||
bool ret;
|
||||
|
||||
ret = landlock_unmask_layers(domain, id, masks, &rule);
|
||||
if (rule)
|
||||
trace_landlock_check_rule_net(
|
||||
domain, rule, access_request,
|
||||
ntohs((__force __be16)id.key.data));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int current_check_access_socket(struct socket *const sock,
|
||||
struct sockaddr *const address,
|
||||
const int addrlen,
|
||||
@@ -62,7 +78,6 @@ static int current_check_access_socket(struct socket *const sock,
|
||||
unsigned short sock_family;
|
||||
__be16 port;
|
||||
struct layer_masks layer_masks = {};
|
||||
const struct landlock_rule *rule;
|
||||
struct landlock_id id = {
|
||||
.type = LANDLOCK_KEY_NET_PORT,
|
||||
};
|
||||
@@ -248,14 +263,14 @@ static int current_check_access_socket(struct socket *const sock,
|
||||
id.key.data = (__force uintptr_t)port;
|
||||
BUILD_BUG_ON(sizeof(port) > sizeof(id.key.data));
|
||||
|
||||
rule = landlock_find_rule(subject->domain, id);
|
||||
access_request = landlock_init_layer_masks(subject->domain,
|
||||
access_request, &layer_masks,
|
||||
LANDLOCK_KEY_NET_PORT);
|
||||
if (!access_request)
|
||||
return 0;
|
||||
|
||||
if (landlock_unmask_layers(rule, &layer_masks))
|
||||
if (unmask_layers_net(subject->domain, id, &layer_masks,
|
||||
access_request))
|
||||
return 0;
|
||||
|
||||
audit_net.family = address->sa_family;
|
||||
|
||||
Reference in New Issue
Block a user