Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions security/landlock/cred.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include "common.h"
#include "cred.h"
#include "domain.h"
#include "ruleset.h"
#include "setup.h"

Expand All @@ -24,6 +25,7 @@ static void hook_cred_transfer(struct cred *const new,

if (old_llcred->domain) {
landlock_get_ruleset(old_llcred->domain);
landlock_get_domain(old_llcred->domain2);
*landlock_cred(new) = *old_llcred;
}
}
Expand All @@ -37,10 +39,8 @@ static int hook_cred_prepare(struct cred *const new,

static void hook_cred_free(struct cred *const cred)
{
struct landlock_ruleset *const dom = landlock_cred(cred)->domain;

if (dom)
landlock_put_ruleset_deferred(dom);
landlock_put_ruleset_deferred(landlock_cred(cred)->domain);
landlock_put_domain_deferred(landlock_cred(cred)->domain2);
}

#ifdef CONFIG_AUDIT
Expand Down
1 change: 1 addition & 0 deletions security/landlock/cred.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ struct landlock_cred_security {
* @domain: Immutable ruleset enforced on a task.
*/
struct landlock_ruleset *domain;
struct landlock_domain *domain2;

#ifdef CONFIG_AUDIT
/**
Expand Down
145 changes: 145 additions & 0 deletions security/landlock/domain.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net>
* Copyright © 2018-2020 ANSSI
* Copyright © 2024-2025 Microsoft Corporation
* Copyright © 2025 Tingmao Wang <m@maowtm.org>
*/

#include <kunit/test.h>
Expand All @@ -24,6 +25,150 @@
#include "domain.h"
#include "id.h"

struct landlock_domain *landlock_alloc_domain(size_t num_inode_entries,
u16 num_layers)
{
struct landlock_domain *new_domain =
kzalloc(sizeof(struct landlock_domain), GFP_KERNEL_ACCOUNT);

if (!new_domain)
return NULL;
refcount_set(&new_domain->usage, 1);
new_domain->num_layers = num_layers;
if (landlock_hash_init(num_inode_entries, &new_domain->inode_table)) {
kfree(new_domain);
return NULL;
}

return new_domain;
}

static void free_domain(struct landlock_domain *const domain)
{
might_sleep();

landlock_hash_free(&domain->inode_table, LANDLOCK_KEY_INODE);
kfree(domain);
}

void landlock_put_domain(struct landlock_domain *const domain)
{
might_sleep();

if (domain && refcount_dec_and_test(&domain->usage)) {
free_domain(domain);
}
}

static void free_domain_work(struct work_struct *const work)
{
struct landlock_domain *domain;

domain = container_of(work, struct landlock_domain, work_free);
free_domain(domain);
}

/* Only called by hook_cred_free(). */
void landlock_put_domain_deferred(struct landlock_domain *const domain)
{
if (domain && refcount_dec_and_test(&domain->usage)) {
INIT_WORK(&domain->work_free, free_domain_work);
schedule_work(&domain->work_free);
}
}

/**
* @curr_table may be NULL.
*/
static int merge_domain_table(const enum landlock_key_type key_type,
const struct landlock_hashtable *const curr_table,
struct landlock_hashtable *const new_table,
struct landlock_layer new_layer,
const struct rb_root *ruleset_rb_root)
{
int err;
struct landlock_rule *iter, *iter2;

if (curr_table) {
err = landlock_hash_clone(new_table, curr_table, key_type);
if (err) {
return err;
}
}

/* Merge in new rules */
rbtree_postorder_for_each_entry_safe(iter, iter2, ruleset_rb_root,
node) {
WARN_ON_ONCE(iter->layers[0].level != 0);
WARN_ON_ONCE(iter->num_layers != 1);
new_layer.access = iter->layers[0].access;

err = landlock_hash_upsert(new_table, iter->key, key_type,
new_layer);
if (err) {
return err;
}
}

return 0;
}

/**
* @landlock_merge_ruleset2 - Merge a ruleset with a (possibly NULL)
* domain, and return a new merged domain.
*/
struct landlock_domain *
landlock_merge_ruleset2(const struct landlock_domain *curr_domain,
const struct landlock_ruleset *next_ruleset)
{
size_t num_inodes = 0;
int err;
struct landlock_domain *new_domain;
struct landlock_layer new_layer = {
.level = 1,
};
struct landlock_rule *iter, *iter2;

if (WARN_ON_ONCE(!next_ruleset)) {
return ERR_PTR(-EINVAL);
}

if (curr_domain) {
new_layer.level = curr_domain->num_layers + 1;
num_inodes = landlock_hash_count(&curr_domain->inode_table);
}

/* Find new expected size of inode table */
rbtree_postorder_for_each_entry_safe(iter, iter2,
&next_ruleset->root_inode, node) {
if (!curr_domain ||
landlock_hash_find(&curr_domain->inode_table, iter->key) ==
NULL) {
num_inodes += 1;
}
}

new_domain = landlock_alloc_domain(num_inodes, new_layer.level);
if (!new_domain)
return ERR_PTR(-ENOMEM);

err = merge_domain_table(LANDLOCK_KEY_INODE,
curr_domain ? &curr_domain->inode_table : NULL,
&new_domain->inode_table, new_layer,
&next_ruleset->root_inode);
if (err) {
landlock_put_domain(new_domain);
return ERR_PTR(err);
}

#ifdef DEBUG
pr_debug("landlock_merge_ruleset2: inode hash table:\n");
landlock_hash_debug_print(&new_domain->inode_table, LANDLOCK_KEY_INODE);
#endif /* DEBUG */

return new_domain;
}

#ifdef CONFIG_AUDIT

/**
Expand Down
45 changes: 45 additions & 0 deletions security/landlock/domain.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net>
* Copyright © 2018-2020 ANSSI
* Copyright © 2024-2025 Microsoft Corporation
* Copyright © 2025 Tingmao Wang <m@maowtm.org>
*/

#ifndef _SECURITY_LANDLOCK_DOMAIN_H
Expand All @@ -17,9 +18,53 @@
#include <linux/refcount.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/workqueue.h>

#include "access.h"
#include "audit.h"
#include "hash.h"

struct landlock_domain {
struct landlock_hashtable inode_table;

union {
/**
* @work_free: Enables to free a ruleset within a lockless
* section. This is only used by
* landlock_put_domain_deferred() when @usage reaches zero.
*/
struct work_struct work_free;

struct {
/**
* @usage: Reference count for this struct.
*/
refcount_t usage;

/**
* @num_layers: Number of layers in this domain.
*/
u16 num_layers;
};
};
};

struct landlock_domain *landlock_alloc_domain(size_t num_inode_entries,
u16 num_layers);

static inline void landlock_get_domain(struct landlock_domain *const domain)
{
if (domain)
refcount_inc(&domain->usage);
}

void landlock_put_domain(struct landlock_domain *const domain);

void landlock_put_domain_deferred(struct landlock_domain *const domain);

struct landlock_domain *
landlock_merge_ruleset2(const struct landlock_domain *curr_domain,
const struct landlock_ruleset *new_ruleset);

enum landlock_log_status {
LANDLOCK_LOG_PENDING = 0,
Expand Down
Loading