Skip to content

Commit de5817b

Browse files
committed
Merge tag 'landlock-6.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux
Pull landlock updates from Mickaël Salaün: "This mostly factors out some Landlock code and prepares for upcoming audit support. Because files with invalid modes might be visible after filesystem corruption, Landlock now handles those weird files too. A few sample and test issues are also fixed" * tag 'landlock-6.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux: selftests/landlock: Add layout1.umount_sandboxer tests selftests/landlock: Add wrappers.h selftests/landlock: Fix error message landlock: Optimize file path walks and prepare for audit support selftests/landlock: Add test to check partial access in a mount tree landlock: Align partial refer access checks with final ones landlock: Simplify initially denied access rights landlock: Move access types landlock: Factor out check_access_path() selftests/landlock: Fix build with non-default pthread linking landlock: Use scoped guards for ruleset in landlock_add_rule() landlock: Use scoped guards for ruleset landlock: Constify get_mode_access() landlock: Handle weird files samples/landlock: Fix possible NULL dereference in parse_path() selftests/landlock: Remove unused macros in ptrace_test.c
2 parents 37b33c6 + 2a794ee commit de5817b

File tree

14 files changed

+489
-195
lines changed

14 files changed

+489
-195
lines changed

samples/landlock/sandboxer.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ static int parse_path(char *env_path, const char ***const path_list)
9191
}
9292
}
9393
*path_list = malloc(num_paths * sizeof(**path_list));
94+
if (!*path_list)
95+
return -1;
96+
9497
for (i = 0; i < num_paths; i++)
9598
(*path_list)[i] = strsep(&env_path, ENV_DELIMITER);
9699

@@ -127,6 +130,10 @@ static int populate_ruleset_fs(const char *const env_var, const int ruleset_fd,
127130
env_path_name = strdup(env_path_name);
128131
unsetenv(env_var);
129132
num_paths = parse_path(env_path_name, &path_list);
133+
if (num_paths < 0) {
134+
fprintf(stderr, "Failed to allocate memory\n");
135+
goto out_free_name;
136+
}
130137
if (num_paths == 1 && path_list[0][0] == '\0') {
131138
/*
132139
* Allows to not use all possible restrictions (e.g. use

security/landlock/access.h

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
/*
3+
* Landlock LSM - Access types and helpers
4+
*
5+
* Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net>
6+
* Copyright © 2018-2020 ANSSI
7+
* Copyright © 2024-2025 Microsoft Corporation
8+
*/
9+
10+
#ifndef _SECURITY_LANDLOCK_ACCESS_H
11+
#define _SECURITY_LANDLOCK_ACCESS_H
12+
13+
#include <linux/bitops.h>
14+
#include <linux/build_bug.h>
15+
#include <linux/kernel.h>
16+
#include <uapi/linux/landlock.h>
17+
18+
#include "limits.h"
19+
20+
/*
21+
* All access rights that are denied by default whether they are handled or not
22+
* by a ruleset/layer. This must be ORed with all ruleset->access_masks[]
23+
* entries when we need to get the absolute handled access masks, see
24+
* landlock_upgrade_handled_access_masks().
25+
*/
26+
/* clang-format off */
27+
#define _LANDLOCK_ACCESS_FS_INITIALLY_DENIED ( \
28+
LANDLOCK_ACCESS_FS_REFER)
29+
/* clang-format on */
30+
31+
typedef u16 access_mask_t;
32+
33+
/* Makes sure all filesystem access rights can be stored. */
34+
static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_FS);
35+
/* Makes sure all network access rights can be stored. */
36+
static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_NET);
37+
/* Makes sure all scoped rights can be stored. */
38+
static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_SCOPE);
39+
/* Makes sure for_each_set_bit() and for_each_clear_bit() calls are OK. */
40+
static_assert(sizeof(unsigned long) >= sizeof(access_mask_t));
41+
42+
/* Ruleset access masks. */
43+
struct access_masks {
44+
access_mask_t fs : LANDLOCK_NUM_ACCESS_FS;
45+
access_mask_t net : LANDLOCK_NUM_ACCESS_NET;
46+
access_mask_t scope : LANDLOCK_NUM_SCOPE;
47+
};
48+
49+
union access_masks_all {
50+
struct access_masks masks;
51+
u32 all;
52+
};
53+
54+
/* Makes sure all fields are covered. */
55+
static_assert(sizeof(typeof_member(union access_masks_all, masks)) ==
56+
sizeof(typeof_member(union access_masks_all, all)));
57+
58+
typedef u16 layer_mask_t;
59+
60+
/* Makes sure all layers can be checked. */
61+
static_assert(BITS_PER_TYPE(layer_mask_t) >= LANDLOCK_MAX_NUM_LAYERS);
62+
63+
/* Upgrades with all initially denied by default access rights. */
64+
static inline struct access_masks
65+
landlock_upgrade_handled_access_masks(struct access_masks access_masks)
66+
{
67+
/*
68+
* All access rights that are denied by default whether they are
69+
* explicitly handled or not.
70+
*/
71+
if (access_masks.fs)
72+
access_masks.fs |= _LANDLOCK_ACCESS_FS_INITIALLY_DENIED;
73+
74+
return access_masks;
75+
}
76+
77+
#endif /* _SECURITY_LANDLOCK_ACCESS_H */

