Skip to content

Commit a4ac404

Browse files
BoardzMasterl0kod
authored andcommitted
landlock: Refactor landlock_find_rule/insert_rule helpers
Add a new landlock_key union and landlock_id structure to support a socket port rule type. A struct landlock_id identifies a unique entry in a ruleset: either a kernel object (e.g. inode) or typed data (e.g. TCP port). There is one red-black tree per key type. Add is_object_pointer() and get_root() helpers. is_object_pointer() returns true if key type is LANDLOCK_KEY_INODE. get_root() helper returns a red-black tree root pointer according to a key type. Refactor landlock_insert_rule() and landlock_find_rule() to support coming network modifications. Adding or searching a rule in ruleset can now be done thanks to a Landlock ID argument passed to these helpers. Signed-off-by: Konstantin Meskhidze <[email protected]> Link: https://lore.kernel.org/r/[email protected] [mic: Fix commit message typo] Co-developed-by: Mickaël Salaün <[email protected]> Signed-off-by: Mickaël Salaün <[email protected]>
1 parent d722036 commit a4ac404

File tree

3 files changed

+165
-54
lines changed

3 files changed

+165
-54
lines changed

security/landlock/fs.c

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,9 @@ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
158158
access_mask_t access_rights)
159159
{
160160
int err;
161-
struct landlock_object *object;
161+
struct landlock_id id = {
162+
.type = LANDLOCK_KEY_INODE,
163+
};
162164

163165
/* Files only get access rights that make sense. */
164166
if (!d_is_dir(path->dentry) &&
@@ -170,17 +172,17 @@ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
170172
/* Transforms relative access rights to absolute ones. */
171173
access_rights |= LANDLOCK_MASK_ACCESS_FS &
172174
~landlock_get_fs_access_mask(ruleset, 0);
173-
object = get_inode_object(d_backing_inode(path->dentry));
174-
if (IS_ERR(object))
175-
return PTR_ERR(object);
175+
id.key.object = get_inode_object(d_backing_inode(path->dentry));
176+
if (IS_ERR(id.key.object))
177+
return PTR_ERR(id.key.object);
176178
mutex_lock(&ruleset->lock);
177-
err = landlock_insert_rule(ruleset, object, access_rights);
179+
err = landlock_insert_rule(ruleset, id, access_rights);
178180
mutex_unlock(&ruleset->lock);
179181
/*
180182
* No need to check for an error because landlock_insert_rule()
181183
* increments the refcount for the new object if needed.
182184
*/
183-
landlock_put_object(object);
185+
landlock_put_object(id.key.object);
184186
return err;
185187
}
186188

@@ -197,15 +199,18 @@ find_rule(const struct landlock_ruleset *const domain,
197199
{
198200
const struct landlock_rule *rule;
199201
const struct inode *inode;
202+
struct landlock_id id = {
203+
.type = LANDLOCK_KEY_INODE,
204+
};
200205

201206
/* Ignores nonexistent leafs. */
202207
if (d_is_negative(dentry))
203208
return NULL;
204209

205210
inode = d_backing_inode(dentry);
206211
rcu_read_lock();
207-
rule = landlock_find_rule(
208-
domain, rcu_dereference(landlock_inode(inode)->object));
212+
id.key.object = rcu_dereference(landlock_inode(inode)->object);
213+
rule = landlock_find_rule(domain, id);
209214
rcu_read_unlock();
210215
return rule;
211216
}

security/landlock/ruleset.c

Lines changed: 98 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
3535
return ERR_PTR(-ENOMEM);
3636
refcount_set(&new_ruleset->usage, 1);
3737
mutex_init(&new_ruleset->lock);
38-
new_ruleset->root = RB_ROOT;
38+
new_ruleset->root_inode = RB_ROOT;
3939
new_ruleset->num_layers = num_layers;
4040
/*
4141
* hierarchy = NULL
@@ -68,8 +68,20 @@ static void build_check_rule(void)
6868
BUILD_BUG_ON(rule.num_layers < LANDLOCK_MAX_NUM_LAYERS);
6969
}
7070

71+
static bool is_object_pointer(const enum landlock_key_type key_type)
72+
{
73+
switch (key_type) {
74+
case LANDLOCK_KEY_INODE:
75+
return true;
76+
77+
default:
78+
WARN_ON_ONCE(1);
79+
return false;
80+
}
81+
}
82+
7183
static struct landlock_rule *
72-
create_rule(struct landlock_object *const object,
84+
create_rule(const struct landlock_id id,
7385
const struct landlock_layer (*const layers)[], const u32 num_layers,
7486
const struct landlock_layer *const new_layer)
7587
{
@@ -90,8 +102,13 @@ create_rule(struct landlock_object *const object,
90102
if (!new_rule)
91103
return ERR_PTR(-ENOMEM);
92104
RB_CLEAR_NODE(&new_rule->node);
93-
landlock_get_object(object);
94-
new_rule->object = object;
105+
if (is_object_pointer(id.type)) {
106+
/* This should be catched by insert_rule(). */
107+
WARN_ON_ONCE(!id.key.object);
108+
landlock_get_object(id.key.object);
109+
}
110+
111+
new_rule->key = id.key;
95112
new_rule->num_layers = new_num_layers;
96113
/* Copies the original layer stack. */
97114
memcpy(new_rule->layers, layers,
@@ -102,12 +119,27 @@ create_rule(struct landlock_object *const object,
102119
return new_rule;
103120
}
104121

105-
static void free_rule(struct landlock_rule *const rule)
122+
static struct rb_root *get_root(struct landlock_ruleset *const ruleset,
123+
const enum landlock_key_type key_type)
124+
{
125+
switch (key_type) {
126+
case LANDLOCK_KEY_INODE:
127+
return &ruleset->root_inode;
128+
129+
default:
130+
WARN_ON_ONCE(1);
131+
return ERR_PTR(-EINVAL);
132+
}
133+
}
134+
135+
static void free_rule(struct landlock_rule *const rule,
136+
const enum landlock_key_type key_type)
106137
{
107138
might_sleep();
108139
if (!rule)
109140
return;
110-
landlock_put_object(rule->object);
141+
if (is_object_pointer(key_type))
142+
landlock_put_object(rule->key.object);
111143
kfree(rule);
112144
}
113145

@@ -129,8 +161,8 @@ static void build_check_ruleset(void)
129161
* insert_rule - Create and insert a rule in a ruleset
130162
*
131163
* @ruleset: The ruleset to be updated.
132-
* @object: The object to build the new rule with. The underlying kernel
133-
* object must be held by the caller.
164+
* @id: The ID to build the new rule with. The underlying kernel object, if
165+
* any, must be held by the caller.
134166
* @layers: One or multiple layers to be copied into the new rule.
135167
* @num_layers: The number of @layers entries.
136168
*
@@ -144,26 +176,35 @@ static void build_check_ruleset(void)
144176
* access rights.
145177
*/
146178
static int insert_rule(struct landlock_ruleset *const ruleset,
147-
struct landlock_object *const object,
179+
const struct landlock_id id,
148180
const struct landlock_layer (*const layers)[],
149-
size_t num_layers)
181+
const size_t num_layers)
150182
{
151183
struct rb_node **walker_node;
152184
struct rb_node *parent_node = NULL;
153185
struct landlock_rule *new_rule;
186+
struct rb_root *root;
154187

155188
might_sleep();
156189
lockdep_assert_held(&ruleset->lock);
157-
if (WARN_ON_ONCE(!object || !layers))
190+
if (WARN_ON_ONCE(!layers))
191+
return -ENOENT;
192+
193+
if (is_object_pointer(id.type) && WARN_ON_ONCE(!id.key.object))
158194
return -ENOENT;
159-
walker_node = &(ruleset->root.rb_node);
195+
196+
root = get_root(ruleset, id.type);
197+
if (IS_ERR(root))
198+
return PTR_ERR(root);
199+
200+
walker_node = &root->rb_node;
160201
while (*walker_node) {
161202
struct landlock_rule *const this =
162203
rb_entry(*walker_node, struct landlock_rule, node);
163204

164-
if (this->object != object) {
205+
if (this->key.data != id.key.data) {
165206
parent_node = *walker_node;
166-
if (this->object < object)
207+
if (this->key.data < id.key.data)
167208
walker_node = &((*walker_node)->rb_right);
168209
else
169210
walker_node = &((*walker_node)->rb_left);
@@ -195,24 +236,24 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
195236
* Intersects access rights when it is a merge between a
196237
* ruleset and a domain.
197238
*/
198-
new_rule = create_rule(object, &this->layers, this->num_layers,
239+
new_rule = create_rule(id, &this->layers, this->num_layers,
199240
&(*layers)[0]);
200241
if (IS_ERR(new_rule))
201242
return PTR_ERR(new_rule);
202-
rb_replace_node(&this->node, &new_rule->node, &ruleset->root);
203-
free_rule(this);
243+
rb_replace_node(&this->node, &new_rule->node, root);
244+
free_rule(this, id.type);
204245
return 0;
205246
}
206247

207-
/* There is no match for @object. */
248+
/* There is no match for @id. */
208249
build_check_ruleset();
209250
if (ruleset->num_rules >= LANDLOCK_MAX_NUM_RULES)
210251
return -E2BIG;
211-
new_rule = create_rule(object, layers, num_layers, NULL);
252+
new_rule = create_rule(id, layers, num_layers, NULL);
212253
if (IS_ERR(new_rule))
213254
return PTR_ERR(new_rule);
214255
rb_link_node(&new_rule->node, parent_node, walker_node);
215-
rb_insert_color(&new_rule->node, &ruleset->root);
256+
rb_insert_color(&new_rule->node, root);
216257
ruleset->num_rules++;
217258
return 0;
218259
}
@@ -230,7 +271,7 @@ static void build_check_layer(void)
230271

231272
/* @ruleset must be locked by the caller. */
232273
int landlock_insert_rule(struct landlock_ruleset *const ruleset,
233-
struct landlock_object *const object,
274+
const struct landlock_id id,
234275
const access_mask_t access)
235276
{
236277
struct landlock_layer layers[] = { {
@@ -240,7 +281,7 @@ int landlock_insert_rule(struct landlock_ruleset *const ruleset,
240281
} };
241282

242283
build_check_layer();
243-
return insert_rule(ruleset, object, &layers, ARRAY_SIZE(layers));
284+
return insert_rule(ruleset, id, &layers, ARRAY_SIZE(layers));
244285
}
245286

246287
static inline void get_hierarchy(struct landlock_hierarchy *const hierarchy)
@@ -263,6 +304,7 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
263304
struct landlock_ruleset *const src)
264305
{
265306
struct landlock_rule *walker_rule, *next_rule;
307+
struct rb_root *src_root;
266308
int err = 0;
267309

268310
might_sleep();
@@ -273,6 +315,10 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
273315
if (WARN_ON_ONCE(!dst || !dst->hierarchy))
274316
return -EINVAL;
275317

318+
src_root = get_root(src, LANDLOCK_KEY_INODE);
319+
if (IS_ERR(src_root))
320+
return PTR_ERR(src_root);
321+
276322
/* Locks @dst first because we are its only owner. */
277323
mutex_lock(&dst->lock);
278324
mutex_lock_nested(&src->lock, SINGLE_DEPTH_NESTING);
@@ -285,11 +331,15 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
285331
dst->access_masks[dst->num_layers - 1] = src->access_masks[0];
286332

287333
/* Merges the @src tree. */
288-
rbtree_postorder_for_each_entry_safe(walker_rule, next_rule, &src->root,
334+
rbtree_postorder_for_each_entry_safe(walker_rule, next_rule, src_root,
289335
node) {
290336
struct landlock_layer layers[] = { {
291337
.level = dst->num_layers,
292338
} };
339+
const struct landlock_id id = {
340+
.key = walker_rule->key,
341+
.type = LANDLOCK_KEY_INODE,
342+
};
293343

294344
if (WARN_ON_ONCE(walker_rule->num_layers != 1)) {
295345
err = -EINVAL;
@@ -300,8 +350,8 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
300350
goto out_unlock;
301351
}
302352
layers[0].access = walker_rule->layers[0].access;
303-
err = insert_rule(dst, walker_rule->object, &layers,
304-
ARRAY_SIZE(layers));
353+
354+
err = insert_rule(dst, id, &layers, ARRAY_SIZE(layers));
305355
if (err)
306356
goto out_unlock;
307357
}
@@ -316,21 +366,30 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,
316366
struct landlock_ruleset *const child)
317367
{
318368
struct landlock_rule *walker_rule, *next_rule;
369+
struct rb_root *parent_root;
319370
int err = 0;
320371

321372
might_sleep();
322373
if (!parent)
323374
return 0;
324375

376+
parent_root = get_root(parent, LANDLOCK_KEY_INODE);
377+
if (IS_ERR(parent_root))
378+
return PTR_ERR(parent_root);
379+
325380
/* Locks @child first because we are its only owner. */
326381
mutex_lock(&child->lock);
327382
mutex_lock_nested(&parent->lock, SINGLE_DEPTH_NESTING);
328383

329384
/* Copies the @parent tree. */
330385
rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
331-
&parent->root, node) {
332-
err = insert_rule(child, walker_rule->object,
333-
&walker_rule->layers,
386+
parent_root, node) {
387+
const struct landlock_id id = {
388+
.key = walker_rule->key,
389+
.type = LANDLOCK_KEY_INODE,
390+
};
391+
392+
err = insert_rule(child, id, &walker_rule->layers,
334393
walker_rule->num_layers);
335394
if (err)
336395
goto out_unlock;
@@ -362,8 +421,9 @@ static void free_ruleset(struct landlock_ruleset *const ruleset)
362421
struct landlock_rule *freeme, *next;
363422

364423
might_sleep();
365-
rbtree_postorder_for_each_entry_safe(freeme, next, &ruleset->root, node)
366-
free_rule(freeme);
424+
rbtree_postorder_for_each_entry_safe(freeme, next, &ruleset->root_inode,
425+
node)
426+
free_rule(freeme, LANDLOCK_KEY_INODE);
367427
put_hierarchy(ruleset->hierarchy);
368428
kfree(ruleset);
369429
}
@@ -454,20 +514,23 @@ landlock_merge_ruleset(struct landlock_ruleset *const parent,
454514
*/
455515
const struct landlock_rule *
456516
landlock_find_rule(const struct landlock_ruleset *const ruleset,
457-
const struct landlock_object *const object)
517+
const struct landlock_id id)
458518
{
519+
const struct rb_root *root;
459520
const struct rb_node *node;
460521

461-
if (!object)
522+
root = get_root((struct landlock_ruleset *)ruleset, id.type);
523+
if (IS_ERR(root))
462524
return NULL;
463-
node = ruleset->root.rb_node;
525+
node = root->rb_node;
526+
464527
while (node) {
465528
struct landlock_rule *this =
466529
rb_entry(node, struct landlock_rule, node);
467530

468-
if (this->object == object)
531+
if (this->key.data == id.key.data)
469532
return this;
470-
if (this->object < object)
533+
if (this->key.data < id.key.data)
471534
node = node->rb_right;
472535
else
473536
node = node->rb_left;

0 commit comments

Comments
 (0)