security/landlock/fs.c

Lines changed: 59 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include <uapi/linux/fiemap.h>
3737
#include <uapi/linux/landlock.h>
3838

39+
#include "access.h"
3940
#include "common.h"
4041
#include "cred.h"
4142
#include "fs.h"
@@ -388,14 +389,6 @@ static bool is_nouser_or_private(const struct dentry *dentry)
388389
unlikely(IS_PRIVATE(d_backing_inode(dentry))));
389390
}
390391

391-
static access_mask_t
392-
get_handled_fs_accesses(const struct landlock_ruleset *const domain)
393-
{
394-
/* Handles all initially denied by default access rights. */
395-
return landlock_union_access_masks(domain).fs |
396-
LANDLOCK_ACCESS_FS_INITIALLY_DENIED;
397-
}
398-
399392
static const struct access_masks any_fs = {
400393
.fs = ~0,
401394
};
@@ -572,6 +565,12 @@ static void test_no_more_access(struct kunit *const test)
572565
#undef NMA_TRUE
573566
#undef NMA_FALSE
574567

568+
static bool is_layer_masks_allowed(
569+
layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
570+
{
571+
return !memchr_inv(layer_masks, 0, sizeof(*layer_masks));
572+
}
573+
575574
/*
576575
* Removes @layer_masks accesses that are not requested.
577576
*
@@ -589,7 +588,8 @@ scope_to_request(const access_mask_t access_request,
589588

590589
for_each_clear_bit(access_bit, &access_req, ARRAY_SIZE(*layer_masks))
591590
(*layer_masks)[access_bit] = 0;
592-
return !memchr_inv(layer_masks, 0, sizeof(*layer_masks));
591+
592+
return is_layer_masks_allowed(layer_masks);
593593
}
594594

595595
#ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
@@ -778,16 +778,21 @@ static bool is_access_to_paths_allowed(
778778
if (WARN_ON_ONCE(domain->num_layers < 1 || !layer_masks_parent1))
779779
return false;
780780

781+
allowed_parent1 = is_layer_masks_allowed(layer_masks_parent1);
782+
781783
if (unlikely(layer_masks_parent2)) {
782784
if (WARN_ON_ONCE(!dentry_child1))
783785
return false;
786+
787+
allowed_parent2 = is_layer_masks_allowed(layer_masks_parent2);
788+
784789
/*
785790
* For a double request, first check for potential privilege
786791
* escalation by looking at domain handled accesses (which are
787792
* a superset of the meaningful requested accesses).
788793
*/
789794
access_masked_parent1 = access_masked_parent2 =
790-
get_handled_fs_accesses(domain);
795+
landlock_union_access_masks(domain).fs;
791796
is_dom_check = true;
792797
} else {
793798
if (WARN_ON_ONCE(dentry_child1 || dentry_child2))
@@ -847,31 +852,39 @@ static bool is_access_to_paths_allowed(
847852
child1_is_directory, layer_masks_parent2,
848853
layer_masks_child2,
849854
child2_is_directory))) {
850-
allowed_parent1 = scope_to_request(
851-
access_request_parent1, layer_masks_parent1);
852-
allowed_parent2 = scope_to_request(
853-
access_request_parent2, layer_masks_parent2);
854-
855-
/* Stops when all accesses are granted. */
856-
if (allowed_parent1 && allowed_parent2)
857-
break;
858-
859855
/*
860856
* Now, downgrades the remaining checks from domain
861857
* handled accesses to requested accesses.
862858
*/
863859
is_dom_check = false;
864860
access_masked_parent1 = access_request_parent1;
865861
access_masked_parent2 = access_request_parent2;
862+
863+
allowed_parent1 =
864+
allowed_parent1 ||
865+
scope_to_request(access_masked_parent1,
866+
layer_masks_parent1);
867+
allowed_parent2 =
868+
allowed_parent2 ||
869+
scope_to_request(access_masked_parent2,
870+
layer_masks_parent2);
871+
872+
/* Stops when all accesses are granted. */
873+
if (allowed_parent1 && allowed_parent2)
874+
break;
866875
}
867876

868877
rule = find_rule(domain, walker_path.dentry);
869-
allowed_parent1 = landlock_unmask_layers(
870-
rule, access_masked_parent1, layer_masks_parent1,
871-
ARRAY_SIZE(*layer_masks_parent1));
872-
allowed_parent2 = landlock_unmask_layers(
873-
rule, access_masked_parent2, layer_masks_parent2,
874-
ARRAY_SIZE(*layer_masks_parent2));
878+
allowed_parent1 = allowed_parent1 ||
879+
landlock_unmask_layers(
880+
rule, access_masked_parent1,
881+
layer_masks_parent1,
882+
ARRAY_SIZE(*layer_masks_parent1));
883+
allowed_parent2 = allowed_parent2 ||
884+
landlock_unmask_layers(
885+
rule, access_masked_parent2,
886+
layer_masks_parent2,
887+
ARRAY_SIZE(*layer_masks_parent2));
875888

876889
/* Stops when a rule from each layer grants access. */
877890
if (allowed_parent1 && allowed_parent2)
@@ -895,8 +908,10 @@ static bool is_access_to_paths_allowed(
895908
* access to internal filesystems (e.g. nsfs, which is
896909
* reachable through /proc/<pid>/ns/<namespace>).
897910
*/
898-
allowed_parent1 = allowed_parent2 =
899-
!!(walker_path.mnt->mnt_flags & MNT_INTERNAL);
911+
if (walker_path.mnt->mnt_flags & MNT_INTERNAL) {
912+
allowed_parent1 = true;
913+
allowed_parent2 = true;
914+
}
900915
break;
901916
}
902917
parent_dentry = dget_parent(walker_path.dentry);
@@ -908,39 +923,29 @@ static bool is_access_to_paths_allowed(
908923
return allowed_parent1 && allowed_parent2;
909924
}
910925

911-
static int check_access_path(const struct landlock_ruleset *const domain,
912-
const struct path *const path,
913-
access_mask_t access_request)
914-
{
915-
layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};
916-
917-
access_request = landlock_init_layer_masks(
918-
domain, access_request, &layer_masks, LANDLOCK_KEY_INODE);
919-
if (is_access_to_paths_allowed(domain, path, access_request,
920-
&layer_masks, NULL, 0, NULL, NULL))
921-
return 0;
922-
return -EACCES;
923-
}
924-
925926
static int current_check_access_path(const struct path *const path,
926-
const access_mask_t access_request)
927+
access_mask_t access_request)
927928
{
928929
const struct landlock_ruleset *const dom = get_current_fs_domain();
930+
layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};
929931

930932
if (!dom)
931933
return 0;
932-
return check_access_path(dom, path, access_request);
934+
935+
access_request = landlock_init_layer_masks(
936+
dom, access_request, &layer_masks, LANDLOCK_KEY_INODE);
937+
if (is_access_to_paths_allowed(dom, path, access_request, &layer_masks,
938+
NULL, 0, NULL, NULL))
939+
return 0;
940+
941+
return -EACCES;
933942
}
934943

935-
static access_mask_t get_mode_access(const umode_t mode)
944+
static __attribute_const__ access_mask_t get_mode_access(const umode_t mode)
936945
{
937946
switch (mode & S_IFMT) {
938947
case S_IFLNK:
939948
return LANDLOCK_ACCESS_FS_MAKE_SYM;
940-
case 0:
941-
/* A zero mode translates to S_IFREG. */
942-
case S_IFREG:
943-
return LANDLOCK_ACCESS_FS_MAKE_REG;
944949
case S_IFDIR:
945950
return LANDLOCK_ACCESS_FS_MAKE_DIR;
946951
case S_IFCHR:
@@ -951,9 +956,12 @@ static access_mask_t get_mode_access(const umode_t mode)
951956
return LANDLOCK_ACCESS_FS_MAKE_FIFO;
952957
case S_IFSOCK:
953958
return LANDLOCK_ACCESS_FS_MAKE_SOCK;
959+
case S_IFREG:
960+
case 0:
961+
/* A zero mode translates to S_IFREG. */
954962
default:
955-
WARN_ON_ONCE(1);
956-
return 0;
963+
/* Treats weird files as regular files. */
964+
return LANDLOCK_ACCESS_FS_MAKE_REG;
957965
}
958966
}
959967

@@ -1414,11 +1422,7 @@ static int hook_path_mknod(const struct path *const dir,
14141422
struct dentry *const dentry, const umode_t mode,
14151423
const unsigned int dev)
14161424
{
1417-
const struct landlock_ruleset *const dom = get_current_fs_domain();
1418-
1419-
if (!dom)
1420-
return 0;
1421-
return check_access_path(dom, dir, get_mode_access(mode));
1425+
return current_check_access_path(dir, get_mode_access(mode));
14221426
}
14231427

14241428
static int hook_path_symlink(const struct path *const dir,

security/landlock/fs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/init.h>
1414
#include <linux/rcupdate.h>
1515

16+
#include "access.h"
1617
#include "ruleset.h"
1718
#include "setup.h"
1819

0 commit comments

Comments
 (0)